0
\$\begingroup\$

I started learning open gl and graphics programming for a while (im using open tk as im working on c#) so i finally came across lighting where you make the ambient diffusion. Im pretty sure that you need to find the dot product between the direction between the light and the fragment position with the normal. But after a while i was puzzled on how to find the fragment position. If you look at the vertex shader here:

out vec3 FragPos;  
out vec3 Normal;
  
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
}

(The code is from learnOpengl) it sets the fragpos to the vec3 of each vertex position and pass it to the fragment shader. I mean fragments are the parts where it shows the objects colour right? how could vertex position could be each fragment position. I don't know if i have misunderstood things completely but after a few words in the opengl website it says

"This in variable will be interpolated from the 3 world position vectors of the triangle to form the FragPos vector that is the per-fragment world position. Now that all the required variables are set we can start the lighting calculations." i dont get this phrase.

Also this is my first time posting here so please point out any mistakes if i did any.

\$\endgroup\$
2
  • \$\begingroup\$ The part you quoted is the answer: the three values of FragPos from three vertices of a triangle will be interpolated to produce the coords for each fragment. Is there a specific part you don't understand about that quote, or a specific word? Do you understand what's "interpolation"? \$\endgroup\$ Commented Nov 14, 2023 at 13:32
  • \$\begingroup\$ No i know what interpolation is, but i was not satisfied with the information on how it is converted. \$\endgroup\$ Commented Nov 16, 2023 at 3:46

1 Answer 1

0
\$\begingroup\$

After the vertex shader has run for a particular group of vertices, there are a few extra stages that happen automatically before invoking the fragment shader for the resulting fragments:

  1. Primitive assembly: this step operates on a sequence of three vertices A, B, C that are meant to form a triangle (according to your index buffer or strip/fan topology).

    This is where clipping happens if the triangle crosses the image plane or goes outside the rasterization bounds, cutting it into triangles that stay in-bounds to avoid nasty projection errors.

    This is also where we do back face culling, checking the winding order (when we move from A to B to C's on-screen positions, do we arc clockwise or counter-clockwise?) and discarding it if it's wound the wrong way.

    Triangles ABC that survive proceed to...

  2. Rasterization: this step figures out which pixels / fragments of the output buffer triangle ABC touches, that we should draw into in the fragment shading stage. We'll invoke the fragment shader once for each one, but we need to do a little more calculation first to figure out the inputs to use when we call that shading function.

    We assign each fragment the triangle touches a barycentric coordinate (a, b, c) where a+b+c=1. A fragment exactly at the screen position of vertex A gets a=1, b=0, c=0; one exactly at vertex B gets a=0, b=1, c=0. A fragment exactly halfway between vertices A and B in screen space gets a=0.5, b=0.5, c=0. A fragment at the exact centroid of the triangle in screen space has a=b=c=1/3.

    These can be used as blending weights to form a weighted average between properties set at the three vertices, but we need one more stage to finish the job...

  3. Interpolation: this step is responsible for taking the values of out or varying variables set by the vertex shader, and transforming them into the input values to the fragment shader.

    To do that, we (usually) need to apply perspective correction to the barycentric coordinates we have for each fragment.

    The formula for this uses the screen-space barycentric coordinates a b c we sourced above, and the w component of the gl_position from each of A, B, and C (which tells us how far away the vertex is/how much perspective magnifies/shrinks stuff nearby):

    $$\begin{bmatrix}a^\prime, & b^\prime, & c^\prime\end{bmatrix} = \frac {\begin{bmatrix}\frac a {A_w},& \frac b {B_w}, & \frac c {C_w}\end{bmatrix}} {\frac a {A_w} + \frac b {B_w} + \frac c {C_w}}$$

    Now for each variable \$v\$ calculated from the vertex shader, for which we have three values from the three vertices, \$v_A\$, \$v_B\$, \$v_C\$, we calculate a blended value that will be used as input for this fragment as:

    $$v_F = a^\prime v_A + b^\prime v_B + c^\prime v_C$$

    That includes the world position of the vertex that we output from the vertex shader you showed as FragPos. Before invoking the fragment shader, we use the formula above to calculate a blended world position for this fragment based on how close it is to each vertex, taking into account the depth via the w component. That then becomes the input value of FragPos that the fragment shader sees.

    (I say "usually" above because you can choose other behaviours to achieve certain effects, like keeping the value from one "provoking" vertex constant across the whole triangle and discarding the values from the other vertices, or turning off perspective correction to do the interpolation in screen space. But perspective correction is the default and we rarely need to change it.)

So that's the gory details of how we go from a vertex position written out from the vertex shader to an intermediate fragment position read in by the fragment shader.

Does that clear up how we get per-fragment positions from the initial vertex positions?

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for your answer, I was pretty confused with the process of how it is converted it just doesnt feel right when you learn something but doesnt chave an overview on how it is done. \$\endgroup\$ Commented Nov 16, 2023 at 3:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .