Ilingis 发表于 2022-8-15 09:29

【05】Unity URP 卡通渲染 原神角色渲染记录-Double Pass ...

点击下面链接,B站上传了实际Game窗口效果,视频有压缩,实际运行效果更好些~
系列一共5篇,这个是第5篇:
【01】Unity URP 卡通渲染 原神角色记录-Diffuse: Ramp + AO + Double Shadow
【02】Unity URP 卡通渲染 原神角色记录-Specular: Metal+Non-Metal
【03】Unity URP 卡通渲染 原神角色记录-Function-Based Light and Shadow: Emission+SDF脸部阴影
【04】Unity URP 卡通渲染 原神角色记录-Depth-Based Effect: 7Spaces + 屏幕空间等距深度边缘光Rim Light
【05 | 当前浏览】Unity URP 卡通渲染 原神角色记录-Double Pass Effect: Render Feature + 平滑法线Outline
1. 前言

这一篇主要写RenderFeature,以及利用RenderFeature,结合LayerMask进行BackFace描边,同时也会提及平滑法线的计算脚本。
2. Unity URP管线的光栅化Rasterization顺序问题

我们知道图形渲染,主要有光栅化Rasterization,光线追踪Ray Tracing两种路径。这两者不矛盾,可以一起用。
那么Unity URP管线是如何进行光栅化渲染的呢?这里我们重点关注渲染的顺序,为了更好的说明,直接来一段源码吧:



我们发现核心有8个阶段

URP管线的渲染顺序,从代码中可以发现是如下的顺序:
阴影 --> PrePass --> G Buffer --> Deferred Lights(延迟光照) --> 不透明物体 --> 天空盒子 --> 透明物体 --> 屏幕空间后处理
G Buffer说明: 就是把渲染表面Surface所需的所有数据,写入Geometry Buffer,包括材质,位置,法线等
Positions, normals, and materials for each surface are rendered into the geometry buffer (G-buffer)3. 什么是RenderFeature?

Renderer Feature可让我们向URP Renderer添加额外的渲染通道,支持我们进行Asset资产配置来重写从而可以自定义渲染的顺序、渲染的对象、材质等等。
A Renderer Feature is an asset that lets you add extra Render passes to a URP Renderer and configure their behavior.也就是说,在上面的渲染顺序中,我们可以加塞,弄一个自己的pass插进去。
这有什么用呢?比如像描边这种事情,肯定都是不透明物体渲染完后,我们再额外描个边,这时候就可以插队到Opaque之后,放一个自己的Pass。
4. 如何创建Render Feature?

URP管线中,有2种方式可以做render feature:
第一种,Render Object
URP contains the pre-built Renderer Feature called Render Objects.这种方式很简单,点点按钮就好了



选第一个

这种方式,可以参考官方的教程,不是今天的重点:

第二种,Scriptable Renderer Feature
官方有个教程,可以看看
这种方式的核心就是写代码来定义一个Render Feature,我是使用这种方式来定义一个描边pass。
Renderer Feature定义的核心思路:放在Opaque物体之后,用LayerMask只选择人物Layer,因为我们不需要对其他物体进行描边。
把宵宫的人物放在6层,Character



宵宫的人物层index是6

创建脚本的方式可以参考官方教程,重点是override Execute方法这里,确保描边pass只用于宵宫所在的人物层:
ShaderTagId outlineTag = new ShaderTagId("Outline");

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
    DrawingSettings drawingSettings = CreateDrawingSettings(outlineTag, ref renderingData, SortingCriteria.CommonOpaque);
    int myLayerIndexVariable = 6; // 宵宫的Layer Index
    int myLayerMaskVariable = 1 << myLayerIndexVariable; //用于Filter传参
    RenderQueueRange myRenderQueueRange = RenderQueueRange.opaque; //只包含不透明物体
    FilteringSettings filteringSettings = new FilteringSettings(myRenderQueueRange, myLayerMaskVariable);
    context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings);
}LayerMask成功的之后,我们会发现,只有人物才描边,这里放一个对比图,左边的宵宫在6层,右边的球default:



同样的Shader,只描边人物,描边效果暂时忽略,这会代码是不对的

之后记得在管线设置中,添加这个Render Feature



这里的ShaderTagId 是Outline

5. 法线平滑

有了Render Feature之后,我们就继续下一步,看看如何计算平滑法线。
物体的硬边问题:处于硬边的顶点,会有多条法线存在,如图所示



我们的目标是求多个法线的平均值

所以,平滑法线的核心,就是发现有多个normal的vertex顶点,然后求平均法线,最后把平均法线存进mesh的tangent空间。
如果对于图形学7大空间,有疑惑,请看第4篇,有讲解
核心代码如下,C#脚本,挂在人物身上就行:
for (int i = 0; i < mesh.vertexCount; i++)
{
   Vector3 avgNormal = normalDictionary].normalized;
   tangents = new Vector4(avgNormal.x, avgNormal.y, avgNormal.z, 0f);
}
mesh.tangents = tangents;6. BackFace描边

这个相关的文章挺多了,说一下核心思路:
在正常的UniversalForward Pass渲染完成后,我再加一个额外的Pass,进行顶点沿法线方向偏移,之后输出颜色。
UniversalForward Pass: Cull Back
Outline Pass: Cull Front
这里有一个选择问题,我们在什么空间内,进行顶点偏移,我的选择是Object Space模型空间,效果最好。
然后来看一下相关代码,很简单:
Pass
{
    Name "Outline"
    Tags {
         "LightMode" = "Outline" //与ShaderTagId一致
    }
    Cull Front
    //.....其他参数根据工程需要配置
            
    HLSLPROGRAM
    #pragma vertex OutlinePassVert
    #pragma fragment OutlinePassFrag

    struct Attributes
    {
       float4 vertex : POSITION;
       float3 normal : NORMAL;
       float2 uv : TEXCOORD0;
       float4 vertColor : COLOR;
       float4 tangent : TANGENT;
    };

    struct Varyings
    {
       float4 pos : SV_POSITION;
       float2 uv : TEXCOORD0;
       float3 vertColor : COLOR;
    };

    Varyings OutlinePassVert (Attributes v)
    {
       Varyings o;
               
       UNITY_SETUP_INSTANCE_ID(v);
       UNITY_TRANSFER_INSTANCE_ID(v, o);
       UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
       //从tangent space取出平滑法线,进行外扩,处理硬边断裂
       v.vertex.xyz += v.tangent.xyz * 0.01 * _OutlineWidth * v.vertColor.a;//顶点色a通道控制粗细
       o.pos = TransformObjectToHClip(v.vertex.xyz);
       o.uv = v.uv;
       o.vertColor = v.vertColor.rgb;
       return o;
    }

    float4 OutlinePassFrag(Varyings i) : SV_TARGET
    {
       return float4(i.vertColor, 1) * _OutlineColor;//顶点色rgb混合描边颜色
    }

    ENDHLSL
}接下来,我们看几个效果图的对比,首先是没有平滑法线的样子,只有正常的法线normal



身体的部分,差点意思

然后是从tangent空间,取出平滑法线后再描边,我们可以发现硬边区域的描边,有了很大改善



硬边区域,差别明显,还是平滑好使

最后额外提一句,脖子上的金属项链,不要描边,我是通过宏+Mask,扣掉了蝴蝶项链的大部分神之眼的描边,以下是反例,如果直接输出颜色大概长下面的样子:



项链和神之眼,不要描边,会很怪

7. 后处理ToneMapping,物理模拟和动画

后处理的部分,我通过Render Feature + Blit方式,添加了GranTurismo的ToneMapping,Unity URP默认只有Neural和ACES两种,不太给力。
动作可以去Mixamo上面白嫖,物理模拟用的Magica Cloth,骨骼和弹簧模拟2种类型的结合。
8. 来看看最终成品效果


看着还行吧,有待进一步提升
https://www.zhihu.com/video/1542407530570989568
本篇是这个系列的最后一篇,感谢阅读,后面有空再写写其他的,完结撒花ヽ(°▽°)ノ
9. 参考链接

Unity官方:URP系列教程 | 手把手教你如何用Renderer Feature
Unity官方:URP系列教程 | 如何使用Scriptable Renderer Feature来自定义后处理效果
2173:【01】从零开始的卡通渲染-描边篇
页: [1]
查看完整版本: 【05】Unity URP 卡通渲染 原神角色渲染记录-Double Pass ...