使用OpenGL ES 2.0渲染多个对象

Mat*_*rty 31 opengl-es-2.0

我正在尝试学习OpenGL ES 2.0来做一些iPhone游戏开发.我已经阅读了多个教程和一些OpenGL ES 2.0规范.我见过的所有例子都创建了一个网格,将其加载到顶点缓冲区然后渲染它(带有预期的平移,旋转,渐变等)

我的问题是:如何渲染场景中具有不同网格并独立移动的多个对象?如果我有一辆汽车和一辆摩托车,我可以创建2个顶点缓冲区并为每个渲染调用保留两个网格数据,然后为每个对象发送不同的着色器矩阵吗?或者我是否需要以某种方式转换网格然后将它们组合成一个网格,以便它们可以一次渲染?我正在寻找更多的高级策略/程序结构而不是代码示例.我想我的错误心理模式是如何运作的.

谢谢!

Ton*_*ony 12

我发现这样做的最好方法是使用除VBO之外的VAO.

我将首先使用VBO回答您的问题.

首先,假设您将两个对象的两个网格存储在以下数组中:

GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;
Run Code Online (Sandbox Code Playgroud)

哪里:

GLfloat gCubeVertexData1[36] = {...};
GLfloat gCubeVertexData2[36] = {...};
Run Code Online (Sandbox Code Playgroud)

而且你还必须使用vertix缓冲区:

GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;
Run Code Online (Sandbox Code Playgroud)

现在,要绘制这两个立方体(没有VAO),你必须做类似的事情:在绘图函数中(来自OpenGLES模板):

//Draw first object, bind VBO, adjust your attributes then call DrawArrays
glGenBuffers(1, &_vertexBufferCube1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));


glDrawArrays(GL_TRIANGLES, 0, 36);



//Repeat for second object:
glGenBuffers(1, &_vertexBufferCube2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
glUseProgram(_program);

glDrawArrays(GL_TRIANGLES, 0, 36);
Run Code Online (Sandbox Code Playgroud)

这将回答你的问题.但是现在要使用VAO,你的绘制函数代码要简单得多(这很好,因为它是重复的函数):

首先,您将定义VAO:

GLuint _vertexArray1;
GLuint _vertexArray2;
Run Code Online (Sandbox Code Playgroud)

然后你将完成之前在draw方法中完成的所有步骤,你将在setupGL函数中完成但在绑定到VAO之后.然后在您的绘图功能中,您只需绑定到您想要的VAO.

这里的VAO就像一个包含很多属性的配置文件(想象一下智能设备配置文件).每次要更改它们时,不要更改颜色,桌面,字体等,而是一次性将其保存在配置文件名称下.然后你只需切换配置文件.

所以你在setupGL中做了一次,然后你在draw中切换它们.

当然,您可以说您可以将代码(没有VAO)放入函数中并调用它.这是事实,但据Apple称,VAO的效率更高:

http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html#//apple_ref/doc/uid/TP40008793-CH107-SW1

现在到代码:

在setupGL中:

glGenVertexArraysOES(1, &_vertexArray1); //Bind to first VAO
glBindVertexArrayOES(_vertexArray1);

glGenBuffers(1, &_vertexBufferCube1); //All steps from this one are done to first VAO only
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));

glGenVertexArraysOES(1, &_vertexArray2); // now bind to the second
glBindVertexArrayOES(_vertexArray2);

glGenBuffers(1, &_vertexBufferCube2); //repeat with the second mesh
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));


glBindVertexArrayOES(0);
Run Code Online (Sandbox Code Playgroud)

最后在你的绘制方法中:

glBindVertexArrayOES(_vertexArray1);
glDrawArrays(GL_TRIANGLES, 0, 36);


glBindVertexArrayOES(_vertexArray2);    
glDrawArrays(GL_TRIANGLES, 0, 36);
Run Code Online (Sandbox Code Playgroud)


Tay*_*orP 10

您为不同的对象维护单独的顶点/索引缓冲区,是的.例如,您可能有一个RenderedObject类,每个实例都有自己的顶点缓冲区.一个RenderedObject可能从房屋网格中获取它的顶点,一个可能来自角色网格等.

在渲染过程中,为您正在使用的顶点缓冲区设置适当的变换/旋转/着色,可能类似于:

void RenderedObject::render()
{
    ...
    //set textures/shaders/transformations

    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
    ...
}
Run Code Online (Sandbox Code Playgroud)

正如其他答案所述,bufferID只是一个GLuint而不是缓冲区的全部内容.如果您需要有关创建顶点缓冲区和填充数据的更多详细信息,我很乐意添加它们.

  • 我的问题是延迟了2年,但是:如果你有不同的顶点缓冲区用于不同的对象,你还必须将程序绑定到所有这些对象.在我的情况下,你很容易获得超过1000个对象,你必须每次都调用setVertexAttribPointer.在我的情况下,这有结果,它变得非常慢(实际上,60-70%的时间用于"setVertexAttribPointer"< - 你知道这个还是我做错了什么? (2认同)