寄存器分配图着色_【02】从零开始的卡通渲染-着色篇1
專欄目錄
2173:【01】從零開始的卡通渲染-描邊篇?zhuanlan.zhihu.com2173:【02】從零開始的卡通渲染-著色篇1?zhuanlan.zhihu.com2173:【03】從零開始的卡通渲染-著色篇2?zhuanlan.zhihu.com2173:【04】從零開始的卡通渲染-PBR篇?zhuanlan.zhihu.com序言:
接上一篇的描邊篇,整理成一個專欄了。在本節中,我們開始討論卡通渲染的一些光照計算方法。
如何讓角色看起來卡通
思考一下,究竟是哪些因素,讓我們覺得角色是卡通的呢。我覺得可以先從下面3點入手
1.減少色階數量
2.冷暖色調分離
3.對明暗區域的手繪控制
減少色階數量
減少色階的數量,給畫面卡通感冷暖色調分離
明面和暗面分配不同冷暖的顏色,給畫面卡通感在美術上根據顏色區分為暖色調(紅色,黃色)和冷色調(藍色、紫色)。在偏真實的光照計算中,往往只計算一個明暗關系,然后由光和物體的顏色決定最終效果。而卡通渲染則會根據明暗關系,為明面和暗面分配不同色調的顏色。比如一個暖色調的明面,配合一個冷色調的暗面。將色調拉開以后,更進一步給人卡通感。相關鏈接 tone-based-shading
《GUILTY GEAR Xrd》中通過單獨的貼圖定義暗面色調,和明面的色調做區分。左圖是未調整暗面色調,右圖是調整了暗面色調的在《GUILTY GEAR Xrd》游戲中,繪制了一張稱為SSS Texture的貼圖,來對暗面的色調進行調整。
對明暗區域的手繪控制
在手繪動畫中為了好的畫面效果,往往其明暗的分布并不是完全正確的。最明顯的,角色的脖子部分通常都出現明顯的陰影。經典光照計算的結果是非常“正確”的,因而缺少卡通的手繪感。需要用其他方式對光照的計算結果進行調整。
按照正確的光照計算,角色的脖子是不會有那么明顯的陰影的下面介紹一下《GUILTY GEAR Xrd》中是如何對明暗區域進行手繪控制的
在《GUILTY GEAR Xrd》中,通過燈光方向,Threshold貼圖,法線方向對光照計算進行手繪風格的控制燈光方向控制:
卡通渲染的角色在部分燈光方向下,可以有最佳的畫面表現。有時候這個燈光方向和場景燈光或者其他角色的燈光方向不一致。為了讓每個角色都有最佳表現,最好每個角色有一盞自己的燈光方向。甚至當這個角色轉向時,這個燈光也跟著角色做一定程度的轉向,來讓角色有一個更好的光影表現。
Threshold貼圖控制:
《GUILTY GEAR Xrd》中將這張貼圖稱作ilmTexture。為了減少歧義,我們這里也這么稱呼好了。
ilmTexture關閉和開啟對角色陰影區域的影響這張貼圖有些類似于AO貼圖,不過它是對光照計算的結果進行一些傾向性的修正。讓一部分區域,比如角色脖子的部分更容易產生陰影。來達到手繪風格的陰影效果。
法線方向控制:
法線控制有兩種方法,一種是直接編輯法線,達到想要的光照結果。一種是創建一個平滑的簡單模型,然后將其法線傳遞到復雜物體上,達到優化陰影的效果。Maya自帶法線傳遞的功能,3ds Max可以通過插件Noors Normal Thief實現法線傳遞的功能。
通過直接編輯法線,達到想要的光照效果創建簡單的頭套模型,傳遞其法線到頭發上,優化頭發的陰影在《火影忍者 究極風暴》中,將人物的面部向外膨脹,再用膨脹后的面部法線來優化面部陰影賽璐璐風格插畫
《百變小櫻》動畫的賽璐璐片賽璐璐片是一種塑料卡片,在早期日本動畫制作流程中的,畫師會在賽璐璐材質的塑料卡片上對原畫進行上色。其特點為通常只有明暗2個色階,明暗變化的交界非常明顯。現在這種風格的卡通渲染比較流行。在本篇中,也將實現偏向這種風格的卡通渲染。
厚涂風格插畫
《明日方舟》塔露拉立繪厚涂風格相較賽璐璐風格,色階更多,明暗交界變化會柔和很多。這個風格也有它的好處,因為3D場景比較難做成賽璐璐的。如何讓賽璐璐風格的角色和非賽璐璐的場景融合是也許需要考慮的。厚涂風格的角色會更容易和場景進行融合。
雙色階的渲染實現
首先我們實現一個明暗邊界分明的光照效果,并支持分別設置明暗區域的顏色,設置暗面顏色為冷色調,和明面的色調做出區分。
Shader "Unlit/CelRender" {Properties{_MainTex ("MainTex", 2D) = "white" {}_MainColor("Main Color", Color) = (1,1,1)_ShadowColor ("Shadow Color", Color) = (0.7, 0.7, 0.8)_ShadowRange ("Shadow Range", Range(0, 1)) = 0.5_ShadowSmooth("Shadow Smooth", Range(0, 1)) = 0.2[Space(10)]_OutlineWidth ("Outline Width", Range(0.01, 2)) = 0.24_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)}SubShader{Tags { "RenderType"="Opaque" }pass{Tags {"LightMode"="ForwardBase"}Cull BackCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"sampler2D _MainTex; float4 _MainTex_ST;half3 _MainColor;half3 _ShadowColor;half _ShadowRange;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2; };v2f vert(a2v v){v2f o;UNITY_INITIALIZE_OUTPUT(v2f, o);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.pos = UnityObjectToClipPos(v.vertex);return o;}half4 frag(v2f i) : SV_TARGET {half4 col = 1;half4 mainTex = tex2D(_MainTex, i.uv);half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);half3 worldNormal = normalize(i.worldNormal);half3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half3 diffuse = halfLambert > _ShadowRange ? _MainColor : _ShadowColor;diffuse *= mainTex;col.rgb = _LightColor0 * diffuse;return col;}ENDCG}Pass{//描邊,參考上一篇}} } 實現明暗邊界分明的光照,并且單獨設置明面和暗面的顏色來區分色調smoothstep柔化明暗邊界
現在我們希望能夠對明暗邊界的變化做一些柔化,讓風格往厚涂的風格靠一些,這樣可以跟更容易地跟一些非賽璐璐風格的場景做融合。這里我們使用smoothstep函數實現這個效果。這個函數可以在根據輸入數據,計算一個范圍在0到1區間的平滑過渡曲線。通過這個函數的結果對明面和暗面的顏色進行插值,來實現明暗邊界的軟硬控制。wiki百科鏈接
smoothstep(0,0.7,x)對代碼進行如下修改
half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half ramp = smoothstep(0, _ShadowSmooth, halfLambert - _ShadowRange);half3 diffuse = lerp(_ShadowColor, _MainColor, ramp);使用smoothstep函數對明暗分界的軟硬進行控制Ramp貼圖
還有一個做法是通過采樣Ramp貼圖來實現對色階和明暗邊界的控制。可以看成是用標準光照的結果為UV,采樣一張用作顏色映射表的貼圖,通過這張貼圖控制光照計算的結果。制作如下圖的ramp貼圖,然后對代碼進行修改。
half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half ramp = tex2D(_rampTex, float2(saturate(halfLambert - _ShadowRange), 0.5)).r;half3 diffuse = lerp(_ShadowColor,_MainColor, ramp);Ramp貼圖,從左向右對應光照從0到1的范圍。注意最左邊光照應該最弱的部分反而設置的比較亮使用Ramp貼圖對色階進行控制,在暗面形成了一個有點亮的反光Ramp貼圖能夠更容易的定義多個色階,不過貼圖需要自己制作。貼圖制作起來并不復雜,也可以通過編寫編輯器工具來生成。這里的貼圖是使用Toony Colors Pro插件生成的。
在制作Ramp貼圖的時候,最左邊光照應該最弱的部分反而設置的比較亮。這個是為了制作出暗面的反光效果。在素描上面有個明暗五調子的知識,在物體邊緣的部分會有一圈反光,所以物體的邊緣不會是最暗的部分。鏈接
素描關系上的三大面五大調子在圖形學上,也有對應的概念,稱為菲涅耳(fresnel)現象。我覺得這體現了一個非常有趣的觀點,無論是圖形學使用光照模型對現實世界的物理現象進行模擬,還是畫家們通過觀察現實世界總結出的美術理論,最終都是殊途同歸的。一個使用公式進行繪圖,一個使用畫筆進行繪圖罷了。
在《偶像大師》系列,也使用了Ramp貼圖來實現色階和色調的控制
左上的貼圖是《偶像大師1》的ramp貼圖,左下是《偶像大師2》的ramp貼圖《偶像大師1》(左)和《偶像大師2》(右)的畫面變化因為本篇的篇幅有點太長了,有關邊緣反光部分的討論,放在下一篇再詳細討論。
ilmTexture貼圖的實現
《GUILTY GEAR Xrd》中使用稱為ilmTexture的貼圖對角色明暗區域實現手繪風格的控制。其中綠通道控制漫反射的陰影閾值,紅通道控制高光強度,藍通道控制高光范圍。這里跟據這個原理,完成一個最簡單的實現。卡通渲染不像Lambert等光照模型有統一的公式,如果要更進一步的表現還需要根據畫面需求做各種trick。比如專欄標題的展示圖片還添加了陰影殘留和陰影色調分離的效果,這方面就由大家自己發揮吧。
Shader "Unlit/CelRenderFull" {Properties{_MainTex ("MainTex", 2D) = "white" {}_IlmTex ("IlmTex", 2D) = "white" {}[Space(20)]_MainColor("Main Color", Color) = (1,1,1)_ShadowColor ("Shadow Color", Color) = (0.7, 0.7, 0.7)_ShadowSmooth("Shadow Smooth", Range(0, 0.03)) = 0.002_ShadowRange ("Shadow Range", Range(0, 1)) = 0.6[Space(20)]_SpecularColor("Specular Color", Color) = (1,1,1)_SpecularRange ("Specular Range", Range(0, 1)) = 0.9_SpecularMulti ("Specular Multi", Range(0, 1)) = 0.4_SpecularGloss("Sprecular Gloss", Range(0.001, 8)) = 4[Space(20)]_OutlineWidth ("Outline Width", Range(0, 1)) = 0.24_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)}SubShader{Pass{Tags { "LightMode"="ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"sampler2D _MainTex; float4 _MainTex_ST;sampler2D _IlmTex; float4 _IlmTex_ST;half3 _MainColor;half3 _ShadowColor;half _ShadowSmooth;half _ShadowRange;half3 _SpecularColor;half _SpecularRange;half _SpecularMulti;half _SpecularGloss;struct a2v{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2; };v2f vert (a2v v){v2f o = (v2f)0;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;return o;}half4 frag (v2f i) : SV_Target{half4 col = 0;half4 mainTex = tex2D (_MainTex, i.uv);half4 ilmTex = tex2D (_IlmTex, i.uv);half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);half3 worldNormal = normalize(i.worldNormal);half3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);half3 diffuse = 0;half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half threshold = (halfLambert + ilmTex.g) * 0.5;half ramp = saturate(_ShadowRange - threshold); ramp = smoothstep(0, _ShadowSmooth, ramp);diffuse = lerp(_MainColor, _ShadowColor, ramp);diffuse *= mainTex.rgb;half3 specular = 0;half3 halfDir = normalize(worldLightDir + viewDir);half NdotH = max(0, dot(worldNormal, halfDir));half SpecularSize = pow(NdotH, _SpecularGloss);half specularMask = ilmTex.b;if (SpecularSize >= 1 - specularMask * _SpecularRange){specular = _SpecularMulti * (ilmTex.r) * _SpecularColor;}col.rgb = (diffuse + specular) * _LightColor0.rgb;return col;}ENDCG}Pass{//描邊,參考上一篇}}FallBack Off }上色后的效果總結
在本節中,介紹了卡通渲染和寫實風格渲染的主要區別和一些實現方法。在下一個章節中,將會討論卡通渲染的邊緣光、后處理、以及和PBR進行融合的方向等。
分享
在寫這篇文章的ramp貼圖部分的時候,想起了以前在OpenGPU論壇看到的Trace大佬翻譯的《偶像大師》相關文章。回去找的時候發現論壇已經沒了,以前上面的很多文章也丟失了。覺得還是有點傷感的,我最早是看著OpenGPU上面的Trace翻譯的科普文章,開始對渲染產生興趣的。從這一點上來說,我非常感謝在網絡上科普圖形學知識的前輩們,給了我們非常好的學習途徑。后來發現因為我一直有備份ppt的習慣,讓我保留了這幾篇文章的備份。雖然都很老了,但是覺得不應該消失在網絡上面。所以我把這兩篇《偶像大師》的文章上傳到百度網盤里了,感興趣的小伙伴可以去下載。 那么我們下一篇見。
百度網盤 提取碼:2whi。
附:參考鏈接
【翻譯】西川善司「實驗做出的游戲圖形」「GUILTY GEAR Xrd -SIGN-」中實現的「純卡通動畫的實時3D圖形」的秘密,前篇(1)
【翻譯】西川善司「實驗做出的游戲圖形」「GUILTY GEAR Xrd -SIGN-」中實現的「純卡通動畫的實時3D圖形」的秘密,前篇(2)
【翻譯】西川善司的「實驗做出的游戲圖形」「GUILTY GEAR Xrd -SIGN-」中實現的「純卡通動畫的實時3D圖形」的秘密,后篇
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的寄存器分配图着色_【02】从零开始的卡通渲染-着色篇1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】可遍历的数据类型有哪些?
- 下一篇: 【Python】集合的交、并、补、差集怎