Search code examples
c#winformsopenglopentk

OpenTK (OpenGL) Lighting: Why textures are only visible in the INSIDE of an object?


I'm trying to implement lighting in my OpenTK rendered scene. The strange thing, that since I implemented diffuse lighting in my shader & code, all object textures are only visible in the INSIDES of objects. Or it only looks like that.

outside

inside

Can you help me with that? I hope it only is a simple dummie problem.

You can find the full code here: https://github.com/BanditBloodwyn/TerritorySimulator.

The important files are:

  • Rendering.Core.Rendering.Renderer.cs
  • Rendering.Core.Classes.Shapes.GLSphere.cs
  • Rendering.Core.Classes.Shaders.GLSL.Fragment

This is the shader:

#version 420 core

in vec3 normal;
in vec2 texCoord;
in vec3 FragPos;

out vec4 FragColor;

uniform sampler2D texture0;
uniform sampler2D texture1;

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};
struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Material material;
uniform Light light;

void main()
{
    // Ambient
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, texCoord));
    
    // Diffuse 
    vec3 norm = normalize(normal);
    vec3 lightDir = normalize(vec3(light.position - FragPos));
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, texCoord));
        
    vec3 result = ambient + diffuse;
    
    //if(result.a < 0.1)
    //    discard;
    
    FragColor = vec4(result, 1.0);
}

This is the render function:

    public void Render()
    {
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        if (Shapes == null || Shapes.Length == 0)
            return;

        IntPtr offset = (IntPtr)0;
        foreach (GLShape shape in Shapes)
        {
            ApplyTextures(shape);

            ApplyModelTransforms(shape, out Matrix4 model);
            objectShader.SetMatrix4("model", model);

            GL.DrawElements(PrimitiveType.Triangles, shape.Indices.Length, DrawElementsType.UnsignedInt, offset);
            offset += shape.IndexBufferSize;
        }
       
        objectShader.SetVector3("material.ambient", new Vector3(1.0f, 0.5f, 0.31f));
        objectShader.SetVector3("material.diffuse", new Vector3(1.0f, 0.5f, 0.31f));
        objectShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));

        objectShader.SetVector3("light.position", new Vector3(100f, 1.0f, 2.0f));
        objectShader.SetVector3("light.ambient", new Vector3(1.0f));
        objectShader.SetVector3("light.diffuse", new Vector3(0.5f));
        objectShader.SetVector3("light.specular", new Vector3(1.0f));

        objectShader.SetMatrix4("view", Camera.GetViewMatrix());
        objectShader.SetMatrix4("projection", Camera.GetProjectionMatrix());
        objectShader.Use();
    }

To make it clearer: According to what I understand, I set the light source position to {100, 1, 2} in the world space. Since the world sphere's radius is 20, the light should be located outside of it, so it should be illuminated from the outside. All parameters for the material are from an OpenTK tutorial which shows me the basics of OpenGL and OpenTK, so I hope they are correct or at least not the reason for my problem. The light parameters are similar to the same tutorial, except for the ambient strength, which I set to 1.0 to "disable" the dimming effect of ambient lighting. "Shapes" is the Array which contains all objects in the world.

Here the shader is set:

    private void SetupObjectShader()
    {
        string vertexPath = Path.Combine(Environment.CurrentDirectory, @"GLSL\", "Vertex.vert");
        string fragmentPath = Path.Combine(Environment.CurrentDirectory, @"GLSL\", "Fragment.frag");

        objectShader = new Shader(vertexPath, fragmentPath);
        objectShader.Use();

        int vertexLocation = objectShader.GetAttribLocation("aPosition");
        GL.EnableVertexAttribArray(vertexLocation);
        GL.VertexAttribPointer(
            vertexLocation,
            3,
            VertexAttribPointerType.Float,
            false,
            8 * sizeof(float),
            0);

        int normCoordLocation = objectShader.GetAttribLocation("aNormal");
        GL.EnableVertexAttribArray(normCoordLocation);
        GL.VertexAttribPointer(
            normCoordLocation,
            3,
            VertexAttribPointerType.Float,
            false,
            8 * sizeof(float),
            3 * sizeof(float));


        int texCoordLocation = objectShader.GetAttribLocation("aTexCoord");
        GL.EnableVertexAttribArray(texCoordLocation);
        GL.VertexAttribPointer(
            texCoordLocation,
            2,
            VertexAttribPointerType.Float,
            false,
            8 * sizeof(float),
            6 * sizeof(float));

        objectShader.SetInt("texture0", 0);
        objectShader.SetInt("texture1", 1);
    }

The stride and offset values for the VertexAttribPointer function will become clear in the next function where you can see how the vertices are built: 3 floats for the position, 3 floats for the normal, 2 texture coordinates.

The objects are created like this (all spheres in my case). Please see how the vertices and the normals are created:

    private void RecreateVertices()
    {
        List<float> vertices = new List<float>();

        float alpha = 2 * (float)Math.PI / rasterization;

        for (int i = 0; i < rasterization + 1; i++)
        {
            for (int j = 0; j < rasterization + 1; j++)
            {
                float x = radius * (float)Math.Sin(i * alpha * 1.0) * (float)Math.Sin(j * alpha);
                float y = radius * (float)Math.Sin(i * alpha * 1.0) * (float)Math.Cos(j * alpha);
                float z = radius * (float)Math.Cos(i * alpha * 1.0);

                float textureX = (float)j / rasterization;
                float textureY = (float)i / rasterization * 2;

                Vector3 normal = CreateNormal(x, y, z);

                vertices.AddRange(new[] { x, y, z, normal[0], normal[1], normal[2], textureX, textureY });
            }
        }

        Vertices = vertices.ToArray();
    }

    private Vector3 CreateNormal(float x, float y, float z)
    {
        Vector3 normVector3 = new Vector3(x, y, z);
        normVector3.Normalize();

        return normVector3;
    }

UPDATE: I made some experiments with the fragment shader. Instead of the textures I tried white lights and an orange color for the objects. Instead of visible illuminated objects I got a static orange image. Maybe this helps with the diagnosis.

enter image description here

#version 420 core

in vec3 normal;
in vec2 texCoord;
in vec3 FragPos;

out vec4 FragColor;

uniform sampler2D texture0;
uniform sampler2D texture1;

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};
struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Material material;
uniform Light light;
uniform vec3 viewPos;

void main()
{
    // Ambient
    // vec3 ambient = light.ambient * vec3(texture(material.diffuse, texCoord));
    vec3 ambient = light.ambient * vec3(1.0f, 1.0f, 1.0f);

    // Diffuse 
    vec3 norm = normalize(normal);
    vec3 lightDir = normalize(vec3(light.position - FragPos));
    float diff = max(dot(norm, lightDir), 0.0);
    //vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, texCoord));
    vec3 diffuse = light.diffuse * diff * vec3(1.0f, 1.0f, 1.0f);

    // Specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // vec3 specular = light.specular * spec * vec3(texture(material.specular, texCoord));
    vec3 specular = light.specular * spec * vec3(1.0f, 1.0f, 1.0f);

    vec3 result = (ambient + diffuse + specular) * vec3(1.0f, 0.5f, 0.31f);

    //if(result.a < 0.1)
    //    discard;

    FragColor = vec4(result, 1.0);
}

Solution

  • I found the problem. I forgot to mention that right above the earth sphere is another sphere with a transparent texture. With the new light implementation the alpha blending doesn't work.

    I put that into a new question. Thank you Rabbid76 for your engagement!