Tôi đang phát triển trò chơi 2D dựa trên sprite đơn giản trong C++ sử dụng OpenGL cho hiển thị tăng tốc phần cứng và SDL cho quản lý cửa sổ và người dùng xử lý đầu vào. Vì đó là một game 2D, tôi sẽ chỉ cần vẽ quad, nhưng vì số lượng sprites là động, tôi không bao giờ có thể dựa vào đó là một số không đổi của quad. Do đó, tôi cần phải rebuffer tất cả các dữ liệu đỉnh thông qua VBO của tôi mỗi khung (vì có thể có nhiều hơn hoặc ít hơn quads hơn có trong khung cuối cùng, và do đó bộ đệm có thể là một kích thước khác nhau).Có hiệu quả hơn khi sử dụng GL_TRIANGLE_STRIP hoặc GL_TRIANGLES được lập chỉ mục để vẽ một số động của Quads
Chương trình mẫu thử nghiệm cho đến nay tạo cửa sổ và cho phép người dùng thêm và xóa quad trong hàng chéo bằng cách sử dụng các phím mũi tên lên và xuống. Ngay bây giờ các quads tôi vẽ là hình vuông màu trắng đơn giản, không được kết cấu. Đây là mã Tôi đang làm việc với (biên dịch và làm việc một cách chính xác theo OS X 10.6.8 và Ubuntu 12.04 với OpenGL 2.1):
#if defined(__APPLE__)
#include <OpenGL/OpenGL.h>
#endif
#if defined(__linux__)
#define GL_GLEXT_PROTOTYPES
#include <GL/glx.h>
#endif
#include <GL/gl.h>
#include <SDL.h>
#include <iostream>
#include <vector>
#include <string>
struct Vertex
{
//vertex coordinates
GLint x;
GLint y;
};
//Constants
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;
const int FPS = 60; //our framerate
//Globals
SDL_Surface *screen; //the screen
std::vector<Vertex> vertices; //the actual vertices for the quads
std::vector<GLint> startingElements; //the index where the 4 vertices of each quad begin in the 'vertices' vector
std::vector<GLint> counts; //the number of vertices for each quad
GLuint VBO = 0; //the handle to the vertex buffer
void createVertex(GLint x, GLint y)
{
Vertex vertex;
vertex.x = x;
vertex.y = y;
vertices.push_back(vertex);
}
//creates a quad at position x,y, with a width of w and a height of h (in pixels)
void createQuad(GLint x, GLint y, GLint w, GLint h)
{
//Since we're drawing the quads using GL_TRIANGLE_STRIP, the vertex drawing
//order is from top to bottom, left to right, like so:
//
// 1-----3
// | |
// | |
// 2-----4
createVertex(x, y); //top-left vertex
createVertex(x, y+h); //bottom-left vertex
createVertex(x+w, y); //top-right vertex
createVertex(x+w, y+h); //bottom-right vertex
counts.push_back(4); //each quad will always have exactly 4 vertices
startingElements.push_back(startingElements.size()*4);
std::cout << "Number of Quads: " << counts.size() << std::endl; //print out the current number of quads
}
//removes the most recently created quad
void removeQuad()
{
if (counts.size() > 0) //we don't want to remove a quad if there aren't any to remove
{
for (int i=0; i<4; i++)
{
vertices.pop_back();
}
startingElements.pop_back();
counts.pop_back();
std::cout << "Number of Quads: " << counts.size() << std::endl;
}
else
{
std::cout << "Sorry, you can't remove a quad if there are no quads to remove!" << std::endl;
}
}
void init()
{
//initialize SDL
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, SDL_OPENGL);
#if defined(__APPLE__)
//Enable vsync so that we don't get tearing when rendering
GLint swapInterval = 1;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &swapInterval);
#endif
//Disable depth testing, lighting, and dithering, since we're going to be doing 2D rendering only
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_DITHER);
glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT);
//Set the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
//Set the modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Create VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
}
void gameLoop()
{
int frameDuration = 1000/FPS; //the set duration (in milliseconds) of a single frame
int currentTicks;
int pastTicks = SDL_GetTicks();
bool done = false;
SDL_Event event;
while(!done)
{
//handle user input
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_UP: //create a new quad every time the up arrow key is pressed
createQuad(64*counts.size(), 64*counts.size(), 64, 64);
break;
case SDLK_DOWN: //remove the most recently created quad every time the down arrow key is pressed
removeQuad();
break;
default:
break;
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
//Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//replace the current contents of the VBO with a completely new set of data (possibly including either more or fewer quads)
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), &vertices.front(), GL_DYNAMIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
//Set vertex data
glVertexPointer(2, GL_INT, sizeof(Vertex), 0);
//Draw the quads
glMultiDrawArrays(GL_TRIANGLE_STRIP, &startingElements.front(), &counts.front(), counts.size());
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Check to see if we need to delay the duration of the current frame to match the set framerate
currentTicks = SDL_GetTicks();
int currentDuration = (currentTicks - pastTicks); //the duration of the frame so far
if (currentDuration < frameDuration)
{
SDL_Delay(frameDuration - currentDuration);
}
pastTicks = SDL_GetTicks();
// flip the buffers
SDL_GL_SwapBuffers();
}
}
void cleanUp()
{
glDeleteBuffers(1, &VBO);
SDL_FreeSurface(screen);
SDL_Quit();
}
int main(int argc, char *argv[])
{
std::cout << "To create a quad, press the up arrow. To remove the most recently created quad, press the down arrow." << std::endl;
init();
gameLoop();
cleanUp();
return 0;
}
Tại thời điểm tôi đang sử dụng GL_TRIANGLE_STRIPS với glMultiDrawArrays() để render của tôi quads. Điều này làm việc, và có vẻ là khá phong nha về hiệu suất, nhưng tôi phải tự hỏi liệu sử dụng GL_TRIANGLES kết hợp với một IBO để tránh các đỉnh trùng lặp sẽ là một cách hiệu quả hơn để render? Tôi đã thực hiện một số nghiên cứu và một số người cho rằng chỉ số GL_TRIANGLES thường vượt trội hơn GL_TRIANGLE_STRIPS, nhưng chúng dường như giả định rằng số lượng quad sẽ vẫn không đổi, và do đó kích thước của VBO và IBO sẽ không phải được rebuffered mỗi khung . Đó là sự do dự lớn nhất của tôi với GL_TRIANGLES được lập chỉ mục: nếu tôi đã thực hiện GL_TRIANGLES được lập chỉ mục, tôi sẽ phải xóa toàn bộ bộ đệm chỉ mục mỗi khung ngoài việc tái tạo toàn bộ VBO mỗi khung hình, một lần nữa vì số lượng động của quad. Vì vậy, về cơ bản, câu hỏi của tôi là: Cho rằng tôi phải rebuffer tất cả các dữ liệu đỉnh của tôi để GPU mỗi khung do số động của quads, nó sẽ là hiệu quả hơn để chuyển sang chỉ số GL_TRIANGLES để vẽ quads , hoặc tôi nên gắn bó với việc thực hiện GL_TRIANGLE_STRIP hiện tại của tôi?
tôi nghĩ trước khi bạn phải lo lắng về GL_TRIANGLES so với GL_TRIANGLE_STRIP, bạn nên giảm thiểu các cuộc gọi glBufferData() của mình.Tối ưu hóa dễ nhất: giữ một lá cờ bẩn để lưu trữ nếu createQuad/removeQuad được gọi kể từ lần gọi glBufferData() cuối cùng và chỉ tạo lại bộ đệm khi cờ được đặt. – Dirk
Đó là một gợi ý tuyệt vời, cảm ơn! Tôi chắc chắn sẽ thực hiện nó. – artisticdude