当前位置:   article > 正文

次表面散射材质_【04】卡通渲染 次表面散射效果的简易实现

substance painter次表面散射没效果

前言

续上两期,第二期我们做了一个简单的光照模型:

MEng Zheng:【02】卡通渲染基本光照模型的实现​zhuanlan.zhihu.com
bf685ebcd12f61c146f9e3025b923a30.png

b19d4b0d61a6bcebebd7837b7c3d5cb3.png

第三期我们利用了LightMap作为了光照计算的辅助:

MEng Zheng:【03】卡通渲染LightMap的使用​zhuanlan.zhihu.com
bf685ebcd12f61c146f9e3025b923a30.png

b1909c22fe03d1b638255225f84ff55f.png

但是第二期做出皮肤的质感,其实可以用次表面散射来做更好地皮肤表面通透效果。

次表面散射效果可以营造一种材质表面透光的效果,也可以使物体视觉上更好地融入环境。我们本期就来尝试做一个近似的效果。

本期我在第二期代码上添加的内容其实很简单,但是我想花很大地篇幅去讲清楚为什么要这么做,以及为什么这么做是有效的,而不是简单地说“怎么做”这件事。

目录

  1. 皮肤渲染次表面散射技术的简单总结
    1. 纹理空间次表面散射
    2. 屏幕空间的次表面散射
    3. 预积分次表面散射
  2. 基于预积分次表面散射技术的卡通渲染向的实现
  3. 总结及下期计划

1. 皮肤渲染次表面散射技术的简单总结

d160a60b9208a8a9937f738ba2bcf415.png

c329b94d520a7f6557d1e153c2310437.png

这两张是《GPU Gems3》中的截图,一个简单的剖面模型,把皮肤剖面做了一个分层,一束光线射入表面的某个点之后,在表面下层经历了一系列的散射,以一定比例的亮度从表面的另一个点射出。这样的过程会使观察到的材质表面有一个透光感。

然后光线传输方程(参照@春日老师的博客https://zhuanlan.zhihu.com/p/54120898)告诉我们,光线经历了无数次散射之后,已经失去了方向性,眼睛接收的散射光亮度跟观察的方向无关,只跟散射光的出射点与距离有关。于是就有了散射剖面:

bc32f4c50d01ff78ee07c2eaa6b208f7.png

偶极子模型拟合出的曲线是比较精确精确的。R(r)表明,距离入射点的径向距离越远,散射光线亮度越低;距离入射点的径向距离越近,散射光线亮度越高。另外这张图还表明,偶极子模拟的R(r)曲线可以用高斯函数近似,也可以利用高斯函数的级数来更准确地近似。级数的近似这个思路应该很好理解,例如泰勒级数就是微分多项式的逼近,傅里叶级数就是正弦多项式的逼近,等等。图中表明4阶高斯比2阶高斯有更好的近似效果。

1.1纹理空间次表面散射

c848132069e7186f2340b7806bc0c896.png

《GPU Gems3》中的这张图表明,红光由于波长最长,在同一径向距离下R(r)的值是最大的,或者可以理解为红光能在次表面扩散得最远。而左图看起来更像一个点扩散函数,点扩散函数是可以用卷积的方式表达的。

于是第一种实现思路就是,既然表明每个点的红光会扩散到其他点上,那换而言之就是其他点在接收漫反射辐照的同时还要接收周围点的散射光,并且接收到距离近的点散射光亮度大,接收到距离远的点散射光亮度小。也就是计算该着色点的亮度还需要把周围点的辐照以一定地比例算进来,然而这个跟距离相关的比例又可以用高斯函数近似。那么这个问题就可以在纹理空间(也可以理解切线空间)用高斯卷积核去实现。

3afc442906b40a98517f8e4262afa4d4.png

b9c49557d98f10e6ea4f2c4ba72edb35.png

基于纹理空间的次表面散射技术的具体做法是,先将光照计算的结果渲染到纹理空间上,再将这张纹理进行多次高斯核卷积(高斯级数)实现多次模糊,再线性加权组合到一起,最后重新渲染到Mesh上。

但是这么做的问题是,它不符合现有的渲染管线,一些优化策略例如背面剔除等就无法应用了。

1.2 屏幕空间的次表面散射

另一种思路是,既然在纹理空间做卷积不合适,那就在屏幕空间做卷积。也就是用后效来实现SSS。

但是这类方法会出现的问题是,会有色光扩散到本不连续的表面上,产生明显的人工痕迹。

b5c7478a146193644556e52d326dc638.png

例如图中鼻子部分的红色光扩散到了眼睛。

c280b07b0deb1be7910fe7c7e2af34e8.png

即便可以在渲染之前先渲染一张物体的Mask防止物体的散射光扩散到其他物体,但是仍然避免不了物体自身的表面不连续扩散。

在此基础上15年又出了可分离次表面散射(SSSSS),将高斯核进行了分解,SSSS文中提出了很多种分解方法(参照@春日老师的博客https://zhuanlan.zhihu.com/p/54120898)来提升卷积的运算速度。

最后说一下我对屏幕空间的次表面散射的看法,首先用后效来实现是一个很好的思路,可以把复杂的运算在屏幕空间这个小空间内高性能地完成。利用屏幕空间做后效的方法有很多,例如屏幕空间环境光遮蔽(SSAO),屏幕空间的光线追踪(screen space ray marching),延迟渲染(Deferred Shading),以及对其进一步优化的分块着色(block shading),分簇着色(cluster shading),等等。但也正是因为如此,一个项目中在屏幕空间上完成的工作可能很多,如果算上一般的后效,例如泛光(bloom)等等,就更多了。在这种情况下,把一个方案放在后效上实现,会不会跟其他的后效有冲突,有依赖?是否有先后顺序,这就成了一个很麻烦的问题,也会带来新的程序耦合。我们希望功能的实现之间尽可能独立,才是一个良好的设计模式。因此,我更倾向于在不改变管线,不改变渲染队列的情况下实现次表面散射的方法。

1.3 预积分次表面散射

预积分的次表面散射技术[2010]基于两个观察结论:肉眼可见的次表面散射现象只会出现在表面曲率较大且正处于明暗交界线位置的区域。换而言之,次表面散射现象的强弱只和两个量有关,一是表面的曲率,而是法线和光线的点积。表面曲率越大,次表面散射在纹理空间的扩散范围就越大。

9252a0e45de7d49eb2451d156e2ae340.png

《GPU Pro3》里关于预积分次表面散射的介绍是,在计算光照前先对整个模型进行一遍预积分计算曲率,在计算次表面散射的时候利用上计算的曲率,再关联上N*L作为扩散的强度和范围的一个参数。当获取了次表面散射的强度之后,就可以利用warp(给红光更慢的衰减速率)或者用高斯函数等方法来近似出次表面散射的效果了。

b7c25b1478d5057a213258134a294815.png

原文中图示表明,利用预积分实现的SS效果与纹理空间的SS效果没有明显的差异。

2. 基于预积分次表面散射技术的卡通渲染向的实现

回到卡通渲染,预积分次表面散射中的曲率,可能是唯一麻烦的问题。但是参照第二期LightMap的思路,曲率这个值,完全可以交给美术直接画到Map里,着色的时候直接采样出来用就行了。但是本期我没有额外去制作这个Map,就暂时先不考虑曲率这个影响因素。我暂时只利用光线和法线的点积作为SS扩散程度的一个参数。

正如我这个系列第一期的方式,先找一个球来测试光照计算:

f13635e11efe4beaca4352038f302ccf.png

首先,高斯函数的形式是:

b是高斯函数的中心,c是方差,方差越大,高斯函数越分散,反之越集中。

a8bd170029c8d3a6b66ea461a829011e.png
方差不同的高斯函数,蓝色曲线的方差小

第2期我们利用Sigmoid函数对每个亮度级做了一个窗函数:

a8e838d92d83b1c5966822fc322739cd.png

红色曲线为亮面的Mask,蓝色为暗面的Mask。

我希望在暗面的次表面散射效果明显,而亮面的效果较弱。于是我分别用这两个Mask以乘法的形式对方差不同的高斯函数做一个截取:

60f62809667d041a55159f4bb1460d14.png

然后以加法的形式合并:

d06ee8220ea93ee08412463ac11a89b7.png

这样我就造出了在两侧扩散程度不同的扩散函数。整体的幅度和方差都可以用可调参数进行调节。如第一节所述,为了更好的拟合真实的扩散函数,应该以高斯级数的形式作为扩散函数。那我这里就简单地用2级。最后把这个函数作为一个比例因子,乘以希望发生次表面散射的颜色,再加到光照计算的结果上。

我把这部分的代码附一下:

  1. Shader "Custom/SSS"
  2. {
  3. Properties
  4. {
  5. _Color ("Color", Color) = (1,1,1,1)
  6. _MainTex ("Albedo (RGB)", 2D) = "white" {}
  7. _Glossiness ("Smoothness", Range(0,1)) = 0.5
  8. _Metallic ("Metallic", Range(0,1)) = 0.0
  9. _SSSColor ("Subsurface Scattering Color", Color) = (1,0,0,1)
  10. _SSSColorSub("Subsurface Scattering 2nd Color", Color) = (0.8,0,0.2,1)
  11. _SSSWeight ("Weight of SSS", Range(0,1)) = 0.5
  12. _SSSSize("Size of SSS", Range(0,1)) = 0.5
  13. _SSForwardAtt("Atten of SS in forward Dir", Range(0,1)) = 0.5
  14. _diffuseBright("Brightness", Range(0,2.0)) = 0.0
  15. _DividLineM("DividLine of Middle", Range(-0.5, 0.8)) = 0.0
  16. _DividLineD("DividLine of Dark", Range(-1.0, 0.0)) = -0.5
  17. _DividSharpness("Sharpness of Divide Line", Range(0.2,5)) = 1.0
  18. }
  19. CGINCLUDE
  20. float Pow2(float x) {
  21. return x * x;
  22. }
  23. float ndc2Normal(float x) {
  24. return x * 0.5 + 0.5;
  25. }
  26. float warp(float x, float w) {
  27. return (x + w) / (1 + w);
  28. }
  29. float3 warp(float3 x, float3 w) {
  30. return (x + w) / (1 + w);
  31. }
  32. float3 warp(float3 x, float w) {
  33. return (x + w.xxx) / (1 + w.xxx);
  34. }
  35. float sigmoid(float x, float center, float sharp) {
  36. float s;
  37. s = 1 / (1 + pow(100000, (-3 * sharp * (x - center))));
  38. return s;
  39. }
  40. float Gaussion(float x, float center, float var) {
  41. return pow(2.718, -1 * Pow2(x - center) / var);
  42. }
  43. ENDCG
  44. SubShader
  45. {
  46. Tags { "RenderType"="Opaque" }
  47. LOD 200
  48. CGPROGRAM
  49. // Physically based Standard lighting model, and enable shadows on all light types
  50. #pragma surface surf SSS fullforwardshadows
  51. // Use shader model 3.0 target, to get nicer looking lighting
  52. #pragma target 3.0
  53. sampler2D _MainTex;
  54. struct Input
  55. {
  56. float2 uv_MainTex;
  57. };
  58. half _Glossiness;
  59. half _Metallic;
  60. fixed4 _Color;
  61. fixed4 _SSSColor;
  62. fixed4 _SSSColorSub;
  63. half _SSSWeight;
  64. half _SSSSize;
  65. half _diffuseBright;
  66. half _DividLineM;
  67. half _DividLineD;
  68. half _DividSharpness;
  69. half _SSForwardAtt;
  70. struct ToonSurfaceOutput
  71. {
  72. fixed3 Albedo; // diffuse color
  73. fixed3 Normal; // tangent space normal, if written
  74. fixed3 Emission;
  75. half Specular; // specular power in 0..1 range
  76. fixed Gloss; // specular intensity
  77. fixed Alpha; // alpha for transparencies
  78. fixed3 diffColor;
  79. half specIntensity;
  80. half smoothMap;
  81. half AO;
  82. };
  83. // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
  84. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
  85. // #pragma instancing_options assumeuniformscaling
  86. UNITY_INSTANCING_BUFFER_START(Props)
  87. // put more per-instance properties here
  88. UNITY_INSTANCING_BUFFER_END(Props)
  89. half4 LightingSSS(ToonSurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
  90. half3 nNormal = normalize(s.Normal);
  91. half NoL = dot(nNormal, lightDir);
  92. half Lambert = NoL;
  93. half SSLambert = warp(Lambert, _SSSWeight);
  94. half roughness = 0.95 - 0.95 * (s.smoothMap * _Glossiness);
  95. half _BoundSharp = 9.5 * Pow2(roughness - 1) + 0.5;
  96. //--------------------------------------------
  97. // diffuse
  98. //--------------------------------------------
  99. half MidSig = sigmoid(Lambert, _DividLineM, _BoundSharp * _DividSharpness);
  100. half DarkSig = sigmoid(Lambert, _DividLineD, _BoundSharp * _DividSharpness);
  101. half MidLWin = MidSig;
  102. half MidDWin = DarkSig - MidSig;
  103. half DarkWin = 1 - DarkSig;
  104. half diffuseLumin1 = (1 + ndc2Normal(_DividLineM)) / 2;
  105. half diffuseLumin2 = (ndc2Normal(_DividLineM) + ndc2Normal(_DividLineD)) / 2;
  106. half diffuseLumin3 = (ndc2Normal(_DividLineD));
  107. half3 diffuseDeflectedColor1 = MidLWin * diffuseLumin1.xxx;
  108. half3 diffuseDeflectedColor2 = MidDWin * diffuseLumin2.xxx;
  109. half3 diffuseDeflectedColor3 = DarkWin * diffuseLumin3.xxx;
  110. half3 diffuseBrightedColor = warp(diffuseDeflectedColor1 + diffuseDeflectedColor2 + diffuseDeflectedColor3, _diffuseBright.xxx);
  111. half3 diffuseResult = diffuseBrightedColor * s.diffColor.rgb;
  112. //----------------------------------------------
  113. // scattering
  114. //----------------------------------------------
  115. half SSMidLWin = Gaussion(Lambert, _DividLineM, _SSForwardAtt * _SSSSize);
  116. half SSMidDWin = Gaussion(Lambert, _DividLineM, _SSSSize);
  117. half SSMidLWin2 = Gaussion(Lambert, _DividLineM, _SSForwardAtt * _SSSSize*0.01);
  118. half SSMidDWin2 = Gaussion(Lambert, _DividLineM, _SSSSize * 0.01);
  119. half3 SSLumin1 = MidLWin * diffuseLumin2 * _SSForwardAtt * (SSMidLWin + SSMidLWin2);
  120. half3 SSLumin2 = ((MidDWin + DarkWin) * diffuseLumin2) * (SSMidDWin+ SSMidDWin2);
  121. half3 SS = _SSSWeight * (SSLumin1 + SSLumin2) * _SSSColor.rgb;
  122. //---------------------------------------------------------------------------
  123. half3 lightResult = diffuseResult.rgb * _LightColor0.rgb + SS;
  124. return half4(lightResult.rgb, s.Alpha);
  125. }
  126. void surf (Input IN, inout ToonSurfaceOutput o)
  127. {
  128. // Albedo comes from a texture tinted by color
  129. fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
  130. o.Albedo = 0.2 * c.rgb;
  131. o.smoothMap = _Glossiness;
  132. o.diffColor = c.rgb * _Color;
  133. o.Alpha = c.a;
  134. }
  135. ENDCG
  136. }
  137. FallBack "Diffuse"
  138. }

然后我们看一下效果:

089c2d4223479646614847ad0004921f.png

16b2de57229dac52198c9362ff54b752.png

加上次表面散射效果之后,就会让材质表面有一种光线通透的感觉了。

接下来把这个实现思路整合到我们第3期的代码中,整理之后的代码我附一下:

  1. Shader "Custom/Bronya"
  2. {
  3. Properties
  4. {
  5. _Color ("Color", Color) = (1,1,1,1)
  6. _MainTex ("Albedo (RGB)", 2D) = "white" {}
  7. _Glossiness ("Smoothness Scale", Range(0,1)) = 0.5
  8. _Metallic ("Metallic", Range(0,1)) = 0.0
  9. _LightMap("Light Map", 2D) = "white" {}
  10. _DividSharpness("Sharpness of Divide Line", Range(0.2,5)) = 1.0
  11. _DividLineSpec("DividLine of Specular", Range(0.5, 1.0)) = 0.8
  12. _DividLineM("DividLine of Middle", Range(-0.5, 0.8)) = 0.0
  13. _DividLineD("DividLine of Dark", Range(-1.0, 0.0)) = -0.5
  14. _diffuseBright("diffuse Brightness", Range(0.0,2.0)) = 1.0
  15. _AOWeight("Weight of Ambient Occlusion", Range(0.0,2.0)) = 1.0
  16. _ShadowAttWeight("Weight of shadow atten", range(0.0, 0.5)) = 0.3
  17. _DarkFaceColor("Color of Dark Face", Color) = (1.0, 1.0, 1.0, 1.0)
  18. _DeepDarkColor("Color of Deep Dark Face", Color) = (1.0, 1.0, 1.0, 1.0)
  19. _FresnelEff("Fresnel Effect", Range(0, 1)) = 0.5
  20. _FresnelColor("Fresnel Color", Color) = (1,1,1,1)
  21. _SSSColor("Subsurface Scattering Color", Color) = (1,0,0,1)
  22. _SSSWeight("Weight of SSS", Range(0,1)) = 0.0
  23. _SSSSize("Size of SSS", Range(0,1)) = 0.0
  24. _SSForwardAtt("Atten of SS in forward Dir", Range(0,1)) = 0.5
  25. _OutlineWidth("Outline Width", Range(0, 0.5)) = 0.024
  26. _OutlineColor("Outline Color", Color) = (0.5,0.5,0.5,1)
  27. }
  28. CGINCLUDE
  29. #include "UnityCG.cginc"
  30. float D_GGX(float a2, float NoH) {
  31. float d = (NoH * a2 - NoH) * NoH + 1;
  32. return a2 / (3.14159 * d * d);
  33. }
  34. float sigmoid(float x, float center, float sharp) {
  35. float s;
  36. s = 1 / (1 + pow(100000, (-3 * sharp * (x - center))));
  37. return s;
  38. }
  39. float Pow2(float x) {
  40. return x * x;
  41. }
  42. float ndc2Normal(float x) {
  43. return x * 0.5 + 0.5;
  44. }
  45. float Normal2ndc(float x) {
  46. return x * 2.0 - 1.0;
  47. }
  48. float warp(float x, float w) {
  49. return (x + w) / (1 + w);
  50. }
  51. float3 warp(float3 x, float3 w) {
  52. return (x + w) / (half3(1.0,1.0,1.0) + w);
  53. }
  54. float Pow3(float x) {
  55. return x * x* x;
  56. }
  57. float Pow5(float x) {
  58. return x * x* x* x* x;
  59. }
  60. float3 Fresnel_schlick(float VoN, float3 rF0) {
  61. return rF0 + (1 - rF0) * Pow5(1 - VoN);
  62. }
  63. float3 Fresnel_extend(float VoN, float3 rF0) {
  64. return rF0 + (1 - rF0) * Pow3(1 - VoN);
  65. }
  66. float Gaussion(float x, float center, float var) {
  67. return pow(2.718, -1 * Pow2(x - center) / var);
  68. }
  69. ENDCG
  70. SubShader
  71. {
  72. Tags { "RenderType"="Opaque" }
  73. LOD 200
  74. CGPROGRAM
  75. // Physically based Standard lighting model, and enable shadows on all light types
  76. #pragma surface surf Toon fullforwardshadows
  77. // Use shader model 3.0 target, to get nicer looking lighting
  78. #pragma target 3.0
  79. sampler2D _MainTex;
  80. sampler2D _LightMap;
  81. float4 _LightMap_TexelSize;
  82. struct Input
  83. {
  84. float2 uv_MainTex;
  85. float2 uv_LightMap;
  86. };
  87. struct ToonSurfaceOutput
  88. {
  89. fixed3 Albedo; // diffuse color
  90. fixed3 Normal; // tangent space normal, if written
  91. fixed3 Emission;
  92. half Specular; // specular power in 0..1 range
  93. fixed Gloss; // specular intensity
  94. fixed Alpha; // alpha for transparencies
  95. fixed3 diffColor;
  96. half specIntensity;
  97. half smoothMap;
  98. half AO;
  99. };
  100. half _Glossiness;
  101. half _Metallic;
  102. fixed4 _Color;
  103. half _DividLineSpec;
  104. half _ShadowAttWeight;
  105. half _DividSharpness;
  106. half _DividLineM;
  107. half _DividLineD;
  108. half _diffuseBright;
  109. half _AOWeight;
  110. fixed4 _DarkFaceColor;
  111. fixed4 _DeepDarkColor;
  112. half _FresnelEff;
  113. fixed4 _FresnelColor;
  114. fixed4 _SSSColor;
  115. half _SSSWeight;
  116. half _SSSSize;
  117. half _SSForwardAtt;
  118. // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
  119. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
  120. // #pragma instancing_options assumeuniformscaling
  121. UNITY_INSTANCING_BUFFER_START(Props)
  122. // put more per-instance properties here
  123. UNITY_INSTANCING_BUFFER_END(Props)
  124. half4 LightingToon(ToonSurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
  125. // Array
  126. half3 nNormal = normalize(s.Normal);
  127. half3 HDir = normalize(lightDir + viewDir);
  128. half NoL = dot(nNormal, lightDir);
  129. half NoH = dot(nNormal, HDir);
  130. half NoV = dot(nNormal, viewDir);
  131. half VoL = dot(viewDir, lightDir);
  132. half VoH = dot(viewDir, HDir);
  133. half roughness = 0.95 - 0.95 * (s.smoothMap * _Glossiness);
  134. half _BoundSharp = 9.5 * Pow2(roughness - 1) + 0.5;
  135. //----------------------------------------------------
  136. // fresnel
  137. //----------------------------------------------------
  138. half3 fresnel = Fresnel_extend(NoV, float3(0.1, 0.1, 0.1));
  139. half3 fresnelResult = _FresnelEff * fresnel * (1 - VoL) / 2;
  140. //--------------------------------------------
  141. // Specular
  142. //--------------------------------------------
  143. half NDF0 = D_GGX(roughness * roughness, 1);
  144. half NDF_HBound = NDF0 * _DividLineSpec;
  145. half NDF = D_GGX(roughness * roughness, NoH) + _ShadowAttWeight * (atten - 1);
  146. half specularWin = sigmoid(NDF, NDF_HBound, _BoundSharp * _DividSharpness);
  147. half SpecWeight = specularWin * (NDF0 + NDF_HBound) / 2 * s.specIntensity; //optional
  148. //----------------------------------------------------
  149. // diffuse
  150. //--------------------------------------------
  151. half Lambert = NoL + _AOWeight * Normal2ndc(s.AO) + _ShadowAttWeight * (atten - 1);
  152. half MidSig = sigmoid(Lambert, _DividLineM, _BoundSharp * _DividSharpness);
  153. half DarkSig = sigmoid(Lambert, _DividLineD, _BoundSharp * _DividSharpness);
  154. half MidLWin = MidSig;
  155. half MidDWin = DarkSig - MidSig;
  156. half DarkWin = 1 - DarkSig;
  157. half diffuseLumin1 = (1 + ndc2Normal(_DividLineM)) / 2;
  158. half diffuseLumin2 = (ndc2Normal(_DividLineM) + ndc2Normal(_DividLineD)) / 2;
  159. half diffuseLumin3 = (ndc2Normal(_DividLineD));
  160. half3 diffuseDeflectedColor1 = MidLWin * diffuseLumin1.xxx;
  161. half3 diffuseDeflectedColor2 = MidDWin * diffuseLumin2.xxx * _DarkFaceColor.rgb * 3 / (_DarkFaceColor.r + _DarkFaceColor.g + _DarkFaceColor.b);
  162. half3 diffuseDeflectedColor3 = DarkWin * diffuseLumin3.xxx * _DeepDarkColor.rgb * 3 / (_DeepDarkColor.r + _DeepDarkColor.g + _DeepDarkColor.b);
  163. half3 diffuseBrightedColor = warp(diffuseDeflectedColor1 + diffuseDeflectedColor2 + diffuseDeflectedColor3, _diffuseBright.xxx);
  164. half3 diffuseResult = diffuseBrightedColor * s.diffColor.rgb;
  165. //------------------------------------------------------
  166. // Subsurface Scattering
  167. //------------------------------------------------------
  168. half SSMidLWin = Gaussion(Lambert, _DividLineM, _SSForwardAtt * _SSSSize);
  169. half SSMidDWin = Gaussion(Lambert, _DividLineM, _SSSSize);
  170. half3 SSLumin1 = (MidLWin * diffuseLumin2) * _SSForwardAtt * SSMidLWin;
  171. half3 SSLumin2 = ((MidDWin+ DarkWin) * diffuseLumin2) * SSMidDWin;
  172. half3 SS = _SSSWeight * (SSLumin1 + SSLumin2) * _SSSColor.rgb;
  173. //--------------------------------------------------------
  174. half3 lightResult = SpecWeight * _LightColor0.rgb + (1 - SpecWeight) * diffuseResult.rgb * _LightColor0.rgb + SS + fresnelResult * _FresnelColor.rgb;
  175. return half4(lightResult.rgb, 1.0);
  176. }
  177. void surf (Input IN, inout ToonSurfaceOutput o)
  178. {
  179. // Albedo comes from a texture tinted by color
  180. fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
  181. o.diffColor = c.rgb;
  182. o.Albedo = 0.2 * c.rgb;
  183. fixed4 ilm = tex2D(_LightMap, IN.uv_LightMap);
  184. //-------------------------------------------
  185. // blur
  186. float2 tmpuv1 = IN.uv_LightMap + _LightMap_TexelSize.xy;
  187. float2 tmpuv2 = IN.uv_LightMap - _LightMap_TexelSize.xy;
  188. float2 tmpuv3 = IN.uv_LightMap;
  189. tmpuv3.x += _LightMap_TexelSize.x;
  190. tmpuv3.y -= _LightMap_TexelSize.y;
  191. float2 tmpuv4 = IN.uv_LightMap;
  192. tmpuv4.x -= _LightMap_TexelSize.x;
  193. tmpuv4.y += _LightMap_TexelSize.y;
  194. fixed4 ilm1 = tex2D(_LightMap, tmpuv1);
  195. fixed4 ilm2 = tex2D(_LightMap, tmpuv2);
  196. fixed4 ilm3 = tex2D(_LightMap, tmpuv3);
  197. fixed4 ilm4 = tex2D(_LightMap, tmpuv4);
  198. //ilm = 0.4 * ilm + 0.15 * (ilm1 + ilm2 + ilm3 + ilm4);
  199. ilm = 0.2 * (ilm + ilm1 + ilm2 + ilm3 + ilm4);
  200. //---------------------------------------
  201. o.smoothMap = ilm.r;
  202. o.specIntensity = ilm.b;
  203. o.AO = ilm.g;
  204. o.Alpha = c.a;
  205. }
  206. ENDCG
  207. Pass{
  208. Name "OUTLINE"
  209. Tags{ "LightMode" = "Always" }
  210. Cull Front
  211. ZWrite On
  212. ColorMask RGB
  213. Blend SrcAlpha OneMinusSrcAlpha
  214. CGPROGRAM
  215. #pragma vertex vert
  216. #pragma fragment frag
  217. #include "UnityCG.cginc"
  218. struct appdata {
  219. float4 vertex : POSITION;
  220. float3 normal : NORMAL;
  221. float4 texCoord : TEXCOORD0;
  222. };
  223. struct v2f {
  224. float4 pos : SV_POSITION;
  225. float4 color : COLOR;
  226. float4 tex : TEXCOORD0;
  227. };
  228. uniform half _OutlineWidth;
  229. sampler2D _MainTex;
  230. fixed4 _OutlineColor;
  231. v2f vert(appdata v) {
  232. // just make a copy of incoming vertex data but scaled according to normal direction
  233. v2f o;
  234. o.pos = UnityObjectToClipPos(v.vertex);
  235. float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
  236. float2 extendDir = normalize(TransformViewToProjection(norm.xy));
  237. _OutlineWidth = _OutlineWidth;
  238. o.pos.xy += extendDir * (o.pos.w * _OutlineWidth * 0.1);
  239. o.tex = v.texCoord;
  240. o.color = half4(0,0,0,1);
  241. return o;
  242. }
  243. half4 frag(v2f i) :COLOR{
  244. fixed4 c = tex2D(_MainTex, i.tex);
  245. return half4(c.rgb* _OutlineColor.rgb, 1.0);
  246. }
  247. ENDCG
  248. }//Pass
  249. }
  250. FallBack "Diffuse"
  251. }

最后展示一下皮肤的效果。

使用了次表面散射的皮肤效果(头发我也加上了,这一点纯粹是为了艺术效果):

fa0384aad15cf66cae3078768d960107.png

没有使用次表面散射的效果:

d961c90c1bf2fbd310c246d99d362161.png

第3期我们设置了一个亮度调整参数,现在我把这个参数降低以模拟强光下的效果:

1968ba23c080a26e4755df25e5af9654.png

这样看起来皮肤的通透感就更强烈了。当然这张图我为了展示效果,有些校色问题没有处理得很好。

最后是一个整体效果的对比展示:

3be9fbebc9be886be9e6f44257f16c1d.png

左图是第3期的效果,右图是本期附上SS之后的效果。右图对环境的融入感是好于左图的。

3. 总结及下期计划

本期内容很少,简单介绍了下我从pbr的皮肤渲染中得到的启发,并且用高斯函数的级数做扩散近似应用到的卡通渲染的光照模型下实现了简单的次表面散射效果。

应上一期的计划,下期我计划实现一个卡通渲染下的金属材质表现。然后用Metallic参数在金属和非金属的渲染工作流之间做插值,达到金属度可调的效果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/86645
推荐阅读
相关标签
  

闽ICP备14008679号