UE5引擎 PC端的Landscape渲染浅分析

前言

游戏目前发展趋势是大世界,而大世界必不可少的元素是大地形,UE引擎从地形系统有完善的渲染管线和工具链。

Landscape的创建

UE的地形在LanscapeMode进行创建,如下所示

 

 地形创建后场景存在 一个ALandscape代表整个地形的渲染体。UE4的地形ALandscape由多个ULandscapeComponent构成,一个ULandscapeComponent组件代表一个地形块。

Landscape创建的参数意义

Number of Compoents 

地形组件的数量代表ALandscape在X方向和Y方向拥有的LandscapeComponent组件的数量,

如下所示红色部分就是一个组件, 下图中一共9个LandscapeComponent组件。

SectionSize

Section是构成地形组件的一个范围单位,和SectionPerComponent一起配合形成了一个地形组件的空间范围。上面是一个地形组件可选的Section范围, 这里的Quad是方格的意思,也就是上面地形的一个Section存在7 * 7 个方格。

 SectionPerComponent

上面Section指明了一个空间范围,而SectionPerComponent代表一个地形组件存在多少个Section,上面一个组件是2 * 2的Section

 所以SectionSize 和 SectionPerComponent一个指出来一个地形组件有多少个格子(Quad),而Number of Compoents 指出来一个整个地形存在多少个组件.

Number of Compoents,SectionSize, SectionPerComponent 和Landscape顶点的计算关系

 上面是9个地形组件,各个地形块保持相连状态,可以看出地形块说无缝的,因为每个相邻组件的相邻边的顶点位置是一样的。

ComponentVerts = SectionSize*SectionPerComponent  + 1 = 63 * 2 + 1 = 127 

而相邻组件之间说存在顶点重合的,因此整个地形总的顶点数

Resolution(Verts) = ComponentVerts * ComponentNum - (ComponentNum  - 1) = 127 * 3 + (3 - 1)= 379

ULandscapeComponent的渲染分析

上面说过一个地形块就是一个LandscapeComponent组件,而ALanscape只是起到管理LandscapeComponent的作用,真正相关的渲染数据和渲染流程发生在ULandscapeComponent, 所以下面具体分析ULandscapeComponent相关的VertexBuffer, VertexFormat, IndexBuffer,SceneProxy,Shader相关等等。

LandscapeComponent基本渲染原理

对于每一个LandscapeComponent,构建平坦网格体的VertexBuffer,然后顶点在VertexShader对HeightMap相应的进行采样得到相应高度和法线,最终输出一个山体。如下所示

 LandscapeComponent的高度图(HeightMap)

LandscapeComponent的高度值和法线是存储在一张HeightMap的,HeightMap内容的修改是编辑器层面上的内容,这里暂时不讲。

(1) HeightMap一定是宽和高不一定是一样的,可能是256 * 256, 也可能是 256 * 128

(2) HeightMap的宽或者高绝对是2的幂指数

(3) 多个地形组件同时共享一张HeightMap,  而LandscapeActor存在多个地形组件, 所以一个LandscapeActor可能同时存在几张HeightMap。

(4)HeightMap存在多级Mip, 从0Mip到7级,分辨率以四分之一缩小。

 HeightmapSceneBias的ZW分别记载了该组件在HeightMapTexture开始位置,以上面64个地形组件,505 * 505分辨率地形为例子。

 

第二个地形组件相关参数为:

ComponentSizeQuads = NumSebsections * CompoenntSizeQuads = 1 * 63 = 63

ComponentVerts = CompoenntSizeQuads *NumSebsections + 1 = 54

 HeightMapScele的ZW指出了该地形组件在HeightMap的偏移量(0.125, 0)

也就是该地形组件在HeightMap的起始像素位置为(0.125 * 512, 0 * 512) = (64, 0), 而组件的ComponentVerts为64,则该地形组件在HeightMap像素范围为(64, 0) - (127, 63)

 (4)HeightMap的格式为RGBA8,RG存储了顶点高度值,R为高8位,G为低8位。而BA存储了顶点法线值。FColor 和顶点高度/法线的转换计算公式如下:

 

FLandscapeComponentSceneProxy 

UE4的每种Mesh(StaticMesh, SkeletalMesh, ProceduralMesh, Landscape等)都存在一个MeshProxy,是游戏线程的Mesh和渲染线程Mesh的中转体,而FLandscapeComponentSceneProxy是游戏线程Landscape和渲染线程Landscape的中转体,包含了landscape的VertexBuffer,IndexBuffer,VertexFormat等等数据。

FLandscapeSharedBuffers

FLandscapeSharedBuffers* SharedBuffers是LandscapeProxy的一个变量, 其包含了Landscapepe

的VertexBuffer,IndexBuffer等. 正常情况下,FLandscapeSharedBuffers* SharedBuffers被

FLandscapeComponentSceneProxy 的全局静态TMap<uint32, FLandscapeSharedBuffers*> SharedBuffersMap所管理,一般而言,所有Landscape组件的FLandscapeSharedBuffers是同一个,也就是说所有地形块的VertexBuffer, IndexBuffer,VertexFactory等是共用的, 每个地形组件不同的是地形偏移量和高度图等(ComponentBaseX, ComponentBaseY, HeightMap)

 FLandscapeVertexBuffer

 LandscapeVertexBuffer的顶点格式为FLandscapeVertex,

 

 总体顶点数为:pow((SubsectionSizeVerts * NumSubsections), 2)

IndexBuffers: FIndexBuffer**

 LandscpaeProxy的索引缓存是个FIndexBuffer**, 这意味着索引缓存存在多个, 这其实是用于Lod机制,对于每个地形组件,预先生成N个索引缓存,索引缓存根据Lod等级而从小到大,比如Lod0可能是上万个三角形索引,而Lod5以后只有几百个三角形,在运行时根据地形目前的Lod等级给与不同的FIndexBuffer,从而实现Lod机制。

IndexBufferNum = 

 假设InSubsectionSizeQuads = 63,则IndexBufferNum  = 6

也就是存在6级Lod, 每级Lod生成三角形的算法如下:

 具体参考:FLandscapeSharedBuffers::CreateIndexBuffers的实现

FLandscapeVertexFactory

FLandscapeVertexFactory是LandscapeProxy的顶点工厂,大概负责定义Vertex输入布局和填充VertexBuffer, 可以看到为VET_Float4,刚好和FLandscapeVertex大小相对应.

FLandscapeVertexFactory关联的Shader顶点工厂-----LandscapeVertexFactory.ush

 有关Landscape顶点如何在VertexShader声明顶点格式,如何变换的代码在LandscapeVertexFactory.ush

 FLandscapeVertexFactory关联的VertexShader使用的UniformBuffer

UnifromBufferFLandscapeVertexFactoryVertexShaderParameters 

 

LandscapeVertexFactory.ush

UE4的各种Mesh(StaticMesh, SkeletalMesh, Lanscape)都存在特定的顶点工厂,LandscapeVertexFactory.ush是地形的顶点工厂, 指定LandscapeVertex输入的顶点格式,输出的顶点格式,顶点变换过程等等

BasePassVertexShader的入口main函数

 Landscape顶点输入FVertexFactoryInput

BaseVertexShader输出FBasePassVSOutput

 其中BassPassVertexShader输出为三部分组成:Position, FactoryInterpolants, BasePassInterpolants

Position就是顶点变换到透视空间的位置, 而BasePassInterpolants是需要额外的各种GBuffer属性,是所有类型顶点工厂(LandscapeVertexFactory, LocalVertexFactory等)的共有顶点输出,而FactoryInterpolants则是每个顶点工厂自定定义的VertexShader输出,LandscapeVertexFactory也有定制的VertexShader输出

 

 LandscapeVertexShader变换的逻辑

总体上而言就是在VertexShader里相应顶点(x, y位置)采样所在HeightMap的RG作为高度值(z位置),这样就得到地形顶点的三维坐标了。当然这里有两个小细节

(1)HeightMap的RG 和地形高度在Shader里的转换:

LandscapeVertexFactory的VertexGetVertexFactoryIntermediates函数有这个过程

float Height = DecodePackedHeight(SampleValue.xy);

 上面SampleValue的范围是[0.0, 1.0], 则最后计算出是高度值范围是[-32768/128, 32767/128] = [-256, 255.9921875], 一般地形创建是默认缩放100倍,也就是变为[-25600, 25599.21875].

(2)顶点高度进行两次HeightMap的Texture2DSampleLevel采样

这里会根据当前地形组件的Lod等级各种因素,算出每个地形顶点的临近的两个Lod采样位置, 对heightmap进行相应位置和Level的采样吗, 具体算法有点复杂,目前我只是看了个大概。最后是两个Lod级别的下LocalPosition的lerp。

 

猜你喜欢

转载自blog.csdn.net/qq_29523119/article/details/125532143