【问题标题】:GLSL Fragment shader UV displacement based on world spaceGLSL Fragment shader 基于世界空间的UV置换
【发布时间】:2019-12-31 18:24:50
【问题描述】:

我正在尝试为网站上的图像创建 rgb 偏移效果。我有基本功能,但问题是通道与纹理的 uv 偏移。因此,如果图像尺寸不同,则每个图像的偏移量在视觉上是不同的。

这是我的片段着色器。

uniform sampler2D texture;
varying vec2 vUv; // vertex uv


void main() {
    vec2 uv = vUv;

    float red = texture2D(texture, vec2(uv.x, uv.y - .1)).r;
    float green = texture2D(texture, uv).g;
    float blue = texture2D(texture, vec2(uv.x, uv.y + .1)).b;
    float alpha = texture2D(texture, uv).a;

    gl_FragColor = vec4(vec3(red, green, blue), alpha);

}

以及它在页面上的呈现方式。

如何在不传入统一值的情况下标准化 uv 偏移量?

【问题讨论】:

  • 为什么不使用额外的制服?使用代表图像分辨率的vec2 制服应该很容易解决此问题。
  • 如果这是唯一的方法,我可以,我想知道是否有办法在不使用任何额外制服的情况下做到这一点?我仍在尝试掌握着色器。
  • 穿制服是正常的,可以说是明智的做法。特别是对于这种情况。不过,传入纹理分辨率可能是错误的解决方案。没有理由硬编码不需要硬编码的东西。

标签: three.js glsl shader fragment-shader


【解决方案1】:

传入偏移量等更多信息是正常的

uniform float offset1;
uniform float offset2;
uniform sampler2D texture;
varying vec2 vUv; // vertex uv


void main() {
    vec2 uv = vUv;

    float red = texture2D(texture, vec2(uv.x, uv.y + offset1)).r;
    float green = texture2D(texture, uv).g;
    float blue = texture2D(texture, vec2(uv.x, uv.y + offset2)).b;
    float alpha = texture2D(texture, uv).a;

    gl_FragColor = vec4(vec3(red, green, blue), alpha);

}

然后您可以在 JavaScript 中进行调整。例如

  const uniforms = {
    offset1:  { value: 0 },
    offset2:  { value: 0 },
    ...
  };

  ...

  uniforms.offset1.value =  2 / textureHeight;
  uniforms.offset2.value = -2 / textureHeight;

如果是我,我可能会这样做

uniform vec2 channelOffsets[4];
uniform vec4 channelMult[4];
uniform sampler2D texture;
varying vec2 vUv; // vertex uv


void main() {
    vec2 uv = vUv;

    vec4 channel0 = texture2D(texture, uv + channelOffset[0]);
    vec4 channel1 = texture2D(texture, uv + channelOffset[1]);
    vec4 channel2 = texture2D(texture, uv + channelOffset[2]);
    vec4 channel3 = texture2D(texture, uv + channelOffset[3]);

    gl_FragColor = 
        channelMult[0] * channel0 +
        channelMult[1] * channel1 +
        channelMult[2] * channel2 +
        channelMult[3] * channel3 ; 
}

并设置它们

  const uniforms = {
    channelOffsets:  { value: [
      new THREE.Vector2(),
      new THREE.Vector2(),
      new THREE.Vector2(),
      new THREE.Vector2(),
    ]},
    channelMults: { value: [
      new THREE.Vector4(1, 0, 0, 0),
      new THREE.Vector4(0, 1, 0, 0),
      new THREE.Vector4(0, 0, 1, 0),
      new THREE.Vector4(0, 0, 0, 1),
    ]},
    ....
  }

...

  uniforms.channelOffsets.value[0].y = -2 / textureHeight;
  uniforms.channelOffsets.value[2].y =  2 / textureHeight;

举一个不那么硬编码的例子。我什至可以使用纹理矩阵而不是偏移量,这将允许旋转和缩放每个通道,并将它们与允许交换通道的矩阵组合。

【讨论】:

    【解决方案2】:

    如果您要使用WebGL 2.0 上下文(请参阅How to use WebGL2),那么您可以使用textureSize 来获取纹理的大小。例如:

    #version 300 es
    
    uniform sampler2D tex;
    in      vec2      vUv;
    out     vec4      color;
    
    void main() {
        vec2 uv = vUv;
    
        vec2  size   = vec2(textureSize(tex, 0));
        float pixel  = 10.0;
        float offset = pixel / size.y;
    
        float red   = texture(tex, vec2(uv.x, uv.y - offset)).r;
        float green = texture(tex, uv).g;
        float blue  = texture(tex, vec2(uv.x, uv.y + offset)).b;
        float alpha = texture(tex, uv).a;
    
        color = vec4(red, green, blue, alpha);
    }
    

    注意,您必须更改纹理采样器制服的名称

    【讨论】:

    • 如果我不用支持IE就完美了
    【解决方案3】:

    如果您需要知道您在屏幕空间(不是 3D 世界空间)中的哪个像素,您可以使用gl_FragCoord。例如,在 1080p 显示器中,gl_FragCoord.x 将从左侧的 0 开始,并在屏幕右侧增加到 1920。 gl_FragCoord.y 的范围会比 [0, 1080] 短一点(由于 OS 菜单栏)。您可以像这样在片段着色器中提取这些坐标:

    vec2 coord = gl_FragCoord.xy;
    

    这样您就可以知道您在屏幕空间中的哪个像素,而无需传递另一个制服。有关详细信息,请参见此处:

    https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/gl_FragCoord.xhtml

    【讨论】:

    • 但这对在 uv 上应用偏移有何帮助?如果我理解正确,UV 从 -1 变为 1。我不能将像素偏移直接应用于 uv,我必须计算纹理与 gl_FragCoord 相比的大小,但我不相信我具有纹理大小而不传递制服(除非使用上面答案中所述的 webgl 2)。
    猜你喜欢
    • 1970-01-01
    • 2018-07-10
    • 2017-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多