使用2D画布作为MeshBasicMaterial纹理的源,ThreeJS纹理始终是黑色的?

时间:2019-12-30 22:55:42

标签: canvas three.js textures

我正在尝试显示一个立方体,其中一侧显示我在其上绘制一些文本的图像。我尝试了几种方法,但是图像和文本始终总是被渲染为黑色。相反,如果我只是简单地使用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();

1 个答案:

答案 0 :(得分:1)

代码有2个问题

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

  2. 将文本绘制到中心引用textSize.height中的计算。没有这样的值,所以y最终变成了NaN并且没有绘制文本。

请注意,您可以通过要求画布来使文本居中。

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

将文字居中绘制位置

对于等待图像加载,您需要决定如何构建代码以等待。您可以等待图像加载后再开始。或者,您可以创建一个画布和一个CanvasTexture。将其传递到多维数据集材质中,然后在图像下载完成后更新该画布,并将CanvasTexture的{​​{1}}设置为true。

needsUpdate
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();