I'm reading these tutorials about modern OpenGL. In tutorial 5, there is an exercise for drawing an extra triangle besides a cube. What I understand is that I can reuse same vertex shader for drawing multiple triangles (i.e. triangles for cube and an extra independent triangle). My problem is with the vertex shader which is
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec3 vertexColor;
layout(location = 2) in vec3 vertexTriangle_modelspace;
// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1); // Cube
//gl_Position = MVP * vec4(vertexTriangle_modelspace,1); // Triangle
// The color of each vertex will be interpolated
// to produce the color of each fragment
fragmentColor = vertexColor;
}
It draws only one gl_Position and the last one. Is it possible to output multiple gl_Positions for one vertex shader?
Vertex shaders don't render triangles. Or cubes. Or whatever. They simply perform operations on a vertex. Whether that vertex is part of a triangle, line strip, cube, Optimus Prime, whatever. It doesn't care.
Vertex shaders take a vertex in and write a vertex out. Objects are made up of multiple vertices. So when you issue a rendering command, each vertex in that command goes to the VS. And the VS writes one vertex for each vertex it receives.
Related
The only practical difference about Phong shading and Goraud shading is that if the calculation of fragment color is done in the vertex shader then it's Goraud else it's phong. I've got a little code of vertex shader and fragment shader code below:
//vertexShader.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
//FragmentShader.fs
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0),32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
It turns out the Phong is a bit more smooth looking on low poly objects. So, the knowledge barrier is basically in how the fragments get shader. First let's look at what the wonderful resource learnopenGL already provides.
In summary, it tells us that the color value supplied at the vertices are interpolated within the boundary of the fragment made by the vertices. So, this is quite meaningful like something like an weighted average is taken for the colors making the vertices.
But, like Phong shading model where the calculation is done right in the fragment shader, what happens? How are the fragment pixels shaded? Say there are three vertices then is the fragment fully colored with the first color, fully with second and then with third and is the total median of the colors taken or something? How is the fragment shaded when the calculated within the fragment shader?
The vertex shader is executed once for each vertex. The fragment shader is executed once for each fragment. The outputs of the vertex shader are interpolated depending on the Barycentric coordinate of the fragment on the triangle primitive and that's the input to the fragment shader.
The input to the fragment shader is different for each fragment (because it is interpolated).
In you special case this means that, Normal and FragPos are different for each fragment. Each triangle has 3 corners. Normal and FragPos are computed for each corner of the triangle in the vertex shader. The attributes for the corners are interpolated for each fragment which is covered by the triangle and that interpolated vectors are the input to the fragment shader.
Since each fragment has a different input (Normal and FragPos) the comuted output (FragColor) is different for each fragment.
The output is just slightly different for neighboring fragments, because the input differs only slightly, too. That causes the smooth lighting.
Note, even if the normal vector (Normal) is a face normal (the same normal for the 3 vertices), then still FragPos is different.
Furthermore the spcular highlight (float spec = pow(max(dot(viewDir, reflectDir), 0.0),32)) is not a linear function. Thus the specular highlight can't be computed correctly by a linear interpolation. It has to be computed per fragment.
Actually there is a difference if Normal and FragPos are interpolated and result is computed in the fragment shader, in compare when result is computed in the vertex shader and is interpolate through the fragments.
The vertex attributes are the input to the vertex shader. The output of the vertex shader is interpolated (always) and the interpolated values are the input to the fragment shader (Rasterization). The output of the fragment shader is written to the framebuffer:
vertex attrtibutes -> vertex shader -> interpolation/rasterization -> fragment shader -> framebuffer.
So, there is a difference if you interpolate Normal and FragPos and compute result in the fragment shader or if you compute result in the vertex shader and interpolate result
For a further information about the rendering pipeline see Rendering Pipeline Overview.
The difference between Phong an Gouraud shading is that:
Gouraud averages colors.
Color is computed based on vertex normal by Vertex Shader and then averaged between fragments of a single triangle based on the distance from each triangle vertex.
Phong averages normals.
Color is computed by Fragment Shader based on averaging of 3 vertex normals of triangle passed through from Vertex Shader.
On high-polygonal mesh both give very close result, as dispersion of per-triangle colors become smaller.
On low-poly mesh, averaging shaded colors in Gouraud gives worse visual result, because averaging colors has close to none physical meaning.
Averaging normals within Phong shading simulates smooth surface properties, so that their averaging might be close or even match original analytical surface definition, leading to much more reasonable and smooth visual results.
The averaging is done not by Shader program itself, but by a fixed hardware functionality between Vertex and Fragment shader stages. So that when you compute color or pass-through normal / UV coordinates and similar in Vertex Shader, these values are interpolated by hardware between 3 vertices across all fragments inside triangle based on fragment barycentric coordinates.
The color computed within Fragment Shader is a final one (before applying Blending or Stencil test, which are also done by fixed hardware functionality). So that putting lighting computation inside Vertex of Fragment shader defines what will be interpolated and what is computed directly.
Simple question, I just got my first specular shader working, and looking over the math, I cant help to think that the angle between each edge should cause the "specularity" to spike/become jagged. But its entirely fluid/spherical.
The idea is to calculate the angle off the vertice-normal, but there are only so many of these, and still the "specular shade" turns out perfectly even.
I cant see how the gpu knows the angle of the fragment based off of the vertice normal alone.
edit:
vert shader
#version 400 core
layout ( location = 0 ) in vec3 vertex_position;
layout ( location = 2 ) in vec2 tex_cord;
layout ( location = 3 ) in vec3 vertex_normal;
uniform mat4 transform; //identity matrix
uniform mat3 lmodelmat; //inverse rotation
out vec2 UV;
out vec3 normal;
void main()
{
UV=tex_cord;
normal=normalize(vertex_normal*lmodelmat); //normalize to keep brightness
gl_Position=transform*vec4(vertex_position,1.0);
}
and frag
#version 400 core
in vec2 UV;
in vec3 normal;
uniform sampler2D mysampler;
uniform vec3 lightpos; //lights direction
out vec4 frag_colour;
in vec3 vert2cam; //specular test
void main()
{
//skip invis frags
vec4 alphatest=texture(mysampler,UV);
if(alphatest.a<0.00001)discard;
//diffuse'ing fragment
float diffuse=max(0.1,dot(normal,lightpos));
//specular'izing fragment
vec3 lpnorm=normalize(lightpos); //vector from fragment to light
vec3 reflection=normalize(reflect(-lpnorm,normal)); //reflection vector
float specularity=max(0,dot(lpnorm,reflection));
specularity=pow(specularity,50);
frag_colour=alphatest*diffuse+specularity;
}
Answer: Interpolation
This will, for the renderer, equate as an averaged curve, and not a jagged edge (flat shading)
Without code .etc. it is hard to precisely answer your question but assuming a simple vector shader -> fragment shader pipeline. The vector shader will be run for each vertex. It will set typically set parameters marked 'varying' (e.g. texture coordinates).
Every 3 vertices will be grouped to form a polygon and the fragment shader run to determine the color of each point within the polygon. The 'varying' parameters set by the vertex shader will be interpolated based on the distance of the fragment from the 3 edges of the polygon (See: Barycentric interpolation).
Hence for example:
gl_FragColor = texture2D(myUniformSampler, vec2(myTextureCoord.s,myTextureCoord.t));
Will sample the texture correctly for each pixel. Assuming you're using per-fragment lighting, the values are probably being interpolated for each fragment shader from the values you set in your vertex shader. If you set the same normal for each edge you'll get a different effect.
Edit (Based on the code you added):
out vec2 UV;
out vec3 normal;
out vec3 color;
Are set per vertex in your vertex shader. Every three vertices defines a polygon. The fragment shader is then run for each point (e.g. pixel) within the polygon to determine the color .etc. of each point.
The values of these parameters:
in vec3 color; /// <<-- You don't seem to be actually using this
in vec2 UV;
in vec3 normal;
in the fragment shader are interpolated based on the distance of the point on the polygon being 'drawn' from each vertex (See: Barycentric interpolation). Hence the normal varies between the vertices defined by your vertex shader.
If for a given polygon defined by three vertices you set the normals to all be facing in the same direction, you will get a different effect.
Since vertex shader is run once per vertex (that mean in triangle 3 times), how does the varying variable gets computed for every fragment, if it's assigned (as in the example) only three times?
Fragment shader:
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
Vertex shader:
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
v_Color = a_Color;
gl_Position = a_Position;
}
So, the question is, how does the system behind this know, how to compute the variable v_Color at every fragment, since this shader assigns v_Color only 3 times (in a triangle).
All outputs of the vertex shader are per vertex. When you set v_Color in the vertex shader, it sets it on the current vertex. When the fragment shader runs, it reads the v_Color value for each vertex in the primitive and interpolates between them based on the fragment's location.
First of all, it is a mistake to assume that the vertex shader is run once per-vertex. Using indexed rendering, primitive assembly can usually access the post T&L cache (result of previous vertex shader invocations) based on vertex index to eliminate evaluating a vertex more than once. However, new things such as geometry shaders can easily cause this to break down.
As for how the fragment shader gets its value, that is generally done during rasterization. Those per-vertex attributes are interpolated along the surface of the primitive (triangle in this case) based on the fragment's distance relative to the vertices that were used to build the primitive. In DX11 interpolation can be deferred until the fragment shader itself runs (called "pull-model" interpolation), but traditionally this is something that happens during rasterization.
Swapping order of two input variables corrupts rendering result. Why is that?
Little info about it's usage:
vertexPosition_modelspace has location 0 and vertexColor has location 1
I bind buffer storing vertex positions and set vertex attrib pointer, then I bind and set buffer for color
Right one:
#version 130
// Input vertex data, different for all executions of this shader.
in vec3 vertexColor;
in vec3 vertexPosition_modelspace;
// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// The color of each vertex will be interpolated
// to produce the color of each fragment
fragmentColor = vertexColor;
}
Wrong one:
#version 130
// Input vertex data, different for all executions of this shader.
in vec3 vertexPosition_modelspace; // <-- These are swapped.
in vec3 vertexColor; // <--
// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// The color of each vertex will be interpolated
// to produce the color of each fragment
fragmentColor = vertexColor;
}
Same problem is with texcoords and it took me hour to discover the problem. Why result gets corrupted if I put texcoord or color input after position? The order should not matter.
It's because of the order you use when you pass your data to shaders. In your OpenGL C or C++ code you are certainly sending the vertex color as the first vertex attrib, and then you send the position. If you intent to swap the order of parameters in the shader, you must swap the order of their initialization too.
I am using OpenGL without the deprecated features and my light calculation is done on fragment shader. So, I am doing smooth shading.
My problem, is that when I am drawing a cube, I need flat normals. By flat normals I mean that every fragment generated in a face has the same normal.
My solution to this so far is to generate different vertices for each face. So, instead of having 8 vertices, now I have 24(6*4) vertices.
But this seems wrong to me, replicating the vertexes. Is there a better way to get flat normals?
Update: I am using OpenGL version 3.3.0, I do not have support for OpenGL 4 yet.
If you do the lighting in camera-space, you can use dFdx/dFdy to calculate the normal of the face from the camera-space position of the vertex.
So the fragment shader would look a little like this.
varying vec3 v_PositionCS; // Position of the vertex in camera/eye-space (passed in from the vertex shader)
void main()
{
// Calculate the face normal in camera space
vec3 normalCs = normalize(cross(dFdx(v_PositionCS), dFdy(v_PositionCS)));
// Perform lighting
...
...
}
Since a geometry shader can "see" all three vertices of a triangle at once, you can use a geometry shader to calculate the normals and send them to your fragment shader. This way, you don't have to duplicate vertices.
// Geometry Shader
#version 330
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out vec3 gNormal;
// You will need to pass your untransformed positions in from the vertex shader
in vec3 vPosition[];
uniform mat3 normalMatrix;
void main()
{
vec3 side2 = vPosition[2] - vPosition[0];
vec3 side0 = vPosition[1] - vPosition[0];
vec3 facetNormal = normalize(normalMatrix * cross(side0, side2));
gNormal = facetNormal;
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gNormal = facetNormal;
gl_Position = gl_in[1].gl_Position;
EmitVertex();
gNormal = facetNormal;
gl_Position = gl_in[2].gl_Position;
EmitVertex();
EndPrimitive();
}
Another option would be to pass MV-matrix and the unrotated AxisAligned coordinate to the fragment shader:
attribute aCoord;
varying vCoord;
void main() {
vCoord = aCoord;
glPosition = aCoord * MVP;
}
At Fragment shader one can then identify the normal by calculating the dominating axis of vCoord, setting that to 1.0 (or -1.0) and the other coordinates to zero -- that is the normal, which has to be rotated by the MV -matrix.