How to get flat normals on a cube
Asked Answered
H

3

6

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.

Hymnal answered 20/2, 2013 at 13:12 Comment(0)
B
12

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 
      ...
      ...
    }
Biddle answered 20/2, 2013 at 13:48 Comment(6)
Could you explain better what is dFdx/dFdy and why does it work? It is the direction of the surface?Marietta
The compute the gradients, given an input value. So in this case; dfdx computes the gradient along the x-axis in view-space and dfdy calculates the gradient along the y-axis. And from these two gradient vectors, you can then calculate the normal, which should be the same for all the vertices on the triangle.Biddle
I'll just add; it's not always clear how exactly GPU will implement the calculation of the gradients, but you can assume that in this case, they are the X & Y gradients of the triangle in view-space most likely on the change of position of the neighboring pixels or vertices.Biddle
opengl.org/discussion_boards/showthread.php/… has a similar answer but shows how to pass the value from the vertex shader.Rhizobium
AFIK the gradients are computed numerically by evaluating adjacent pixels. Even if adjacent pixels (in 2x2 "quad"s) aren't needed fragments are computed, called "helper" pixels. I think this is commonly used for mipmapping behind the scenes when you call texture().Adminicle
@JamesSteele This is beautiful. Not only it solves the problem in a super clever way, it aslo moves vertex normals calculation entirely into shader code.Osmen
P
6

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();
}
Plumbic answered 20/2, 2013 at 13:44 Comment(1)
I also checked, even though this code was originally written for version 400, it should work correctly with version 330 as is.Plumbic
S
3

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.

Siblee answered 20/2, 2013 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.