ue4移动到一定距离_UE4 距离场简单分析

距离上一篇博客已经有点久了,中间忙的飞起,忽然发现很久没写了,这样不好,写一篇和工作无关的吧。

一直想搞清UE4距离场的原理,网上有几乎找不到任何有关UE4距离场实现的内容,加上上篇末说要写一个完全的Rendering过程,而UE4下有个距离场的渲染,刚好用来追踪理解UE4距离场,并顺便理下距离场的Rendering相关。

先说下我现在对UE4模型距离场比较浅显的认识,就是我们把场景里的所有不透明模型信息移植到GPU中,不同于我们直接看到的场景,是按照现实中的摆放,在距离场中,声明了一个3D纹理,我们把这个3D纹理可以看做是一个房子,房子里填满了很多个长方体,长方体之间不穿插。而在场景中的每一个不透明模型,对应房子里的一个长方体,注意,他们之间的位置并没关系,可能在场景中模型属于中间,在房子里,可能放在左上角的长方体中。每个长方体里保存的就是当前模型的距离场数据(简单来说,模型内是负数,模型外是正数),这样就把场景里不透明模型的整个信息全部保存到一张3D纹理中,可以说信息量非常集中。而3D纹理占用的显存因为多了一个深度的维度,非常高,所以这个3D纹理默认分辨率并不是特别高,在4.14里只有512*512*1024*f16=512M。

在这,用UE4里的模型距离场来对比一下深度图,我们知道深度图其实只是相当于对应一个特定角度的摄像机所得到的最近的不透明像素的距离,像Shadow mapping这种只需要比较个二个位置下的深度的大小的结果,用来处理很不错,而需要知道模型与场景非特定角度信息,如AO这些深度度则满足不了要求,只有距离场才能满足。

在开始讲UE4模型距离场的渲染前,我们还要来看下,UE4里的Compute Shader,这种类型的着色器比较特殊,不同于常见的顶点与片断着色器,他并不是渲染管线的一部分,一般来说和CUDA/OpenCL类似,用来做GPU通用计算,同样,也要调度GPU划分线程组,线程组划分线程,主要有如下部分要理解。

SV_GroupID是线程块的三维ID,SV_GroupThreadID对应线程块里的线程组ID,SV_DispatchThreadID对应所有线程里的ID,SV_GroupIndex如固定维度的三维数组和一维数组可以相互转化一样,这个指的是在当前线程组中一维索引。如下是引用 https://msdn.microsoft.com/en-us/library/windows/desktop/ff471568(v=vs.85).aspx 里的图来说明。

这里为什么要理解Compute Shader的这些概念了,因为距离场的构成与使用大部分都是Compute Shader,Compute Shader的基本概念也很容易理清,大部分代码和我们平常写的没什么区别,参数,逻辑,同步。

UE4还有一个概念,叫全局距离场,和模型距离场类似,但是其实只能算是模型距离场的衍生物,可以算是对模型场的一种优化使用方式,不要被这个名字骗了,以为他才是主要的,没有模型距离场,就不可能有全局距离场。

接下来,我们按照UE4中的模型距离场可视化渲染流程来说明过程,主要有如下几个部分,如何创建距离场,对应的CPU与GPU的数据有那些。先看下UE里的模型距离可视化渲染是什么样子的图片。

如上面所说,模型距离场是一个装着格子的房子,如何组装这个房子,更新房子的类就是FDistanceFieldVolumeTextureAtlas,对应的对象是GDistanceFieldVolumeTextureAtlas,每个格子对应一个静态模型的FDistanceFieldVolumeTexture,有个很重要的值就是Size,表示这个长方体格子的三维大小。

首先UStaticMesh在加载时,就自动被GDistanceFieldVolumeTextureAtlas收录了。

然后会调用MeshUtilities->GenerateSignedDistanceFieldVolumeData生成对应FStaticMesh的距离场数据,这段代码不贴了,简单说明下。

计算Mesh格子的大小DistanceFieldVolumeBounds,这个对应模型的FDistanceFieldVolumeTexture数据里的3D纹理的各维大小,从代码上来看,比模型的MeshBounds要大一圈,这样网格就包含在FDistanceFieldVolumeTexture之中,并且还要包含边缘的数据。

而后是VolumeDimensions,对应FDistanceFieldVolumeTexture里的3D纹理的各维索引长度,对应各个像素点,各个维度最小8个像素,不然会穿帮。

最后根据索引解析成三角,同Unity类似的Mesh-SubMesh类似,UE4里FStaticMeshLODResources->FStaticMeshSection结构也是FStaticMeshLODResources包含顶点数据buffer,顶点buffer,而FStaticMeshSection对应材质索引,顶点区块,顶点索引起点等。根据顶点索引查找区块对应材质,如果是不透明的,就添加进距离场运算,然后使用K树分割成多维空间,建立搜索索引,然后生成一个上下密度大约在384,平面密度在600左右的点空间,每个上下对应点生成一条射线,这射线与对应模型前面分解的三角形计算得到距离场数据,在物体内为负,表面接近0,物体外一段距离为正值(主要逻辑在FMeshDistanceFieldAsyncTask::DoWork)。

这样各个UStaticMesh的FDistanceFieldVolumeTexture都有值了,size就是上面的VolumeDimensions,LocalBoundingBox就是DistanceFieldVolumeBounds,对应的DistanceFieldVolume初始化VolumeDimensions个零,CompressedDistanceFieldVolume就是上面最后生成的距离场数据。

嗯,终于到渲染这步了,在FDeferredShadingSceneRenderer::Render中,我们可以看到,在prez-pass之前,就会调用GDistanceFieldVolumeTextureAtlas->UpdateAllocations(),这个方法很简单,就是把如上的所有UStaticMesh的FDistanceFieldVolumeTexture数据,提交到对应的GPU中的3D纹理DistanceFieldTexture中。

接着上面马上调用FDeferredShadingSceneRenderer::UpdateGlobalDistanceFieldObjectBuffers,这个也很简单,上面所说的部分,只是提供给GPU一个距离场,而场景中模型与距离场中的格子对应关系并没有,如格子从GPU距离场到世界空间的互相转化的矩阵,对应UVAdd,UVScale,模型的box bounds等信息,这些信息都会存在Scene->DistanceFieldSceneData.ObjectBuffers里,对应的GPU里的ObjectData,ObjectBounds。ObjectData如上所说,每一个节点,包含格子从GPU距离场到世界空间的互相转化的矩阵,对应UVAdd,UVScale,模型的box 等的所有显存信息。

这里有一个Compute Shader就是FUploadObjectsToBufferCS生成的临时数据提交到RWObjectBounds/RWObjectData的,这里就不分析了,很容易理解,每个Compute Shader的类,如上篇文章所说,每个shader,直接定位到相应位置,类后一定有个宏显示他是在那个usf文件里,对应的入口函数是那个。

在接着如下,可以看到针对每个view生成一个全局距离场的3维纹理,限于

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值