Shader on 2d canvas

Hi I have tried for a week to get this code to work now but I only get a white screen when trying to move all the canvas and texture. Maybe I can find some help here.

I marked the few lines i suspect might be good to move out of the main loop with β€œ!!!”

So basically i just want to avoide re-creating a canvas, which i suspect is shared, maybe it is possible to use one src and one dst i am not sure as I am still new.

TIA!

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <style>
    canvas {
      display: block;
      margin: 0 auto;
      image-rendering: pixelated;
      transform: scale(3);
      transform-origin: 0 0;
      width: 320px;
      height: 200px;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width=320 height=200></canvas>
  <script>  
  /*
  const renderer = new WebGLRenderer();
  const rt = new WebGLRenderTarget();
  animate() {
	// Set rt as the destination of the render
	renderer.setRenderTarget(rt);
	renderer.render(scene, camera);
	// Use result of rt as input for next pass
	material.inputTexture = rt.texture;
	// Passing null sets the canvas as the destination
	renderer.setRenderTarget(null);
	renderer.render(postScene, postCamera); 
    You can keep chaining renderTargets one after the other as needed.
    Make sure you use a different scene for each render pass, otherwise WebGL will stop the operation to prevent an infinite feedback loop.
  */
    const renderer = new THREE.WebGLRenderer({
      canvas: canvas,
      antialias: false
    });
    const scene = new THREE.Scene();
    const camera = new THREE.OrthographicCamera(
      -canvas.width / 2,
      canvas.width / 2,
      canvas.height / 2,
      -canvas.height / 2,
      0, 1000
    );
    camera.position.z = 1;
    const geometry = new THREE.PlaneBufferGeometry(canvas.width, canvas.height);
    const texture = new THREE.CanvasTexture(generateCanvasTexture()); // SRC !!!!!!!!!
    const material = new THREE.ShaderMaterial({
      uniforms: {
        u_texture: {
          value: texture
        }
      },
      vertexShader: `
        varying vec2 v_uv;
        void main() {
          v_uv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform sampler2D u_texture;
        varying vec2 v_uv;
        void main() {
          vec4 color = texture2D(u_texture, v_uv);
          color.g = color.r;
          color.r = 0.0;
          gl_FragColor = color;
        }
      `
    });
    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    let xx = 0;
    let fps = 0;
    let lastFrameTime = 0;
    animate(0);
// ---------------------------------
    function animate(timestamp) {
      requestAnimationFrame(animate);
      fps++;
      if (timestamp >= lastFrameTime + 1000) {
        console.log(`FPS: ${fps}`);
        fps = 0;
        lastFrameTime = timestamp;
      }
      const canvas = generateCanvasTexture(); // !!!!!!!!!!!!!!!
      const ctx = canvas.getContext('2d');  // !!!!!!!!!!!!!!!!!
      ctx.fillStyle = 'red';
      xx = (xx + 1) % 200;
      const x = xx;
      const y = xx;
      ctx.fillRect(x + 10, y + 10, 10, 10);
      mesh.material.uniforms.u_texture.value = new THREE.CanvasTexture(canvas); // SRC CANVAS
      renderer.setPixelRatio(1);
      renderer.render(scene, camera);
    }
    function generateCanvasTexture() {
      const canvas = document.createElement("canvas"); // !!!!!
      const ctx = canvas.getContext('2d'); // !!!!!!!!!!!!!!!!!
      canvas.width = 320;
      canvas.height = 200;
      ctx.fillStyle = 'black';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      return canvas;
    }
  </script>
</body>
</html>

Creating DOM canvas element inside the animation loop is not a good idea. When I try to fix your code, I see a green square moving diagonally inside black area (from top-left to bottom-right corner). Is this what it is supposed to look like?

https://codepen.io/boytchev/full/MWqKYGg

image

Hi, yes that’s correct, I wanted to draw the canvas and apply a shader for the full canvas in 60 fps. That was amazingly quick you saved my sanity as I was trying to understand what I was doing wrong for days.

You’re correct about the DOM are there any other suggestion to watch our for when using threejs for 2dcanvas and shaders? I was considering Babylonjs but I have always been curious about threejs. I usually code vanilla JavaScript and webgl was a bit too much for me, so I thought this was a good time to start use threejs.

Thanks again.

The final goal iis to try to apply this particular shadertoy shader Shader - Shadertoy BETA on the canvas so I am trying to learn how to convert toyshadesr into the GLSL that three.js wants.

related Easy cool vsual effect to apply to an existing image texture while a replacement image is loading? - #5 by hofk

1 Like

It still seems difficult to understand how to write ontop of an already shaded screen. So you have some things not being affected.