如何实现3D模型按不同指标渲染?[复制]

how to implement 3D model rendering By different indexes? [duplicate]

提问人:izzy and simple 提问时间:11/6/2022 更新时间:11/6/2022 访问量:84

问:

我在 OpenGL 中渲染 3D 模型时遇到问题 - 问题是我无法通过单个索引缓冲区实现渲染

(我的意思是渲染:顶点的顶点索引,法线的正常索引,texcoord 的 texcoord 索引)

因此,目前我的程序不使用索引渲染 因为 glDrawElements - 意味着对于 VertexArrayObject 中的所有 VertexBufferObject,一个 ElementBufferObject 用于渲染 (实际上,创建了一个索引缓冲区,但它始终等于 0,1,2,3,4,...)

尽管此方法有效,但与使用多个索引缓冲区相比,它会花费大量视频卡内存

我附上了一张具有所需结果的图片 以及问题所在

enter image description here enter image description here

问题 - 如何确保在绘制模型时使用不同的索引?

我尝试了很多方法,但没有得到想要的结果😥

C++ opengl-3 很高兴

评论


答:

1赞 robthebloke 11/6/2022 #1

您有 3 种选择。

  1. 展平所有 3 个数组,并使用 glDrawArrays。这通常是最简单的选择。
struct Vertex {
   float v[3];
   float n[3];
   float t[2];
};
std::vector<Vertex> flatten(
   float V[], float N[], float T[],
   uint32_t VI[], uint32_t NI[], uint32_t TI[],
   uint32_t num_triangles)
{
   std::vector<Vertex> combined(num_triangles * 3);
   for(uint32_t i = 0; i < num_triangles * 3; ++i) {
      combined[i].v[0] = V[ 3* VI[i] ];
      combined[i].v[1] = V[ 3* VI[i] + 1 ];
      combined[i].v[2] = V[ 3* VI[i] + 2 ];
      combined[I].n[0] = N[ 3* NI[i] ];
      combined[I].n[1] = N[ 3* NI[i] + 1 ];
      combined[I].n[2] = N[ 3* NI[i] + 2 ];
      combined[I].t[0] = N[ 2* TI[i] ];
      combined[I].t[1] = N[ 2* TI[i] + 1 ];
   }
   return combined;
}

void draw(const std::vector<Vertex>& verts) {
   glVertexAttribPointer(VERTS_IDX, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), verts[0].v);
   glVertexAttribPointer(NORMS_IDX, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), verts[0].n);
   glVertexAttribPointer(UVS_IDX, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), verts[0].t);
   glDrawArrays(GL_TRIANGLES, 0, verts.size());
}

  1. 通过对现有索引进行哈希处理来生成一组新的索引。
struct Vertex {
   float v[3];
   float n[3];
   float t[2];
};

struct IndexedMesh {
   std::vector<Vertex> verts;
   std::vector<uint32_t> indices;
};

// just going to use 21bits for each index. 
uint64_t make_index_hash(uint32_t vi, uint32_t ni, uint32_t ti) {
   assert(vi < 0x1FFFFF);
   assert(ni < 0x1FFFFF);
   assert(ti < 0x1FFFFF);
   return ((uint64_t)vi) |
          ((uint64_t)vi) << 21 | 
          ((uint64_t)vi) << 42;
}

IndexedMesh flatten(
   float V[], float N[], float T[],
   uint32_t VI[], uint32_t NI[], uint32_t TI[],
   uint32_t num_triangles)
{
   std::map<uint64_t, uint62_t> index_remap;
   std::vector<Vertex> combined;
   std::vector<uint32_t> combined_indices(num_triangles * 3);

   for(uint32_t i = 0; i < num_triangles * 3; ++i) {

      uint64_t hash = make_index_hash(VI[i], NI[i], TI[I]);

      // check to see if this set of indices has been sued before
      auto iter = index_remap.find(hash);
      if(iter != index_remap.end() {
         combined_indices[i] = iter->second;
      } else {
         index_remap.emplace(hash, combined.size());
         Vertex v = {
             { V[ 3* VI[i] ], V[ 3* VI[i] + 1 ], V[ 3* VI[I] + 2 ] },
             { N[ 3* NI[i] ], N[ 3* NI[i] + 1 ], N[ 3* NI[I] + 2 ] },
             { T[ 2* TI[i] ], T[ 2* TI[i] + 1 ] }
         };
         combined.push_back(v);
      }
   }
   return { combined, combined_indices };
}

void draw(const IndexedMesh& mesh) {
   glVertexAttribPointer(VERTS_IDX, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), mesh.verts[0].v);
   glVertexAttribPointer(NORMS_IDX, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), mesh.verts[0].n);
   glVertexAttribPointer(UVS_IDX, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), mesh.verts[0].t);
   glDrawElements(GL_TRIANGLES, verts.size(), GL_UNSIGNED_INT, mesh.indices.data());
}
  1. 最后一个选项是使用着色器存储缓冲区在着色器中执行此操作。基本思想是将顶点/法线/uv 数组加载到着色器存储缓冲区中,并将索引集加载到传统的每个顶点参数中。

在着色器中,您可以直接为这些数组编制索引。(我已经有一段时间没有使用 glsl 了,所以这更像是一个一般提示,而不是可行的代码)。

我以前使用过这种方法,但无法确定性能影响是什么/是否(我认为选项 2 是性能最高的 tbh)

layout(std430, binding = 0) buffer VertsSSBO
{
    vec3 verts[];
};
layout(std430, binding = 1) buffer NormsSSBO
{
    vec3 norms[];
};
layout(std430, binding = 2) buffer UVsSSBO
{
    vec2 uvcoords[];
};

// the indices are stored in vertex buffers,
// and bound with glVertexAttribPointer
in uint vs_vertex_index;
in uint vs_normal_index;
in uint vs_uv_index;

// outputs to fragment shader
out vec4 fs_normal;
out vec2 fs_uv;

uniform mat4 vs_mvp;  ///< the modelview-projection matrix

void main() {

   // grab the elements from the shader storage buffers
   vec4 v = vec4(verts[vs_vertex_index], 1.0);
   vec4 n = vec4(norms[vs_normal_index], 0.0);
   vec2 t = uvcoords[vs_normal_index];

   // now transform and output as you would usually.
   gl_Position = vs_mvp * v;
   fs_normal = vs_mvp * n;
   fs_uv = t;
}