OpenGL:奇怪的法线渲染
OpenGL: Strange normal rendering
我对 OpenGL 法线有疑问。
我正在渲染龙模型,但我有一些奇怪的正常模式。
这是我渲染的截图:
render_screen
这是我的缓冲区创建方法:
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
if (has_position) {
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kVertexArray);
glVertexAttribPointer(kVertexArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_normal) {
glGenBuffers(1, &normal_buffer);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float), normals.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kNormalArray);
glVertexAttribPointer(kNormalArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_tex_coord) {
glGenBuffers(1, &tex_coord_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tex_coord_buffer);
glBufferData(GL_ARRAY_BUFFER, tex_coords.size() * 3 * sizeof(float), tex_coords.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kTexCoordArray);
glVertexAttribPointer(kTexCoordArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_index) {
glGenBuffers(1, &index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 3 * sizeof(unsigned short), indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
}
并绘制:
glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
Wavefront 对象加载器方法:
bool Loadfs() {
CVector3<float> vertex;
CVector3<unsigned short> vindex, tindex, nindex;
while(file_stream->offset_ < file_stream->size_) {
if (file_stream->buffer_[file_stream->offset_] == '#') {
char comment[512] = {0};
file_stream->ReadLine(comment); // check return code -> "its local variable return address"
file_stream->SkipLine();
}
else if (file_stream->buffer_[file_stream->offset_] == 'v') {
file_stream->offset_++;
if (file_stream->buffer_[file_stream->offset_] == ' ') {
has_position = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
vertices.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 'n') {
has_normal = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
normals.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 't') {
has_tex_coord = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
tex_coords.push_back(vertex);
}
}
else if (file_stream->buffer_[file_stream->offset_] == 'f') {
has_index = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
//
if (has_position) {
vindex.x = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.x = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.x = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.y = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.y = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.y = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.z = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.z = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.z = file_stream->ReadShort();
}
vi.push_back(--vindex);
ti.push_back(--tindex);
ni.push_back(--nindex);
//indices.push_back(--vindex);
}
else file_stream->SkipLine();
}
indices.insert(indices.end(), vi.begin(), vi.end());
return true;
}
此处vs:glsl主要方法:
position_ = MV * vec4(vVertex, 1.0);
normal_ = normalize(N * vNormal);
texture_ = vTexture;
//shadow_ = S * M * vec4(vVertex, 1.0);
gl_Position = MVP * vec4(vVertex, 1.0);
和fs.glsl主要方法:
vec3 rgb = vec3(1.0, 1.0, 1.0);
rgb = PhongShade(g_light, g_material, position_, normal_);
_frag_color = vec4(rgb, 1.0);
//_frag_color = texel * vec4(ambient + diffuse + specular, 1.0);
有人有什么想法吗?
问题在于 OpenGL 使用单个索引缓冲区来索引顶点位置、纹理坐标和法线缓冲区,每个缓冲区都使用相同的索引(三角形有 3 个索引),而 Wavefront obj 格式允许每个面分别为顶点位置、纹理坐标和法线指定单独的索引(一个三角形总共有 9 个索引)。
从你的代码中看不清楚,但很可能你实际上并没有使用你正在阅读的 ti
和 ni
索引数组,而是从 vi
,因此顶点索引被用于索引 normal_buffer
和 tex_coord_buffer
,给出了奇怪的结果。
要解决此问题,您需要创建顶点坐标、tex 坐标和法线坐标的缓冲区,并为面定义中使用的顶点位置索引、tex 坐标索引和法线索引的每个唯一组合创建一个条目obj 文件,然后将索引缓冲区索引到这些数组中,以便特定的面顶点对每个顶点具有相同的索引。理想情况下,这将作为预处理步骤而不是在加载时完成。
或者,放弃使用 glDrawElements
的索引渲染并改用 glDrawArrays
。根据位置、tex 坐标和法线的面顶点索引,为每个面顶点写出一个条目到每个数组,然后渲染它。然而,这效率较低。
另一种解决方案(如果可能的话)是以每个面顶点使用相同索引的位置、tex 坐标和法线的方式导出 obj 文件。
我对 OpenGL 法线有疑问。 我正在渲染龙模型,但我有一些奇怪的正常模式。
这是我渲染的截图: render_screen
这是我的缓冲区创建方法:
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
if (has_position) {
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kVertexArray);
glVertexAttribPointer(kVertexArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_normal) {
glGenBuffers(1, &normal_buffer);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float), normals.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kNormalArray);
glVertexAttribPointer(kNormalArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_tex_coord) {
glGenBuffers(1, &tex_coord_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tex_coord_buffer);
glBufferData(GL_ARRAY_BUFFER, tex_coords.size() * 3 * sizeof(float), tex_coords.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kTexCoordArray);
glVertexAttribPointer(kTexCoordArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_index) {
glGenBuffers(1, &index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 3 * sizeof(unsigned short), indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
}
并绘制:
glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
Wavefront 对象加载器方法:
bool Loadfs() {
CVector3<float> vertex;
CVector3<unsigned short> vindex, tindex, nindex;
while(file_stream->offset_ < file_stream->size_) {
if (file_stream->buffer_[file_stream->offset_] == '#') {
char comment[512] = {0};
file_stream->ReadLine(comment); // check return code -> "its local variable return address"
file_stream->SkipLine();
}
else if (file_stream->buffer_[file_stream->offset_] == 'v') {
file_stream->offset_++;
if (file_stream->buffer_[file_stream->offset_] == ' ') {
has_position = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
vertices.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 'n') {
has_normal = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
normals.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 't') {
has_tex_coord = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
tex_coords.push_back(vertex);
}
}
else if (file_stream->buffer_[file_stream->offset_] == 'f') {
has_index = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
//
if (has_position) {
vindex.x = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.x = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.x = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.y = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.y = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.y = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.z = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.z = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.z = file_stream->ReadShort();
}
vi.push_back(--vindex);
ti.push_back(--tindex);
ni.push_back(--nindex);
//indices.push_back(--vindex);
}
else file_stream->SkipLine();
}
indices.insert(indices.end(), vi.begin(), vi.end());
return true;
}
此处vs:glsl主要方法:
position_ = MV * vec4(vVertex, 1.0);
normal_ = normalize(N * vNormal);
texture_ = vTexture;
//shadow_ = S * M * vec4(vVertex, 1.0);
gl_Position = MVP * vec4(vVertex, 1.0);
和fs.glsl主要方法:
vec3 rgb = vec3(1.0, 1.0, 1.0);
rgb = PhongShade(g_light, g_material, position_, normal_);
_frag_color = vec4(rgb, 1.0);
//_frag_color = texel * vec4(ambient + diffuse + specular, 1.0);
有人有什么想法吗?
问题在于 OpenGL 使用单个索引缓冲区来索引顶点位置、纹理坐标和法线缓冲区,每个缓冲区都使用相同的索引(三角形有 3 个索引),而 Wavefront obj 格式允许每个面分别为顶点位置、纹理坐标和法线指定单独的索引(一个三角形总共有 9 个索引)。
从你的代码中看不清楚,但很可能你实际上并没有使用你正在阅读的 ti
和 ni
索引数组,而是从 vi
,因此顶点索引被用于索引 normal_buffer
和 tex_coord_buffer
,给出了奇怪的结果。
要解决此问题,您需要创建顶点坐标、tex 坐标和法线坐标的缓冲区,并为面定义中使用的顶点位置索引、tex 坐标索引和法线索引的每个唯一组合创建一个条目obj 文件,然后将索引缓冲区索引到这些数组中,以便特定的面顶点对每个顶点具有相同的索引。理想情况下,这将作为预处理步骤而不是在加载时完成。
或者,放弃使用 glDrawElements
的索引渲染并改用 glDrawArrays
。根据位置、tex 坐标和法线的面顶点索引,为每个面顶点写出一个条目到每个数组,然后渲染它。然而,这效率较低。
另一种解决方案(如果可能的话)是以每个面顶点使用相同索引的位置、tex 坐标和法线的方式导出 obj 文件。