GLSL ES не ведет себя одинаково с циклом for и развернутым циклом - PullRequest
1 голос
/ 09 апреля 2019

Кажется, 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). Почему?

Внизу вопроса я предоставил фрагмент кода (и демонстрационный проект ), чтобы показать ошибку.

объяснение кода

Код использует 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>
...