首页 > 代码库 > CSharpGL(30)用条件渲染(Conditional Rendering)来提升OpenGL的渲染效率

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

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             else19             {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             else118             {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)是一项非常厉害的技术。当要渲染一个数据量很大的模型时,用条件渲染技术能够显著提升渲染效率,因为这个技术能够少画一些被遮挡的内容。

可疑的是,我试验出来的条件渲染时的FPS反而更低,不知道是怎么回事。

 

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