【问题标题】:OpenGL: Strange normal renderingOpenGL:奇怪的正常渲染
【发布时间】:2015-06-24 11:35:43
【问题描述】:

我对 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 obj 加载器方法:

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);

有人有什么想法吗?

【问题讨论】:

  • 这已经以类似的形式出现过很多次了。例如:stackoverflow.com/questions/29718501/…stackoverflow.com/questions/23349080/…
  • 但我认为其中一个使用固定函数,其他使用绘制数组方法。我使用元素方法,所以我应该留下波前文件吗?我也在研究光线追踪。您对我的代码中使用的模型文件类型有什么建议吗?谢谢。
  • 如果你想使用 glDrawElements,你可以使用@RetoKoradi 的answer here 中的算法。索引逻辑对于固定功能和可编程管道的工作方式相同,因此不会成为问题。

标签: opengl glsl lighting normals wavefront


【解决方案1】:

问题在于 OpenGL 使用单个索引缓冲区来索引顶点位置、纹理坐标和法线缓冲区,每个缓冲区使用相同的索引(三角形有 3 个索引),而 Wavefront obj 格式允许每个面指定单独的顶点位置、纹理坐标和法线的独立索引(三角形总共 9 个索引)。

从您的代码中不清楚,但很可能您实际上并没有使用您正在读取的tini 索引数组,而是从vi 创建您的index_buffer,所以顶点索引是被用来索引normal_buffertex_coord_buffer,给出了奇怪的结果。

要解决此问题,您需要创建顶点坐标、tex 坐标和法线坐标的缓冲区,并为 obj 文件中的面定义中使用的每个唯一组合的顶点位置索引、tex 坐标索引和法线索引创建一个条目,然后将您的索引缓冲区索引到这些数组中,以使特定的面顶点具有相同的索引。理想情况下,这将作为预处理步骤而不是在加载时完成。

或者,放弃使用glDrawElements 进行索引渲染并改用glDrawArrays。根据位置、tex 坐标和法线的面顶点索引,为每个面顶点的每个数组写一个条目,然后渲染它。然而,这效率较低。

另一种解决方案(如果可能的话)是以这样一种方式导出 obj 文件,即每个面顶点使用相同的位置、tex 坐标和法线索引。

【讨论】:

  • 谢谢你。我还将 ni 和 ti 索引插入到索引中并渲染但相同的东西。我想使用元素方法来提高效率。我有一些 obj 文件使用相同的 v、t、n 索引(例如 f 1/1/1 2/2/2 3/3/3.......)并正确渲染,但其他文件具有不同的正常值,特克斯坐标大小。我该怎么办? (对不起我的英语:)我认为我比英语更懂代码)
  • 您必须使用具有相同索引的格式,例如1/1/1 2/2/2 3/3/3 因为 OpenGL 不支持位置/tex/法线的单独索引。它的内存效率低于 .obj 文件,但您对此无能为力,因为它是 OpenGL 的限制。因此,您要么需要使用具有该格式索引的文件格式,要么在加载时或在预处理步骤中对其进行转换。
猜你喜欢
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 2014-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多