Elmi
Elmi

Reputation: 6213

OpenGL: where is the connection between indices and normals for proper lighting?

I have a working application that displays 3D models in wireframe mode properly. Now I want to add a new mode where these models are show as solids lit by a light source.

Changing the polygon mode from glPolygonMode(GL_FRONT_AND_BACK,GL_LINE) to glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ) does not do the job as the complete normals and lighting stuff is missing. This is clear for me so far. What I do not understand, is, where the normals are applied to the current geometries properly, means where/how does OpenGL know, the provided normal coordinates belong to the coordinates of my (former) wireframe mode?

That's hat I have changed so far:

a) A new shader program for my solid 3D model:

#version 130
in vec3 Normal;
in vec3 fpos;

out vec4 fragment_color;

const vec3 lightPos = vec3(0.0,0.0,5.0);
const vec3 diffColor = vec3(1.0,0.5,0.0);
const vec3 specColor = vec3(1.0,1.0,1.0);

void main () {
 vec3 normal = normalize(Normal);
 vec3 viewDir = normalize(-fpos);
 if (dot(normal, viewDir) < 0.0) normal *= -1.0;

 vec3 lightDir = normalize(lightPos - fpos);
 float lamb = max(dot(lightDir, normal), 0.0);
 float spec = 0.0;

 if (lamb > 0.0) {
  vec3 refDir = reflect(-lightDir, normal);
  float specAngle = max(dot(refDir, viewDir), 0.0);
  spec = pow(specAngle, 4.0);
 }
fragment_color = vec4(lamb * diffColor + spec * specColor, 1.0);
}\0";

b) after setting the coordinates (this is the old, working code already used for my wireframes view)...

glBindVertexArray(entity->m_gl3Element.VAO);
glBindBuffer(GL_ARRAY_BUFFER,entity->m_gl3Element.coordVBO);
glBufferData(GL_ARRAY_BUFFER,m_vertexArray.size()*sizeof(float),m_vertexArray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

...I now also have added the code for loading the normals data...

glBindBuffer(GL_ARRAY_BUFFER,entity->m_gl3Element.normalVBO);
glBufferData(GL_ARRAY_BUFFER, normalArray.size()*sizeof(float),normalArray.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER,entity->m_gl3Element.normalVBO);
glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,0,(void*)0);

...which finally is closed by a call to:

glBindVertexArray(0);

Later the whole stuff is shown by a call to

glDrawArrays(...)

After doing these modifications, my model appears solid but is still completely black, so the lighting/normals stuff is not working properly.

My problem: I do not understand where the normals are applied to the coordinates - is the magical index "2" from the calls glEnableVertexAttribArray(2) and glVertexAttribPointer(2,...) doing this assignment? Does OpenGL always "know" this "2" are the normals?

The normals itself should be fine as they come from a 3D model loaded from disk.

Upvotes: 2

Views: 44

Answers (1)

BDL
BDL

Reputation: 22175

Corresponding positions and normals have to be placed at the same index in both buffers. That means that the normal at index 1 belongs to the position at index 1.

Your actual problem is that you are (probably) using the wrong locations for your attributes. Attribute locations, (the parameter used in glEnableVertexAttribArray and glVertexAttribPointer), specifies the location (kind of an index) of the attribute in the shader. You can either query the locations of the attributes after the shader has been linked with glGetAttributeLocation:


auto location = glGetAttribLocation(program, "vertex_position");
glBindVertexArray(entity->m_gl3Element.VAO);
glBindBuffer(GL_ARRAY_BUFFER,entity->m_gl3Element.coordVBO);
glBufferData(GL_ARRAY_BUFFER,m_vertexArray.size()*sizeof(float),m_vertexArray.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(location);
glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

location = glGetAttribLocation(program, "vertex_normal");
glBindBuffer(GL_ARRAY_BUFFER,entity->m_gl3Element.normalVBO);
glBufferData(GL_ARRAY_BUFFER, normalArray.size()*sizeof(float),normalArray.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(location);
glVertexAttribPointer(location,3,GL_FLOAT,GL_FALSE,0,(void*)0);

or you can pre-define the locations in the shader using layout qualifiers:

layout(location = 0) in vec3 vertex_position;
layout(location = 2) in vec3 vertex_normal;

In either way, the locations you use during VAO setup have to match the locations in your shader.

Note, that since the vertex shader wasn't show, vertex_position and vertex_normal have to be replaced with the actual name in your vertex shader.

Your first version probably worked because you only had one vertex attribute (the one for the position) and it is very likely (but not guaranteed) that that attribute got location 0 auto-assigned. Now that you have two, guessing them is very error prone.

Upvotes: 1

Related Questions