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.

Ran into pretty much the same issue (wanting to convert a shadertoy shader to an app) and figured out it was easier to simply drop threejs and use WebGL directly. Made a template for future uses (edit src/frag.glsl to get started): https://github.com/nmattia/webgl-shader-template