谎言 发表于 2020-11-23 22:05

UNITY崩坏3角色渲染实践

最近二次元手游,卡通渲染都挺火的。虽然公司没开这类型项目,但是渲染来玩一下也好,原理都是一样,比较简单。
在日式卡通中,《罪恶装备》、《崩坏3》的效果都很不错,都是几年前的产品,两者的渲染方式是类似的。
这里用的是《崩坏3》的手游模型,仅是学习,侵权必删。
shader贴图
崩坏3主要用到两张贴图:albedotexilmtex,其中ilmtex,R通道表示高光的强弱,G通道表示阴影区域,B通道控制高光区域的大小(大概)
基础模型
我们先看看崩坏3的基础模型。崩坏3模型设计,贴图配色,法线都做的很好,如果你们游戏模型效果不好,可能你们的基础模型做的比较差。
基础模型
轮廓线
轮廓线的制作方式有很多,网上都很齐全,这里就不多说了,我这里采用把backfaces方式
o.pos = UnityObjectToClipPos(v.vertex.xyz);
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float2 offset = TransformViewToProjection(normal.xy);
o.pos.xy += offset * o.pos.z * _Outline;
拓展的话,可以用模型顶点色通道控制勾边粗细,控制z-offset去掉多余勾边,效果如下
轮廓线
阴影
卡通渲染的漫反射,用的是halfLambert,然后smoothstep控制暗面与亮面的过渡平滑程度,加上ilm B通道中的阴影范围
float wrapLambert = (ndotl * 0.5 + 0.5) + ilmTex.g;
float shadowStep = saturate(smoothstep(_ShadeEdge0, _ShadeEdge1, wrapLambert )); 阴影
但是脸部阴影比较脏,所以我们用wrap diffuse将脸部因为halfLambert产生的阴影去掉
float wrapLambert = (ndotl * _WrapDiffuse + 1 - _WrapDiffuse) + ilmTex.g;再加上自身阴影
float wrapLambert = (ndotl * _WrapDiffuse + 1 - _WrapDiffuse) + ilmTex.g;
float shadow = SHADOW_ATTENUATION(i);
float shadowStep = saturate(smoothstep(_ShadeEdge0, _ShadeEdge1, wrapLambert * shadow)); 阴影部分+环境光+自身贴图效果
阴影部分效果
高光
高光现在采用Blinn-Phong光照模型,再加上step 函数控制明暗
float3 halfVector = normalize(_WorldSpaceLightPos0 + viewDir);
float ndoth = dot(normal, halfVector);
float specularIntensity = pow(ndoth, _Glossiness) * ilmTex.r;高光
边缘光
主要用smoothstep函数风格化,再乘以光照方向
float rimDot = pow(1 - dot(viewDir, normal), _RimThreshold);
float rimIntensity = rimDot * ndotl;
rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);
half4 rim = rimIntensity * _RimColor; 边缘光
然后整体效果加起来,如下
整体效果

整体效果
https://www.zhihu.com/video/1243954287291822080
不足
头发shader,可以改为各向异性shader
高光部分,如果我做的话,还是考虑用金属度,粗糙度贴图,再风格化高光
皮肤部分,加上sss效果,例如用PreIntergated贴图渲染
附上全部代码
Shader "Custom/ToonShader"
{
        Properties
        {
                _Color("Color", Color) = (1,1,1,1)
                _AmbientColor("Ambient Color", Color) = (0.4,0.4,0.4,1)
                _MainTex("Main Texture", 2D) = "white" {}
                _IlmTex("Ilm Texture", 2D) = "black" {}
                _WrapDiffuse("WrapDiffuse",Range(0, 1)) = 0.5
                _ShadeEdge0("ShadeEdge0",Range(0, 1)) = 0.925
                _ShadeEdge1("ShadeEdge1",Range(0, 1)) = 1
                _SpecularColor("Specular Color", Color) = (0.9,0.9,0.9,1)
                _SpecularRange("SpecularRange",Range(0, 1)) = 0.15
                _Glossiness("Glossiness", Range(0.01, 256)) = 8
                _RimColor("Rim Color", Color) = (1,1,1,1)
                _RimAmount("Rim Amount", Range(0, 1)) = 0.042
                _RimThreshold("Rim Threshold", Range(0, 10)) =6
                _OutlineColor("Outline Color", Color) = (1,1,1,1)
                _Outline("Outline Width", Range(0, 5)) = 0.1
                _EmisstionColor("Emisstion Color", Color) = (0,0,0,0)
        }
        SubShader
        {
                Pass
                {
                        Tags
                        {
                                "LightMode" = "ForwardBase"
                                "PassFlags" = "OnlyDirectional"
                        }

                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag
                        #pragma multi_compile_fwdbase
                       
                        #include "UnityCG.cginc"
                        #include "Lighting.cginc"
                        #include "AutoLight.cginc"

                        struct appdata
                        {
                                float4 vertex : POSITION;                               
                                float4 uv : TEXCOORD0;
                                float3 normal : NORMAL;
                        };

                        struct v2f
                        {
                                float4 pos : SV_POSITION;
                                float3 worldNormal : NORMAL;
                                float2 uv : TEXCOORD0;
                                float3 viewDir : TEXCOORD1;       
                                SHADOW_COORDS(2)
                        };

                        sampler2D _MainTex;
                        sampler2D _IlmTex;
                        float4 _MainTex_ST;
                       
                        v2f vert (appdata v)
                        {
                                v2f o;
                                o.pos = UnityObjectToClipPos(v.vertex);
                                o.worldNormal = UnityObjectToWorldNormal(v.normal);               
                                o.viewDir = WorldSpaceViewDir(v.vertex);
                                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                                TRANSFER_SHADOW(o)
                                return o;
                        }
                       
                        half4 _Color;
                        half4 _AmbientColor;
                        half4 _SpecularColor;
                        float _WrapDiffuse, _ShadeEdge0, _ShadeEdge1;
                        float _Glossiness, _SpecularRange;
                        half4 _RimColor;
                        float _RimAmount;
                        float _RimThreshold;
                        half4 _EmisstionColor;

                        half4 frag (v2f i) : SV_Target
                        {
                                half4 color = tex2D(_MainTex, i.uv) * _Color;
                                half4 ilmTex = tex2D(_IlmTex, i.uv);
                                float3 normal = normalize(i.worldNormal);
                                float3 viewDir = normalize(i.viewDir);
                                float ndotl = max(0,dot(_WorldSpaceLightPos0, normal));

                                float wrapLambert = (ndotl * _WrapDiffuse + 1 - _WrapDiffuse) + ilmTex.g;
                                float shadow = SHADOW_ATTENUATION(i);
                                float shadowStep = saturate(smoothstep(_ShadeEdge0, _ShadeEdge1, wrapLambert * shadow));
                                half4 diffuse = lerp(_AmbientColor,_LightColor0, shadowStep);//float4(ShadeSH9(float4(normal, 1)), 1)

                                float3 halfVector = normalize(_WorldSpaceLightPos0 + viewDir);
                                float ndoth = dot(normal, halfVector);
                                float specularIntensity = pow(ndoth, _Glossiness) * ilmTex.r;
                                float specularRange = step(_SpecularRange, specularIntensity + ilmTex.b);
                                half4 specular = specularRange * _SpecularColor;

                                float rimDot = pow(1 - dot(viewDir, normal), _RimThreshold);
                                float rimIntensity = rimDot * ndotl;
                                rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);
                                half4 rim = rimIntensity * _RimColor;

                                return (diffuse + rim + specular + _EmisstionColor * color.a) * color;
                        }
                        ENDCG
                }
                Pass
                {
                        Tags{ "LightMode" = "ForwardBase" }

                        Cull Front
                        Zwrite On
                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag
                        #include "UnityCG.cginc"

                        half _Outline;
                        half4 _OutlineColor;

                        struct a2v
                        {
                                float4 vertex : POSITION;
                                float3 normal : NORMAL;
                                float4 vertColor : COLOR;
                        };

                        struct v2f
                        {
                                float4 pos : SV_POSITION;
                        };


                        v2f vert(a2v v)
                        {
                                v2f o;
                                UNITY_INITIALIZE_OUTPUT(v2f, o);
                                o.pos = UnityObjectToClipPos(v.vertex);
                                float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
                                float2 offset = TransformViewToProjection(normal.xy);
                                o.pos.xy += offset * o.pos.z * _Outline;
                                return o;
                        }

                        half4 frag(v2f i) : SV_TARGET
                        {
                                return _OutlineColor;
                        }
                        ENDCG
                }
        }
        Fallback "Legacy Shaders/VertexLit"
}
页: [1]
查看完整版本: UNITY崩坏3角色渲染实践