opengl 如何通过不同的索引实现3D模型渲染?[duplicate]

xyhw6mcr  于 2022-11-23  发布在  其他
关注(0)|答案(1)|浏览(121)

此问题在此处已有答案

Rendering meshes with multiple indices(2个答案)
Why does OpenGL not support multiple index buffering?(1个答案)
16天前关闭。
我在OpenGL中渲染3D模型时遇到问题-问题是我无法通过单索引缓冲区实现渲染
(我的意思是渲染:按顶点索引的顶点、按法线索引的法线、按纹理坐标索引的纹理坐标)
因此,目前我的程序不使用索引渲染,因为glDrawElements -意味着对于VertexArrayObject中的所有VertexBufferObject,将使用一个ElementBufferObject进行渲染(实际上,创建了一个索引缓冲区,但它始终等于0、1、2、3、4...)
尽管这种方法有效,但与使用多个索引缓冲区相比,它会占用大量的视频卡内存
我已经附上了一张图片,上面有所需的结果和问题所在的图片
第一次
问题-如何确保在绘制模型时使用不同的索引?
我尝试了很多方法,但都没有得到想要的结果😥

t5zmwmid

t5zmwmid1#

你有三个选择。
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;
}

相关问题