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

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

时间: 2024-08-26 05:35:42

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

渲染上下文Rendering Context

渲染描述表,包含一次渲染所需的全部数据.用于提交给游戏渲染引擎的渲染器进行渲染 -------------------------------------------------------------------- 如果把渲染引擎看做一个画家,那么画家开始作画之前需要做一系列的准备工作,譬如:布置好场景,摆好画架,钉好画布,调整好灯光,准备号画笔油彩,站好位置,然后才能开始下笔作画. 前期的这一系列准备过程在D3D和Ogl这样的渲染Api中对应了一系列的接口函数,这些函数初看起来又多又乱,有时

Unity3d 基于物理渲染Physically-Based Rendering之最终篇

前情提要: 讲求基本算法 Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF plus篇 Unity3d 基于物理渲染Physically-Based Rendering之实现 最后我们用fragment shader 实现,加上diffuse漫反射,代码和之前的surface差不多,只是多了reflect方向的计算,reflect方向的计算方法为用CG函数库中函数reflect, float3 reflect(float3 i, flo

Unity渲染路径 Rendering Paths_2_Forward Rendering 正向渲染

Forward Rendering 正向渲染 正向渲染一个基于着色器的渲染路径.它支持逐像素计算光照(包括法线贴图和灯光Cookies)和来自一个平行光的实时阴影.在默认设置中,少数最亮的灯光在逐像素计算光照模式下渲染.其余的灯光计算对象顶点的光照. 根据影响物体的光源的不同,正向渲染路径用单个或多个通道来渲染物体.在正向渲染中,光源本身也会根据他们的设置和强度受到不同的对待. 实现细节 在正向渲染中,影响物体的最亮的几个光源使用逐像素光照模式.接下来,最多有4个点光源会以逐顶点渲染的方式被计算

Unity渲染路径 Rendering Paths_1_Deferred Lighting 延时光照

Deferred Lighting 延时光照 延时光照是有着最高保真度的光照和阴影的渲染路径.如果你有很多实时灯光,最好是使用延时光照.它需要一定水平的硬件支持,仅在 Unity Pro可用,移动设备上不支持. 延迟光照是一种当前最高级的能实现光线和阴影保真的渲染路径 对于能影响任何物体的光线数量没有上限 完全采用以每像素的方式评估光线,这等于意味着全部将以正常贴图的方式正确的和物体交互 所有光线都能拥有信息缓存 所有的光线都能产生阴影 延迟光照的优点: 光照的开销与屏幕的光线尺寸成正比,不用担

Ogre 渲染目标解析与多文本合并渲染

实现目标 因为需求,想找一个在Ogre中好用的文本显示,经过查找和一些比对.有三种方案 一利用Overlay的2D显示来达到效果. http://www.ogre3d.org/tikiwiki/tiki-index.php?page=MovableTextOverlay 二重写Renderable与MovableObject,利用对应字体查找到每个字符元素纹理坐标. http://www.ogre3d.org/tikiwiki/tiki-index.php?page=MovableText 三利

浏览器渲染基本原理(五):优化渲染性能

渲染卡顿是怎么回事? 网页不仅应该被快速加载,同时还应该流畅运行,比如快速响应的交互,如丝般顺滑的动画等. 大多数设备的刷新频率是60次/秒,也就说是浏览器对每一帧画面的渲染工作要在16ms内完成,超出这个时间,页面的渲染就会出现卡顿现象,影响用户体验. 为了保证页面的渲染效果,需要充分了解浏览器是如何处理HTML/JavaScript/CSS的. 渲染流程分为几步? JavaScript:JavaScript实现动画效果,DOM元素操作等. Style(计算样式):确定每个DOM元素应该应用什

基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之LayerColor(8)

在前面文章中讲述了Cocos2d-x引擎OpenGL渲染准备Shader方面,本文主要讲解使用LayerColor来讲述OpenGL的渲染过程. 1.LayerColor对象创建 添加LayerColor元素到游戏中: autolayerColor = LayerColor::create(Color4B(255, 0, 0, 255), 100, 100); layerColor->setPosition(100,100); 下面是LayerColor::create方法: LayerColo

OpenGL基础渲染

OpenGL渲染管线(简化版) 客户端-服务器 客户端是存储在CPU存储器中的,并且在应用程序中执行(或者驱动程序),驱动程序将渲染命令和数据组合起来,发动到服务器执行.服务器和客户机在功能上市异步的,他们是各自独立的软件模块或者硬件模块. 着色器 数据先传给顶点着色器,然后是片段着色器,几何着色器(可选择)出现在两者之间 顶点着色器(Vertex Shader) 片段着色器(Fragment Shader) fragment不是最后的像素数据,但和像素对应:fragment需要经过处理,ble

Windows下使用GetGlyphOutline在OpenGL中渲染字体

欢迎转载,请标明出处:http://blog.csdn.net/tianyu2202/ 无图无JB,先上图.使用OpenGL绘制字体,支持多种字体,支持TrueType轮廓字体,支持自选字体纹理大小和输出大小,支持在三维空间内绘制. 关于OpenGL中字体的显示网上其实有很多的教程,不过经常用到的方式有比较简单的Bitmap方式.比较复杂的FreeType方式.而本文介绍的方式虽然只能在Windows下实现,却有着和FreeType一样的显示效果,最重要的是非常简单,仅仅200多行代码即可实现.