【问题标题】:Best way to sample a fullscreen texture采样全屏纹理的最佳方法
【发布时间】:2014-04-17 17:27:43
【问题描述】:

在片段着色器中采样全屏纹理的最佳方法是什么,例如延迟渲染器中的 g 缓冲区或后处理着色器中的场景纹理?

目前我使用以下两种方式:

  • 将屏幕尺寸作为统一传递给着色器,并从gl_FragCoord 计算 (u,v):

    vec2 texCoord = gl_FragCoord.xy / vScreenSize;
    vec3 c = texture( tex, texCoord ).rgb;
    

    似乎并不理想,因为需要进行除法并且为着色器提供屏幕尺寸很麻烦。

  • gl_FragCoord.xy 转换为ivec 并使用texelFetch

    vec3 c = texelFetch( tex, ivec2(gl_FragCoord.xy), 0 ).rgb;
    

    也不理想,因为需要从float 转换为int

那么有没有更好的方法呢?我真的只想在我的像素着色器正在绘制的确切点对缓冲区进行采样。


// 编辑:


好的,根据来自顶点着色器的插值纹理坐标的建议,我设法找出了以下代码:

顶点着色器:

#version 150

uniform mat4 im_ModelViewProjectionMatrix;

in vec3 iv_Vertex;
noperspective out vec2 vVertCoord;

void main( void )
{
    gl_Position = im_ModelViewProjectionMatrix * vec4(iv_Vertex, 1.0);
    vVertCoord = (gl_Position.xy / gl_Position.w) * (0.5) + vec2(0.5);
}

我基本上用透视分割从剪辑空间位置计算归一化设备坐标 (NDC),然后将 NDC(范围从 [-1,1])映射到区间 [0,1]。这对于全屏四边形非常有用(即使没有透视分割,因为坐标非常简单)。我需要在我的延迟渲染器中绘制为光几何的任意几何虽然有一些问题。在顶点着色器中,我将vVertCoord 输出为红色=x 和绿色=y 的颜色:

#version 150

noperspective in vec2 vVertCoord;
out vec4 colorOut;

void main( void )
{
    colorOut = vec4(vVertCoord.x, vVertCoord.y, 0, 1);
}

这是当我在点光球内时的结果,一切看起来都很好(黑线是故意渲染的):

但是,如果我靠近光几何,结果如下:

左上角的那个红色补丁在那里做什么?您不希望在禁用调试颜色的情况下以真实颜色查看结果,因为它看起来像 lsd-trip,当您移动相机时,一切都会扭曲。这与精度有关吗?请注意,当我在像素着色器中使用 gl_FragCoord 时,一切都很好。

【问题讨论】:

  • 纹理坐标基于 gl_FragCoord 而不是普通的插值属性有什么特别的原因吗?在第一个示例中,这已经消除了除法的需要。 p.s.而不是 vScreenSize 你可以有屏幕尺寸的倒数(把除法变成乘法)
  • 您可能不想要后者,这将消除进行任何纹理过滤的可能性。虽然您可能认为您并不真正关心这一点,但在延迟着色引擎中,以不同的分辨率进行 G-Buffer 生成和着色可能很有用(特别是在通常填充率非常有限的低端硬件上)。

标签: opengl glsl


【解决方案1】:

如果你只是从你的顶点着色器传入一个插值顶点坐标,你不需要做任何特殊的数学运算。

例如,如果您正在绘制一个覆盖屏幕的简单单位正方形,您可以然后会收到全屏纹理,您可以执行以下操作:

vertex shader pseudocode:
layout(position = 0) in vec3 in_vertex;
out vec3 out_vertex;
void main()
{
    //Do your matrix multiplications to your in_vertex to get its final position...
    out_vertex = in_vertex;
}

然后,您的顶点着色器将正确插入 in_vertex 到 x:0...1, y:0...1 范围内(只要您正在绘制一个单位正方形)并将其传递给您的片段着色器。然后,您的片段着色器将像这样使用它:

fragment shader pseudocode:
in vec3 out_vertex;
uniform sampler2D tex;
void main()
{
    gl_fragcolor = texture(tex,vec2(out_vertex.x,out_vertex.y));
}

只要注意 out_vertex 只会在 0...1 的范围内,就不需要其他数学运算。稍微扩展一下这个例子,想象这里是我们的正方形:

(0,1)+-----------+(1,1) | | | | | | | | | | (0,0)+-----------+(0,1)

我们想在精确的中心对这个点进行采样:

(0,1)+-----------+(1,1) | | | | | * | | | | | (0,0)+-----------+(0,1)

我们的顶点着色器会自动从其他 4 个位置插入该位置,并将以下 vec3 传递给片段着色器:

out_vertex = vec3(0.5,0.5,0);

然后可以用来成功采样纹理

【讨论】:

  • 我相信你仍然需要在你的顶点着色器中设置gl_Position。如果您按照@redsoxfantom 的建议绘制一个单位正方形,则可以使用gl_Position = vec4(2.0 * out_vertex, 0.0) - 1.0; 之类的东西。您不需要任何矩阵乘法。如果您需要更多解释和细节,请告诉我。在评论中很难涵盖它,但我可以将其发布为答案。
  • 很好的答案,虽然我最终得到了不同的代码,因为我并不总是绘制全屏四边形,还绘制光几何体,在这种情况下顶点坐标没有多大用处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-21
  • 2014-05-04
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多