3.5 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
描边 2023-12-08 16:49:45

实现功能

  • 后处理描边
  • MeshDraw描边

其他游戏做法

蓝色协议

采用后处理、Backface以及预绘制描边。 蓝色协议的方案#轮廓

杂项

李兄实现Outline思路

Depth与Normal描边

ToonOutlineMain()

float3x3 laplacianOperator = float3x3(-1, -1, -1,
                                        -1,  8, -1,
float3x3 Gx = float3x3( -1, +0, +1,
                                        -1, -1, -1);
                        -2, +0, +2,
                        -1, +0, +1);
float3x3 Gy = float3x3( +1, +2, +1,
                        +0, +0, +0,
                        -1, -2, -1);

使用GetPixelValue()取得Normal与Depth之后使用拉普拉斯算子与Sobel算子进行边缘检测

float4 fs0 = s0 * laplacianOperator[0][0];
float4 fs1 = s1 * laplacianOperator[0][1];
float4 fs2 = s2 * laplacianOperator[0][2];
float4 fs3 = s3 * laplacianOperator[1][0];
float4 fs4 = s4 * laplacianOperator[1][1];
float4 fs5 = s5 * laplacianOperator[1][2];
float4 fs6 = s6 * laplacianOperator[2][0];
float4 fs7 = s7 * laplacianOperator[2][1];
float4 fs8 = s8 * laplacianOperator[2][2];

float4 sampledValue = fs0 + fs1 + fs2 + fs3 + fs4 + fs5 + fs6 + fs7 + fs8;
OutlineMask0 = saturate(1.0 - length(sampledValue)); //Line is black
float4 ds0x = s0 * Gx[0][0];
float4 ds1x = s1 * Gx[0][1];
float4 ds2x = s2 * Gx[0][2];
float4 ds3x = s3 * Gx[1][0];
float4 ds4x = s4 * Gx[1][1];
float4 ds5x = s5 * Gx[1][2];
float4 ds6x = s6 * Gx[2][0];
float4 ds7x = s7 * Gx[2][1];
float4 ds8x = s8 * Gx[2][2];
float4 SGX = ds0x + ds1x + ds2x + ds3x + ds4x + ds5x + ds6x + ds7x + ds8x;

float4 ds0y = s0 * Gy[0][0];
float4 ds1y = s1 * Gy[0][1];
float4 ds2y = s2 * Gy[0][2];
float4 ds3y = s3 * Gy[1][0];
float4 ds4y = s4 * Gy[1][1];
float4 ds5y = s5 * Gy[1][2];
float4 ds6y = s6 * Gy[2][0];
float4 ds7y = s7 * Gy[2][1];
float4 ds8y = s8 * Gy[2][2];
float4 SGY = ds0y + ds1y + ds2y + ds3y + ds4y + ds5y + ds6y + ds7y + ds8y;

OutlineMask1 = saturate(2.0 - step(0.9, length(sqrt(SGX * SGX + SGY * SGY))));

这个算法巧妙的地方在于对计算卷积核之后使用length(float4(Normal,Depth))来取得结果Sobel也是length(sqrt(SGX * SGX + SGY * SGY)。

最后使用OutColor.rgba = OutlineMask0;//lerp(OutlineMask0, OutlineMask1, 0.5);进行混合。使用OutlineIDMap.a作为宽度控制项并乘以通过Depth重映射后的变量作为宽度Fix因子。

ID描边

对ToonIDTexture进行Sobel描边。只使用了Sobel

混合结果

蓝色协议的做法

模型外扩

后处理

  1. 使用Sobel算子进行深度检测只勾数值差别较大的区域:脸部顶点色定义区域 与 模型外部轮廓。

  2. 使用Sobel进行ID贴图检测。只勾数值差别较大的区域

  3. 进行法线点积dot检测。在 深度差异小 以及 同一个ID区域内进行检测:手指区域。

  4. 使用Sobel过滤器进行深度检测描边。

  5. 使用Sobel过滤器进行Id图检测描边。

  6. 使用Sobel过滤器进行Normal检测描边。用于处理一些难以分ID深度差又很小的地方通过获取周围点法线求点乘的方式判断出轮廓。![[08-Assets/Images/ImageBag/UrealEngineNPR/蓝色协议_Normal检测描边.png)

  7. 预先画好的轮廓GBuffer

所以使用需要 OutlineId、OutlineWidth感觉可以传递一个全局Outline信息贴图再通过ID查表来获取但只能在角色较少时使用、OutlinePaint 、OutlineZShift个人感觉不需要