OpenGL -正确的方式来加载多面
OpenGL - Correct way to load multi faces
如果我有一个立方体模型(立方体有6个面)。我怎样用vbo来画每张脸呢?我需要调用glDrawElements 6次吗?还是有别的函数可以一次画出来?通常我是这样画的:
for (int i = 0; i < facesNum; i++)
glDrawElements(GL_TRIANGLE_FAN, 4 + i*4, GL_UNSIGNED_INT, (GLvoid*)(i*4));
这是最好的方法吗?
你可以使用原始重启(OpenGL 3.1+)来重新启动一个原语,如三角形风扇,而渲染,就好像你启动了另一个glDraw*
命令。
使用glEnable(GL_PRIMITIVE_RESTART)
使能,然后使用glPrimitiveRestartIndex(restartIndex)
设置索引(如0xFFFF
)用于重启。然后,当OpenGL遇到重启索引时,它将停止当前绘制的原语并开始另一个原语。
这允许你用一个索引缓冲区和绘制命令绘制多个三角形带、扇形带、线环或带。只需在每个原语的索引数据之间添加restart索引。
一般来说,你要做的是将你的对象绘制为GL_TRIANGLES
而不是GL_TRIANGLE_FAN
,这允许你只绘制所有12个三角形(6个面*每个面2个三角形)通过一次调用glDrawElements
。
假设你的顶面由顶点索引0、1、2、3按逆时针顺序组成,例如,您可以将索引缓冲区的那一部分从0,1,2,3
更改为0,1,2,0,2,3
。
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);
(36,因为我们为立方体绘制了12个三角形,每个三角形有3个顶点)
你应该只用一个VBO调用DrawElements一次。有一些替代方案,比如IBO,但我将提供一个VBO的示例,因为这是您所要求的。理想情况下,您应该将所有顶点存储在保存顶点对象的数组或std::vector中。将所有立方体面顶点放入这个数组或std::vector中。您不需要每个面都有一个数组/向量。您将希望保持数据紧密紧凑,并避免使用virtual关键字,因为它创建了一个非暴露的指针(vtptr),这会增加类的内存占用。你想要保持数据紧凑的原因是当你把它发送给OpenGL时,它会期望步长。如果您对类的内存占用有疑问,那么使用sizeof(ClassName)函数进行快速输出是没有害处的。
你的顶点类看起来像这样:
class Vertex {
public:
~Vertex() {}
Vertex() {}
Vector3<float> vertexPosition;
Vector4<float> vertexColor;
Vector2<float> vertexTextureCoords;
Vector3<float> vertexNormal;
Vector3<float> vertexTangent;
Vector3<float> vertexBitangent;
Vector4<int> vertexBoneIndexes;
Vector4<float> vertexBoneWeights;
};
其中所有的vector类都是非虚的,唯一的数据成员是vector组件。
以下是我遵循的步骤:- 创建顶点对象的std::向量(或数组)并填充顶点数据
用适当的OpenGL调用生成VBO(这是我的引擎的一个例子)。我使用一个具有包装函数的单例,以便与图形API无关。参数的顺序与OpenGL期望的VBO生成函数的顺序完全相同。
cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer(); sharedRenderer->generateVBOBuffers( 1, &entityToCreate->m_VBOBufferID ); sharedRenderer->bindVBOBuffer( entityToCreate->m_VBOBufferID ); sharedRenderer->bufferDataForVBO( ( sizeof( cbengine::Vertex ) * entityToCreate- >m_verts.size() ), &entityToCreate->m_verts.front() );
在你的渲染函数中进行以下OpenGL调用。这就是为什么我建议保持数据紧凑的原因。OpenGL需要知道预期的数据类型、数据类型的大小以及从数据开始的偏移量。
glEnableVertexAttribArray( m_vertexLocation ); glVertexAttribPointer( m_vertexLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexPosition ) ); glEnableVertexAttribArray( m_colorLocation ); glVertexAttribPointer( m_colorLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexColor ) ); glEnableVertexAttribArray( m_diffuseTextureCoordLocation ); glVertexAttribPointer( m_diffuseTextureCoordLocation, 2, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTextureCoords ) ); glEnableVertexAttribArray( m_normalCoordLocation ); glVertexAttribPointer( m_normalCoordLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexNormal ) ); glEnableVertexAttribArray( m_tangentLocation ); glVertexAttribPointer( m_tangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTangent ) ); glEnableVertexAttribArray( m_bitangentLocation ); glVertexAttribPointer( m_bitangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBitangent ) ); glEnableVertexAttribArray( m_boneIndexesLocation ); // Apparently GL_INT causes issues glVertexAttribPointer( m_boneIndexesLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneIndexes ) ); glEnableVertexAttribArray( m_boneWeightsLocation ); glVertexAttribPointer( m_boneWeightsLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneWeights ) );
最后,调用DrawArrays
sharedRenderer->drawArrays( drawPrim, 0, verts.size() );
这是如何完成你想要的一个非常快速的例子。其他要记住的事情是选择的缠绕顺序,启用/禁用纹理,以及您可能想要发送给着色器的自定义顶点属性数据。如果你的引擎不支持骨骼动画,你可能不希望包含骨骼权重和骨骼索引向量。
- std::原子加载和存储都需要吗
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 为什么加载SDF会导致Mobilizer创建闭环错误
- C++atioglxx.pdb未加载错误glBufferData OpenGL
- 如何使用tinyxml2从XML加载父实体和子实体
- 如何在C++中使用pybind11加载一个pickle python列表
- 系统.将数组移交给c#中动态加载的c++DLL时发生AccessViolationException
- 当我尝试加载内核模块时,如何修复C++中的这个 malloc() 错误?
- 树莓上的 Libtorch 无法加载 pt 文件,但在 ubuntu 上工作
- 如何以编程方式查找加载的共享库的版本?
- 如果 QApplication 执行延迟,QWebEngineView 在加载内容时会以静默方式失败
- 如何以编程方式知道Adobe Reader完成了加载文件
- RedHatLinux :是否可以以编程方式加载 libcert.so 共享对象
- 加载到内存的文件的大小远大于其磁盘大小?!!任何压缩方式
- 我是否可以以可以在加载时删除 dll 的方式加载它?
- 为什么SDL不加载我的图像时,主应用程序通过快捷方式访问
- OpenGL -正确的方式来加载多面
- 改变加载资源的方式
- 如何从文本文件中只加载某些行,这取决于它们在C和c++中的开始方式