【问题标题】:apply LUT to an image GLSL将 LUT 应用于图像 GLSL
【发布时间】:2021-03-17 10:08:26
【问题描述】:

我对 CG 很陌生,正在尝试实现一个片段着色器,将 png LUT 应用于图片,但我没有得到预期的结果,现在我的代码使图片非常蓝。
这是一个示例 LUT: [![在此处输入图片描述][1]][1]

当我使用以下代码将 LUT 应用到某些图像时,整个图片变得非常蓝。
代码:

precision mediump float;

uniform sampler2D u_image;
uniform sampler2D u_lut;

// LUT resolution for one component (4, 8, 16, ...)
uniform float u_resolution;

layout(location = 0) out vec4 fragColor;

in vec2 v_uv;

void main(void)
{
    vec2 tiles = vec2(u_resolution, u_resolution);
    vec2 tilesSize = vec2(u_resolution * u_resolution);
    vec3 imageColor = texture(u_image, v_uv).rgb;
    // min and max are used to interpolate between 2 tiles in the LUT
    float index = imageColor.b * (tiles.x * tiles.y - 1.0);
    float index_min = min(u_resolution - 2.0, floor(index));
    float index_max = index_min + 1.0;

    vec2 tileIndex_min;
    tileIndex_min.y = floor(index_min / tiles.x);
    tileIndex_min.x = floor(index_min - tileIndex_min.y * tiles.x);
    vec2 tileIndex_max;
    tileIndex_max.y = floor(index_max / tiles.x);
    tileIndex_max.x = floor(index_max - tileIndex_max.y * tiles.x);

    vec2 tileUV = mix(0.5/tilesSize, (tilesSize - 0.5)/tilesSize, imageColor.rg);

    vec2 tableUV_1 = tileIndex_min / tiles + tileUV / tiles;
    vec2 tableUV_2 = tileIndex_max / tiles + tileUV / tiles;

    vec3 lookUpColor_1 = texture(u_lut, tableUV_1).rgb;
    vec3 lookUpColor_2 = texture(u_lut, tableUV_2).rgb;
    vec3 lookUpColor = mix(lookUpColor_1, lookUpColor_2, index - index_min);
    fragColor = vec4(lookUpColor, 1.0);
}
 

【问题讨论】:

    标签: image image-processing graphics webgl


    【解决方案1】:

    由于您使用的是 WebGL2,因此您可以只使用 3D 纹理

    #version 300 es
    precision highp float;
    
    in vec2 vUV;
    
    uniform sampler2D uImage;
    uniform mediump sampler3D uLUT;
    
    out vec4 outColor;
    
    void main() {
      vec4 color = texture(uImage, vUV);
      vec3 lutSize = vec3(textureSize(uLUT, 0));
      vec3 uvw = (color.rgb * float(lutSize - 1.0) + 0.5) / lutSize;
      outColor = texture(uLUT, uvw);
    }
    

    您可以使用UNPACK_ROW_LENGTHUNPACK_SKIP_PIXELS 将PNG 切片加载到3D 纹理中

    function createLUTTexture(gl, img, filter, size = 8) {
      const tex = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_3D, tex);
      gl.texStorage3D(gl.TEXTURE_3D, 1, gl.RGBA8, size, size, size);
      // grab  slices
      for (let z = 0; z < size; ++z) {
        gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, z * size);
        gl.pixelStorei(gl.UNPACK_ROW_LENGTH, img.width);
        gl.texSubImage3D(
          gl.TEXTURE_3D, 
          0,     // mip level
          0,     // x
          0,     // y
          z,     // z
          size,  // width,
          size,  // height,
          1,     // depth
          gl.RGBA, 
          gl.UNSIGNED_BYTE,
          img,
        );
      }
      gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, filter);
      gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, filter);
      return tex;
    }
    

    例子:

    const fs = `#version 300 es
    precision highp float;
    
    in vec2 vUV;
    
    uniform sampler2D uImage;
    uniform mediump sampler3D uLUT;
    
    out vec4 outColor;
    
    void main() {
      vec4 color = texture(uImage, vUV);
      vec3 lutSize = vec3(textureSize(uLUT, 0));
      vec3 uvw = (color.rgb * float(lutSize - 1.0) + 0.5) / lutSize;
      outColor = texture(uLUT, uvw);
    }
    `;
    
    const vs = `#version 300 es
    in vec4 position;
    in vec2 texcoord;
    
    out vec2 vUV;
    
    void main() {
      gl_Position = position;
      vUV = texcoord;
    }
    `;
    
    const lutURLs = [
      'default.png',
      'bgy.png',
      '-black-white.png',
      'blues.png',
      'color-negative.png',
      'funky-contrast.png',
      'googley.png',
      'high-contrast-bw.png',
      'hue-minus-60.png',
      'hue-plus-60.png',
      'hue-plus-180.png',
      'infrared.png',
      'inverse.png',
      'monochrome.png',
      'nightvision.png',
      '-posterize-3-lab.png',
      '-posterize-3-rgb.png',
      '-posterize-4-lab.png',
      '-posterize-more.png',
      '-posterize.png',
      'radioactive.png',
      'red-to-cyan.png',
      'saturated.png',
      'sepia.png',
      'thermal.png',
    ];
    let luts = {};
    
    const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    
    async function main() {
    
      const gl = document.querySelector('canvas').getContext('webgl2');
      if (!gl) {
        alert('need WebGL2');
        return;
      }
    
      const img = await loadImage('https://i.imgur.com/CwQSMv9.jpg');
      document.querySelector('#img').append(img);
      const imgTexture = twgl.createTexture(gl, {src: img, yFlip: true});
     
      // compile shaders, link program, lookup locatios
      const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
      
      // calls gl.createBuffer, gl.bindBuffer, gl.bufferData for
      // a plane with positions, and texcoords
      const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl, 2);
      
      gl.useProgram(programInfo.program);
      
      // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
      twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
      
      gl.activeTexture(gl.TEXTURE0 + 1);
    
      for (;;) {
        for (let name of lutURLs) {
          let lut = luts[name];
          if (!lut) {
            let url = name;
            let filter = gl.LINEAR;
            if (url.startsWith('-')) {
              filter = gl.NEAREST;
              url = url.substr(1);
            }
            const lutImg = await loadImage(`https://webglsamples.org/color-adjust/adjustments/${url}`);
            lut = {
              name: url,
              texture: createLUTTexture(gl, lutImg, filter),
            };
            luts[name] = lut;
          }
    
          document.querySelector('#info').textContent = lut.name;
    
          // calls gl.uniformXXX, gl.activeTexture, gl.bindTexture
          twgl.setUniformsAndBindTextures(programInfo, {
            uImg: imgTexture,
            uLUT: lut.texture,
          });
          
          // calls gl.drawArrays or gl.drawElements
          twgl.drawBufferInfo(gl, bufferInfo);
    
          await wait(1000);
        }
      }
    }
    main();
    
    function createLUTTexture(gl, img, filter, size = 8) {
      const tex = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_3D, tex);
      gl.texStorage3D(gl.TEXTURE_3D, 1, gl.RGBA8, size, size, size);
      // grab  slices
      for (let z = 0; z < size; ++z) {
        gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, z * size);
        gl.pixelStorei(gl.UNPACK_ROW_LENGTH, img.width);
        gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, z * size);
        gl.pixelStorei(gl.UNPACK_ROW_LENGTH, img.width);
        gl.texSubImage3D(
          gl.TEXTURE_3D, 
          0,     // mip level
          0,     // x
          0,     // y
          z,     // z
          size,  // width,
          size,  // height,
          1,     // depth
          gl.RGBA, 
          gl.UNSIGNED_BYTE,
          img,
        );
      }
      gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, filter);
      gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, filter);
      return tex;
    }
    
    function loadImage(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onerror = reject;
        img.onload = () => resolve(img);
        img.crossOrigin = "anonymous";
        img.src = url;
      });
    }
    .split { display: flex; }
    .split>div { padding: 5px; }
    img { width: 150px; }
    <div class="split">
      <div>
        <div id="img"></div>
        <div>original</div>
      </div>
      <div>
        <canvas width="150" height="198"></canvas>
        <div>LUT Applied: <span id="info"></span></div>
      </div>
    </div>
    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

    至于在 2D 中进行操作,this 作为视频解释它链接在顶部。还有this,如果你想看看一个可行的着色器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-14
      • 2020-05-31
      相关资源
      最近更新 更多