在OpenGL中實現Geometry Instancing

來源:互聯網
上載者:User

聲明:本文僅供個人學習交流使用,著作權歸原作者所有。

譯者:tyxxy

Emial:tyxxyhm@hotmail.com。

如需轉載請註明出處:http://tyxxy.spaces.live.com/

原文地址:http://blog.benjamin-thaut.de/?p=29

本人也在學習階段,若有翻譯得不準確的地方,請不吝賜教。也歡迎有相同興趣的同仁前來探討。為了保證原文原意不被曲解,特保留英文原文,若有翻譯不清楚的地方,請參考原文。2008-5-11

 

Introduction

簡介

To coincide with the release of dx10 class gpus, instancing has become available in OpenGL due to the EXT_draw_instanced extension.

為了與DX10層級的GPU發行同步,OpenGL添加了擴充EXT_draw_instanced,從而使OpenGL具備執行個體化(instancing)繪製的能力。

By itself this extension, which enables you to draw a vertexbuffer multiple times in conjunction with a instance ID accessable in the vertexshader, is of little use.

擴充本身允許你多次繪製同一個頂點緩衝(vertexbuffer),且vertexshader中可以訪問執行個體的ID,但單有這些還是不怎麼有用。

But if you look closer at the extension string you will notice another new extension “EXT_bindable_uniform” which enables you to specify a buffer object as data source for an uniform. With these the GLSL shader has access to much more data. With a geforce8 it is possible to have 12 of these buffers each having a maximum of 64kb, thus in total of 768kb can be stored. The most important use of these buffers is that data only has to be uploaded once to the card, where it can later by reaccessed without the need to resend the data. This allows you to store the worldmatrix transformations of the drawn objects on the graphics card; the subsequentual performance increase is obvious.

但是如果認真查看擴充的字串,你會注意到另一個新的擴充“EXT_bindable_uniform”,可以用該擴充指定一個緩衝對象作為一個統一參數的資料來源。有了這個,GLSL shader就能訪問到多得多的資料。在GeForce8xxx顯卡中,可以有12個這樣的緩衝,每個緩衝可以存放最多64kb資料,這樣總共能存放768kb資料。這些緩衝最重要的用途是:資料可以一次性傳輸到顯卡中,隨後對其訪問就不需要再次發送資料了。你可以將物體的world matrix儲存到顯卡中,這樣潛在的效能提升會是很明顯的。

How-To

如何做

First we have to create a buffer on the graphics card which we store the objects world matrix data in, since the amount of data a buffer can hold is limited we have to divide the data between various buffers.

首先我們在顯卡上建立一個buffer,將物體的world matrix儲存在裡邊。由於單個buffer所能夠儲存的資料量是有限的,故得將其分割並將每部分儲存在不同的buffer中。

見程式列表清單List 1

C++ Code:

List 1:在顯卡上建立world matrix buffers

1.                   mat4 *WorldMats;

2.                   //how many of the objects we wish to draw

3.                   int iNumberOfInstances;

4.                   //Buffer Array

5.                   GLuint *UniformBuffers;

6.                   //the size of a single buffer

7.                   int *UniformBuffersSize;

8.                   //total number buffers

9.                   int AnzBuffers;

10.                     

11.                 void Init(){

12.                  

13.                   ...

14.                  

15.                   iNumberOfInstances = 65535;

16.                   // create the world matrix of all instances

17.                   WorldMats = new mat4[iNumberOfInstances];

18.                     

19.                   ...

20.                     

21.                   #define DRAWS 512

22.                   int remaining=iNumberOfInstances;

23.                  

24.                   UniformBuffers = new GLuint[AnzBuffers];

25.                   UniformBuffersSize = new int[AnzBuffers];

26.                  

27.                   for(int i=0;i<AnzBuffers;i++){

28.                     //the size of the remaining/current buffer

29.                     UniformBuffersSize[i] = remaining;

30.                     if(UniformBuffersSize[i]> DRAWS)

31.                       UniformBuffersSize[i] = DRAWS;

32.                     //create and bind the buffer

33.                     glGenBuffers(1,&UniformBuffers[i]);

34.                     glBindBuffer(GL_UNIFORM_BUFFER_EXT, UniformBuffers[i]);

35.                     //establish the size and sort of the buffer

36.                     //the buffer has to be at least the same size as

37.                    //the uniform in the shader

38.                     glBufferData(GL_UNIFORM_BUFFER_EXT, 16*sizeof(float)*DRAWS, NULL, GL_STATIC_READ);

39.                     //send the data

40.                     glBufferSubData(GL_UNIFORM_BUFFER_EXT, 0, 16*sizeof(float)*UniformBuffersSize[i], &WorldMats[i*DRAWS]);

41.                     //count down the remaining matrixs

42.                     remaining -= DRAWS;

43.                   }

44.                  

45.                   //finished, thus unbind the buffer

46.                   glBindBuffer(GL_UNIFORM_BUFFER_EXT, 0);

}

Now that the data is stored the graphics card, we can turn to the actual rendering (knowledge of vertexbuffers and GLSL shaders is assumed)

這樣資料就儲存到顯卡中了,現在我們來看實際的渲染代碼(假設你已經有頂點buffer和GLSL shader的相關知識)。代碼List 2。

List 2:渲染代碼

1.                   void Draw(){

2.                    

3.                     ...

4.                    

5.                     //loop through the buffers

6.                     for(int i=0;i<AnzBuffers;i++){

7.                       InstancingShader->BindBufferToUniform(0,UniformBuffers[i]);//將緩衝綁定到統一參數中

8.                    

9.                       //bind the instancing shader

10.                     InstancingShader->Use();

11.                     //draw

12.                     Wuerfel->DrawInstanced(UniformBuffersSize[i]);

13.                     //unbind the current instancing shader

14.                     UnloadShader();

15.                   }

16.                   //unbind the buffer (bind to 0)

17.                   InstancingShader->BindBufferToUniform(0,0);

18.                 }

InstancingShader->BindBufferToUniform(0,UniformBuffers[i]);

Inside this function I bind the buffer to the uniform, with the openGL function

glUniformBufferEXT(program, location, buffer)

1/ the handle/ID of the shader program object

2/ the location of the uniform

3/ the buffers ID.

 

在函數InstancingShader->BindBufferToUniform(0,UniformBuffers[i]);中,調用OpenGL函數glUniformBufferExt(program, location, buffer)將緩衝綁定到統一參數中。

函數glUniformBufferExt參數的解析如下:

1、  Program:shader program object的控制代碼/ID;

2、  uniform:統一參數的位置

3、  buffer:緩衝ID

 

The determination of this uniforms location is similar to the usual method of locating uniforms in GLSL. It’s very important that the binding of the buffer happens before the use of the shader. If the shader is currently in use the binding attempt will be simply ignored.

該統一參數位置的確定跟GLSL常用的統一參數的定位方法類似。很重要的一點是在shader使用緩衝之前必須先綁定緩衝。如果當前shader正在使用中,那麼綁定嘗試將會被簡單忽略。

 

Wuerfel->DrawInstanced(UniformBuffersSize[i]);

The actual rendering. This is the same as the standard vertexarray methods except that glDrawArraysInstancedEXT is used instead of glDrawArrays with the last parameter containing the number of instances to be drawn. For indexed VBO’s this would be glDrawElementsInstancedEXT. Instancing objects that are not constructed from triangles or quads are more difficult to draw since MultiDrawArraysInstanced etc are not available. To draw models that are constructed from triangle strips you must use an extra instance for each triangle strip.

函數Wuerfel->DrawInstanced(UniformBuffersSize[i]);是實質的繪製調用。所調用的函數glDrawArraysInstancedEXT除了多一個表示繪製執行個體數量的參數外,其他跟glDrawArrays對標準頂點數組的操作方法無異。對於索引VBO,則所用函數是glDrawElementsInstancedEXT。不是由三角形或者四邊形構建的執行個體對象則比較難繪製,因為沒有MultiDrawArraysInstanced方法。為了繪製由多個三角形條帶構建的模型,你必須為每個三角行條帶使用一個額外的執行個體。

Last but not least the GLSL instancing shader

最後但也一樣很重要的是GLSL執行個體化shader。

List 3:GLSL shader

C:

1.                  #version 120

2.                  #extension GL_EXT_bindable_uniform: enable

3.                  #extension GL_EXT_gpu_shader4: enable

4.                   

5.                  bindable uniform mat4 WorldMats[512];

6.                   

7.                  void main(void){

8.                    vec4 position = WorldMats[gl_InstanceID] * gl_Vertex;

9.                    position = gl_ModelViewMatrix * position;

10.                 gl_Position = gl_ProjectionMatrix * position;

11.                

12.                 vec3 normal = mat3(WorldMats[gl_InstanceID]) * gl_Normal; 

13.                 normal = mat3(gl_ModelViewMatrix) * normal;

14.                

15.                 vec3 lightVectorView = normalize(gl_LightSource[0].position.xyz - position.xyz);

16.                

17.                 gl_FrontColor = ((gl_LightSource[0].diffuse * max(dot(normal, lightVectorView), 0.0)) + gl_LightSource[0].ambient + 0.2) * gl_Color;

18.               }

The defines at the beginning are necessary to specify that we use shader model 4.0 and the EXT_bindable_uniform extension. The most important parts of the shader are the first 3 lines of the main function. There the individual world matrix of each instance is accessed with the instance ID to compute the correct position of each vertex. In this case the view matrix would be the OpenGL model view matrix. The rest of the main functions creates a simple per vertex diffuse lighting as the fixed function pipeline does. To avoid problems with transforming normals into the worldspace, avoid scaling within the matrices. If you want this method to work in all cases you have to compute a normal matrix per instance by yourself and pass it to the shader too.

開始處的定義是必須的,它用於指示將採用shader model 4.0和EXT_bindable_uniform擴充。Shader中最重要的部分是主函數的前三句。在那裡,每個執行個體各自的world matrix都用Instance ID來訪問,並用於計算每個頂點的正確位置。在這種情況下,view matrix將會是OpenGL中的modelView matrix(沒有乘上model矩陣的modelView matrix當然就是view matrix啦)。主函數的其餘部分建立了一個簡單的逐頂點diffuse光照,這跟固定功能流水線所做的工作一樣。為了避免將法線變換到世界空間時出現的問題,不要在矩陣中做縮放。如果希望該方法在所有情況下都能正常工作,那就需要對每一個執行個體計算一個歸一化的矩陣並將其傳到shader中。

Performance

效能評價

In the following diagram we compare the three drawing methods (X axis is the number of drawn instances per frame, the Y axis shows the frames per second)

在中,我們比較三種繪製方法(X軸是每幀繪製執行個體的個數,Y軸是FPS)

 
 

We can conclude that EXT_draw_instanced is about twice as fast as nvidias pseudo instancing which in turn is about twice as fast as the standard drawing method. With instancing a geforce 8800 GTX is capable of drawing 131072 cubes 45 times a second.

我們推斷, EXT_draw_instanced比nvidias偽執行個體化速度大約快兩倍,而偽執行個體化又是常規繪製方法速度的兩倍。採用執行個體化技術,Geforce8800GTX每秒能夠繪製131072個方盒子45次。

Since the number of objects the user wishes to draw at the same time varies, I’ve benchmarked various sizes. In the following diagram I've drawn 131072 cubes (X axis is the number of cubes drawn with one call, the Y axis shows the frames per second).

由於使用者在每次繪製時,所期望繪製的對象數量不斷變化,我已對不同數量的繪製做了一個基準。在接下來的圖表中,我繪製了131072個立方盒(X軸是每次調用繪製的立方盒數量,Y軸是FPS)

 

Drawing 16 cubes a call, this method has no performance increase compared to pseudo instancing. With group sizes of 256 or larger the performance increase is much smaller ( a 0.5fps with each doubling of the groupsize )

Due to the buffer size limitation of EXT_bindable_uniform the maximum group size is 1024.

每次調用繪製16個立方盒,該方法跟偽執行個體化相比,效能上沒有提升。每組的數量大於等於256時,效能提升有限(組大小每增大一倍,提高0.5fps)。

由於EXT_bindable_uniform緩衝大小的限制,最大的組大小是1024。

 

Conclusion

Instancing performs best if the objects to be drawn are static, if the objects are moving, requiring you to update the world matrices each frame, the benefits over pseudo-instancing are greatly reduced. Because you can update the data at once and not send the world matrices one by one, as it is done by pseudo instancing, it would be still faster than pseudo instancing. To sum up the new draw call is definitely effective and coupled with the bindable uniform extension very useful. The downsides though are at the moment only a limited number of graphics cards support the extensions as well as the current driver’s instability with their usage. I regularly experienced driver memory access violations when I wanted to terminate my program.

結論:

當繪製的物體時靜態時,執行個體化能工作得最好。如果物體是不停移動的,你每幀都需要更新世界矩陣,這樣相對於偽執行個體化的優勢就降低了。由於你可以一次更新資料,但不能逐個發送world matrix,就像偽執行個體化做的一樣,但這還是比偽執行個體化快一些。總得來說,新的繪製調用肯定是很高效的,與可綁定統一參數擴充一起使用時,尤為有用。但是目前支援該擴充的顯卡非常有限,而且使用該方法時,當前的驅動也不甚穩定。當我結束程式時會經常碰到驅動記憶體訪問違例的問題。

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/swq0553/archive/2010/12/08/6063654.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.