Opengl 采样全屏纹理的最佳方法

Opengl 采样全屏纹理的最佳方法,opengl,glsl,Opengl,Glsl,在片段着色器中采样全屏纹理的最佳方法是什么,例如延迟渲染器中的g缓冲区,或后处理着色器中的场景纹理 目前,我使用以下两种方法: 将屏幕大小作为均匀值传递给着色器,并从gl\u FragCoord计算(u,v): vec2 texCoord = gl_FragCoord.xy / vScreenSize; vec3 c = texture( tex, texCoord ).rgb; 这似乎并不理想,因为需要分割,并且为着色器提供屏幕大小很麻烦 将gl\u FragCoord.xy转换为ivec

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

目前,我使用以下两种方法:

  • 将屏幕大小作为均匀值传递给着色器,并从
    gl\u FragCoord
    计算(u,v):

    vec2 texCoord = gl_FragCoord.xy / vScreenSize;
    vec3 c = texture( tex, texCoord ).rgb;
    
    这似乎并不理想,因为需要分割,并且为着色器提供屏幕大小很麻烦

  • gl\u 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]。这对于全屏四边形非常有效(即使没有透视分割,因为坐标非常简单)。但是,我需要在延迟渲染器中绘制为灯光几何体的任意几何体存在一些问题。在顶点着色器中,我将输出为红色=x和绿色=y的颜色:

#version 150

noperspective in vec2 vVertCoord;
out vec4 colorOut;

void main( void )
{
    colorOut = vec4(vVertCoord.x, vVertCoord.y, 0, 1);
}
这是当我在点光源球体内时的结果,一切看起来都很好(黑线是故意渲染的):

但是,如果我移近灯光几何体,结果是:


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

如果只是从顶点着色器传入插值顶点坐标,则无需进行任何特殊的数学运算

例如,如果您正在绘制一个覆盖屏幕的简单的单位正方形,您可以并且将接收全屏纹理,您可以这样做:

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;
}
然后,顶点着色器将正确插值位于x:0…1,y:0…1范围内的in_顶点(只要绘制的是单位正方形),并将其传递给片段着色器。然后,片段着色器将像这样使用它:

fragment shader pseudocode:
in vec3 out_vertex;
uniform sampler2D tex;
void main()
{
    gl_fragcolor = texture(tex,vec2(out_vertex.x,out_vertex.y));
}
不需要其他数学运算,只要注意out_顶点将仅在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_FragCoord而不是普通插值属性有什么特殊原因吗?在第一个例子中,这已经消除了对该司的需要。p、 您可以使用屏幕大小的倒数(将除法转换为乘法),而不是vScreenSize。您可能不需要后者,这将消除进行任何纹理过滤的可能性。虽然您可能认为自己并不真正关心这一点,但在延迟着色引擎中,以不同的分辨率生成G缓冲区和着色(尤其是在填充率通常非常有限的低端硬件上)是很有用的。我相信您仍然需要在顶点着色器中设置
gl\u位置。如果您按照@redsoxfantom的建议绘制一个单位正方形,您可以使用类似于
gl\u Position=vec4(2.0*out\u vertex,0.0)-1.0。你不需要任何矩阵乘法。如果你需要更多的解释和细节,请告诉我。在评论中很难涵盖它,但我可以把它作为一个答案发布。答案很好,虽然我最终得到了一个不同的代码,因为我不总是绘制全屏四边形,但也绘制光几何体,在这种情况下,顶点坐标没有多大用处。