【转载】原神(Genshin Impact)角色着色器分解 [Unity URP]

1,654 阅读3分钟

原文链接

Genshin Impact Character Shader Breakdown [Unity URP] | Adrian Mendez

英文比较简单我就不翻译了,我做了一些知识点的补充。另外,原文代码是基于图片的,而且因为字体的原因,某些字母和数字十分相似,所以我把代码给整理出来,方便大家复制粘贴(bai piao)

正文

效果图

Genshin Impact Character Shader Breakdown [Unity URP] (17).jpg

Genshin Impact Character Shader Breakdown [Unity URP] (6).jpg

Genshin Impact Character Shader Breakdown [Unity URP].jpg

Genshin Impact Character Shader Breakdown [Unity URP] (15).jpg

自定义光照

  • Custom Lighting Genshin Impact Character Shader Breakdown [Unity URP] (1).jpg

  • 代码如下

float lDot = dot(mainLight.dir, WorldNormalVec);

First, do a simple NdotL (NdotL is the dot product between the normal and the light direction).

  • Custom Lighting
    Genshin Impact Character Shader Breakdown [Unity URP] (3).jpg

  • 代码如下

Float _LightSmooth = 0.1;


float lDot = dot(mainLight.dir, WorldNormalVec);
float lSmooth = smoothstep(0, _LightSmooth, lDot):

The most important thing in Cel shading style is the transition between light and shadow, in Genshin Impact they not do an extreme hardness so 0.1 should be enough.

又是我们的老朋友 smoothstep 君~

  • Custom Lighting Genshin Impact Character Shader Breakdown [Unity URP] (8).jpg

  • 代码如下

Float _LightSmooth = 0.1;
Color _ShadowColor = (0, 0.09, 0.42, 1);
Color _BaseColor = (0.95, 0.44, 0.24, 1);


float lDot = dot(mainLight.dir, WorldNormalVec);
float lSmooth = smoothstep(0, _LightSmooth, lDot);
float3 lLerp = lerp(_ShadowColor, _BaseColor, lSmooth);

Do a lerp with the previous result to tint the shadow and the light to have more color control.

  • Custom Lighting
    Genshin Impact Character Shader Breakdown [Unity URP] (7).jpg

  • 代码如下

Float _LightSmooth = 0.1;
Color _ShadowColor = (0.87, 0.73, 0.69, 1);
Color _BaseColor = (1, 1, 1, 1);
Texture _MainTexture;


float lDot = dot(mainLight.dir, WorldNormalVec);
float lSmooth = smoothstep(0, _LightSmooth, lDot);
float3 lLerp = lerp(_ShadowColor, _BaseColor, lSmooth);
float3 color = lLerp * _MainTexture.rgb;

Multiply the result by the base texture.

外部阴影

  • Outer shadow
    Genshin Impact Character Shader Breakdown [Unity URP] (2).jpg

Genshin impact adds an outer extra small shadow with a different color. Do another NdotL but this time offset it a bit and make the transition harder, tint it with a similar color.

各向异性的头发

  • Anisotropic Hair
    Genshin Impact Character Shader Breakdown [Unity URP] (22).jpg

  • 代码如下

Texture _MaskTexture;
Float _AnisoHairFresnelPow;
Float _AnisoHairFresnelIntensity;


/// @note 基于 Schlick 近似简化后(还假设 R_{0} = 0)的 Fresnel
float anisoHairFresnel = pow((1.0 -saturate(dot(normalize(Normal), normalize(ViewDir)))), _AnisoHairFresnelPow) * _AnisoHairFresnelIntensity;

float anisoHair = saturate(1 - anisoHairFresnel) * _MaskTexture.(your mask channel) * LightDot;

Fresnel 原方程比较复杂。对于计算机图形学,你通常会看到人们使用 Schlick 近似 来简化计算 Fresnel 对反射的贡献。

R(θ)=R0+(1R0)(1cosθ)5R(\theta)=R_{0}+(1-R_{0})(1-cos\theta)^{5}

其中 R0=(n1n2n1+n2)2R_{0}=(\frac{n_{1}-n_{2}}{n_{1}+n_{2}})^{2}

R0R_{0} 是平行于法线方向的入射光的反射系数(即当 θ = 0 时)

Hair shines only when it receives light and you can also remove the sides with fresnel for a better result.

脸部阴影

  • Face Shadow
    Genshin Impact Character Shader Breakdown [Unity URP] (5).jpg

A simple NdotL is not the best option for cel shading artstyle, especially for the face. Depending on the direction of the light the result can be ugly.
We can edit the normals direction to avoid bad results, but there is a better option.

  • Face Shadow
    Genshin Impact Character Shader Breakdown [Unity URP] (19).jpg

With this texture we can modify the shadow shape:

  1. Channel R will be for degrees 0 to 180.
  2. Channel G will be for degrees 180 to 360.

我觉得作者这里可能表达有问题:

  • dotR 的函数图如下 image.png

  1. R 通道对应 dotR > 0 的情况,应该是 -90°90°(如果是 HeadRightMainLight 的夹角的话,作者可能的意思是 HeadForwardMainLight 的夹角)
  2. G 通道对应 dotR < 0 的情况,应该是 90度270°
  • 代码如下

SCRIPT:

Float _HeadForward = Head.transform.forward;
Float _HeadRight = Head.transformright;

SHADER:

Texture _ShadowTex;


float dotF = dot(_HeadForward.xz, mainLight.dir.xz);
float dotR = dot(_HeadRight.xz, mainLight.dir.xz);


float dotFStep = step(0, dotF);
float dotRAcos = (acos(dotR)/PI) * 2;
float dotRAcosDir = (dotR < 0) ? 1 - dotRAcos : dotRAcos - 1;
float texShadowDir = (dotR < 0) ? _ShadowTexg.g : _ShadowTex.r;
float shadowDir = step(dotRAcosDir, texShadowDir) * dotFStep;

金属质感

  • Metallic
    Genshin Impact Character Shader Breakdown [Unity URP] (4).jpg

The metallic parts looks like a kind of blend between diffuse texture and matcap. So, make a Matcap should be enough, but I did something similar and simple.

  • Metallic Genshin Impact Character Shader Breakdown [Unity URP] (16).jpg
  1. Use a Gradient Texture with the desired colors to tint your metal and do a DOT between Normal Vector and View Direction + Light Direction to do the UVs.
  2. Distort the Gradient Texture UVs with a normal map if necessary.

这里的 法线混合 算法值得关注

  • 代码如下
Texture _MetallicNormalTex;
Texture _GradientTex; 

// -(UV)
float2 MetallicUV = dot(WorldNormalVec, normalize(WorldViewDir + mainLight.dir);

// -(Normal Map)
void NormalBlend(float3 A, float3 B, out float3 Out)
{
    Out = normalize(float3(A.rg + B.rg, A.b * B.b));
}

float2 MetallicUV = dot(NormalBlend(WorldNormalVec, _MetallicNormalTex), normalize(WorldViewDir + mainLight.dir));

// -(Final)
float3 MetallicColor = SampleTex(_GradientTex, MetallicUV));
  • Metallic Genshin Impact Character Shader Breakdown [Unity URP] (4).gif

  • Metallic Genshin Impact Character Shader Breakdown [Unity URP].gif

  • Metallic Genshin Impact Character Shader Breakdown [Unity URP] (1).gif

  • Metallic with Normal Map Genshin Impact Character Shader Breakdown [Unity URP] (3).gif

光和雾

  • Light & Fog
    Genshin Impact Character Shader Breakdown [Unity URP] (13).jpg

Finally multiply the final result by the color light and fog. The character shader is finished. Let's move to postprocess.

后处理

描边

  • Outline Genshin Impact Character Shader Breakdown [Unity URP] (10).jpg

Outline postprocess is better since the traditional method: duplicate, scale and flip the mesh... is less precise.

For custom postprocess we need the blit render feature, I used this:
URP_BlitRenderFeature

  • Outline
    Genshin Impact Character Shader Breakdown [Unity URP] (14).jpg
  1. Detect the edges using the Depth Scene Texture And the Normal Scene Texture to do the same.
  2. Combine both results for maximum edge coverage.

More info on this article: Outline Shader

特殊的面部描边

  • Outline Face
    Genshin Impact Character Shader Breakdown [Unity URP] (21).jpg

To avoid unwanted outlines in places like the face, the depth value is manipulated to make it difficult to appear.

边缘高亮

  • Edge Highlight
    Genshin Impact Character Shader Breakdown [Unity URP] (18).jpg

Genshin Impact it has a white outline around the character like in most Animes.

This IS NOT A FRESNEL.
Is a post-process that reads the character silhouette and detect the edges with Sobel filter to add them to the final image.

  • Edge Highlight Genshin Impact Character Shader Breakdown [Unity URP] (11).jpg

Read the depth scene texture then detect the edges with Sobel filter. The outline will be inserted on the side with the value of greater depth.
Finally, move the edge highlight down a bit.

  • 代码如下
Texture _Tex;
Float _Thickness = 0.01;

static float2 sobelSamplePoints[9]=
{
    float2(-1, 1), float2(0, 1), float2(1, 1), 
    float2(-1, 0), float2(0, 0), float2(1, 0),
    float2(-1, -1), float2(0, -1), float2(1, -1),
};

static float sobelXMatrix[9] =
{
    1, 0, -1,
    2, 0, -2, 
    1, 0, -1
};

float2 sobel = 0;
for(int i=0; i<9; i++)
{
    float depth = SAMPLE2D(_Tex, sampler_Tex, UV + sobelSamplePoints[i] * _Thickness);
    sobel += depth * float2(sobelXMatrix[i], sobelYMatrix[i]);
}

Out = length(sobel);

自定义色调映射

  • Postprocess Tonemapper
    Genshin Impact Character Shader Breakdown [Unity URP] (20).jpg

Unity URP has 2 tonemappers without adjustable parameters:

  1. Neutral: Maintains color saturation but we will continue to have inadequate light in the most bright areas.
  2. Aces: Fixes bright areas but contrasts and desaturates colors.
  • Postprocess Tonemapper Genshin Impact Character Shader Breakdown [Unity URP] (12).jpg

Neutral would be the best option, but it would be better to write a custom tonemapper.

  • Postprocess Tonemapper Genshin Impact Character Shader Breakdown [Unity URP] (9).jpg

I used Gran Turismo tonemapper (GT Tonemap) Since it keeps the color saturation and fixes burned areas. This would be ideal for cartoon style

  • 顺便调戏一下 ChatGPT: image.png

  • 它所说的一款赛车游戏 image.png

  • 我的测试结果:(左)结果图,(右)原图 GT Tonemap.png

玻璃效果

  • Glasses Effect
    Genshin Impact Character Shader Breakdown [Unity URP] (2).gif

I added a parallax effect for Sucrose's glasses, this is not used in the original but I added it as an extra.