العديد من الكواد، وليس الكثير من الإطارات في الثانية على الرغم من VBO
-
27-10-2019 - |
سؤال
لقد حصلت على بعض الوقت وقرأت عن VBO وهذا ما حصلت عليه:
http://img64.imageshack.us/img64/5733/fps8.jpg
حسنًا، إنه أفضل بكثير من ذي قبل.تم تجميعها في الإصدار.أستخدم VBO (ربما، إذا كان كل شيء على ما يرام) وglDrawArrays للرسم.
وهنا رمز الرسم.من فضلك أعطني النصيحة حول كيفية تحسينه.أردت مع التضاريس ...اه بضعة آلاف من الإطارات في الثانية، هل هذا حقيقي؟
void DrawVBO (void)
{
int i;
CVert* m_pVertices;
CTexCoord* m_pTexCoords;
unsigned int m_nTextureId;
m_pVertices = NULL;
m_pTexCoords = NULL;
m_nVertexCount = 0;
m_nVBOVertices = m_nVBOTexCoords = m_nTextureId = 0;
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wskaźniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
wglGetProcAddress("glDeleteBuffersARB");
}
todrawquads=0;
nIndex=0;
// my function counting how many quads I will draw
for (i=0;i<MAX_CHUNKS_LOADED;i++)
{
if (chunks_loaded[i].created==1)
{
countquads(i);
}
}
m_nVertexCount=4*todrawquads;
m_pVertices = new CVec[m_nVertexCount];
m_pTexCoords = new CTexCoord[m_nVertexCount];
// another my function adding every quad which i'm going to draw (its verticles) to array
for (i=0;i<MAX_CHUNKS_LOADED;i++)
{
if (chunks_loaded[i].created==1)
{
addchunktodraw(i,m_pVertices,m_pTexCoords);
}
}
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity (); // Reset The Modelview Matrix
fps++;
// Camera settings.
//gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
//m_nTextureId = t_terrain;
// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );
// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY ); // Enable Vertex Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // Set The TexCoord Pointer To The TexCoord Buffe
glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Disable Texture Coord Arrays
liniergb();
glutSwapBuffers();
delete [] m_pVertices; m_pVertices = NULL;
delete [] m_pTexCoords; m_pTexCoords = NULL;
}
إذن ماذا يمكنني أن أفعل به؟(الرمز أعلاه هو وظيفة السحب الرئيسية)
يحرر
لقد قمت بنقل هذا :
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wskaźniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)
wglGetProcAddress("glDeleteBuffersARB");
}
إلى وظيفتي الرئيسية.ربما لا يوجد تحسن.
تحرير2
الآن، أستطيع أيضًا أن أرى أنه يأكل الذاكرة.كل بضع ثوانٍ، يزداد استخدام ذاكرة البرنامج أكثر فأكثر...ما هو الخطأ ؟ما الذي لا أحذفه؟
تحرير3
حسنًا، شكرًا جزيلاً.لقد قمت بنقل بعض التعليمات البرمجية خارج وظيفة السحب و ...المزيد من الإطارات في الثانية!ًشكراً جزيلا !
http://img197.imageshack.us/img197/5193/fpsfinal.jpg
إنها خريطة بحجم 640 × 640 كتلة (أكبر 40 مرة) مع 650000 كواد (حوالي 70 مرة أكثر) ولا تزال ~ 170 إطارًا في الثانية.عظيم !ولا تسرب الذاكرة.شكرًا لك مرة أخرى !
المحلول
يجب أن تحتوي وظيفة DrawVBO الخاصة بك على ما يلي فقط:
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity (); // Reset The Modelview Matrix
fps++;
// Camera settings.
//gluLookAt (zom, zom, zom, 0.0, 0.0, 0.0, 0, 0, 1);
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY ); // Enable Vertex Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // Set The TexCoord Pointer To The TexCoord Buffe
glDrawArrays( GL_QUADS, 0, m_nVertexCount); // Draw All Of The Triangles At Once
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Disable Texture Coord Arrays
liniergb();
glutSwapBuffers();
تحتاج إلى نقل الباقي إلى وظيفة منفصلة يتم استدعاؤها مرة واحدة فقط عند بدء التشغيل (أو عندما تتغير التضاريس).
نصائح أخرى
هل تقوم بإعادة تحميل كافة المخازن المؤقتة الخاصة بك وتحريرها مرة أخرى في كل إطار على حدة؟توقف عن فعل ذلك وسيرتفع معدل الإطارات لديك.
لاحظ أن كودك الحالي سوف ينفد في النهاية من معرفات VBO، نظرًا لأنك لا تحذف مطلقًا معرفات VBO التي تقوم بإنشائها.
ربط وظائف التمديد قطعاً لا يلزم القيام بكل إطار أيضًا.
تبرز بعض الأشياء:
في وظيفة الرسم، تقوم بتخصيص الذاكرة لبياناتك الهندسية
m_pVertices = new CVec[m_nVertexCount];
m_pTexCoords = new CTexCoord[m_nVertexCount];
يعد تخصيص الذاكرة عملية مكلفة للغاية، وهي واحدة من تلك الأشياء التي يجب القيام بها مرة واحدة فقط.ليس المقصود من OpenGL أن يتم "تهيئته" - لكن هياكل البيانات التي ستمررها إليها هي!
هنا تقوم بنسخ المخازن المؤقتة المخصصة حديثًا إلى OpenGL، مرارًا وتكرارًا مع كل إطار.وهذا هو بالضبط عكس ما يجب القيام به.
// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );
الفكرة الكاملة لـ VBOs هي تحميل البيانات مرة واحدة فقط، ونسخها إلى OpenGL ثم عدم إعادة تخصيصها مرة أخرى.لاحظ أن هذه ليست تهيئة OpenGL، إنها تهيئة بيانات، وهو أمر معقول تمامًا.أرى أنك قمت بتسمية المتغيرات الخاصة بك m_pVertices
و m_pTexCoords
تشير إلى أن هذه هي متغيرات أعضاء الفئة.ثم الحل بسيط:انقل رمز التهيئة بالكامل إلى بعض وظائف المُحمل.أيضًا بدلاً من صفائف C++ العارية أقترح بشدة استخدام std::vector
.
لذلك دعونا نصلح هذا:
// Load Extensions only once. Well, once per context actually, however
// why don't you just use an extension wrapper and forget about those
// gritty details? Google GLEW or GLee
void init_extensions()
{
if( IsExtensionSupported( "GL_ARB_vertex_buffer_object" ) )
{
// Pobierz wska\u017aniki na funkcje OpenGL
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
}
}
class Block0r
{
protected:
GLuint m_nTextureId;
GLuint m_nVBOVertices;
GLuint m_nVBOTexCoords;
GLuint m_nVertexCount;
// Call this one time to load the data
void LoadVBO()
{
std::vector<CVert> vertices;
std::vector<CTexCoord> texCoords;
// my function counting how many quads I will draw
todrawquads = 0;
for(int i=0; i < MAX_CHUNKS_LOADED; i++) {
if( chunks_loaded[i].created == 1 ) {
countquads(i);
}
}
m_nVertexCount = 4*todrawquads;
vertices.resize(vertexcount);
texcoords.resize(vertexcount);
for (i=0;i<MAX_CHUNKS_LOADED;i++) {
if (chunks_loaded[i].created==1) {
addchunktodraw(i, &vertices[0], &texcoords[0]);
}
}
glGenBuffersARB( 1, &m_nVBOVertices );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, vertices.size()*sizeof(CVert), &vertices[0], GL_STATIC_DRAW_ARB );
glGenBuffersARB( 1, &m_nVBOTexCoords );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, texCoords.size()*sizeof(CTexCoord), &texCoords[0], GL_STATIC_DRAW_ARB );
}
void DrawVBO()
{
glClearColor (1,1,1, 0.0);
glColor3f(1,1,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
setup_projection(); // this should really be done in the drawing handler
glMatrixMode(GL_MODELVIEW); // don't asssume a certain matrix being active!
glLoadIdentity();
fps++;
gluLookAt (zoom, zoom, zoom, 0.0, 0.0, 0.0, 0, 0, 1);
glRotatef((rot_x / 180 * 3.141592654f),1,0,0);
glRotatef((rot_y / 180 * 3.141592654f),0,1,0);
glRotatef((rot_z / 180 * 3.141592654f),0,0,1);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (GLvoid *) 0 );
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (GLvoid *) 0 );
glDrawArrays( GL_QUADS, 0, m_nVertexCount);
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
liniergb();
glutSwapBuffers();
}
}
في ملحوظة جانبية:تعليقات مثل // Generate And Bind The Vertex Buffer
أو // Set The Vertex Pointer To The Vertex Buffer
ليست مفيدة للغاية.تخبرنا هذه التعليقات بشكل متكرر بما يمكن للمرء قراءته من الكود على أي حال.يجب إضافة التعليقات إلى التعليمات البرمجية، والتي لا يمكن فهم الأعمال الداخلية عليها على الفور، أو إذا كان عليك القيام بنوع من الاختراق لإصلاح شيء ما، وهذا الاختراق من شأنه أن يحير شخصًا آخر، أو نفسك في غضون بضعة أشهر.