Search code examples
canvasthree.jsglsltextures

THREE.JS | GLSL Using canvas as shader uniform texture


I am trying to use canvas as uniform texture:

uniforms = {
    
    canvasTexture: { type: "t", value: new THREE.Texture(canvas) }
        
};

also have tried:

uniforms = {
    
    canvasTexture: { type: "t", value: new THREE.CanvasTexture(canvas) }
        
};

But I don't see anything but black screen.

I have also trying to update uniform at animate():

mesh.material.uniforms.canvasTexture.needsUpdate = true;

Without any success. Any siggestions?

 
var renderer, scene, camera, canvas, ctx, mesh, canvasTexture;
    
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild( renderer.domElement );
renderer.setSize( window.innerWidth, window.innerHeight );
    
camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );

scene = new THREE.Scene();

var canvas = document.createElement("canvas");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
ctx = canvas.getContext("2d");

var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
    
uniforms = {
    
    canvasTexture: { type: "t", value: new THREE.Texture(canvas) }
        
};

var material = new THREE.ShaderMaterial( {

    uniforms: uniforms,
    vertexShader: document.getElementById( "vs-canvasTest" ).textContent,
    fragmentShader: document.getElementById( "fs-canvasTest" ).textContent,

} );

mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
    
window.addEventListener("resize", onWindowResize, false);

animate();
    
function onWindowResize() { 
    
    renderer.setSize( window.innerWidth, window.innerHeight );
    
    canvas.height = window.innerHeight;
    canvas.width = window.innerWidth;
        
}
    
function animate() {
    
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
    
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "#FF0000";
    ctx.fill();
    
    mesh.material.uniforms.canvasTexture.needsUpdate = true;

}
body { margin: 0; }
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

    <script src="https://unpkg.com/[email protected]/build/three.min.js"></script>

</head>
<body>
    
<script id="vs-canvasTest" type="x-shader/x-vertex">

    varying vec2 vUv;

    void main() {

        vUv = uv;
        gl_Position = vec4( position, 1.0 );

    }

</script>

<script id="fs-canvasTest" type="x-shader/x-fragment">

    varying vec2 vUv;
    uniform sampler2D canvasTexture;

    void main() {

        gl_FragColor = texture2D(canvasTexture, vUv);

    }


</script>

</body>  
</html>


Solution

  • It's a tiny mistake, I also make it all the time. Here's what you have now:

    mesh.material.uniforms.canvasTexture.needsUpdate = true;

    Here's what you need:

    mesh.material.uniforms.canvasTexture.value.needsUpdate = true;

    You just need .value before .needsUpdate so you're targeting the texture instead of the uniform that contains the texture.

    var renderer, scene, camera, canvas, ctx, mesh, tex;
        
    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    document.body.appendChild( renderer.domElement );
    renderer.setSize( window.innerWidth, window.innerHeight );
        
    camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
    
    scene = new THREE.Scene();
    
    var canvas = document.createElement("canvas");
    canvas.height = window.innerHeight;
    canvas.width = window.innerWidth;
    ctx = canvas.getContext("2d");
    
    var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
    
    uniforms = {
        
        canvasTexture: { value: new THREE.Texture(canvas) }
            
    };
    
    var material = new THREE.ShaderMaterial( {
    
        uniforms: uniforms,
        vertexShader: document.getElementById( "vs-canvasTest" ).textContent,
        fragmentShader: document.getElementById( "fs-canvasTest" ).textContent,
    
    } );
    
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
        
    window.addEventListener("resize", onWindowResize, false);
    
    animate();
        
    function onWindowResize() { 
        
        renderer.setSize( window.innerWidth, window.innerHeight );
        
        canvas.height = window.innerHeight;
        canvas.width = window.innerWidth;
            
    }
        
    function animate() {
        
        requestAnimationFrame( animate );
        renderer.render( scene, camera );
        
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "#FF0000";
        ctx.fill();
        
        mesh.material.uniforms.canvasTexture.value.needsUpdate = true;
    
    }
    body { margin: 0; }
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>Test</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    
        <script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
    
    </head>
    <body>
        
    <script id="vs-canvasTest" type="x-shader/x-vertex">
    
        varying vec2 vUv;
    
        void main() {
    
            vUv = uv;
            gl_Position = vec4( position, 1.0 );
    
        }
    
    </script>
    
    <script id="fs-canvasTest" type="x-shader/x-fragment">
    
        varying vec2 vUv;
        uniform sampler2D canvasTexture;
    
        void main() {
    
            gl_FragColor = texture2D(canvasTexture, vUv);
    
        }
    
    
    </script>
    
    </body>  
    </html>