OpenGL:索引和法线之间的连接在哪里以实现正确的光照?

huangapple go评论57阅读模式
英文:

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 shown 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, meaning where/how does OpenGL know the provided normal coordinates belong to the coordinates of my former wireframe mode?

That's what 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 themselves should be fine as they come from a 3D model loaded from disk.

英文:

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) &lt; 0.0) normal *= -1.0;

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

 if (lamb &gt; 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);
}
#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) &lt; 0.0) normal *= -1.0;
vec3 lightDir = normalize(lightPos - fpos);
float lamb = max(dot(lightDir, normal), 0.0);
float spec = 0.0;
if (lamb &gt; 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&quot;;
&quot;;

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

glBindVertexArray(entity-&gt;m_gl3Element.VAO);
glBindBuffer(GL_ARRAY_BUFFER,entity-&gt;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-&gt;m_gl3Element.normalVBO);
glBufferData(GL_ARRAY_BUFFER, normalArray.size()*sizeof(float),normalArray.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER,entity-&gt;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.

答案1

得分: 1

相应的位置和法线必须放置在两个缓冲区中的相同索引处。这意味着索引1处的法线属于索引1处的位置。

你当前的问题可能是你在属性中使用了错误的位置。属性位置(在glEnableVertexAttribArrayglVertexAttribPointer中使用的参数)指定了着色器中属性的位置(一种索引)。你可以在着色器链接后使用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);

或者你可以在着色器中使用布局限定符预定义属性的位置:

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

无论哪种方式,VAO 设置中使用的位置必须与着色器中的位置匹配。

请注意,由于未显示顶点着色器,vertex_positionvertex_normal 必须替换为你的顶点着色器中的实际名称。

你的第一个版本可能之所以工作是因为你只有一个顶点属性(用于位置),很可能(但不保证)该属性被自动分配了位置0。现在你有两个属性,猜测它们的位置会很容易出错。

英文:

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, &quot;vertex_position&quot;);
glBindVertexArray(entity-&gt;m_gl3Element.VAO);
glBindBuffer(GL_ARRAY_BUFFER,entity-&gt;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, &quot;vertex_normal&quot;);
glBindBuffer(GL_ARRAY_BUFFER,entity-&gt;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.

huangapple
  • 本文由 发表于 2023年6月12日 21:19:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76457083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定