type
status
date
slug
summary
tags
category
icon
password
前言
《动物派对》马上就要上线了,我很喜欢这款游戏,这次的china joy上还买到了他们的周边哈哈哈,真可爱。
最近在家,打算做一个相对完成的渲染流程,就选择《动物派对》进行复刻啦!
选择《动物派对》的理由有几点:
1,渲染风格是我个人喜欢的类型
2,美术资源的获取相对简单
3,延迟渲染管线是我个人之前没有搭建过的,正好学习
选择写文章记录下来,目的是分享和展示,以及自己备忘。
《动物派对》截帧分析与渲染技术点罗列
下载了之前官方测试时候的Demo。用RenderDoc截了几帧。
这里单纯罗列使用到了哪些技术。
渲染管线类型
延迟渲染
整体的思路和unity urp的大同小异。
GBuffer
Gbuffer一共五张Render Texture
Gbuffer A :RGB 是非金属 的Albedo ,A 通道为AO
GBuffer B:RGB是世界空间法线
GBuffer C:RGB是金属的Albedo,A是粗糙度
GBuffer D: 烘培了的阴影Mask的物体 会渲染进 R通道,其他没有烘培光影的动态物体 会输入RGBA。
(Buffer A 与C 之所以要分成两种 Albedo,从渲染公式角度讲,金属度越高,漫反射越少。)
(Buffer A 与C 的 Albedo 使用金属度 进行了权重分配)
地形材质
游戏中战斗的地面是可以进行材质混合的地形材质。可以进行草地,泥土,砖块三种材质的混合。也是使用贴图最多的材质。算上光照贴图和shadowMask,用了十张图。
材质的混合使用顶点色配合贴图中的高度图通道进行。
阴影方案:
游戏使用了级联阴影,阴影图的大小是4096*4096.
人物渲染
有毛发的动物,毛发方案是多pass实现的。
抗锯齿方案:
TAA
全局光方案:
HBAO+shadowMask图+LightMap+光照探针+反射探针
HBAO
HBAO前后区别
Light Map与shadow Mask
运动模糊
只给动物加了运动模糊
其他后处理:Chromatic Aberration + Bloom +景深 +色调映射
Chromatic Aberration
What is chromatic aberration?Chromatic aberration, also known as color fringing, is a color distortion that creates an outline of unwanted color along the edges of objects in a photograph. Often, it appears along metallic surfaces or where there’s a high contrast between light and dark objects, such as a black wall in front of a bright blue sky. Each type of aberration causes different colors of outlines along an object’s edge.The failure of a camera lens to focus each of white light’s different wavelengths onto the same focal point may lead to blue-yellow, red-green, or magenta-purple fringing. This is due to the refractive index of glass; various wavelengths of light travel through the lens at different speeds, making it difficult for some lenses to focus each hue on the same focal plane.什么是色差?色差,也被称为彩色边缘,是一种颜色失真,沿着照片中物体的边缘产生不需要的颜色轮廓。通常,它出现在金属表面,或者明暗物体之间有很高对比度的地方,比如明亮的蓝天前的黑墙。每种类型的像差都会导致物体边缘的轮廓颜色不同。相机镜头无法将不同波长的白光中的每一个聚焦到同一焦点上,可能会导致蓝黄、红绿或品红色-紫色边缘。这是由于玻璃的折射率;不同波长的光以不同的速度穿过透镜,使得一些透镜难以将每个色调聚焦在同一焦平面上。
下图中的内容是场景中的球拍。左边是原图,右边是进行了处理之后的效果
下图是整体画面的前后变化。
色调映射
这个调色对于整体风格起到了非常重要的作用
罗列完技术点之后,就可以正式开始复刻
美术资源获取
动物派对的demo没有做任何加密,很简单就可以用RenderDoc截取,大部分贴图资源我都是直接从RenderDoc中导出。关于模型资源,主要使用的是ninja ripper 2来得到,不过不是那么好用,花了不少时间去整理素材。而且还出来了法线反了的情况,需要一个个调整之后再导入到引擎,不是那么友好。另外,还使用了市面上的一些renderDoc模型导出插件,把csv格式转换为FBX格式,不过也偶尔会出一些问题,不过再两个软件的搭配下勉强算是把美术资源搞定了。之后有时间也有研究一下RenderDoc插件的想法,听说可以实现直接把整个场景导出,听着非常吸引人。
PreZ
《动物派对》游戏本身是没有使用Pre Z去处理植物的,也许是因为渲染压力并没有这么大,并且植物也没有造成大量的遮挡。
但是我在demo中还是使用了,只是单纯因为想练练手(笑)。如果是项目中,需要权衡开销以及需求等等问题。
PreZ Pass中,执行的是和depth only pass中相同的shader pass,区别是,depth only pass 的渲染目标是一张单独的深度图,而PreZ的目标与GBuffer填充阶段是相同的。并且只会渲染部分特定的物体。PreZ Pass只会渲染”Paints“层级下的物体。
unity时间参数的问题
目前我在unity2019.4和2021.3版本中都发现时间参数需要传递两次。
按照注释说的,是 context.SetupCameraProperties(camera);这个设置矩阵的函数影响了时间参数。说实话我没搞懂,就姑且无脑按照官方说的做了,目前我暂时也没发现有啥问题。
下面是官方urp的源码片段。
GBuffer填充
在我自己渲染的过程中,GBuffer阶段算上深度缓存,一共需要输出6张Render Texture
_GBuffer_Albedo的RGB通道是Albedo,A通道输入美术资源自带的AO
_GBuffer_Normal的RGB通道存储世界空间法线
_GBuffer_SpecularSmoothness的RGB通道存储金属的Specular,A通道存储光滑度
_GBuffer_GI 存储的是GI信息,动态物体的GI信息来自光照探针与反射探针,静态物体的GI信息来自光照贴图与反射探针。并且还会把主光源的实时阴影计算进去。()
_GBuffer_ShadowMask 记录 shadowMask
_GDepth除了存储深度之后,还会存储模板值,GBuffer阶段输入不同材质的模板值,在光照计算阶段根据模板值渲染不同的结果。
如何写入模板值
我们当然可以在shader中手动写入模板相关的操作,不过unity也提供了统一管理渲染状态的方法
context.DrawRenderers中可以传入一个自定义的SubShader中的Tag类型名 ,一组shaderTagId以及一组RenderStateBlock。
通过这种方式就可以让添加了这些shaderTagId的材质使用对应的RenderStateBlock。也就可以控制模板值如何写入
草地渲染
《动物派对》中同样没有基于GPU Instancing的草地渲染,这也是我个人自己加上的,因为之前没写过,浅浅学习一下,看看存在哪些技术点。
为什么不用unity的地形系统的植被功能?
地形系统只能在Unity提供的mesh上进行植被的绘制,但是我希望草能长在我提供的mesh上
unity提供的GPU Instancing 方案如何使用,为什么不用?
默认管线中,只需要勾选 材质 中 的 “GPU Instancing ”,就可以开启GPU Instancing(默认管线没有SRP Batcher)
在URP管线中,如果材质 不支持SRP Btatcher 并且 勾选 材质 中 的 “GPU Instancing ”,就可以使用GPU Instancing
那么按照常理,在SRP中,只要我自己编写的 shader 支持 GPU Instancing ,不支持SRP Btatcher,是不是就可以直接使用GPU Instancing 了,那不就不用自己去实现一套草海绘制方案了吗?
其实并不是,unity官方文档表示,“Custom GPU instanced shaders”在SRP中是不支持的,只有在内置管线才支持(https://docs.unity3d.com/cn/current/Manual/gpu-instancing-shader.html)
也就是说,我的材质不能直接享受GPU Instancing 的合批,所以想在srp中使用GPU Instancing ,只能调用 unity提供的API去自己实现了。
选择哪个核心API?
unity提供了
Graphics.DrawMeshInstanced](https://docs.unity3d.com/cn/current/ScriptReference/Graphics.DrawMeshInstanced.html)
与
两者的区别是什么?
文档中描述Graphics.DrawMeshInstanced,会把摄影机之外的实例进行剔除与排序(具体排序的限制一下没看懂),但是不会做遮挡剔除。也就是说不用担心视锥体之外的实例太多导致性能浪费。
CommandBuffer.DrawMeshInstanced 不会进行剔除,因为它是比较底层的,使用这个的时候需要考虑一下剔除的问题。
再考虑别的方面的区别。
Graphics 类的 优点是简单易用,无需创建
CommandBuffer
,并且可以在不同的脚本中调用,缺点是不够灵活,无法在渲染过程中更精确地控制渲染命令的顺序和逻辑。难以实现高级的渲染效果。CommandBuffer 的优缺点和Graphics 类相反。
关于Graphics.DrawMeshInstanced到底是如何起作用的猜测,我觉得这个函数最后应该还是会调用类似 CommandBuffer.DrawMeshInstanced的函数来把渲染命令增加到渲染队列中。
分析到这里,在没有见过源码的情况下,结合demo现阶段的需求,我无法证明CommandBuffer.DrawMeshInstanced比 Graphics.DrawMeshInstanced好。
不过我还是选择CommandBuffer.DrawMeshInstanced,毕竟使用CommandBuffer还是比较有底。
使用CommandBuffer.DrawMeshInstanced,最核心的需求是。明确Mesh,Instance Material,以及一组坐标数据。
这些坐标数据需要以buffer或者贴图的形式直接传递给shader,在shader中利于instanceID 取采样到位置信息。
最大的需要解决的问题就是如何获取到坐标信息。
这次我的做法非常简单,我使用Prefab World Builder 作为我刷草的工具,把带有标记脚本的GameObject刷到场景中,来获取到一组坐标信息。需要说明的是,这绝对不是一个可商用的方式(我猜),我自己也没有对这套流程进行完善,只是为了能刷上草做的效果。随便学习unity中GPU Instancing 草的技术要点。
拷贝阴影图
后续的Deferred shading中需要采样深度图,所以提前拷贝一份。
HBAO
HBAO的详细算法和原理解析在博客的其他文章中。贴一张效果演示。
在这里我把草地和场景的AO强度进行了区分。
原因是我觉得草地的AO效果强度需要比其他区域弱一些。、
DeferredShading
unity中使用了两种方式去渲染 分别是
RenderStencilLights 与 RenderTileLights
我暂时没有学会怎么写写RenderTileLights部分的代码,浅浅看了一下,应该是性能更优的做法。之后再研究一下。
这次的暂时先使用RenderStencilLights中的思路。
大致思路是,通过绘制光源形状的几何体确定光源作用范围,之后用模板值标记,然后在作用范围的像素中进行光照渲染。不过平行光不用做确定范围这一步,因为平行光对满屏都起作用。
DeferredShading pass中,传递完一些必要的数据之后,开始遍历每个有效灯光,每个灯光的循环中,传递灯光相关数据,并且绘制光源几何体,进行光照渲染。每种光照模型都需要单独的进行一次光照渲染,与之对应的是之前的GBuffer阶段输入了几种模板值,
调色
左为调色前,右为调色后
- 作者:应坤龙
- 链接:https://www.ykl1998.com/article/12755cc2-cff2-8092-9b49-c913bff238e0
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。