https://blog.csdn.net/liumazi/article/details/78858811
渲染逆向方法:
用Adreno Profiler抓幀, 並且分析其中的OpenGL調用及相關資源(頂點數據、紋理、Shader代碼等)
其中Shader代碼和在Unity里選中Shader並點擊Compile and show code看到的gles3部分類似, 建議轉換成更易於閱讀的形式
卡通着色思路:
光照計算仍然是漫反射+高光反射, 其中漫反射是HalfLambert, 高光反射公式來自於Blinn-Phong模型
比較關鍵的點是, 漫反射部分不是漸變的, 而是分了3檔, 以實現卡通風格的層次感, 即所謂的梯度漫反射(Ramped Diffuse)
上圖是還原出來的Unity Shader及渲染結果, 下面簡要介紹一下該Shader的實現細節..
Main Color:
主紋理, rgb為基礎顏色, a的作用見下文
Bloom Mask:
未使用
Light Map:
光照紋理, rgb作用見下文
mainColor.a:
主紋理alpha通道, 表示不受光照影響的程度, 為1.0時無論是否有光照影響都為原顏色, 為0.0時僅包含光照計算結果,
如身體和頭發主紋理中, 一些黃色的部分, alpha值較高, 用來保持高亮
從主紋理中分離出來的alpha通道, 見下圖
Avatar_Kiana_C2_Texture_Body_Color_RGB2048_A.png和Avatar_Kiana_C2_Texture_Hair_Color_Common_A.png
以及 輸出-主紋理-A通道.png
UsingBloomMask:
材質屬性, 是否使用BloomMask紋理調整不受光照影響的程度, 默認關閉
公式為 mainColor.a *= tex2D(_BloomMaskTex, i.texcoord5.xy).x
注意: 只能減弱或保持原狀, 因為紋理采樣結果在0.0~1.0之間
UsingDitherAlpha:
材質屬性, 是否應用alpha抖動, 默認關閉
FirstShadowMultColor:
材質屬性, 暗面亮度一, xyz分量分別對應主紋理rgb通道, 值越小越暗, 此處為(0.72941, 0.6, 0.65098)
公式為fristShadowColor = mainColor.rgb * _FirstShadowMultColor.xyz
SecondShadowMultColor:
材質屬性, 暗面亮度二, xyz分量分別對應主紋理rgb通道, 值越小越暗, 此處為(0.65098, 0.45098, 0.549019)
公式為secondShadowColor = mainColor.rgb * _SecondShadowMultColor.xyz
i.color.r和lightMapColor.g和LightArea和SecondShadow:
頂點顏色r通道和光照紋理g通道, 兩者乘積rgProduct用來做暗面顏色選擇, 公式如下
diffuseColor = rgProduct >= 0.090000033 ? otherColor : shadowColor
otherColor = ((rgProductFix + i.halfLambert) * 0.5 >= _LightArea) ? mainColor.rgb : fristShadowColor
shadowColor = ((rgProduct + i.halfLambert) * 0.5 >= _SecondShadow) ? fristShadowColor : secondShadowColor
其中,
rgProductFix是rgProduct經過微調的一個值;
i.halfLambert是半蘭伯特值, 即法線與光線夾角的余弦值映射到0.0~1.0范圍內, 夾角越小值越接近1, 用於表示漫反射部分的強度;
_LightArea和_SecondShadow是材質屬性, 分別為用於選擇 原顏色和暗面一、暗面一和暗面二的閾值;
上述公式可理解為,
如果rgProduct非常小的話, 在暗面一和暗面二中選一個; 否則, 在原顏色和暗面一中選一個,
相當於漫反射率不再是根據法線漸變, 而是分了3檔(按閾值選擇), 以此實現卡通風格的層次感,
更進一步地, 當rgProduct非常小的時候, 除非i.halfLambert比較大, 否則選的往往是暗面二
下圖1 輸出-明暗分布.png, 其中不同顏色代表了不同的區域, 綠色和黑色是暗面一和暗面二, 白色和紅色是原顏色和暗面一
下圖2 輸出-頂點R通道×光照紋理G通道.png, 其中比較黑的區域正對應了上圖的綠色和黑色區域
Shininess:
材質屬性, 光澤度, Blinn-Phong光照模型中用於計算高光反射的指數, 用於控制高光區域的亮點大小, 值越大亮點半徑越小
lightMapColor.b:
光照紋理b通道, 用於使某些區域更容易出現高光, 因為出現高光的條件是 lightMapColor.b + blinnSpec > 1.0
其中blinnSpec是按照Blinn-Phong光照模型計算出來的高光度, 越大表示高光反射越強,
下圖 ID_118_B.png, 這是頭發的光照紋理b通道, 比較明顯; 以及 ID_113_B.png和 輸出-光照紋理-B通道.png
lightMapColor.r和LightSpecColor和SpecMulti:
lightMapColor.r為光照紋理r通道, LightSpecColor和SpecMulti為材質屬性, 共同控制當存在高光時高光的顏色,
公式為 _LightSpecColor.xyz * _SpecMulti * lightMapColor.r
見下圖 ID_113_R.png、ID_118_R.png、輸出-光照紋理-R通道.png
最終產生的高光分布: (比想象中弱很多)
Color:
材質屬性, 用於調整光照計算結果, 只能調的更弱或者保持原狀,
公式為litColor.xyz = (diffuseColor + specColor) * _Color.xyz;
其他輸出圖:
輸出-主紋理-RGB通道.png 和 輸出-頂點色-RGB通道.png
皮膚shader:
與上述shader類似, 但是沒有BloomMask, 增加了ProbToggle用於調節膚色(未見實際使用), 並且mainColor.a不再有上述作用
描邊的實現:
將模型在觀察空間中, 按法線往外擴張, 以剔除正面的方式繪制 (Shader中聲明的頂點屬性是切線, 但實際提交的數據應該還是法線)
下圖 輸出-膨脹方向.png, 其中紅色表示右下, 綠色表示左上, 黃色表示右上, 黑色表示左下, 注意: 只是大概的方向不是絕對的
最終描邊結果 輸出-最終結果.png, 為了更明顯, 描邊顏色設置為了黑色, 游戲中實際的值為(0.4117647, 0.3112941, 0.3768184)