Elite Games - Свобода среди звезд!

Уроки для программистов - VBO

Глава 7. VBO

Что же это? Это технология с помощью которой можно поместить информацию о вершинах напрямую в видеокарту и оставить её там. Дает огромный прирост скорости работы приложения. Чтобы внедрить её в наш движок, вначале нужно загрузить расширение GL_ARB_vertex_buffer_object, поддерживаемое всеми современными видеокартами.

http://www.gamedev.ru/terms/VBO — еще тут есть подробная информация.

Итак, как вы рисуете объекты. Есть 3 способа.
1) самый медленный


glBegin(GL_TRIANGLES);
for i:=0 to count-1 do begin
glTexCoord2fv(@ Texcoord[i]);
glNormal3fv(@ Normal[i]);
glVertex3fv(@ Vertex[i]);
end;
glEnd;




2)более быстрый
Идея такова — не давать карточке вершини «поштучно», а отправить сразу массивами


разрешаем массивы
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

в элементе массива 2 значения (если у вас 3, то пишите 3)
GL_FLOAT — тип данных (С++ float, Delphi single)
0 — у вас есть отдельно каждый массив, данные идут подрят
@ Texcoord[0]-указатель на первый элемент массива

glTexCoordPointer(2, GL_FLOAT, 0, @ Texcoord[0]);
у нормалей всегда 3 элемента, поэтому нас даже не спрашивают
glNormalPointer(GL_FLOAT, 0, @ Normal[0]);
glVertexPointer(3, GL_FLOAT, 0, @ Vertex[0]);

рисование
GL_TRIANGLES — то же что и в glBegin (GL_TRIANGLES -самый эффективный)
Есть еще один массив Indx — массив индексов, служит для экономии памяти.
Например вам нужно вывести вершины 1,2,3, потом 2, 3, 4. Без массива Indx вам нужно будет объявить 2,3 — дважды.
А с помощью Indx пишите в нем так [1,2,3,2,3,4] и все.

Count — кол-во элементов в Indx
GL_UNSIGNED_INT — тип элементов в Indx (в Delphi — cardinal)
@ Indx[0] — указатель на первый элемент массива

glDrawElements(GL_TRIANGLES, Count, GL_UNSIGNED_INT, @ Indx[0]);

ВНИМАНИЕ! Даже если у вас в Indx все идет без повторений, это все равно быстрее, чем без массива Indx

запрещаем массивы
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);




Теперь вернемся к VBO.
Идея такова, вместо того чтоб брать данные об массивах с оперативки, нужно брать их из видяхи.

Делается так:


glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[1] );
glNormalPointer( GL_FLOAT, 0, nil );

glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[2] );
glTexCoordPointer( 2, GL_FLOAT, 0,nil );

glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, VBOpoints[3] );

glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[0] );
glVertexPointer( 3, GL_FLOAT, 0, nil );

glDrawElements(GL_TRIANGLES, Count, GL_UNSIGNED_INT, nil);
glVertexPointer — последняя команда перед glDrawElements!! так быстрее


Видим, появился массив VBOpoints.


генерируем 4 имени под 4 массива
glGenBuffersARB( 4, @ VBOpoints[0] );
данные из Vertex (Count*12 --размер)
glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[0] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, Count*12, Vertex, GL_STATIC_DRAW_ARB );

данные из texcoord (Count*12 --размер)
glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[1] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, Count*12, Normal, GL_STATIC_DRAW_ARB );

данные из texcoord (Count*8 --размер)
glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOpoints[2] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, Count*8, texcoord, GL_STATIC_DRAW_ARB );

данные из Indx (Count*4 --размер)
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, VBOpoints[3] );
glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, Count*4, Indx, GL_STATIC_DRAW_ARB );

чистим оперативку
SetLength(vertex,0);
SetLength(normal,0);
SetLength(texcoord,0);
SetLength(indx,0);


После завершения работы нужно почистить данные о VBO


glDeleteBuffersARB(4,@ VBOpoints[0]);




Чтобы использовать VBO нужно загрузить поддержку расширения GL_ARB_vertex_buffer_object:


function Load_GL_ARB_vertex_buffer_object: Boolean;
var
extstring: String;
begin

Result := FALSE;
extstring := String(PChar(glGetString(GL_EXTENSIONS)));

if glext_ExtensionSupported('GL_ARB_vertex_buffer_object', extstring) then
begin
glBindBufferARB := wglGetProcAddress('glBindBufferARB');
if not Assigned(glBindBufferARB) then Exit;
glDeleteBuffersARB := wglGetProcAddress('glDeleteBuffersARB');
if not Assigned(glDeleteBuffersARB) then Exit;
glGenBuffersARB := wglGetProcAddress('glGenBuffersARB');
if not Assigned(glGenBuffersARB) then Exit;
glIsBufferARB := wglGetProcAddress('glIsBufferARB');
if not Assigned(glIsBufferARB) then Exit;
glBufferDataARB := wglGetProcAddress('glBufferDataARB');
if not Assigned(glBufferDataARB) then Exit;
glBufferSubDataARB := wglGetProcAddress('glBufferSubDataARB');
if not Assigned(glBufferSubDataARB) then Exit;
glGetBufferSubDataARB := wglGetProcAddress('glGetBufferSubDataARB');
if not Assigned(glGetBufferSubDataARB) then Exit;
glMapBufferARB := wglGetProcAddress('glMapBufferARB');
if not Assigned(glMapBufferARB) then Exit;
glUnmapBufferARB := wglGetProcAddress('glUnmapBufferARB');
if not Assigned(glUnmapBufferARB) then Exit;
glGetBufferParameterivARB := wglGetProcAddress('glGetBufferParameterivARB');
if not Assigned(glGetBufferParameterivARB) then Exit;
glGetBufferPointervARB := wglGetProcAddress('glGetBufferPointervARB');
if not Assigned(glGetBufferPointervARB) then Exit;
Result := TRUE;
end;

end;


А после инициализации OpenGL делать проверку на наличие данного расширения у видеокарты:


JL_USE_VBO := Load_GL_ARB_vertex_buffer_object;



Теперь рисование в классе JLGeometry такое.

Псевдокод:

var me:PJLMesh;

me:=Meshes.GetMeshByName(MeshId);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

if JL_USE_VBO then begin
glBindBufferARB( GL_ARRAY_BUFFER_ARB, me.VBOpoints[1] );
glNormalPointer( GL_FLOAT, 0, nil );


glClientActiveTextureARB(GL_TEXTURE0_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, me.VBOpoints[2] );
glTexCoordPointer( 2, GL_FLOAT, 0,nil );

if me.VBOpoints[5]<>0 then begin
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, me.VBOpoints[5] );
glTexCoordPointer( 3, GL_FLOAT, 0,nil );
end;
if me.VBOpoints[4]<>0 then begin
glClientActiveTextureARB(GL_TEXTURE2_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, me.VBOpoints[4] );
glTexCoordPointer( 3, GL_FLOAT, 0,nil );
end;


glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, me.VBOpoints[3] );

glBindBufferARB( GL_ARRAY_BUFFER_ARB, me.VBOpoints[0] );
glVertexPointer( 3, GL_FLOAT, 0, nil );

glDrawElements(GL_TRIANGLES, me.Count, GL_UNSIGNED_INT, nil);
end else begin

glClientActiveTextureARB(GL_TEXTURE0_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, @ me.Texcoord[0]);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(3, GL_FLOAT, 0, @ me.Tangent[0]);
glClientActiveTextureARB(GL_TEXTURE2_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(3, GL_FLOAT, 0, @ me.Binormal[0]);


glNormalPointer(GL_FLOAT, 0, @ me.Normal[0]);
glVertexPointer(3, GL_FLOAT, 0, @ me.Vertex[0]);

glDrawElements(GL_TRIANGLES, me.Count, GL_UNSIGNED_INT, @ me.Indx[0]);
end;

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE2_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);


Более подробно о том, как изменять и читать данные из VBO вы можете прочитать здесь:
http://www.gamedev.ru/community/opengl/articles/?id=5
Jurec
К началу раздела | Наверх страницы Сообщить об ошибке
Уроки для программистов - VBO
Все документы раздела: Для тех, кто хочет писать игры | Движок на OpenGL | Создание игр в Game Maker | Bump mapping | Использование Direct Input | XNA framework |


Дизайн Elite Games V5 beta.18
EGM Elite Games Manager v5.17 02.05.2010