gl_FragCoord and glViewport

#1

I’ve been scanning the specifications for both OpenGL and GLSL, and I can’t find a definitive answer as to how exactly are gl_FragCoord's x and y components computed, other than they are the

fragment’s window-space position

(OpenGL 4.6 specification, page 491)

which does not help me understand why glViewport() doesn’t seem to affect the values of gl_FragCoord in the fragment stage, since that is the case for window-space coordinates in the vertex stage.

The actual problem I’m trying to solve is rendering a bunch of draws (that depend on gl_FragCoord for their shading) to an atlas, without modifying the shaders of the draws to be aware of the atlas. Here’s pseudocode for the driving part (all viewports have identical dimensions, just different offsets):

	for (size_t i = 0; i < FrameCount; ++i)
	{
		auto& Vp = Viewports[i];
		glViewport(Vp.X, Vp.Y, Vp.X + Vp.Width, Vp.Y + Vp.Height);
		BindConstants(Vp.Width, Vp.Height);
		glDrawArrays(GL_TRIANGLES, 0, Triangles);
	}

BindConstants() simply binds a uniform with the viewport’s width & height. When trying to simply output the UVs with the following shader:

#version 330
uniform vec2 Resolution;

void main(void)
{
	vec2 UV = gl_FragCoord.xy / Resolution;
	gl_FragColor = vec4(UV, 0.0, 1.0);
}

I get the following image, which clearly shows that colour is being clipped to 1.0 beyond the first viewport in a 5x5 atlas:

Frame buffer after running the above shader

I’ve also done additional simple tests that show that gl_FragCoord is based off of the entire frame buffer’s origin, rather than what’s specified via glViewport().

I have worked around this issue by rendering to a temporary FBO and blitting to the target frame buffer at the desired offset, but it’s silly to go to such lengths for a dead-simple use-case like this.

Am I missing something? Is it possible at all without a temporary FBO, and, above all, without modifying the shaders?

#2

It does affect them; you can see it affecting them. Your problem is that you seem to think that “window-space coordinate” is viewport-local. It isn’t.

Window space, as the name suggests, is in the space of the window. (0, 0) is at the bottom left of the framebuffer; (width, height) is at the top-right. gl_FragCoord.xy will get values within this rectangular region.

The purpose of the viewport transform is to transform from NDC space to window-space. NDC space is viewport-local; (-1, -1) in NDC space is the bottom-left of the viewport.

You can’t. So long as it uses gl_FragCoord, the shader will know where that fragment is being rendered within the viewport. The only way to do what you’re trying to do is to make the shader transform gl_FragCoord into a viewport-local coordinate system.

That requires providing it the viewport and doing a transformation with it. Which mean you have to modify the shader to do exactly that.

Alternatively, you can pass NDC-space coordinates from your vertex shader to the FS. Just take gl_Position.xy and divide it by gl_Position.w.

1 Like
#3

Thanks for explaining. As I have limited options for modifying the incoming shaders, I think I’ll stick to my FBO workaround.

However:

It does affect them; you can see it affecting them. Your problem is that you seem to think that “window-space coordinate” is viewport-local. It isn’t.

You are contradicting yourself here. :slight_smile: If that coordinate isn’t viewport-local, then clearly the viewport isn’t affecting them.

To my defense, the language surrounding viewport control in the specification isn’t exactly crystal-clear. See chapter 13.8.1, page 458:

viewport

So please forgive me for assuming that variables denoted using the same symbols and the same language (“window coordinate”), even if in slightly different contexts, actually function similarly.

#4

I guess that all depends on how you define “affecting them”. I would define “X affecting Y” as “the math that goes into computing X involves the use of Y”. Which is how it works.

They do function similarly. I have no idea what you’re referring to here. The math itself is quite simple.

I think you should run through the math a few times, using NDC-space coordinates and known viewport settings. You’ll find that OpenGL matches up with that.