0

I'm trying to get a screen position of a vertex in pixels inside a vertex shader, I saw some others posts here but I can't find answer that works for me. this is what I've got in my vertex Shader:

#version 400
layout (location = 0) in vec3 inPosition;
uniform mat4 MVP; // modelViewProjection
uniform vec2 window;

void main()
{
    // vertex in screen space
    vec2 fake_frag_coord = (MVP * vec4(inPosition,1.0)).xy;
    float X = (fake_frag_coord.x*window.x/2.0) + window.x;
    float Y = (fake_frag_coord.y*window.y/2.0) + window.y;
}

It's not working very well and I know it's a strange think to do inside a vertex shader but I want to multiply my vertex offset by a 2d texture, so I need to find the pixel the vertex is on top to be able to multiply it by the pixel of the texture.

thanks! Luiz

user1822451
  • 101
  • 1
  • 5
  • 4
    Is your transform perspective or orthographic? If perspective, you have to perform perspective division. Take a look at http://stackoverflow.com/questions/23603231/offset-gl-position-or-gl-vertex-by-pixels-value, questions are quite close. – keltar May 28 '14 at 11:42
  • I don't get why you want the value in pixels. fake_frag_coord will be in the range [-1,1]. You just need to multiple by 0.5 and add 0.5 so it is in the range [0, 1], then call `texture` instead of `texelFetch`. Since you have `X` and `Y` as float anyway `texelFetch` loose a bit of its purpose. And +1 @keltar. You must perform a perspective division (division by w I think). – agrum May 28 '14 at 18:44
  • If you do want to keep the pixel coordinate though, add by `window.x/2` and `window.y/2`, not `window.x` and `window.y`. Don't forget the perspective division still. – agrum May 28 '14 at 18:48
  • @keltar: You have to perform division by `w` regardless whether it is a perspective projection or not. An orthographic projection produces a constant `w` coordinate *(as opposed to perspective, whose `w` coordinate varies with distance down the z-axis)*, but there is no guarantee that the value is constantly **1.0**. This is why I prefer to avoid the term "perspective divide" and favor "homogeneous divide" instead (though many people don't know what you are talking about =p) – Andon M. Coleman Jun 05 '14 at 07:50
  • @AndonM.Coleman well, term is not mine.. But interesting. The only case when (as I see it) it could be not 1.0 is when vertex itself initially have special *w*. Does it have use cases? Thanks for correction, of course. – keltar Jun 05 '14 at 10:36
  • @keltar: Yes, that is one possibility. But more practically, consider a situation where `m44` in an orthographic projection matrix is changed from **1.0** to some other value. Using **0.5** for `m44` will shrink the viewing volume by half, thus objects will appear to double in size. Using **2.0** for `m44` will grow the viewing volume by a factor of **2.0**, thus objects will appear to shrink. Using a non-**1.0** value for `m44` can be used to apply uniform scaling to a scene with minimal effort. Doing the same thing with a scale matrix would involve a lot more work. – Andon M. Coleman Jun 05 '14 at 17:30

3 Answers3

1

I have corrected your vertex shader with proper terms, and shown you the exact sequence of transformations that actually happens when GL computes gl_FragCoord (window-space).

#version 400
layout (location = 0) in vec4 inPosition; // Always use vec4, it makes life easier!
uniform mat4 MVP; // modelViewProjection
uniform vec2 window;

void main()
{
    // Vertex in clip-space
    vec4 fake_frag_coord  = (MVP * inPosition);     // Range:   [-w,w]^4

    // Vertex in NDC-space
    fake_frag_coord.xyz /= fake_frag_coord.w;       // Rescale: [-1,1]^3
    fake_frag_coord.w    = 1.0 / fake_frag_coord.w; // Invert W

    // Vertex in window-space
    fake_frag_coord.xyz *= vec3 (0.5) + vec3 (0.5); // Rescale: [0,1]^3
    fake_frag_coord.xy  *= window;                  // Scale and Bias for Viewport

    // Assume depth range: [0,1] --> No need to adjust fake_frag_coord.z

    [...]
}

Texture coordinates and window-space coordinates are very different things, however. Generally you need normalized coordinates for traditional texture fetches, that means you want the coordinates in the range [0,1].

Luckily window-space and texture-space share the same origin convention (0,0) = bottom-left, so you can cut out the line below to get the appropriate texture coordinates:

    fake_frag_coord.xy  *= window;                  // Scale and Bias for Viewport
Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • 2
    commenting on Andon M. Coleman's answer: the line `fake_frag_coord.xyz *= vec3 (0.5) + vec3 (0.5);` should really be `fake_frag_coord.xyz *= vec3(0.5); fake_frag_coord.xyz += vec3(0.5);` (PS: I am not allowed to comment directly) – vaifrax Feb 22 '17 at 22:37
1

I think Andon M. Coleman's answer is fine. However, I like to point out a more general issue with the approach discussed in the question: there might be no meaningful screen space position for a vertex at all.

The vertex might lie utside the viewing frustum. This will not be a a problem if the vertices you draw are guaranteed to lie in the frustum, or if you are drawing only points.

But it will fail if you have primitives intersecting the near plane. You might think that in such a case, you just get some coordinates which are outside [-1,1] in NDC space, and if you just use them to assign some output value for the vertex, the clipping state will make it right. But that assumption is wrong. You might values which are pefectly in [-1,1] in NDC space even for vertices which are outside the frustum, and it it will appear as if the vertices lie in front of the camera for all vertices wich actually lie behind the camera. And no subsequent clipping stage is able to fix this.

The only way to get this right would be to actually carry out the clipping operation, before doing the divide by w. And this is something you don't want to do in a vertex shader.

derhass
  • 43,833
  • 2
  • 57
  • 78
0

If you want to get this working on the js part of things, this is how I adapted Andon M. Coleman's reply:

var winW = window.innerWidth;
var winH = window.innerHeight;

camera.updateProjectionMatrix();
// Not sure about the order of these! I was using orthographic camera so it didn't matter but double check the order if it doesn't work!
var MVP = camera.projectionMatrix.multiply(camera.matrixWorldInverse);

// position to vertex clip-space
var fake_frag_coord = position.applyMatrix4(MVP);                       // Range:   [-w,w]^4

// vertex to NDC-space
fake_frag_coord.x = fake_frag_coord.x / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.y = fake_frag_coord.y / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.z = fake_frag_coord.z / fake_frag_coord.w;  // Rescale: [-1,1]^3
fake_frag_coord.w = 1.0 / fake_frag_coord.w;                                // Invert W

// Vertex in window-space
fake_frag_coord.x = fake_frag_coord.x * 0.5;
fake_frag_coord.y = fake_frag_coord.y * 0.5;
fake_frag_coord.z = fake_frag_coord.z * 0.5;

fake_frag_coord.x = fake_frag_coord.x + 0.5;
fake_frag_coord.y = fake_frag_coord.y + 0.5;
fake_frag_coord.z = fake_frag_coord.z + 0.5;

// Scale and Bias for Viewport (We want the window coordinates, so no need for this)
fake_frag_coord.x = fake_frag_coord.x / winW;
fake_frag_coord.y = fake_frag_coord.y / winH;
Community
  • 1
  • 1
Robert Rodriguez
  • 181
  • 1
  • 12