Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating Canvas Billboard in THREE.js

I'm trying to animate a Canvas-based texture that is mapped onto a plane, like a billboard. I've made a point of including material.needsUpdate & texture.needsUpdate, but I'm still unable to get the texture to come to life. I've also included a rotating cube just so I know the animation routine is functioning on some level.

Here is the code:

<body>
  <script src="http://mrdoob.github.com/three.js/build/three.min.js"></script>
  <script>
    if (window.innerWidth === 0) {
        window.innerWidth = parent.innerWidth;
        window.innerHeight = parent.innerHeight;
    }

    var camera, scene, renderer;
    var mesh, geometry, material;
    var light, sign, animTex;
    var canvas, context;

    init();
    animate();

    function init() {

        camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1200);
        camera.position.z = 700;

        scene = new THREE.Scene();

        material = new THREE.MeshLambertMaterial(
            {
                color: 0x885522,
                wireframe: false,
                overdraw: false
            });

        geometry = new THREE.CubeGeometry(80, 120, 100, 1, 1, 1);
        mesh = new THREE.Mesh(geometry, material);

        sign = createSign();

        light = new THREE.DirectionalLight(0xFFFFFF, 3.0);
        light.position = new THREE.Vector3(5, 10, 7);
        light.target = new THREE.Vector3(0, 0, 0);

        scene.add(mesh);
        scene.add(sign);
        scene.add(light);

        renderer = new THREE.CanvasRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
    }

    function createSign() {
        canvas = document.createElement("canvas");
        context = canvas.getContext("2d");
        canvas.width = 200;
        canvas.height = 200;
        context = canvas.getContext("2d");
        var texture = new THREE.Texture(canvas);
        texture.needsUpdate = true;
        var material = new THREE.MeshBasicMaterial({ map : texture, overdraw: true });
        material.needsUpdate = true;
        var mesh = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), material);
        mesh.doubleSided = true;
        return mesh;
    }

    function animate() {

        var time = Date.now()*0.01;
        var sinTime = Math.sin(time * 0.05) * 100;
        var cosTime = Math.cos(time * 0.05) * 100;

        mesh.rotation.y = sinTime*0.01;

        requestAnimationFrame(animate);
        context.fillStyle = "black";
        context.fillRect(0, 0, canvas.width, canvas.height);
        context.fillStyle = "white";
        context.fillRect((canvas.width/2) + sinTime, (canvas.height/2) + cosTime, 20, 20)
        renderer.render(scene, camera);
    }

This runs, but I can't seem to get the Canvas texture material to update. What have I overlooked?

like image 541
Jack Avatar asked Sep 16 '12 21:09

Jack


2 Answers

Place this right before your render() call:

sign.material.map.needsUpdate = true;
like image 163
WestLangley Avatar answered Sep 23 '22 07:09

WestLangley


The needsUpdate flag is reset (to false) every time the texture is used (every render loop), so it needs to be set to true in the render loop (before the render call, or it'll be a frame off). So in your example, put sign.material.map.needsUpdate = true before renderer.render( scene, camera ). texture.needsUpdate = true and material.needsUpdate = true are not needed.

Also, you only need to set the needsUpdate flag on the texture, as the material properties are not changing.

like image 45
boxtrain Avatar answered Sep 21 '22 07:09

boxtrain