What is the best way to sample fullscreen textures in a fragment shader, so for example the g-buffer in a deferred renderer, or the scene texture within a postprocess shader?
At the moment I use the following two ways:
Pass the screen size to the shader as a uniform and calculate (u,v) from gl_FragCoord
:
vec2 texCoord = gl_FragCoord.xy / vScreenSize;
vec3 c = texture( tex, texCoord ).rgb;
Does not seem to be ideal since a division is required and supplying the shader with the screen size is cumbersome.
Convert gl_FragCoord.xy
into an ivec
and use texelFetch
:
vec3 c = texelFetch( tex, ivec2(gl_FragCoord.xy), 0 ).rgb;
Also not ideal because the conversion from float
to int
is required.
So is there a better way? I really just want to sample a buffer at the exact point where my pixel shader is drawing.
// EDIT:
Ok, with the suggestions of interpolated texture-coordinates that come from the vertex shader I managed to figure out the following code:
Vertex shader:
#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);
}
I basically calculate normalized device coordinates (NDC) from the clip space position with the perspective division and then map the NDCs (that range from [-1,1]) into the interval [0,1]. This works great for fullscreen quads (even without the perspective division because coordinates are so simple). The arbitrary geometry I need to draw as light-geometry in my deferred renderer has some problems though. In the vertex shader I output vVertCoord
as a color for red=x and green=y:
#version 150
noperspective in vec2 vVertCoord;
out vec4 colorOut;
void main( void )
{
colorOut = vec4(vVertCoord.x, vVertCoord.y, 0, 1);
}
This is the result when I am inside a point-light-sphere, everything looks fine (the black lines are rendered on purpose):
However, if I move closer to the light-geometry this is the result:
What is that red patch in the top left corner doing there? You don't want to see the results in real-color with the debugging colors disabled because it looks like an lsd-trip, everything warps around when you move the camera. Is this precision related? Note that all is fine when I use gl_FragCoord in the pixel shader instead.
You don't have to do any special math if you just pass in an interpolated vertex coordinate from your vertex shader.
For example, if you were drawing a simple unit square that covered your screen, you could just and would then recieve the full screen texture, you could just do something like this:
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;
}
Your vertex shader will then properly interpolate the in_vertex to be on the range x:0...1, y:0...1 (as long as you're drawing a unit square) and pass it to your fragment shader. Your fragment shader will then use it like so:
fragment shader pseudocode:
in vec3 out_vertex;
uniform sampler2D tex;
void main()
{
gl_fragcolor = texture(tex,vec2(out_vertex.x,out_vertex.y));
}
No other math is needed, as long as you take care that the out_vertex will only ever be in the range of 0...1. To extend this example a bit, imagine here is our square:
(0,1)+-----------+(1,1)
| |
| |
| |
| |
| |
(0,0)+-----------+(0,1)
And we wanted to sample this point in the exact center:
(0,1)+-----------+(1,1)
| |
| |
| * |
| |
| |
(0,0)+-----------+(0,1)
Our vertex shader will automatically interpolate that position from the other 4 positions and pass the following vec3 to the fragment shader:
out_vertex = vec3(0.5,0.5,0);
Which can then be used to sample the texture successfully
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With