3

I want to compute the eye-space width of a pixel's projected pyramid at the current vertex location in a glsl vertex shader, but I can't seem to get the math right. Here is an obviously incorrect example:

// GLSL VERTEX SHADER
#version 410 compatibility

uniform vec4 viewport; // same as glViewport​
in vec4 gl_Vertex;

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelWidth = gl_Position.z / viewport.z;
<snip>

But this does not account for the FOV or clipping planes.

atb
  • 1,412
  • 1
  • 14
  • 30

2 Answers2

2

I worked through the math and figured it out. :) As I had hoped there are no additional matrix transformations required, just one divide:

// GLSL VERTEX SHADER
#version 410 compatibility

uniform vec4 viewport; // same as glViewport​
in vec4 gl_Vertex;

float pixelWidthRatio = 2. / (viewport.z * gl_ProjectionMatrix[0][0]);

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelWidth = gl_Position.w * pixelWidthRatio;
    <snip>

Or alternatively:

<snip>
float pixelHeightRatio = 2. / (viewport.w * gl_ProjectionMatrix[1][1]);

void main ()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    float pixelHeight = gl_Position.w * pixelHeightRatio;
<snip>

As expected, pixelWidth and pixelHeight are the same if the pixels are square.

atb
  • 1,412
  • 1
  • 14
  • 30
1

In a vertex shader there is no such thing as a world-space width of a pixel. "Width" is projected into a pixel only at the given Z-distance. In general you have a pyramid projected into a pixel.

Here you go:

  1. Convert two screen-points into NDC points:

        vec2 Screen = vec2( ScreenWidth, ScreenHeight );
        vec3 Point1 = vec3( ScreenPt1 / Screen * 2.0 - vec2( 1.0 ), 1.0 );
        vec3 Point2 = vec3( ScreenPt2 / Screen * 2.0 - vec2( 1.0 ), 1.0 );
    
  2. Unproject NDC points into world-space positions:

        vec4 R1 = vec4( Point1, 1.0 );
        vec4 R2 = vec4( Point2, 1.0 );
    
        R1 = Projection.GetInversed() * R1;
        R1 = ModelView.GetInversed() * R1;
        R1 /= R1.W;
    
        R2 = Projection.GetInversed() * R2;
        R2 = ModelView.GetInversed() * R2;
        R2 /= R2.W;
    
  3. Find the distance between R1 and R2.

In a fragment shader you can use local derivatives. Take a look here:

http://www.opengl.org/sdk/docs/manglsl/xhtml/dFdx.xml

and here:

http://www.opengl.org/sdk/docs/manglsl/xhtml/fwidth.xml

Available only in the fragment shader, dFdx and dFdy return the partial derivative of expression p in x and y, respectively. Deviatives are calculated using local differencing. Expressions that imply higher order derivatives such as dFdx(dFdx(n)) have undefined results, as do mixed-order derivatives such as dFdx(dFdy(n)). It is assumed that the expression p is continuous and therefore, expressions evaluated via non-uniform control flow may be undefined.

Sergey K.
  • 24,894
  • 13
  • 106
  • 174
  • 1
    These functions are available only in the fragment shader and I am asking about the vertex shader, so this doesn't help me. – atb Jun 07 '12 at 18:35
  • OK, using your semantics I need the world-space width of the "pixel pyramid" at the z-depth of the current vertex in the vertex shader. Your answer still doesn't help me, but thanks for the info. – atb Jun 07 '12 at 18:45
  • But I only have one point, and your example code does not account for viewport resolution... – atb Jun 07 '12 at 18:54
  • That is nasty. Updated. And you have 2 points: your (X, Y, Z) and (X, Y+1, Z). Here +1 means one pixel. – Sergey K. Jun 07 '12 at 19:01
  • I see what you are doing, thanks. Still, I believe it should be possible to compute the width directly using only gl_Position.zw and glViewport​.xz, without actually transforming any points. Your example might be too expensive for me... – atb Jun 07 '12 at 19:17
  • You cannot do it without modelview and projection matrices in general case. For some special case you can use school-geometry to solve triangles based on FOV, but this will reuire trigonometric functions that will kill the performance :) – Sergey K. Jun 07 '12 at 19:29
  • You're answer isn't wrong, just more complex than necessary. I worked through it myself and posted an answer, let me know what you think. Thanks again for all your help! – atb Jun 11 '12 at 14:45