GLSL ES与for循环和展开循环的行为不同

时间:2019-04-09 14:01:02

标签: arrays webgl fragment-shader glsles loop-unrolling

似乎GLSL ES 3.0无法正确执行我的代码。

我写了两次相同的代码,首先是展开式的;第二个是for循环:


    // Unrolled version:

    float minDistance = 1000000.0;

    vec3 finalColor1 = vec3(0.0);
    int index1 = 0;

    float distance = colorDifferenceCIE94FromRGB(pixel, colors[0]);
    if(distance < minDistance) {
        finalColor1 = colors[0];
        minDistance = distance;
        index1 = 0;
    }
    distance = colorDifferenceCIE94FromRGB(pixel,  colors[1]);
    if(distance < minDistance) {
        finalColor1 = colors[1];
        minDistance = distance;
        index1 = 1;
    }
    distance = colorDifferenceCIE94FromRGB(pixel, colors[2]);
    if(distance < minDistance) {
        finalColor1 = colors[2];
        minDistance = distance;
        index1 = 2;
    }
    distance = colorDifferenceCIE94FromRGB(pixel, colors[3]);
    if(distance < minDistance) {
        finalColor1 = colors[3];
        minDistance = distance;
        index1 = 3;
    }
    distance = colorDifferenceCIE94FromRGB(pixel, colors[4]);
    if(distance < minDistance) {
        finalColor1 = colors[4];
        minDistance = distance;
        index1 = 4;
    }
    distance = colorDifferenceCIE94FromRGB(pixel,  colors[5]);
    if(distance < minDistance) {
        finalColor1 = colors[5];
        minDistance = distance;
        index1 = 5;
    }
    distance = colorDifferenceCIE94FromRGB(pixel,  colors[6]);
    if(distance < minDistance) {
        finalColor1 = colors[6];
        minDistance = distance;
        index1 = 6;
    }
    distance = colorDifferenceCIE94FromRGB(pixel,  colors[7]);
    if(distance < minDistance) {
        finalColor1 = colors[7];
        minDistance = distance;
        index1 = 7;
    }
    distance = colorDifferenceCIE94FromRGB(pixel,  colors[8]);
    if(distance < minDistance) {
        finalColor1 = colors[8];
        minDistance = distance;
        index1 = 8;
    }

    // For Loop version:

    int index2 = 0;

    vec3 finalColor2 = pixel;

    minDistance = 100000.0;
    for(int i=0 ; i<9 ; i++) {
        distance = colorDifferenceCIE94FromRGB(pixel, colors[i]);
        if(distance < minDistance) {
            finalColor2 = colors[i];
            minDistance = distance;
            index2 = i;
        }
    }

    gl_FragColor = vec4((uv.x < 0.5 ? finalColor1 : finalColor2), 1.0);


屏幕左侧应与屏幕右侧完全相同,但不是(至少在我的Macbook Pro Unibody 2012 OS X 10.14.4上)。为什么?

我在问题的底部提供了一个摘要(和一个demo project)来显示该错误。

代码说明

代码使用Paper.js在画布上绘制一个红色圆圈,然后将该画布赋予Three.js CanvasTexture,该画布将应用于全屏四边形(平面网格)。

片段着色器计算(给定纹理的)像素颜色与一组颜色之间的距离;然后呈现最接近的颜色。该操作执行两次,一次是在已展开版本中/依次执行,一次是在for循环中执行。第一个版本的结果显示在屏幕左侧,第二个版本的显示在屏幕右侧。

结果应该完全相同,但是奇怪的是事实并非如此。为什么?

您可以在第157行中取消注释:

colors[8] = vec3(0.8, 0.4, 0.1);

(或第148至152行)两个代码的执行方式相似。

这是我在计算机上得到的:

Left Right are different

左边的白色方块是paper.js画布,右边的黑色方块是three.js场景;第二个圆圈也应该完全是红色。

代码段

<!DOCTYPE html>
<html>
  <head>
    <title>Dynamic array glsl test</title>
    <meta charset="UTF-8" />

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/103/three.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>

    <style>
      #three,
      #paper {
        width: 100px;
        height: 100px;
      }
      #three {
        /*display: none;*/
      }
      #paper {
        /*display: none;*/
      }
    </style>
  </head>

  <body>
    <canvas id="paper"></canvas>
    <canvas id="three"></canvas>
    <script type="application/glsl" id="fragmentShader2">
      varying vec2 vUV;

      uniform sampler2D textureSampler;
      uniform vec2 screenResolution;
      uniform vec2 textureResolution;

      void main(void) {

          vec2 uv = gl_FragCoord.xy / screenResolution.xy - 0.5;                    // [-0.5, 0.5]

          float screenRatio = screenResolution.x / screenResolution.y;
          float textureRatio = textureResolution.x / textureResolution.y;

          vec2 textureUV = textureRatio > screenRatio ? vec2(uv.x, uv.y * textureRatio / screenRatio) : vec2(uv.x / textureRatio * screenRatio, uv.y);

          gl_FragColor = texture2D(textureSampler, textureUV + 0.5);
          // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    </script>

    <script type="application/glsl" id="fragmentShader">
      precision highp float;

      varying vec2 vUV;

      uniform sampler2D textureSampler;
      uniform vec2 screenResolution;
      uniform vec2 textureResolution;
      uniform float hueRotation;

      vec3 colors[9];

      #define PI 3.1415926535897932384626433832795


      vec3 hsv2rgb(vec3 c) {
          vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
          vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
          return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
      }

      vec4 hsv2rgb(vec4 c) {
          return vec4(hsv2rgb(c.xyz), c.w);
      }

      vec3 rgb2hsv(vec3 c) {
          vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
          vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
          vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

          float d = q.x - min(q.w, q.y);
          float e = 1.0e-10;
          return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
      }

      vec4 rgb2hsv(vec4 c) {
          return vec4(rgb2hsv(c.xyz), c.w);
      }

      vec4 rotateHue(vec4 c, float angle) {
          vec4 chsv = rgb2hsv(c);
          chsv.x = mod(chsv.x + angle, 1.0);
          return hsv2rgb(chsv);
      }

      vec3 mixColors(vec3 c1, vec3 c2) {
          return sqrt(0.5 * c1 * c1 + 0.5 * c2 * c2);
      }

      vec3 mixColors(vec3 c1, vec3 c2, vec3 c3) {
          return sqrt( (c1 * c1 / 3.0) + (c2 * c2 / 3.0) + (c3 * c3 / 3.0));
      }

      vec3 rgb2xyz(vec3 rgb) {
          rgb.r = rgb.r > 0.04045 ? pow( ( rgb.r + 0.055 ) / 1.055, 2.4) : rgb.r / 12.92;
          rgb.g = rgb.g > 0.04045 ? pow( ( rgb.g + 0.055 ) / 1.055, 2.4) : rgb.g / 12.92;
          rgb.b = rgb.b > 0.04045 ? pow( ( rgb.b + 0.055 ) / 1.055, 2.4) : rgb.b / 12.92;

          rgb *= 100.0;

          return vec3(rgb.r * 0.4124 + rgb.g * 0.3576 + rgb.b * 0.1805,
                      rgb.r * 0.2126 + rgb.g * 0.7152 + rgb.b * 0.0722,
                      rgb.r * 0.0193 + rgb.g * 0.1192 + rgb.b * 0.9505);
      }


      vec3 xyz2lab(vec3 xyz) {
          xyz = xyz / vec3(94.811, 100.000, 107.304);

          xyz = vec3( xyz.r > 0.008856 ? pow( xyz.r, 1.0/3.0) : (7.787 * xyz.r) + (16.0 / 116.0),
                      xyz.g > 0.008856 ? pow( xyz.g, 1.0/3.0) : (7.787 * xyz.g) + (16.0 / 116.0),
                      xyz.b > 0.008856 ? pow( xyz.b, 1.0/3.0) : (7.787 * xyz.b) + (16.0 / 116.0));

          return vec3( (116.0 * xyz.y) - 16.0, 500.0 * (xyz.x - xyz.y), 200.0 * (xyz.y - xyz.z) );
      }

      vec3 rgb2lab(in vec3 rgb) {
          vec3 xyz = rgb2xyz(rgb);
          vec3 lab = xyz2lab(xyz);
          return(lab);
      }

      float colorDifferenceCIE94FromLab(vec3 cieLab1, vec3 cieLab2) {

          // Just to make it more readable
          float cL1 = cieLab1.r;
          float ca1 = cieLab1.g;
          float cb1 = cieLab1.b;

          float cL2 = cieLab2.r;
          float ca2 = cieLab2.g;
          float cb2 = cieLab2.b;

          float c1 = sqrt(ca1 * ca1 + cb1 * cb1);
          float c2 = sqrt(ca2 * ca2 + cb2 * cb2);

          float dL = cL2 - cL1;

          float dC = c2 - c1;

          float dE = sqrt( (cL1 - cL2) * (cL1 - cL2) + (ca1 - ca2) * (ca1 - ca2) + (cb1 - cb2) * (cb1 - cb2) );

          float dH = (dE * dE) - (dL * dL) - (dC * dC);

          dH = dH > 0.0 ? sqrt(dH) : 0.0;

          float kL = 1.0;
          float kC = 1.0;
          float kH = 1.0;
          float k1 = 0.045;
          float k2 = 0.015;

          float sL = 1.0;
          float sC = 1.0 + ( k1 * c1 ); // sX
          float sH = 1.0 + ( k2 * c1 ); // sH

          float dLw = dL / (kL * sL);
          float dCw = dC / (kC * sC);
          float dHw = dH / (kH * sH);

          float deltaE94 = sqrt(dLw * dLw + dCw * dCw + dHw * dHw);

          return deltaE94;
      }

      float colorDifferenceCIE94FromRGB(vec3 rgb1, vec3 rgb2) {
          vec3 lab1 = rgb2lab(rgb1);
          vec3 lab2 = rgb2lab(rgb2);
          return colorDifferenceCIE94FromLab(lab1, lab2);
      }

      // float colorDifferenceCIE94FromRGB(vec3 rgb1, vec3 rgb2) {
      //     return abs(rgb2.g - rgb1.g);
      // }

      void main()
      {
          vec2 uv = gl_FragCoord.xy / screenResolution.xy;

          vec3 pixel = texture2D(textureSampler, uv).xyz;


          vec3 white = vec3(1.0);
          vec3 black = vec3(0.0);
          vec3 c1 = rotateHue(vec4(1.0, 0.0, 0.0, 1.0), hueRotation).xyz;
          vec3 c2 = rotateHue(vec4(0.0, 1.0, 0.0, 1.0), hueRotation).xyz;
          vec3 c3 = rotateHue(vec4(0.0, 0.0, 1.0, 1.0), hueRotation).xyz;

      /*
          vec3 c1 = vec3(1.0, 0.0, 0.0);
          vec3 c2 = vec3(0.0, 1.0, 0.0);
          vec3 c3 = vec3(0.0, 0.0, 1.0);
      */
          vec3 c12 = mixColors(c1, c2);
          vec3 c13 = mixColors(c1, c3);
          vec3 c23 = mixColors(c2, c3);
          vec3 c123 = mixColors(c1, c2, c3);

          colors[0] = white;
          colors[1] = black;
          colors[2] = c1;
          colors[3] = c2;
          colors[4] = c3;
          colors[5] = c12;
          colors[6] = c13;
          colors[7] = c23;
          colors[8] = c123;
          // colors[8] = vec3(0.8, 0.4, 0.1);

          /*
          colors[0] = white;
          colors[1] = black;
          colors[2] = vec3(1.0, 0.0, 0.8);
          colors[3] = vec3(0.0, 0.7, 0.4);
          colors[4] = vec3(0.0, 0.8, 0.9);
          colors[5] = vec3(0.8, 0.4, 0.1);
          colors[6] = vec3(0.4, 0.9, 0.0);
          colors[7] = vec3(0.1, 0.2, 7.0);
          colors[8] = vec3(0.9, 0.1, 0.0);
          */
          float minDistance = 1000000.0;

          vec3 finalColor1 = vec3(0.0);
          int index1 = 0;

          float distance = colorDifferenceCIE94FromRGB(pixel, colors[0]);
          if(distance < minDistance) {
              finalColor1 = colors[0];
              minDistance = distance;
              index1 = 0;
          }
          distance = colorDifferenceCIE94FromRGB(pixel,  colors[1]);
          if(distance < minDistance) {
              finalColor1 = colors[1];
              minDistance = distance;
              index1 = 1;
          }
          distance = colorDifferenceCIE94FromRGB(pixel, colors[2]);
          if(distance < minDistance) {
              finalColor1 = colors[2];
              minDistance = distance;
              index1 = 2;
          }
          distance = colorDifferenceCIE94FromRGB(pixel, colors[3]);
          if(distance < minDistance) {
              finalColor1 = colors[3];
              minDistance = distance;
              index1 = 3;
          }
          distance = colorDifferenceCIE94FromRGB(pixel, colors[4]);
          if(distance < minDistance) {
              finalColor1 = colors[4];
              minDistance = distance;
              index1 = 4;
          }
          distance = colorDifferenceCIE94FromRGB(pixel,  colors[5]);
          if(distance < minDistance) {
              finalColor1 = colors[5];
              minDistance = distance;
              index1 = 5;
          }
          distance = colorDifferenceCIE94FromRGB(pixel,  colors[6]);
          if(distance < minDistance) {
              finalColor1 = colors[6];
              minDistance = distance;
              index1 = 6;
          }
          distance = colorDifferenceCIE94FromRGB(pixel,  colors[7]);
          if(distance < minDistance) {
              finalColor1 = colors[7];
              minDistance = distance;
              index1 = 7;
          }
          distance = colorDifferenceCIE94FromRGB(pixel,  colors[8]);
          if(distance < minDistance) {
              finalColor1 = colors[8];
              minDistance = distance;
              index1 = 8;
          }

          int index2 = 0;

          vec3 finalColor2 = pixel;

          minDistance = 100000.0;
          for(int i=0 ; i<9 ; i++) {
              distance = colorDifferenceCIE94FromRGB(pixel, colors[i]);

              if(distance < minDistance) {
                  finalColor2 = colors[i];
                  minDistance = distance;
                  index2 = i;
              }
          }

          vec3 green = vec3(0.0, 1.0, 0.0);
          vec3 red = vec3(1.0, 0.0, 0.0);

          /*
          // Display colors:
          if(uv.x < 0.1) {
              float y = uv.y;
              finalColor1 = colors[int(floor(y * 8.9))];
          }
          */


          // gl_FragColor = vec4(index1 == index2 ? green : red,1.0);
          gl_FragColor = vec4((uv.x < 0.5 ? finalColor1 : finalColor2), 1.0);
      }
    </script>
    <script type="application/glsl"  id="vertexShader">
      varying vec2 vUv;

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

    <!--  <script src="src/index.js"></script> -->

    <script type="application/javascript">
      //import * as vertexShader from "./vertex-shader";
      //import * as fragmentShader from "./fragment-shader";

      let fragmentShader = document.getElementById("fragmentShader").textContent;
      let vertexShader = document.getElementById("vertexShader").textContent;



      let screenWidth = window.innerWidth;
      let screenHeight = window.innerHeight;
      // let screenWidth = document.body.clientWidth;
      // let screenHeight = document.body.clientHeight;
      let uniforms = {};
      let scene = null;

      let parameters = {
        hueRotation: 0
      };
      let renderer, camera, texture, raster;

      var paperCanvas = document.getElementById("paper");
      paper.setup(paperCanvas);

      function initialize() {
        let canvas = $("#three").get(0);
        let context = canvas.getContext("webgl2");
        renderer = new THREE.WebGLRenderer({
          context: context,
          canvas: canvas,
          antialias: true,
          preserveDrawingBuffer: true
        });

        renderer.setSize(screenWidth, screenHeight);

        scene = new THREE.Scene();

        camera = new THREE.OrthographicCamera(
          screenWidth / -2,
          screenWidth / 2,
          screenHeight / 2,
          screenHeight / -2,
          1,
          1000
        );

        texture = new THREE.CanvasTexture(paper.view.element, THREE.UVMapping);
        texture.needsUpdate = true;


        window.texture = texture;

        uniforms = {
          screenResolution: {
            type: "v2",
            value: new THREE.Vector2(screenWidth, screenHeight)
          },
          textureSampler: { value: texture },
          textureResolution: new THREE.Uniform(
            new THREE.Vector2(
              canvas ? canvas.height / 2 : 0,
              canvas ? canvas.width / 2 : 0
            )
          ),
          hueRotation: { type: "f", value: parameters.hueRotation }
        };

        let material = new THREE.ShaderMaterial({
          uniforms: uniforms,
          // extensions: { derivatives: true },
          vertexShader: vertexShader.trim(),
          fragmentShader: fragmentShader.trim(),
          side: THREE.DoubleSide
        });

        let mesh = new THREE.Mesh(
          new THREE.PlaneGeometry(screenWidth, screenHeight),
          material
        );
        // mesh = new THREE.Mesh(geometry, material);

        mesh.position.z = -1;
        window.mesh = mesh;
        scene.add(mesh);

        //raster = new paper.Raster("./Velo.jpg");
        //raster.onLoad = rasterLoaded;

        let circle = new paper.Path.Circle(paper.view.bounds.center, 25);
        circle.fillColor = "red";
        
        setTimeout(() => { texture.needsUpdate = true }, 100);
      }
      $(document).ready(()=> {
        let paperCanvas = $("#paper").get(0);
        screenWidth = paperCanvas.clientWidth;
        screenHeight = paperCanvas.clientHeight;
        initialize();
      })
      
      //document.addEventListener("DOMContentLoaded", initialize);

      function rasterLoaded() {
        console.log("raster loaded");
        // raster.fitBounds(paper.view.bounds);

        console.log("texture:", texture);
        if (texture) {
          console.log("texture.needsUpdate");
          texture.needsUpdate = true;
        }
        setTimeout(() => updateUniforms(parameters), 100);
      }

      function updateUniforms(parameters) {
        if (uniforms == null) {
          return;
        }

        uniforms.hueRotation.value = parameters.hueRotation;
        texture.needsUpdate = true;
      }

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

      animate();
    </script>
  </body>
</html>

0 个答案:

没有答案