3
votes

I currently am developing a program that displays buildings and terrain in 3D in Java's LWJGL framework, which I understand is very similar to OpenGL. I am trying to differentiate between sides of buildings using flat shading. For example, a cube generated by my program should look like this:

enter image description here

To achieve this, I attempted to implement the method here:https://gamedev.stackexchange.com/questions/152991/how-can-i-calculate-normals-using-a-vertex-and-index-buffer.

It seems this is vertex shading, which colors every single pixel in a sort of gradient. My implementation currently looks like this:

enter image description here

Obviously this doesn't look like what I desired. Here are my vertex and fragment shaders:

#version 150

in vec3 position;
in vec2 textureCoordinates;
in vec3 normal;

out vec2 pass_textureCoordinates;
out vec3 surfaceNormal;
out vec3 toLightVector;
out vec3 toCameraVector;

uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;

void main(void){

    vec4 worldPosition = transformationMatrix * vec4(position, 1.0);

    gl_Position = projectionMatrix * viewMatrix * worldPosition;
    pass_textureCoordinates = textureCoordinates;

    surfaceNormal = (transformationMatrix * vec4(normal, 0.0)).xyz;
    toLightVector = lightPosition - worldPosition.xyz;
    toCameraVector = (inverse(viewMatrix) * vec4(0.0,0.0,0.0,1.0)).xyz - worldPosition.xyz;

}
#version 150

in vec2 pass_textureCoordinates;
in vec3 surfaceNormal;
in vec3 toLightVector;
in vec3 toCameraVector;

out vec4 out_Color;

uniform sampler2D modelTexture;
uniform vec3 lightColour;
uniform float shineDamper;
uniform float reflectivity;

void main(void){

    vec3 unitNormal = normalize(surfaceNormal);
    vec3 unitLightVector = normalize(toLightVector);

    float nDotl = dot(unitNormal, unitLightVector);
    float brightness = max(nDotl, 0.7);
    vec3 diffuse = brightness * lightColour;

    vec3 unitVectorToCamera = normalize(toCameraVector);
    vec3 lightDirection = -unitLightVector;
    vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);

    float specularFactor = dot(reflectedLightDirection, unitVectorToCamera);
    specularFactor = max(specularFactor, 0.0);
    float dampedFactor = pow(specularFactor, shineDamper);
    vec3 finalSpecular = dampedFactor * reflectivity * lightColour;


    out_Color = vec4(diffuse, 1.0) * texture(modelTexture,pass_textureCoordinates) + vec4(finalSpecular, 1.0);

}

If a cube-shaped building I'm trying to render is indexed like so: enter image description here

How can I create the array of normals programmatically for flat shading?

1
"LWJGL ... which I understand is very similar to OpenGL" - LWJGL's job is to expose the C APIs of native libraries to Java using the Java Native Interface (JNI). Nothing more and nothing less. And one of those native libraries is OpenGL. So when you use the OpenGL binding of LWJGL you are using OpenGL.Kai Burjack

1 Answers

0
votes

The "sort of gradient" is caused by the Specular highlights. If the normal vectors are normal to the faces of the cube, then a Lambertian diffuse light will give you a flat look.
All you have to do is to initialize the uniform variable reflectivity by 0.0. (actually that's the default value).


It is possible to compute the face normal vectors by partial derivatives (dFdx, dFdy) in the fragment shader or by the cross product in the geometry shader. But this options will cause a loss of quality and speed.

See
From within a fragment shader how do I get the surface - not vertex - normal
3D object is colored in a way that looks like a 2D object