gpujs / gpu.js

GPU Accelerated JavaScript
https://gpu.rocks
MIT License
15.03k stars 643 forks source link

Formula Not Working as Expected when inside GPU.js kernel #828

Open sojs-coder opened 6 months ago

sojs-coder commented 6 months ago

No meme sorry

What is wrong?

I have made a lighting shader using GPU.js. After taking the exact same code and modifying it to work with variables passed to a GPU.js kernel, it stops working.

This is a photo of it working... image

The exact same formula put into GPU.js kernel (I am away that the kernel's height is inverted from normal canvas.js... this is not the problem

image

Where does it happen?

Firefox, chrome browsers.

How do we replicate the issue?

Copied and pasted no worky code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Directional Lighting Scene</title>
  <style>
    canvas { border: 1px solid #000; }
  </style>
</head>
<body>  
  <script src = "matter.js"></script>
  <script src = "gpu.js"></script>
  <script src = "anvil.js"></script>
  <canvas id="canvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    // Define colored triangles in the scene
    const triangles = [
      { vertices: [[50, 50], [150, 50], [100, 150]], color: [255, 0, 0, 255] },    // Red triangle
      { vertices: [[200, 50], [300, 50], [250, 150]], color: [0, 255, 0, 255] },  // Green triangle
      { vertices: [[100, 200], [200, 200], [150, 300]], color: [0, 0, 255, 255] }  // Blue triangle
    ];

    function drawScene() {
      ctx.fillStyle = "white";
      ctx.fillRect(0,0,canvas.width,canvas.height);
      for (const triangle of triangles) {
        const path = new Path2D();
        const [startX, startY] = triangle.vertices[0];
        path.moveTo(startX, startY);

        for (let i = 1; i < triangle.vertices.length; i++) {
          const [x, y] = triangle.vertices[i];
          path.lineTo(x, y);
        }

        path.closePath();

        const [r, g, b, a] = triangle.color;
        ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
        ctx.fill(path);
      }
    }
    const directionalLights = [
      [Math.PI / 4, 100, 100, 1, 400, Math.PI / 3],
      [Math.PI, 300, 300, 0.8, 500, Math.PI / 4]
    ];
    var gpu = new GPU()
    var diffuseKernel = gpu.createKernel(function(pix, width, height, dlights, numdlights, ambient){
      var pixNum = ((width * height-this.thread.y) +this.thread.x) * 4;
      var red = pix[pixNum];
      var green = pix[pixNum+1];
      var blue = pix[pixNum+2];
      var brightness = ambient;

      var dlight = dlights[0];
      var lightDir = dlights[0][0];
      var lightX = dlights[0][1];
      var lightY = dlights[0][2];
      var intensity = dlights[0][3];
      var diffuse = dlights[0][4];
      var spread = dlights[0][5];
      const dirX = this.thread.x - lightX;
      const dirY = this.thread.y - lightY;
      const distance = Math.sqrt(dirX * dirX + dirY * dirY);

      const angleToLight = Math.atan2(dirY, dirX);
      const angleDiff = Math.abs(angleToLight - lightDir);

      if (angleDiff <= spread / 2) {
        // Calculate diffuse lighting
        const diffuseFactor = Math.max(0, Math.cos(angleDiff) * (1 - (distance / diffuse)));
        brightness += intensity * diffuseFactor;

      }

      this.color((red/255)*brightness, (green/255)*brightness, (blue/255)*brightness,pix[pixNum+2]/255)
    },{
      output: [canvas.width,canvas.height],
      graphical: true
    })
    function applyDirectionalLights(x, y, currentColor) {
      let ambient = 0.05;
      let finalColor = [0,0,0,255]; // Initialize with ambient color
      let pixelLit = false;
      brightness = ambient;
      for (const light of directionalLights) {
        const [lightDir, lightX, lightY, intensity, diffuse, spread] = light;
        // Calculate direction from light to pixel
        const dirX = x - lightX;
        const dirY = y - lightY;
        const distance = Math.sqrt(dirX * dirX + dirY * dirY);

        // Check if pixel is within the light spread angle
        const angleToLight = Math.atan2(dirY, dirX);
        const angleDiff = Math.abs(angleToLight - lightDir);

        if (angleDiff <= spread / 2) {
          // Calculate diffuse lighting
          const diffuseFactor = Math.max(0, Math.cos(angleDiff) * (1 - (distance / diffuse)));
          brightness += intensity * diffuseFactor;

        }
      }
      finalColor = currentColor.map((channel, index) => Math.min(255, channel * brightness));
      return finalColor
    }

    // Draw the initial scene
    drawScene();
    function diffuse(){
      const width = canvas.width;
      const height = canvas.height;

      const pix = ctx.getImageData(0, 0, width, height).data;
      diffuseKernel(pix, width, height, directionalLights, directionalLights.length, 0.05);
      const pixels = this.diffuseKernel.getPixels();

      ctx.putImageData(new ImageData(pixels, width, height), 0, 0);
    }

    var mouseX, mouseY = [-1,-1]

    document.addEventListener("mousemove",(e)=>{
      mouseX = event.clientX - canvas.getBoundingClientRect().left;
      mouseY = event.clientY - canvas.getBoundingClientRect().top;

    })
    // Call the diffuse function to apply directional lighting
      diffuse();
    setInterval(()=>{
      ctx.clearRect(0,0,canvas.width,canvas.height);
      directionalLights[0][0] = Math.atan2(mouseY - directionalLights[0][1], mouseX - directionalLights[0][2]);
      drawScene();
      diffuse();
    },1000/0.5);
  </script>
</body>
</html>

Copied and pasted worky code

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Directional Lighting Scene</title>
    <style>
      canvas { border: 1px solid #000; }
    </style>
  </head>
  <body>  
    <script src = "matter.js"></script>
    <script src = "gpu.js"></script>
    <script src = "anvil.js"></script>
    <canvas id="canvas" width="400" height="400"></canvas>
    <script>
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');

      // Define colored triangles in the scene
      const triangles = [
        { vertices: [[50, 50], [150, 50], [100, 150]], color: [255, 0, 0, 255] },    // Red triangle
        { vertices: [[200, 50], [300, 50], [250, 150]], color: [0, 255, 0, 255] },  // Green triangle
        { vertices: [[100, 200], [200, 200], [150, 300]], color: [0, 0, 255, 255] }  // Blue triangle
      ];

      function drawScene() {
        ctx.fillStyle = "white";
        ctx.fillRect(0,0,canvas.width,canvas.height);
        for (const triangle of triangles) {
          const path = new Path2D();
          const [startX, startY] = triangle.vertices[0];
          path.moveTo(startX, startY);

          for (let i = 1; i < triangle.vertices.length; i++) {
            const [x, y] = triangle.vertices[i];
            path.lineTo(x, y);
          }

          path.closePath();

          const [r, g, b, a] = triangle.color;
          ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
          ctx.fill(path);
        }
      }
      const directionalLights = [
        [Math.PI / 4, 100, 100, 1, 400, Math.PI / 3],
        [Math.PI, 300, 300, 0.8, 500, Math.PI / 4]
      ];
      function applyDirectionalLights(x, y, currentColor) {
        let ambient = 0.05;
        let finalColor = [0,0,0,255]; // Initialize with ambient color
        let pixelLit = false;
        brightness = ambient;
        for (const light of directionalLights) {
          const [lightDir, lightX, lightY, intensity, diffuse, spread] = light;

          // Calculate direction from light to pixel
          const dirX = x - lightX;
          const dirY = y - lightY;
          const distance = Math.sqrt(dirX * dirX + dirY * dirY);

          // Check if pixel is within the light spread angle
          const angleToLight = Math.atan2(dirY, dirX);
          const angleDiff = Math.abs(angleToLight - lightDir);

          if (angleDiff <= spread / 2) {
            // Calculate diffuse lighting
            const diffuseFactor = Math.max(0, Math.cos(angleDiff) * (1 - (distance / diffuse)));
            brightness += intensity * diffuseFactor;

          }
        }
        finalColor = currentColor.map((channel, index) => Math.min(255, channel * brightness));
        return finalColor
      }

      // Draw the initial scene
      drawScene();
      function diffuse(){
        const width = canvas.width;
        const height = canvas.height;

        const pix = ctx.getImageData(0, 0, width, height).data;

        // Loop through each pixel
        for(var x = 0; x < width; x++){
          for(var y = 0; y < height; y++){
            // Calculate the color of the pixel
            const pixelColor = [pix[(x + y * width) * 4 + 0], pix[(x + y * width) * 4 + 1], pix[(x + y * width) * 4 + 2], pix[(x + y * width) * 4 + 3]];
            var [r,g,b,a] = applyDirectionalLights(x,y,pixelColor);
            ctx.fillStyle = `rgba(${r},${g},${b},${a})`
            ctx.fillRect(x,y,1,1);
          }
        }
      }

      var mouseX, mouseY = [-1,-1]

      document.addEventListener("mousemove",(e)=>{
        mouseX = event.clientX - canvas.getBoundingClientRect().left;
        mouseY = event.clientY - canvas.getBoundingClientRect().top;

      })
      // Call the diffuse function to apply directional lighting
        diffuse();
      setInterval(()=>{
        ctx.clearRect(0,0,canvas.width,canvas.height);
        directionalLights[0][0] = Math.atan2(mouseY - directionalLights[0][1], mouseX - directionalLights[0][2]);
        drawScene();
        diffuse();
      },1000/10);
    </script>
  </body>
  </html>

How important is this (1-5)?

I have to delay the directional lighting system in my game as long as this is not resolved 4 ( I can work on other things in the meantime, but still important)

Expected behavior (i.e. solution)

Provided image

Other Comments

If it is a bug in my own code.... I'm sorry but I can't find it, ChatGPT can't find it, Bard can't find it, and my math teacher can't find it.