【问题标题】:ThreeJS texture is always black using 2D canvas as source for MeshBasicMaterial texture?使用 2D 画布作为 MeshBasicMaterial 纹理的来源时,ThreeJS 纹理始终是黑色的?
【发布时间】:2020-04-19 14:32:29
【问题描述】:

我正在尝试显示一个立方体,其一侧显示我在其上绘制一些文本的图像。我尝试了几种方法,但图像和文本总是最终呈现为黑色。相反,如果我只是使用 ThreeJS 文本加载器之类的东西直接加载图像,我可以很好地看到图像(当然上面没有文本)。我的代码出了什么问题,以至于我在初始化期间看不到所需的图像以及我在上面打印的文本?

var scene, camera, renderer;
var WIDTH  = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
var cube = null;

var imgBackSide = null;

function init() {
    // Create a document element to house the back side image for cards.
    imgBackSide = document.createElement('img');
    imgBackSide.src = '/images/cards/card-back-side-400x400.png';

    scene = new THREE.Scene();

    initCamera();
    initRenderer();
    initCube();

    document.body.appendChild(renderer.domElement);
}

function initCamera() {
    camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
    camera.position.set(0, 3.5, 5);
    camera.lookAt(scene.position);
}

function initRenderer() {
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(WIDTH, HEIGHT);
}

function canvasDrawText(ctx, canvas, text, x, y) {
    // if x isn't provided
    if ( x === undefined || x === null ){
        let textSize    = ctx.measureText(text);
        x = (canvas.width - textSize.width) / 2;
    }

    // if y isn't provided
    if ( y === undefined || y === null ){
        let textSize    = ctx.measureText(text);
        y = (canvas.height - textSize.height) / 2;
    }

    // actually draw the text
    // ctx.fillStyle = fillStyle;
    ctx.fillText(text, x, y);
}

function newCardBacksideTexture(cardBackSideText) {
    // Create an IMG element to hold the card back side image and load it.
    let canvas = document.createElement('canvas');
    let ctx = canvas.getContext('2d');

    canvas.width = 400;
    canvas.height = 400;

    ctx.drawImage(imgBackSide, 0, 0);

    // Draw the card label on top of the background.
    ctx.font    = 'bolder 90px Verdana';
    canvasDrawText(ctx, canvas, cardBackSideText);
    // dynamicTexture.texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

    return new THREE.CanvasTexture(canvas);
}

function initCube() {
    let cubeGeometry = new THREE.BoxGeometry(2, 0.1, 2);
    let loader = new THREE.TextureLoader();

    let dummy = loader.load("/images/cards/white-square-400x400.png");

    let materialArray = [
        new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
        new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
        // Card face.
        new THREE.MeshBasicMaterial( { map: loader.load('/images/card-face.jpg') } ),
        // Card back side.
        new THREE.MeshBasicMaterial( { map: newCardBacksideTexture('BACK SIDE OF CARD') } ),
        //
        new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
        new THREE.MeshBasicMaterial( { map: loader.load('/images/cards/white-square-400x400.png') } ),
    ];

    cube = new THREE.Mesh( cubeGeometry, materialArray );
    scene.add(cube);
}

function rotateCube() {
    cube.rotation.x -= SPEED * 2;
    cube.rotation.y -= SPEED;
    cube.rotation.z -= SPEED * 3;
}

function render() {
    requestAnimationFrame(render);
    rotateCube();
    renderer.render(scene, camera);
}

init();
render();

【问题讨论】:

    标签: canvas three.js textures


    【解决方案1】:

    代码有 2 个问题

    1. 它不会等待您在画布上绘制的图像加载,因此当它尝试绘制该图像时不会绘制任何内容。

    2. 在中心绘制文本的计算参考textSize.height。没有这样的值,所以 y 最终成为 NaN 并且没有绘制文本。

    请注意,您可以通过让画布来更轻松地使文本居中。

    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    

    将文本在绘图位置居中

    至于等待图像加载,您需要决定如何构建代码以等待。您可以在开始之前等待图像加载。或者您可以创建一个画布和一个CanvasTexture。将其传递到立方体材质中,然后在图像下载完成后更新该画布并将CanvasTexture 的needsUpdate 设置为true。

    var scene, camera, renderer;
    var WIDTH  = window.innerWidth;
    var HEIGHT = window.innerHeight;
    var SPEED = 0.01;
    var cube = null;
    
    var imgBackSide = null;
    
    function init() {
        scene = new THREE.Scene();
    
        initCamera();
        initRenderer();
        initCube();
    
        document.body.appendChild(renderer.domElement);
    }
    
    function initCamera() {
        camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
        camera.position.set(0, 3.5, 5);
        camera.lookAt(scene.position);
    }
    
    function initRenderer() {
        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(WIDTH, HEIGHT);
    }
    
    function canvasDrawText(ctx, canvas, text, x, y) {
        // if x isn't provided
        if (x === undefined || x === null) {
          x = canvas.width / 2;
          ctx.textAlign = 'center';
        } else {
          ctx.textAlign = 'left';
        }
        
        // if y isn't provided
        if ( y === undefined || y === null ) {
          y = canvas.height / 2;
          ctx.textBaseline = 'middle';
        } else {
          ctx.textBaseline = 'alphabetic';
        }
    
        // actually draw the text
        // ctx.fillStyle = fillStyle;
        ctx.fillText(text, x, y);
    }
    
    function newCardBacksideTexture(cardBackSideText) {
        // Create an IMG element to hold the card back side image and load it.
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');
    
        canvas.width = 400;
        canvas.height = 400;
        
        const canvasTex = new THREE.CanvasTexture(canvas);
    
        // Create a document element to house the back side image for cards.
        imgBackSide = document.createElement('img');
        imgBackSide.onload = () => {
          ctx.drawImage(imgBackSide, 0, 0);
    
          // Draw the card label on top of the background.
          ctx.font    = 'bolder 90px Verdana';
          canvasDrawText(ctx, canvas, cardBackSideText);
          // dynamicTexture.texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
          
          canvasTex.needsUpdate = true;
        };
        
        // this is needed only if the image
        // comes from another domain
        imgBackSide.crossOrigin = "anonymous";
        
        imgBackSide.src = 'https://i.imgur.com/TSiyiJv.jpg';
    
        return canvasTex;
    }
    
    function initCube() {
        let cubeGeometry = new THREE.BoxGeometry(2, 0.1, 2);
        let loader = new THREE.TextureLoader();
    
        let dummy = loader.load("https://i.imgur.com/ZKMnXce.png");
    
        let materialArray = [
            new THREE.MeshBasicMaterial( { map: loader.load('https://i.imgur.com/ZKMnXce.png') } ),
            new THREE.MeshBasicMaterial( { map: loader.load('https://i.imgur.com/ZKMnXce.png') } ),
            // Card face.
            new THREE.MeshBasicMaterial( { map: loader.load('https://i.imgur.com/ZKMnXce.png') } ),
            // Card back side.
            new THREE.MeshBasicMaterial( { map: newCardBacksideTexture('BACK SIDE OF CARD') } ),
            //
            new THREE.MeshBasicMaterial( { map: loader.load('https://i.imgur.com/ZKMnXce.png') } ),
            new THREE.MeshBasicMaterial( { map: loader.load('https://i.imgur.com/ZKMnXce.png') } ),
        ];
    
        cube = new THREE.Mesh( cubeGeometry, materialArray );
        scene.add(cube);
    }
    
    function rotateCube() {
        cube.rotation.x -= SPEED * 2;
        cube.rotation.y -= SPEED;
        cube.rotation.z -= SPEED * 3;
    }
    
    function render() {
        requestAnimationFrame(render);
        rotateCube();
        renderer.render(scene, camera);
    }
    
    init();
    render();
    <script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>

    【讨论】:

      猜你喜欢
      • 2020-04-14
      • 2020-11-17
      • 2013-04-28
      • 1970-01-01
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多