文章出處

CSharpGL(30)用條件渲染(Conditional Rendering)來提升OpenGL的渲染效率

當場景中有比較復雜的模型時,條件渲染能夠加速對復雜模型的渲染。

條件渲染(Conditional Rendering)

當我們能夠斷定一個模型被其他模型擋住(因此不會被Camera看到)時,我們就可以跳過對此模型的渲染。這就是條件渲染的根本。

那么如何去判斷?方法就是用一個簡單的包圍盒(比如一個立方體)去渲染一下,看看fragment是不是有變化(即包圍盒上的某些部分通過了depth test,最終渲染到Framebuffer上了)。如果沒有任何一個fragment發生改變,就說明這個包圍盒是被擋住了,那么被包圍起來的模型也必然是被擋住了。

下載

CSharpGL已在GitHub開源,歡迎對OpenGL有興趣的同學加入(https://github.com/bitzhuwei/CSharpGL

原理

本篇需要用到2個知識點。

遮面查詢(Occlusion Query)

這里OpenGL提供了一個Query Object。類似Buffer Object,Vertex Array Object等,也是通過glGen*等方式使用的。

Query Object的作用就是記錄一個包圍盒是否改變了某些fragment。

如下代碼所示,在通常的渲染前后用glBeginQuery和glEndQuery包圍起來,Query就會記錄是否有fragment被改變了。

1 glBeginQuery(GL_SAMPLES_PASSED, queryId);
2 glDrawArrays(GL_TRIANGLES, 0, 3);
3 glEndQuery(GL_SAMPLES_PASSED);

之后用下述方式即可獲知是否有fragment被改變了。只要SampleRendered()返回值為false,那么這個模型就不用渲染了。

 1         /// <summary>
 2         /// 
 3         /// </summary>
 4         /// <returns></returns>
 5         public bool SampleRendered()
 6         {
 7             var result = new int[1];
 8             int count = 1000;
 9             while (result[0] == 0 && count-- > 0)
10             {
11                 glGetQueryObjectiv(this.Id, OpenGL.GL_QUERY_RESULT_AVAILABLE, result);
12             }
13 
14             if (result[0] != 0)
15             {
16                 glGetQueryObjectiv(this.Id, OpenGL.GL_QUERY_RESULT, result);
17             }
18             else
19             {
20                 result[0] = 1;
21             }
22 
23             return result[0] != 0;
24         }

條件渲染(Conditional Rendering)

上述Query對象使用時的一個缺點是,CPU必須用循環等待GPU的Query結果。這就拖延了后續渲染步驟,降低了FPS。

為避免CPU循環等待,OpenGL提供了下面2個指令,他們的作用就是用GPU代替了CPU循環的功能。

1 glBeginConditionalRender(uint id, uint mode);
2 glEndConditionalRender();

其使用方式也像Query對象一樣,把通常的渲染指令包圍起來即可。

1 glBeginConditionalRender(queryId, GL_QUERY_WAIT);
2 glDrawArrays(GL_TRIANGLE_FAN, 0, numVertices);
3 glEndConditionalRender();

示例

一個特別的3D模型

我們需要設計一個頂點多而又被一個簡單的模型遮擋住的模型。這個復雜模型就用點云表示,用于遮擋的模型就用一個簡單的Cube就可以了。

運用CSharpGL封裝的功能,很快就可以做出這個模型來。

  1     /// <summary>
  2     /// demostrates how to perform conditional rendering.
  3     /// </summary>
  4     internal class ConditionalRenderer : RendererBase
  5     {
  6         private const int xside = 5, yside = 5, zside = 5;
  7         private const int pointCount = 10000;
  8         private static readonly vec3 unitLengths = new vec3(1, 1, 1);
  9         private const float scaleFactor = 1.0f;
 10 
 11         private List<Tuple<CubeRenderer, RendererBase, Query>> coupleList = new List<Tuple<CubeRenderer, RendererBase, Query>>();
 12         private DepthMaskSwitch depthMaskSwitch = new DepthMaskSwitch(false);
 13         private ColorMaskSwitch colorMaskSwitch = new ColorMaskSwitch(false, false, false, false);
 14 
 15         private bool enableConditionalRendering = true;
 16 
 17         public bool ConditionalRendering
 18         {
 19             get { return enableConditionalRendering; }
 20             set { enableConditionalRendering = value; }
 21         }
 22 
 23         private bool renderBoundingBox = false;
 24 
 25         public bool RenderBoundingBox
 26         {
 27             get { return renderBoundingBox; }
 28             set { renderBoundingBox = value; }
 29         }
 30 
 31         private bool renderTargetModel = true;
 32 
 33         public bool RenderTargetModel
 34         {
 35             get { return renderTargetModel; }
 36             set { renderTargetModel = value; }
 37         }
 38 
 39         public static ConditionalRenderer Create()
 40         {
 41             var result = new ConditionalRenderer();
 42             {
 43                 var wallRenderer = CubeRenderer.Create(new Cube(new vec3(unitLengths.x * 2, unitLengths.y * 2, 0.1f) * new vec3(xside, yside, zside)));
 44                 wallRenderer.WorldPosition = new vec3(0, 0, 6);
 45                 var boxRenderer = CubeRenderer.Create(new Cube(new vec3(unitLengths.x * 2, unitLengths.y * 2, 0.1f) * new vec3(xside, yside, zside)));
 46                 boxRenderer.WorldPosition = new vec3(0, 0, 6);
 47                 var query = new Query();
 48                 result.coupleList.Add(new Tuple<CubeRenderer, RendererBase, Query>(boxRenderer, wallRenderer, query));
 49             }
 50             for (int x = 0; x < xside; x++)
 51             {
 52                 for (int y = 0; y < yside; y++)
 53                 {
 54                     for (int z = 0; z < zside; z++)
 55                     {
 56                         var model = new RandomPointsModel(unitLengths, pointCount);
 57                         RandomPointsRenderer renderer = RandomPointsRenderer.Create(model);
 58                         renderer.PointColor = Color.FromArgb(
 59                             (int)((float)(x + 1) / (float)xside * 255),
 60                             (int)((float)(y + 1) / (float)yside * 255),
 61                             (int)((float)(z + 1) / (float)zside * 255));
 62                         renderer.WorldPosition =
 63                             (new vec3(x, y, z) * unitLengths * scaleFactor)
 64                             - (new vec3(xside - 1, yside - 1, zside - 1) * unitLengths * scaleFactor * 0.5f);
 65                         var cubeRenderer = CubeRenderer.Create(new Cube(unitLengths));
 66                         cubeRenderer.WorldPosition = renderer.WorldPosition;
 67                         var query = new Query();
 68                         result.coupleList.Add(new Tuple<CubeRenderer, RendererBase, Query>(cubeRenderer, renderer, query));
 69                     }
 70                 }
 71             }
 72 
 73             result.Lengths = new vec3(xside + 1, yside + 1, zside + 1) * unitLengths * scaleFactor;
 74 
 75             return result;
 76         }
 77 
 78         private ConditionalRenderer()
 79         { }
 80 
 81         protected override void DoInitialize()
 82         {
 83             foreach (var item in this.coupleList)
 84             {
 85                 item.Item1.Initialize();
 86                 item.Item2.Initialize();
 87                 item.Item3.Initialize();
 88             }
 89         }
 90 
 91         protected override void DoRender(RenderEventArgs arg)
 92         {
 93             if (this.ConditionalRendering)
 94             {
 95                 this.depthMaskSwitch.On();
 96                 this.colorMaskSwitch.On();
 97                 foreach (var item in this.coupleList)
 98                 {
 99                     item.Item3.BeginQuery(QueryTarget.AnySamplesPassed);
100                     item.Item1.Render(arg);
101                     item.Item3.EndQuery(QueryTarget.AnySamplesPassed);
102                 }
103                 this.colorMaskSwitch.Off();
104                 this.depthMaskSwitch.Off();
105                 var result = new int[1];
106                 foreach (var item in this.coupleList)
107                 {
108                     item.Item3.BeginConditionalRender(ConditionalRenderMode.QueryByRegionWait);
109                     //if (item.Item3.SampleRendered())
110                     {
111                         if (this.renderTargetModel) { item.Item2.Render(arg); }
112                         if (this.renderBoundingBox) { item.Item1.Render(arg); }
113                     }
114                     item.Item3.EndConditionalRender();
115                 }
116             }
117             else
118             {
119                 foreach (var item in this.coupleList)
120                 {
121                     if (this.renderTargetModel) { item.Item2.Render(arg); }
122                     if (this.renderBoundingBox) { item.Item1.Render(arg); }
123                 }
124             }
125         }
126     }
conditional rendering demo. 

條件渲染效果

下面讓藍色的墻遮擋住彩色點云。

總結

條件渲染(Conditional Rendering)是一項非常厲害的技術。當要渲染一個數據量很大的模型時,用條件渲染技術能夠顯著提升渲染效率,因為這個技術能夠少畫一些被遮擋的內容。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()