Unity卡通渲染知识点笔记

描边

描边有两种方法,一种是用两个pass,第一个pass绘制模型本身,第二个pass则是绘制边缘。这种技术的要点是在第一个pass使用cull back把背面拣选掉,第二个pass使用cull front 把正面拣选掉,这时候就只剩下侧边的边缘线颜色。

在第二个pass中,在绘制时,让模型顶点沿着法线方向向外移动一定的距离。这时候绘制出的一个纯色模型把原来的模型给包住。但如果是在世界空间中移动的话,当镜头拉远时,描边线会变得很细小。所以为了解决这个问题,一般将法线在裁剪空间中或者在NDC空间中做位移:

在裁剪空间中做位移的操作如下:

 1// 这段代码是一段URP代码
 2 Varyings vert(Attributes input) 
 3  {
 4      // scaledScreenParams 是用当前窗口的屏幕宽高像素值乘以渲染缩放系数renderScale的值。renderScale默认为1,
 5      float4 scaledScreenParams = GetScaledScreenParams();
 6      float aspect = abs(scaledScreenParams.x / scaledScreenParams.y);//求得X因屏幕比例缩放的倍数
 7      // 下面的计算是在裁剪空间中操作。当然也可以直接将法线转到NDC
 8      Varyings output;
 9      VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
10      VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS);
11      float3 normalCS = TransformWorldToHClipDir(normalInput.normalWS);//法线转换到裁剪空间
12      float2 extendDis = normalize(normalCS.xy) *(_OutlineWidth*0.01);//根据法线和线宽计算偏移量
13      extendDis.x /= aspect ;//由于屏幕比例可能不是1:1,所以偏移量会被拉伸显示,根据屏幕比例把x进行修正
14      output.positionCS = vertexInput.positionCS;
15      // 因为后续会转换成NDC坐标,会除w进行缩放,所以先乘一个w,那么该偏移的距离就不会在NDC下有变换
16      output.positionCS.xy += extendDis * output.positionCS.w ; // 要预先乘一个做透视除法的值
17      return output;
18  }

在NDC空间做变换代码如下:

 1// 这是一段基于Unity内建管线的顶点着色器代码
 2v2f o;
 3UNITY_INITIALIZE_OUTPUT(v2f, o);
 4float4 pos = UnityObjectToClipPos(v.vertex);
 5float3 viewNormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal.xyz);
 6float3 ndcNormal = normalize(TransformViewToProjection(viewNormal.xyz)) * pos.w;//将法线变换到NDC空间
 7float4 nearUpperRight = mul(unity_CameraInvProjection, float4(1, 1, UNITY_NEAR_CLIP_VALUE, _ProjectionParams.y));//将近裁剪面右上角位置的顶点变换到观察空间
 8float aspect = abs(nearUpperRight.y / nearUpperRight.x);//求得屏幕宽高比
 9ndcNormal.x *= aspect;
10pos.xy += 0.01 * _OutlineWidth * ndcNormal.xy;
11o.pos = pos;
12return o;

描边的断裂问题

enter description here

因为这个模型每个面的顶点的法线都垂直于这个平面。所以描边的外扩也是垂直于平面,当模型有转角的情况下,描边就会像这样裂开。要解决这个问题,需要对模型外扩使用的法线数据进行修改。这里需要将在相同位置顶点的法线数据,进行平均计算,将算出来的新法线写入模型切线数据中。然后使用这个切线数据进行法线外扩。至于为什么要写到切线数据里,这是因为只有法线和切线数据会随着骨骼动画而改变。所以如果渲染的是有骨骼动画的角色,写入切线数据里就不用做额外处理,计算上简单一些。如果碰到了角色使用法线贴图或者各项异性材质这种需要原始切线数据的情况,那么可以先把平均法线转换到切线空间,再保存到UV或者顶点颜色上。计算的时候,从切线空间把平均法线数据还原到世界空间,计算上会稍微麻烦一点。不过因为切线空间也是随骨骼动画改变的,所以这个方法的结果也是正确的。这里我写了一个编辑器工具,完成对mesh数据的添加。

参考网页

【01】从零开始的卡通渲染-描边篇

【02】从零开始的卡通渲染-着色篇1

【03】从零开始的卡通渲染-着色篇2

【04】从零开始的卡通渲染-PBR篇

卡通渲染之描边技术的实现(URP)

【02】卡通渲染基本光照模型的实现

卡通渲染小记一(原神篇)

Tone-based Shading of Matte Objects

Unity卡通渲染总结 NPR Toon Shading

【Unity URP】卡通渲染中的刘海投影·改

URP管线-NPR非真实感卡通渲染Toon shader

卡通渲染技术总结

High-Quality Real-Time Outline Rendering 描边学习记录

程序干货:日式卡通渲染基础技术(上)

程序干货:日式卡通渲染基础技术(下)

实现Anisotropic Cel-shading Hair(URP)

卡通渲染从0到0.5