我正在开发基于Tile的OpenGL,C++应用程序。 我添加从应用示例屏幕,这样它会更清楚:VBO比绘制图元的过时方法更慢 - 为什么?
我有Tile
类,其中包含的Object
秒的阵列。每个瓷砖最多可以存储15个物体 - 其示例是Tile
,其上有绿色和黄色方块(两个物体),因此它的绘制为10x10x15 = 1500 Object
s(在最糟糕的情况下,因为我没有处理“空” ' 那些)。通常情况下,我的测试中使用了大约600个。 Object
有它自己的图形,可以绘制。每个Object
一次属于一个Tile
,但它可以移动(例如图片中的红色方块)。
Object
s背景将会有一个边框,并且它们需要很好的伸缩性,所以我使用9-patch模式绘制它们(它们由9个四边形组成)。
没有图纸Tile
s(他们的Object
准确),我的申请有600 fps
左右。首先,我一直使用过时的方法绘制那些Tile
s - 使用glBegin(GL_QUADS)
/glEnd()
和glDisplayList
s。由于该绘图,我的表现大幅下降 - 从600
到320 fps
。这是我一直在绘制它们:
bool Background::draw(const TPoint& pos, int width, int height)
{
if(width <= 0 || height <= 0)
return false;
//glFrontFace(GL_CW);
glPushMatrix();
glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f); // Move background to right direction
if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list,
// then recalculate everything and save in display list
{
// That size will be now saved in display list
m_savedWidth = width;
m_savedHeight = height;
// If this background doesn't have unique display list id specified yet,
// then let OpenGL generate one
if(m_displayListId == NO_DISPLAY_LIST_ID)
{
GLuint displayList;
displayList = glGenLists(1);
m_displayListId = displayList;
}
glNewList(m_displayListId, GL_COMPILE);
GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width);
// Instead of coordinates range 0..1 we need to specify new ones
GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0;
GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0;
GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width;
GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height;
/* 9-cell-pattern
-------------------
| 1 | 2 | 3 |
-------------------
| | | |
| 4 | 9 | 5 |
| | | |
-------------------
| 6 | 7 | 8 |
-------------------
*/
glBindTexture(GL_TEXTURE_2D, m_texture); // Select Our Texture
// Top left quad [1]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0, maxTexCoordBorderY);
glVertex2i(0, 0 + m_borderWidth);
// Top left
glTexCoord2f(0.0, 0.0);
glVertex2i(0, 0);
// Top right
glTexCoord2f(maxTexCoordBorderX, 0.0);
glVertex2i(0 + m_borderWidth, 0);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
glEnd();
// Top middle quad [2]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Top left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 0.0);
glVertex2i(0 + m_borderWidth, 0);
// Top right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 0.0);
glVertex2i(0 + width - m_borderWidth, 0);
// Bottom right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
glEnd();
// Top right quad [3]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 0.0);
glVertex2i(0 + width - m_borderWidth, 0);
// Top right
glTexCoord2f(1.0, 0.0);
glVertex2i(0 + width, 0);
// Bottom right
glTexCoord2f(1.0, maxTexCoordBorderY);
glVertex2i(0 + width, 0 + m_borderWidth);
glEnd();
// Middle left quad [4]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0, 0 + height - m_borderWidth);
// Top left
glTexCoord2f(0.0, maxTexCoordBorderY);
glVertex2i(0, 0 + m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
glEnd();
// Middle right quad [5]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Top right
glTexCoord2f(1.0, maxTexCoordBorderY);
glVertex2i(0 + width, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width, 0 + height - m_borderWidth);
glEnd();
// Bottom left quad [6]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(0.0f, 1.0);
glVertex2i(0, 0 + height);
// Top left
glTexCoord2f(0.0f, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0, 0 + height - m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordBorderX, 1.0);
glVertex2i(0 + m_borderWidth, 0 + height);
glEnd();
// Bottom middle quad [7]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 1.0);
glVertex2i(0 + m_borderWidth, 0 + height);
// Top left
glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Top right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 1.0);
glVertex2i(0 + width - m_borderWidth, 0 + height);
glEnd();
// Bottom right quad [8]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 1.0);
glVertex2i(0 + width - m_borderWidth, 0 + height);
// Top left
glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
// Top right
glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY);
glVertex2i(0 + width, 0 + height - m_borderWidth);
// Bottom right
glTexCoord2f(1.0, 1.0);
glVertex2i(0 + width, 0 + height);
glEnd();
GLfloat xTexOffset;
GLfloat yTexOffset;
if(m_borderWidth > 0)
{
glBindTexture(GL_TEXTURE_2D, m_centerTexture); // If there's a border, we have to use
// second texture now for middle quad
xTexOffset = 0.0; // We are using another texture, so middle middle quad
yTexOffset = 0.0; // has to be texture with a whole texture
}
else
{
// Don't bind any texture here - we're still using the same one
xTexOffset = maxTexCoordBorderX; // But it implies using offset which equals
yTexOffset = maxTexCoordBorderY; // maximum texture coordinates
}
// Middle middle quad [9]
glBegin(GL_QUADS);
// Bottom left
glTexCoord2f(xTexOffset, maxTexCoordHeight - yTexOffset);
glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth);
// Top left
glTexCoord2f(xTexOffset, yTexOffset);
glVertex2i(0 + m_borderWidth, 0 + m_borderWidth);
// Top right
glTexCoord2f(maxTexCoordWidth - xTexOffset, yTexOffset);
glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth);
// Bottom right
glTexCoord2f(maxTexCoordWidth - xTexOffset, maxTexCoordHeight - yTexOffset);
glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth);
glEnd();
glEndList();
}
glCallList(m_displayListId); // Now we can call earlier or now created display list
glPopMatrix();
return true;
}
那里可能有太多的代码,但我想展示一切。这个版本的主要内容是使用显示列表和glVertex2i
,这是不赞成的。
我认为这种放缓的问题是使用这种过时的方法,我读的速度很慢,所以我决定去VBO
。我用this tutorial,并根据它,我改变了我的方法是这样的:
bool Background::draw(const TPoint& pos, int width, int height)
{
if(width <= 0 || height <= 0)
return false;
glPushMatrix();
glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f); // Move background to right direction
if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list,
// then recalculate everything and save in display list
{
// That size will be now saved in display list
m_savedWidth = width;
m_savedHeight = height;
GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width);
// Instead of coordinates range 0..1 we need to specify new ones
GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0;
GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0;
GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width;
GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height;
/* 9-cell-pattern, each number represents one quad
-------------------
| 1 | 2 | 3 |
-------------------
| | | |
| 4 | 9 | 5 |
| | | |
-------------------
| 6 | 7 | 8 |
-------------------
*/
/* How vertices are distributed on one quad made of two triangles
v1 ------ v0
| /|
| / |
|/ |
v2 ------ v3
*/
GLfloat vertices[] = {
// Top left quad [1]
m_borderWidth, 0, 0, // v0
0, 0, 0, // v1
0, m_borderWidth, 0, // v2
0, m_borderWidth, 0, // v2
m_borderWidth, m_borderWidth, 0, // v3
m_borderWidth, 0, 0, // v0
// Top middle quad [2]
width-m_borderWidth, 0, 0, // v0
m_borderWidth, 0, 0, // v1
m_borderWidth, m_borderWidth, 0, // v2
m_borderWidth, m_borderWidth, 0, // v2
width-m_borderWidth, m_borderWidth, 0, // v3
width-m_borderWidth, 0, 0, // v0
// Top right quad [3]
width, 0, 0, // v0
width-m_borderWidth, 0, 0, // v1
width-m_borderWidth, m_borderWidth, 0, // v2
width-m_borderWidth, m_borderWidth, 0, // v2
width, m_borderWidth, 0, // v3
width, 0, 0, // v0
// Middle left quad [4]
m_borderWidth, m_borderWidth, 0, // v0
0, m_borderWidth, 0, // v1
0, height-m_borderWidth, 0, // v2
0, height-m_borderWidth, 0, // v2
m_borderWidth, height-m_borderWidth, 0, // v3
m_borderWidth, m_borderWidth, 0, // v0
// Middle right quad [5]
width, m_borderWidth, 0, // v0
width-m_borderWidth, m_borderWidth, 0, // v1
width-m_borderWidth, height-m_borderWidth, 0, // v2
width-m_borderWidth, height-m_borderWidth, 0, // v2
width, height-m_borderWidth, 0, // v3
width, m_borderWidth, 0, // v0
// Bottom left quad [6]
m_borderWidth, height-m_borderWidth, 0, // v0
0, height-m_borderWidth, 0, // v1
0, height, 0, // v2
0, height, 0, // v2
m_borderWidth, height, 0, // v3
m_borderWidth, height-m_borderWidth, 0, // v0
// Bottom middle quad [7]
width-m_borderWidth, height-m_borderWidth, 0, // v0
m_borderWidth, height-m_borderWidth, 0, // v1
m_borderWidth, height, 0, // v2
m_borderWidth, height, 0, // v2
width-m_borderWidth, height, 0, // v3
width-m_borderWidth, height-m_borderWidth, 0, // v0
// Bottom right quad [8]
width, height-m_borderWidth, 0, // v0
width-m_borderWidth, height-m_borderWidth, 0, // v1
width-m_borderWidth, height, 0, // v2
width-m_borderWidth, height, 0, // v2
width, height, 0, // v3
width, height-m_borderWidth, 0, // v0
// Middle middle quad [9]
width-m_borderWidth, m_borderWidth, 0, // v0
m_borderWidth, m_borderWidth, 0, // v1
m_borderWidth, height-m_borderWidth, 0, // v2
m_borderWidth, height-m_borderWidth, 0, // v2
width-m_borderWidth, height-m_borderWidth, 0, // v3
width-m_borderWidth, m_borderWidth, 0 // v0
};
copy(vertices, vertices + 162, m_vCoords); // 162, because we have 162 coordinates
int dataSize = 162 * sizeof(GLfloat);
m_vboId = createVBO(m_vCoords, dataSize);
}
// bind VBOs for vertex array
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vboId); // for vertex coordinates
glEnableClientState(GL_VERTEX_ARRAY); // activate vertex coords array
glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 162);
glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex array
// bind with 0, so, switch back to normal pointer operation
glBindBufferARB(GL_ARRAY_BUFFER_ARB, NO_VBO_ID);
glPopMatrix();
return true;
}
这是很类似于以前的版本,但不是它正在从存储阵列中的数据创建glDisplayList
和glVertex2i()
我用VBO
。
但结果让我失望,因为我得到了业绩下滑,而不是刺激,我几乎没有 ~260 fps
,我必须指出,在我还没有实现利用纹理这种方法的版本,所以只有四边形现在没有任何纹理绑定到它。
我读了一些文章,发现这可能是这样的原因放慢,并发现可能是由于小VBO
的大金额和我也许应该有一个包含各种背景数据的一个VBO
代替每个背景分开VBO
。但问题在于Object
可以四处移动,并且它们具有不同的纹理(并且纹理地图集对我来说不是一个好的解决方案),所以我很难更新那些改变它们状态的变化。目前,当Object
s正在更改时,我只是重新创建它的VBO
而其余VBO
s保持不变。
所以我的问题是 - 我做错了什么?使用较大(〜600)数量的小VBO
s是否比使用glVertex2i
绘制的过时方法慢?在我的情况下,可能会有什么 - 也许不是最好的,但更好的解决方案?
每秒帧数是一个[可怕的比较度量](http://www.mvps.org/directx/articles/fps_versus_frame_time.htm)。每帧切换至(毫秒)秒。 – genpfault 2013-02-27 15:57:40
@genpfault谢谢,这绝对是好事。但事实是,VBO在这种情况下速度较慢。 – 2013-02-27 16:09:50
一些驱动程序实际上非常擅长编译显示列表。 – JasonD 2013-02-27 16:21:02