Question

So, I've begun a quest to implement awesome lighting without using OpenGL's lighting system. I've successfully implemented Phong diffuse lighting. Specular is giving me trouble.

I need to know what spaces the OpenGL constants I'm using occupy, because it seems they are mis-transformed, and that it results in lighting glitches.

I have confirmed that there is no problem with my C++ code by successfully loading and running a Phong diffuse shader. The C++ code may, however, be passing invalid data to the shaders, which is one of the things I'm worried about. I will paste my shaders with comments, as well as all C++ code directly pertaining to the shaders (although I'm 90% sure the problem is in the shaders).

In these images, the light sources are large points, and the axes are shown. The lights are rotating at y = 0 around an icosphere.

Here's the diffuse, so you get an idea what the model is... Note I haven't done per-pixel yet... Note I haven't done per-pixel yet...

Here's the Fresnel lighting, as shown in source... Note how the lit faces are facing the light, not somewhere between the light and the camera Note how the lit faces are facing the light, not somewhere between the light and the camera

Here's the Blinn-Phong, which I had to multiply by 30... Note again how the lit faces point towards the light source Note again how the lit faces point towards the light source, and also the fact that I had to multiply the Specular factor (S) by 30 to achieve this

Vertex Shader Source (loaded from "dirlight.vs")

const int MAXLIGHTS = 4;

uniform bool justcolor = false;

uniform int lightcount;
uniform vec4 lightposs[MAXLIGHTS];
uniform vec4 lightdirs[MAXLIGHTS];
uniform vec4 lightdifs[MAXLIGHTS];
uniform vec4 lightambs[MAXLIGHTS];

//diffuse
vec4 D;
//specular, normaldotlight
float S, NdotL[MAXLIGHTS];
//normal, eyevec, lightvecs, halfvecs
vec3 N, E, L[MAXLIGHTS], H[MAXLIGHTS];

void main() {
    //if(lightcount > MAXLIGHTS) lightcount = MAXLIGHTS;
    D = vec4(0.0, 0.0, 0.0, 0.0);
    S = 0.0;

    N = gl_Normal;
    E = normalize(vec3(-gl_Vertex));

    for(int i = 0; i < lightcount; i++)
    {
        //calculating direction to light source
        L[i] = normalize(vec3(lightposs[i] - gl_Vertex));

        //normal dotted with direction to light source
        NdotL[i] = max(dot(N, L[i]), 0.0);

        //diffuse term, works just fine
        D += gl_Color * lightdifs[i] * NdotL[i];

        if(NdotL[i] >= 0.0)
        {
            //halfvector = normalize(lightdir + eyedir)
            H[i] = normalize(L[i] + E);

            //Blinn-Phong, only lights up faces whose normals 
            //point directly to the light source for some reason...
            //S += max(0.0, dot(H[i], N));

            //Fresnel, lights up more than Blinn-Phong 
            //but the faces still point directly to the light source, 
            //not somewhere between the lightsource and myself, like they should.
            S += pow(max(0.0, dot(reflect(L[i], N), E)), 50.0);
        }
        else
        {
            H[i] = vec3(0.0, 0.0, 0.0);
        }
    }

    //currently only showing specular. To show diffuse add D.
    gl_FrontColor = justcolor ? gl_Color : vec4(S * 0.3, S * 0.3, S * 0.3, 1.0);

    gl_Position = ftransform();
}

Fragment Shader Source (loaded from "dirlight.fs")

void main()
{
    gl_FragColor = gl_Color;
}

Excerpt from C++ main initialization...

//class program manages shaders
Program shaders = Program();
//attach a vertex shader, compiled from source in dirlight.vs
shaders.addShaderFile(GL_VERTEX_SHADER, "dirlight.vs");
//attach a fragment shader compiled from source in dirlight.fs
shaders.addShaderFile(GL_FRAGMENT_SHADER, "dirlight.fs");
//link program
shaders.link();
//use program
shaders.use();

//Program::getUniformLoc(const char* name) grabs the location
//of the uniform specified
GLint sTime = shaders.getUniformLoc("time");
GLint lightcount = shaders.getUniformLoc("lightcount");
GLint lightdir = shaders.getUniformLoc("lightdirs");
GLint lightdif = shaders.getUniformLoc("lightdifs");
GLint lightamb = shaders.getUniformLoc("lightambs");
GLint lightpos = shaders.getUniformLoc("lightposs");
GLint justcolor = shaders.getUniformLoc("justcolor");

glUniform1i(justcolor, 0);
glUniform1i(lightcount, 2);
//diffuse light colors
GLfloat lightdifs[] = {1.f, 1.f, 1.f, 1.f,
                      1.f, 1.f, 1.f, 1.f};
glUniform4fv(lightdif, 2, lightdifs);
glUniform4f(lightamb, 0.4f, 0.4f, 0.4f, 1.f);

Excerpt from C++ main loop...

//My lights rotate around the origin, where I have placed an icosphere
GLfloat lightposs[] = {-4 * sinf(newTime), lighth, -4 * cosf(newTime), 0.0f,
                       -4 * sinf(newTime + M_PI), lighth, -4 * cosf(newTime + M_PI), 0.0f};
glUniform4fv(lightpos, 2, lightposs);
Was it helpful?

Solution

There are few important things missing from your code. First you should transform vertex position and normal into eye space. Lighting calculations are easiest there. Vertex position transforms using the modelview matrix, the normals transform with the transposed inverse of the modelview. Usually light positions are in world coordinates, so it makes sense to supply an additional matrix from world to eye coordinates.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top