0

I am having some trouble with my Phong shading algorithm. It seems like the normals are off and I always get a flat shaded surface.

I am implementing deferred rendering, so in my geometry pass I store the vertex' position and normal in textures. In the lighting pass, I output the normals as color to check their "values".
EDIT 1 My Gbuffer uses two textures for storing the positions and normals. Both textures use the GL_RGB16F format.

EDIT 5 As you can see in this image enter image description here I do not get an elegant color transition. The head on the left is the result of my engine, on the right you can see the same model in Blender. In Blender, the normals (in blue) look correct (no duplicates, etc) but in my engine, they behave like a face-normal (every 4 showing in the same direction). I am using Assimp to load the models and I include the aiProcess_GenSmoothNormals flag when importing them.

The geometry-pass vertex shader:

#version 440 core

layout (location = 0) in vec4 positionOS;
layout (location = 1) in vec3 normalOS;
layout (location = 2) in vec2 uv;

out VertexData
{
    vec3 PositionVS;
    vec3 NormalVS;
    vec2 UV;
} vs_out;

uniform mat3 normalViewMatrix;
uniform mat4 modelViewMatrix;

struct Camera
{
    mat4 viewMatrix;
    mat4 projMatrix;
};

layout(std140, binding = 0) uniform CameraBlock
{
    Camera cam;
};

void main()
{
    vec4 positionVS = modelViewMatrix * positionOS;
    vs_out.PositionVS = positionVS.xyz;
    vs_out.NormalVS = normalViewMatrix * normalOS;
    vs_out.UV = uv;

    gl_Position = cam.projMatrix * positionVS;
}

The geometry-pass fragment shader:

#version 440 core

layout (location = 0) out vec3 PositionVS;
layout (location = 1) out vec3 NormalVS;

in VertexData
{
    vec3 PositionVS;
    vec3 NormalVS;
    vec2 UV;
} vs_in;

void main()
{
    PositionVS = vs_in.PositionVS;
    NormalVS = normalize(vs_in.NormalVS);
}

EDIT 4 Reading vertex values from Assimp (aiMesh)

void MeshLoader::prepareMesh(const aiMesh & mesh)
{
    { // vertices

        for (int i = 0; i < mesh.mNumVertices; i++) {
            if (mesh.HasPositions()) {
                glm::vec3 position = glm::vec3(mesh.mVertices[i].x, mesh.mVertices[i].y, mesh.mVertices[i].z);

                positions.emplace_back(position.x, position.y, position.z, 1);
            }

            if (mesh.HasNormals()) {
                normals.emplace_back(mesh.mNormals[i].x, mesh.mNormals[i].y, mesh.mNormals[i].z);
            }

            if (mesh.HasTangentsAndBitangents()) {
                tangents.emplace_back(mesh.mTangents[i].x, mesh.mTangents[i].y, mesh.mTangents[i].z);
                bitangents.emplace_back(mesh.mBitangents[i].x, mesh.mBitangents[i].y, mesh.mBitangents[i].z);
            }

            if (mesh.HasTextureCoords(0)) {
                uvs.emplace_back(mesh.mTextureCoords[0][i].x, mesh.mTextureCoords[0][i].y);
            }

        }
    }

    { // polygons
        numFaces = mesh.mNumFaces;

        for (int i = 0; i < numFaces; i++) {
            indices.push_back(mesh.mFaces[i].mIndices[0]);
            indices.push_back(mesh.mFaces[i].mIndices[1]);
            indices.push_back(mesh.mFaces[i].mIndices[2]);
        }
    }
}

I would like to find the solution for this mess. I know that the normals are the issue but I can't figure out why.
EDIT 2 By the look of the sphere, it seems like an interpolation problem, hence the shader code above. If the shaders are correct then maybe the importer class is causing this issue?
EDIT 3 I've also implemented a forward rendering version and display the normals as color, but I get the same result as in the deferred version.

Ripi2
  • 7,031
  • 1
  • 17
  • 33
Miso_Soup
  • 19
  • 5
  • 1
    Looks like an issue with either exporting or loading the model. We can help with neither based on the code provided. – Yakov Galka Jun 01 '19 at 02:35
  • 2
    Possible duplicate of [Why does my OpenGL Phong shader behave like a flat shader?](https://stackoverflow.com/questions/4703432/why-does-my-opengl-phong-shader-behave-like-a-flat-shader) – Peter Wishart Jun 01 '19 at 03:45
  • @PeterWishart I've already read that post und tried to solve the problem using blender and analyzing the normals, but nothing could help :/ – Miso_Soup Jun 01 '19 at 09:21
  • @ybungalobill does that mean that my shaders are correct? If so, I can provide the code of the model importer class. – Miso_Soup Jun 01 '19 at 11:30
  • How have you checked if the actual model you're loading already has smooth normals? – derhass Jun 02 '19 at 12:10
  • @derhass take a look at my 5th edit. I hope it can give you more information. – Miso_Soup Jun 03 '19 at 12:05
  • @Miso_Soup Were you able to figure this out? I'm also facing a similar issue. Only difference is that I'm not using deferred shading. – Pranav May 15 '20 at 07:02
  • @Pranav Hi! I used a very "dumb" approach: In Blender, you can adjust the angle of the Normals for smoothness. I set them to the highest value and it worked. You can also use the answer below. Let Assimp compute the normals for you. Both methods are not optimal, but I did not find another way to solve my problem. – Miso_Soup May 16 '20 at 11:21
  • @Miso_Soup Ah I see. But even recomputing the normals using assimp does not work since the problem is of repeated vertices right? – Pranav May 16 '20 at 13:43

2 Answers2

1

I am using Assimp to load the models and I include the aiProcess_GenSmoothNormals flag when importing them.

From the documentation:

This is ignored if normals are already there at the time this flag is evaluated. Model importers try to load them from the source file, so they're usually already there.

So if you exported your model with flat normals, this flag will do nothing. So you have two options:

  1. Export the model with correct smooth normas. (The better option because this gives you full control over the normals, preserving wanted hard edges and so on. Also less calcualtion when needed.)
  2. Force assimp to re-calculate the normals. Have a look at the aiProcess_RemoveComponent flag:

    This step is also useful if you want to force Assimp to recompute normals or tangents. The corresponding steps don't recompute them if they're already there (loaded from the source asset). By using this step you can make sure they are NOT there.

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

Since you are dealing with deferred rendering, my advice would be to make sure, that the screen-space normal textures actually saves the normal values correctly. First of all, how much space do you allocate for the normal texture? A GL_RGBA16 texture should do the job.

The second thing you have to watch out for is, that in glsl negative color values will be cut when written to a texture buffer, since the texture buffers are unsigned. Try to use some kind of converter method to convert color data to vector data, and vise versa. The methods

vec4 toCoord(vec4 color) {
    return vec4(2.0 * color.xyz - vec3(1, 1, 1), color.w);
}

vec4 toColor(vec4 coord) {
    return vec4(0.5 * coord.xyz + vec3(0.5, 0.5, 0.5), coord.w);
}

should work just fine for that. If you didn't already do one of these two things, give it a try :)

If it does not work, some inside on the way you write and read normal data in the fragment shaders to and from the texture buffers would be helpfull :)

  • Thank you for your suggestion! I will try it, but I don't think it'll change much since I get the same problem when using a forward rendering method (see EDITs 1 and 3) :( – Miso_Soup Jun 01 '19 at 11:48