GLSL 着色器:像素循环中的意外行为

问题描述 投票:0回答:1

我正在开发 GLSL 着色器,并在使用 while 循环遍历像素时遇到问题。相同的代码在桌面设备上运行良好,但在移动设备(Android 和 iOS)上产生不同的结果。

相同的逻辑在桌面和移动设备上产生不一致的效果。

结果图片

我尝试在纹理区域内添加效果,但似乎会产生不同的结果。

(function() {
  "use strict";
  var scene;
  var renderer;
  var camera;
  var uniforms;
  var material;

  const screenSize = {
    desk: {
      w: 640,
      h: 480
    },
    mob: {
      w: 240,
      h: 320
    },
  };
  async function loadTexture(url) {
    return new Promise((resolve, reject) => {
      const loader = new THREE.TextureLoader();
      loader.load(
        url,
        (texture) => resolve(texture), // Resolve with the loaded texture
        undefined, // Progress callback, if needed
        (error) => reject(error) // Reject on error
      );
    });
  }

  async function init() {
    var canvas = document.getElementById("three-canvas");
    // Initialize WebGL Renderer
    renderer = new THREE.WebGLRenderer({
      canvas
    });
    // renderer.setSize(window.innerWidth, window.innerHeight);
    const size = screen.width < 600 ? screenSize.mob : screenSize.desk;
    renderer.setSize(size.w, size.h);

    // Initialize Scenes
    scene = new THREE.Scene();

    // Initialize Camera
    camera = new THREE.PerspectiveCamera(
      45,
      //   window.innerWidth / window.innerHeight,
      size.w / size.h,
      1,
      100
    );
    camera.position.z = 10;

    // Create Light
    var light = new THREE.PointLight(0xffffff);
    light.position.set(0, 0, 500);
    scene.add(light);

    // Create Ball
    var vertShader = document.getElementById("vertexShader").innerHTML;
    var fragShader = document.getElementById("fragmentShader").innerHTML;

    const geometry = new THREE.PlaneGeometry(8, 8);
    const texture = await loadTexture("https://camweara-customers.s3.ap-south-1.amazonaws.com/Teststore/test.png");
    console.log("texture: ", texture);

    uniforms = {
      texture1: {
        type: "t",
        value: texture
      },
      // mouse: { type: 'v2', value: new THREE.Vector2(0.0, 0.0) }
    };
    console.log("uniforms: ", uniforms);

    // Create a shader material
    material = new THREE.ShaderMaterial({
      vertexShader: vertShader,
      fragmentShader: fragShader,
      uniforms: uniforms,
      transparent: true,
    });

    // Create the plane mesh
    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane);
  }

  function animate() {
    requestAnimationFrame(animate);
    render();
  }

  function render() {
    renderer.render(scene, camera);
  }

  window.onload = function() {
    init();
    animate();
  };
})();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js">
  </script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      height: 100%;
      overflow: hidden;
      background: #1d1d1d;
    }
    
    canvas {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  </style>
</head>

<body>
  <canvas id="three-canvas"></canvas>

    <script id="vertexShader" type="x-shader/x-vertex">
      varying vec2 vUv;

      void main() {
          vUv = uv;
          gl_Position =   projectionMatrix * modelViewMatrix * vec4(position,1.0);
      }
    </script>

    <script id="fragmentShader" type="x-shader/x-fragment">
      precision highp float;

      uniform sampler2D texture1;
      varying vec2 vUv;

      void main() {
          vec4 bg_clr = texture2D(texture1, vUv);
          vec4 finalClr = vec4(0.0, 0.0, 1.0, 1.0);

          float pixelStep = 0.001;
          vec4 finalX;
          vec4 finalY;

          if(bg_clr.w > 0.2){
            float i = 0.0;
            while(i < 1.0){
                vec2 pos = vUv - vec2(i, 0.0);
                vec4 xColor = texture2D(texture1, pos);
                if(xColor.w < 0.01){
                    finalX = vec4(1.0,0.0,0.0,1.0);
                    break;
                }
                i = i + pixelStep;
            }
            i=0.0;
            while(i < 1.0){
                vec2 pos = vUv + vec2(0.0, i);
                vec4 xColor = texture2D(texture1, pos);
                if(xColor.w < 0.1){
                    finalY = vec4(0.0,1.0,0.0,1.0);
                    break;
                }
                i = i + pixelStep;
            }
            finalClr = mix(finalX,finalY,0.5);
          }

          gl_FragColor = finalClr;
      }
    </script>
</body>

</html>

three.js glsl shader webgl
1个回答
0
投票

我不确定是什么导致了您的问题,但我有一些想法,希望其中一些能对您有所帮助。

首先,移动驱动程序被认为不可靠,有时不遵循规范。

如果您的硬件、浏览器或驱动程序较旧,则可能不支持 WebGL 2,并且 Three.js 会回退到 WebGL 1。在 WebGL 1 中,片段着色器中不强制支持

highp
。因此,当您声明
precision highp float;
时,您可能会导致未定义的行为。您可以在
renderer.capabilities.isWebGL2
renderer.capabilities.precision
中检查这些内容。

除此之外,我没有看到任何未定义的行为。您在 (0-1) 区间之外对纹理进行采样,这应该不是问题,但它是移动的,所以谁知道呢?

但是,我可以看到你的代码很不寻常。您可以在着色器中使用

while
if
break
跳转到代码的其他部分。由于在不同的片段着色器调用中,循环在不同的时间退出,这将导致着色器发散,迫使 GPU 或多或少以串行方式执行代码,而不是使用其许多内核,这违背了 GPU 的目的。此外,您可能会在一次片段着色器调用中对纹理进行 2000 次采样。您可以在一个帧中调用数百万个片段着色器。这是大量的纹理采样,这是对性能要求最高的操作之一。这可能会非常慢。对于较小的纹理,缓存可能会节省你的时间,但对于较大的纹理,它会非常慢。

我不确定你想要达到的期望结果是什么,但是如果你想用一种颜色对低于某个 alpha 阈值的像素进行着色,并用另一种颜色对上面的像素进行着色,那么它可以大大简化:

void main() {
  vec4 bg_clr = texture2D(texture1, vUv);
  gl_FragColor = mix(vec4(0.0, 0.0, 1.0, 1.0), vec4(0.5,0.5,0.0,1.0), bvec4(bg_clr.w > 0.2));
}
© www.soinside.com 2019 - 2024. All rights reserved.