(原)Unreal渲染模块 管线 - 程序和场景查询
@author: 白袍小道
查看随意,转载随缘
第一部分:
这里主要关心加速算法,和该阶段相关的UE模块的结构和组件的处理。
What-HOW-Why-HOW-What(嘿嘿,老规矩)
1、渲染模块这里有个主要任务需要完成:将需要在屏幕上(或者某设备)显示出来的Primitives(几何体,或者绘制图元)输入到pipeline的下一阶段。
2、渲染的每帧,计算Camera,Light,Primitives输出到几何阶段(非几何着色)
插一句:Geometry State包含了视点变换,顶点着色,投影、裁剪、映射
3、几个空间数据结构和算法:
层次包围,入口裁剪、QuadTree, 空间分隔树, Kd树,八叉树,场景图、细节裁剪
空间数据结构:
是将几何体组织在N维空间中的一系列层次(上抱下,下抱下下,类推)数据结构
层次包围体BVH
入口裁剪PortalCulling
细节裁剪( 屏幕尺寸裁剪 )
具有包围体的问题,将这个包围体投射到投影平面,然后以像素为单位来估算投影面积,如果像素的数量小于用户定义的阈值,那么不对这个物体进行进一步处理。
遮挡删除
遮挡剔除必要性:
不难理解,可见性问题可以通过Z缓冲器的硬件构造来实现,即使可以使用Z缓冲器正确解决可见性问题,但其中Z缓冲并不是在所有方面都不是一个很"聪明"的机制。例如,假设视点正沿着一条直线观察,其中,在这条直线上有10个球体,虽然这10个球体进行了扫描转换,同时与Z缓冲器进行了比较并写入了颜色缓冲器和Z缓冲器,但是这个从这个视点渲染出的图像只会显示一个球体,即使所有10个球体都将被光栅化并与Z缓冲区进行比较,然后可能写入到颜色缓冲区与Z缓冲区。
上图中间部分显示了在给定视点处场景的深度复杂度,深度复杂度指的是对每个像素重写的次数。对于有10个球体的情形,最中间的位置,深度复杂度为10,因为在这个地方渲染了10个球体(假设背面裁剪是关闭的),而且这意味着其中有9次像素写入是完全没有必要的。
两种主要形式的遮挡裁剪算法,分别是基于点的遮挡裁剪和基于单元的遮挡裁剪
伪代码--------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
下面是常用几种遮挡算法
1、硬件遮挡查询(UE中有,也可以自己先写写,然后测试对照)
硬件遮挡查询的基本思想是,当和Z缓冲器中内容进行比较时,用户可以通过查询硬件来找到一组多边形是否可见的,且这些多边形通常是复杂物体的包围体(如长方体或者k-DOP)。如果其中没有多边形可见,那么便可将这个物体裁剪掉。硬件实现对查询的多边形进行光栅化,并且将其深度和Z缓冲器进行比较
2、HZB(同上)
层次Z-缓冲算法用八叉树来维护场景模型,并将画面的Z缓冲器作为图像金字塔(也称为Z-金字塔(Z-pyramid)),该算法因此在图像空间中进行操作。其中,八叉树能够对场景的遮挡区域进行层次剔除,而Z-金字塔则可以对单个基元和边界体积进行层次Z缓冲。 因此Z-金字塔可以作为此算法的遮挡表示。
通过从前到后遍历八叉树并裁剪遇到的八叉树节点,此算法可以仅访问可见的八叉树节点及其子节点(右上角的节点),
的容器只对可见包围体中的多边形进行渲染。
3、遮挡地平线算法
通过从前到后渲染一个场景,我们可以定位到地平线在哪里进行渲染,而任何在当前地平线之后和之下的物体都可以被裁剪掉。
4、遮挡物收缩与视锥扩张算法(也有类似处理)
可以使用基于点的遮挡算法来生成基于单元的可见性,根据给定的量来缩小场景中所有遮挡物来达到延伸有效可见点的目的,
通常与Occluder Shrinking算法一起配合使用
5、LOD这个放到后面细说。【篇幅不少】
6、裁剪图策略:后面加入
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第二部分:
下面按照延时渲染来过一下。
涉及主要类
DeferredShadingSceneRenderer
FSceneRender,FSceneViewFamily,FViewInfo,Fscene,FView
FMeshElementCollector
SceneOcclusion
FSceneViewState
TStaticMeshDrawList
FRenderTask\FDrawVisibleAnyThreadTask
DrawingPolicy,FPrimitiveSceneProxy
一、FDeferredShadingSceneRenderer::InitViews
这里主要通过检测可见性,透明排序等,完成视图初始化。这里我们关注检测可见性
、
灯光信息的可见分配。
、预处理可见性
位于文件:SceneVisibility.cpp
FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList)
2.1 |
得到当前预处理可见性的数据 |
FSceneViewState::GetPrecomputedVisibilityData
完成任务:
*返回给定视图位置的可见性数据数组(数据),如果不存在,则返回NULL。
*数据位通过场景中每个原语的VisibilityId进行索引。
*此方法在必要时解压缩数据,并基于视图状态中的bucket和chunk(优化时候也需要注意)索引缓存数据
(这里若需要查看可以通过几个调试选项【详细在后面备注】,然后烘培)
如何完成:
a、计算盒子的ViewOrigin是否在格子中,这里利用反过来将盒子散列到bucket去减少了计算量。
这里盒子偏移,Bucket索引计算可以看看。
b、若有必要解压数据
2.1后(构建有时会覆盖截锥体,就是迭代覆盖视口的图元可见性Map【view.PrimitiveVisibilityMap】)
2.2 |
更新HLOD转换/可见性状态 |
这里开启后是为了允许HLOD在踢裆删除阶段可以使用
2.3 |
计算使用标准视锥体裁剪的图元的数目---FrustumCull |
接下来是引擎特性(处理view.PrimitiveVisibilityMap)
2.4a |
更新了视图的原始fade状态(略)。 |
2.4b |
(扫描VisibilityMap后面就说VMP)如果几何体标记为Hide,view.PrimitiveVisibilityMap标记 |
2.4c |
视图属性标记了只显示几何体的,那其他同样标记 |
2.4d |
反射捕获(Reflection Captures)的处理:只对接受非移动的. |
2.4e |
剔除线框中的小框对象【就一个投射矩阵判断】【主要是提高编辑器,因为线框模式的禁止了遮挡,我去】 |
2.4f |
(不在线框中的)进行剔除 |
2.5 |
OcclusionCull |
这里才是算法实现的开始,包括使用前面说的预计算数据,根据特征级别(opengl,dx)做不同的OC处理
a、软处理(自己整CPU处理)FSceneSoftWareOCclusion。
b、FetchVisibilityForPrimitives处理(还有FHZBOcclusionTester)
c、标记到非OC(没得整)组
2.5.1 |
FSceneSoftWareOCclusion |
几个重要事情:
SubmitScene
CollectOccludeeGeom
Sort potential occluders by weight
Add sorted occluders to scene up to GSOMaxOccluderNum
reserve space for occludees vis flags
ProcessOcclusionFrame:
上面基本是数据的规整,这里才是执行算法过程
2.5.1.1 |
ProcessOccluderGeom,对着算法来一遍(嘿嘿) |
每个模型
a\ 转换模型到裁剪空间【矩阵操作】
const FMatrix LocalToClip = Mesh.LocalToWorld * SceneData.ViewProj;
VectorRegister mRow0 = VectorLoadAligned(LocalToClip.M[0]);
VectorRegister mRow1 = VectorLoadAligned(LocalToClip.M[1]);
VectorRegister mRow2 = VectorLoadAligned(LocalToClip.M[2]);
VectorRegister mRow3 = VectorLoadAligned(LocalToClip.M[3]);
for (int32 i = 0; i < NumVtx; ++i)
{
VectorRegister VTempX = VectorLoadFloat1(&MeshVertices[i].X);
VectorRegister VTempY = VectorLoadFloat1(&MeshVertices[i].Y);
VectorRegister VTempZ = VectorLoadFloat1(&MeshVertices[i].Z);
VectorRegister VTempW;
// Mul by the matrix
VTempX = VectorMultiply(VTempX, mRow0);
VTempY = VectorMultiply(VTempY, mRow1);
VTempZ = VectorMultiply(VTempZ, mRow2);
VTempW = VectorMultiply(GlobalVectorConstants::FloatOne, mRow3);
// Add them all together
VTempX = VectorAdd(VTempX, VTempY);
VTempZ = VectorAdd(VTempZ, VTempW);
VTempX = VectorAdd(VTempX, VTempZ);
// Store
VectorStoreAligned(VTempX, &MeshClipVertices[i]);
uint8 VertexFlags = ProcessXFormVertex(MeshClipVertices[i], W_CLIP);
MeshClipVertexFlags[i] = VertexFlags;
}
每个三角形
b\ 修正三角形:ClippedVertexToScreen,TestFrontface,AddTriangle
【满足裁顶点加或修正三角形:按深度(获取离屏最远的)】
uint16 I0 = MeshIndices[i*3 + 0];
uint16 I1 = MeshIndices[i*3 + 1];
uint16 I2 = MeshIndices[i*3 + 2];
uint8 F0 = MeshClipVertexFlags[I0];
uint8 F1 = MeshClipVertexFlags[I1];
uint8 F2 = MeshClipVertexFlags[I2];
if ((F0 & F1) & F2)
{
// fully clipped
continue;
}
FVector4 V[3] =
{
MeshClipVertices[I0],
MeshClipVertices[I1],
MeshClipVertices[I2]
};
uint8 TriFlags = F0 | F1 | F2;
if (TriFlags & EScreenVertexFlags::ClippedNear)
{
static const int32 Edges[3][2] = {{0,1}, {1,2}, {2,0}};
FVector4 ClippedPos[4];
int32 NumPos = 0;
for(int32 EdgeIdx = 0; EdgeIdx < 3; EdgeIdx++)
{
int32 i0 = Edges[EdgeIdx][0];
int32 i1 = Edges[EdgeIdx][1];
bool dot0 = V[i0].W < W_CLIP;
bool dot1 = V[i1].W < W_CLIP;
if (!dot0)
{
ClippedPos[NumPos] = V[i0];
NumPos++;
}
if (dot0 != dot1)
{
float t = (W_CLIP - V[i0].W) / (V[i0].W - V[i1].W);
ClippedPos[NumPos] = V[i0] + t*(V[i0] - V[i1]);
NumPos++;
}
}
// triangulate clipped vertices
for (int32 j = 2; j < NumPos; j++)
{
FScreenTriangle Tri;
float Depths[3];
bool bShouldDiscard = false;
bShouldDiscard|= ClippedVertexToScreen(ClippedPos[0], Tri.V[0], Depths[0]);
bShouldDiscard|= ClippedVertexToScreen(ClippedPos[j-1], Tri.V[1], Depths[1]);
bShouldDiscard|= ClippedVertexToScreen(ClippedPos[j], Tri.V[2], Depths[2]);
if (!bShouldDiscard && TestFrontface(Tri))
{
// Min tri depth for occluder (further from screen)
float TriDepth = FMath::Min3(Depths[0], Depths[1], Depths[2]);
AddTriangle(Tri, TriDepth, Mesh.PrimId, 1, OutData);
}
}
}
else
{
FScreenTriangle Tri;
float Depths[3];
bool bShouldDiscard = false;
for (int32 j = 0; j < 3 && !bShouldDiscard; ++j)
{
bShouldDiscard|= ClippedVertexToScreen(V[j], Tri.V[j], Depths[j]);
}
if (!bShouldDiscard && TestFrontface(Tri))
{
// Min tri depth for occluder (further from screen)
float TriDepth = FMath::Min3(Depths[0], Depths[1], Depths[2]);
AddTriangle(Tri, TriDepth, Mesh.PrimId, /*MeshFlags*/ 1, OutData);
}
}
2.5.1.2 |
按深度整理【最接近屏幕的在前】 栅格化遮挡删除 |
2.5.2 |
FetchVisibilityForPrimitives |
这里先略(那啥,放到另外一个地方)
1、若支持并行处理:构建数据FVisForPrimParams,利用上多任务FetchVisibilityForPrimitivesTask处理FHZBBound
2、FOcclusionQueryBatcher::BatchPrimitive (一个算法)
2.6 |
StereoPass |
若视图开启了InstancedStereoPass
->确保右眼视图中的图元在左眼(实例化)视图中可见。
->ComputeAndMarkRelevanceForViewParallel;
2.7 |
GatherDynamicMeshElements |
FSceneRender::GatherDynamicMeshElments
、排序BasePass,实现HiZ剔除
按是否使用线程且还有可用渲染线程
. |
FDeferredShadingSceneRenderer::SortBasePassStaticData |
如果我们不使用深度(EarlyZPassMode==None)仅通过排序静态绘制列表桶大致从前到后,以最大化HiZ剔除。不会干扰状态排序,且每个列表都是单独排序的
. |
FDeferredShadingSceneRenderer::AsyncSortBasePassStaticData |
、提交视野的自定义数据
(略)
、RHI资源(构建)
(略)
检验:
总结:
(原)Unreal渲染模块 管线 - 程序和场景查询的更多相关文章
- (原)Unreal 渲染模块引言Temp
@author:白袍小道 引言 本文只在对Unreal渲染模块做一些详细的理解,务求能分析出个大概. 其中框架的思想和实现的过程,是非常值得学习和推敲一二的. 涉及资源系统,材 ...
- (原)Unreal渲染模块 管线 - 着色器(1)
@author: 白袍小道 转载悄悄说明下 随缘查看,施主开心就好 说明: 本篇继续Unreal搬山部分的渲染模块的Shader部分, 主要牵扯模块RenderCore, ShaderCore, RH ...
- (原)Unreal 渲染模块 渲染流程
@author:白袍小道 浏览分享随缘,评论不喷亦可. 扯淡部分: 在temp中,乱七八糟的说了下大致的UE过程.下面我们还是稍微别那么任性,一步步来吧. UE渲染模块牵扯到场景遍历. ...
- (原)Unreal渲染模块 源码和实例分析说明
@author:白袍小道 说明 1.由于小道就三境武夫而已,而UE渲染部分不仅管理挺大,而且牵扯技术和内容驳杂,所以才有这篇梳理. 2.尽量会按书籍和资料,源码,小模块的调试和搬山(就是敲键盘)..等 ...
- (整)Unreal渲染模块 框总览
@author: 黑袍小道 随缘查看 说明 由于搬山的渲染这部分担心自己理解错误,故而搬移官方下,后面整个完成再反过来更新 (这当且仅当做Unreal的帮助文档). 图形编程 模块 渲 ...
- (原)Unreal Shader模块(一): 着色创建
一.着色加载 这里说的Shader是编译后的文件或内存 源码说明 --------------------------------------------------------------- ...
- Django---Http协议简述和原理,HTTP请求码,HTTP请求格式和响应格式(重点),Django的安装与使用,Django项目的创建和运行(cmd和pycharm两种模式),Django的基础文件配置,Web框架的本质,服务器程序和应用程序(wsgiref服务端模块,jinja2模板渲染模块)的使用
Django---Http协议简述和原理,HTTP请求码,HTTP请求格式和响应格式(重点),Django的安装与使用,Django项目的创建和运行(cmd和pycharm两种模式),Django的基 ...
- (原) Unreal搬山-引言(图多慎)
@author:白袍小道 扯淡:(图多) 何为搬山,这里借了剑来少年郎一句.(若有同道中人,甚是开心,开心的很) 江湖岂能没前辈) (江湖很大,足够你浪) (刺客信条 \荒野 \神秘海域 \死亡空间 ...
- DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render
DRF框架 全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...
随机推荐
- PHP编译安装时常见错误及解决办法,大全
1. configure: error: xslt-config not found. Please reinstall the libxslt >= 1.1.0 distribution ...
- ffmpeg处理RTMP流媒体的命令大全
最近浏览国外网站时候发现,翻译不准确的敬请谅解. 1.将文件当做直播送至live ffmpeg -re -i localFile.mp4 -c copy -f flv rtmp://server/li ...
- 实现接口Controller定义控制器
实现接口Controller定义控制器 控制器提供访问应用程序的行为,通常通过服务接口定义或注解定义两种方法实现. 控制器解析用户的请求并将其转换为一个模型.在Spring MVC中一个控制器可以包含 ...
- 一步步做程序优化-讲一个用于OpenACC优化的程序(转载)
一步步做程序优化[1]讲一个用于OpenACC优化的程序 分析下A,B,C为三个矩阵,A为m*n维,B为n*k维,C为m*k维,用A和B来计算C,计算方法是:C = alpha*A*B + beta* ...
- chrome 浏览器插件开发(二)—— 通信 获取页面变量 编写chrome插件专用的库
在chrome插件的开发过程中,我遇到了一些问题,在网上找了不少文章,可能是浏览器升级的原因,有一些是有效的也有无效的.下面我简单的分享一下我遇到的坑,以及我把这些坑的解决方案整理而成的js库 —— ...
- element-UI时间控件:日期时间的选择范围的控制方法
例:如一段已知的时间范围,为2018-10-01 - 2019-01-01 :当前为2018-07-09日,则今天以前的时间不能选择,以及2019-01-01以后的时间不能选:实现如下: <el ...
- linux 设置自动关机和重启命令shutdown
1.shutdown使用命令:Shutdown [选项] [时间] r 关机后立即重启 h 关机 2. 立即关机: shutdown -h now
- matlab2018a安装后帮助文档打不开解决方法
安装matlab2018a破解版后,帮助文档提示需要许可证问题(破解版没有可用许可证): 解决方法是把文档设置为离线即可(预设---->帮助---->安装在本地---->小窗口)
- git--分布式版本管理系统
参考博客:廖雪峰的官方网站 一.window安装git Git官网直接下载安装程序,默认选项安装即可. 1.设置自己的git(cmd命令或者git bash进入) git config --globa ...
- Oauth2.0协议 http://www.php20.com/forum.php?mod=viewthread&tid=28 (出处: 码农之家)
概要 OAuth2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0. OAuth 2.0关注客户端开发者的简易性.要么通过组织在资源拥有者和HTTP服 ...