人物3渲2学习总结

描边

法线外扩法

中文一般称为“法线外扩法”、“背面膨胀法”,日文资料中会称作「背面法」,它们指的是同一个技术

解决方式

第一个Pass:正常打光和绘制
第二个Pass:

Vertex Shader阶段将顶点向顶点法线方向移动一定距离
Rasterization阶段将Cull Mode设置为Cull Front

	 Pass
        {
        Cull Front
		ZWrite On
		ColorMask RGB
		Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata {
		        float4 vertex : POSITION;
		        float3 normal : NORMAL;
		        float4 texCoord : TEXCOORD0;
		        //float4 vertexColor : COLOR;
	        };
	        struct v2f {
	        	float4 pos : POSITION;
	        	float4 color : COLOR;
	        	float4 tex : TEXCOORD0;
	        };
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            float _Outline;
            float4 _OutlineColor;
            v2f vert(appdata v) {
		        // just make a copy of incoming vertex data but scaled according to normal direction
	        	v2f o;
	        	o.pos = UnityObjectToClipPos(v.vertex);
		        float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
		        float2 offset = TransformViewToProjection(norm.xy);
	        	o.pos.xy += offset * _Outline;
	        	o.tex = v.texCoord;
		
	        	o.color = _OutlineColor;
	        	return o;
	        }
            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }

缺点

  1. 描边出现断裂,或者出现了看起来很奇怪的描边
    在这里插入图片描述
    解决方案:用法线刷修改顶点让顶点平滑,出现这个问题的原因是每个面的法线都是垂直于平面的,所以在边角处就连接不上了。一般这种问题的解决方案是另外存储一套平滑法线。具体做法是计算与该点位置相同的所有点的法线的平均值并存储在顶点上。
  2. 描边线条控制
    想眼睛,头发这种细节描边需要修正的
    头发,由于模型的复杂性,在使用法线外扩法时往往会出现不太想要的描边。为了防止出现这些不受欢迎的描边,可以给每个顶点设置一个深度值、将顶点埋进里面从而被遮挡住。《罪恶装备Xrd》里就将这个信息写入了顶点色B通道中。
    细节:由于法线外扩法是基于模型的,所以实际上对线条还是有较高的自由度来控制。如果想要做到有粗细变化的描边,可以参考罪恶装备Xrd中将粗细变化的数值写入顶点色当中。实际上就是一个外扩系数:数值越大,越向外扩,线条越粗。至于线条的颜色,如果顶点色或者其他UV通道够用,可以将描边颜色写进顶点里面。然后相乘。

菲涅尔方程

解决方法

N*V
适用于球形物体的描边

问题

但平面时不好控制轮廓线

顶点着色器

blender 的Freestyle

解决方法

他们首先通过预处理的方式提取那些感兴趣的边缘,保存到额外的Mesh资源上。在渲染时通过Geometry Shader将这些边缘绘制出来。
出于性能跟兼容性问题还是尽量避免使用Geometry Shader的好。但是这种思路还是非常不错的,因为这种方式对线条的控制也有很高的自由度,并且相比起法线外扩法可以创造更多细节。而且目前Compute Shader已经相当成熟且普及了,所以考虑用Compute Shader+软光栅的方式说不定也是一条走得通的道路。

问题

颜色不能自定义,塞到顶点色里面,而且性价比不高。

基于图像空间的线条检测

参考文章
通过检测场景图像中 Normal 和 Depth 的不连续性,我们可以获得细节较为丰富的勾线。无论场景的复杂性如何,这种方法的性能都是恒定的,我们还添加了对勾线颜色的色相、明度、饱和度的调整,使勾线更为自然。
在这里插入图片描述

常用的算法是Sobel算子
缺点则是较难控制勾线的宽度,如果我们想实现距离相关的线宽,我们只能在几个像素的范围内调整它,因此基于图像的方法主要适用于场景线条渲染,对于靠近摄像头很近的物体,我们最好使用 Backface 的方法。
在这里插入图片描述
看这里实现方式

对于后处理进行canny卷积

我卷失败了,没卷出来以后有时间再搞。
和上一方法有异曲同工之处。
转化为灰度图进行canny卷积

打光

皮肤和衣服部分

多通道Ramp的shading方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
采用多层上色,我的效果好拉,没官方好看。

解释

RampLayerSoftness越大越软阴影,小则硬
我没将_TintBase 叠加上去
我没加阴影是因为,当阴影加上后会映在人身上看起来就不二次元了。

代码
Properties :
		[Header(Tint Base)]
		_DiffuseRamp ("DiffuseRamp", 2D) = "Black" {}
        _SpecularRamp ("SpecularRamp", 2D) = "Black" {}
        _TintBase ("Tint Base", COLOR) = (0,0,0,1)
        [Header(RampLayer1)]
        _RampLayerOffset1 ("RampLayerOffset1", Range(-0.5,0.5)) = 0.5
        _TintLayer1 ("Tint_Layer1", COLOR) = (0,0,0,1)
        [Header(RampLayer2)]
        _RampLayerOffset2 ("RampLayerOffset2", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness2 ("RampLayerSoftness2", Range(0,1)) = 0.5
        __TintLayer2 ("Tint Layer2", COLOR) = (0,0,0,1)
        [Header(RampLayer3)]
        _RampLayerOffset3 ("RampLayerOffset3", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness3 ("RampLayerSoftness3", Range(0,1)) = 0.5
        __TintLayer3 ("Tint Layer3", COLOR) = (0,0,0,1)
        [Header(Specular)]
         _Shineness ("Shineness", Float) = 0.5
         _SpecularColor ("Specular Color", COLOR) = (0,0,0,1)
         _SpecularIntensity ("SpecularIntensity", Float) = 1
         _SpecularSmooth ("Specular Smooth", Float) = 1
Pass :
			sampler2D _DiffuseRamp;
        	sampler2D _SpecularRamp;
            float4 _TintBase;
            float4 __TintLayer1;
            float _RampLayerOffset1;

            float4 __TintLayer2;
            float _RampLayerOffset2;
            float _RampLayerSoftness2;
            
            float4 __TintLayer3;
            float _RampLayerOffset3;
            float _RampLayerSoftness3;

            float _Shineness;
            float _SpecularIntensity;
            float4 _SpecularColor;
            float _SpecularSmooth;
frag :
				half atten = LIGHT_ATTENUATION (i);
                half3 base_col = tex2D(_MainTex, i.uv).xyz;
                half3 normal_world= normalize(i.normalDir);
                half3 tangent_world= normalize(i.tangentDir);
                half3 binormal_world= normalize(i.binormalDir);
                half3 pos_world= normalize(i.pos_world);
                half3 normal_data = UnpackNormal(tex2D(_Normal,i.uv));
                half3x3 TBN = half3x3 (tangent_world,binormal_world,normal_world);
                
                normal_world = mul(normal_data,TBN);
                half3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-pos_world);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射
                half NdotL =  max(0,dot(normal_world,lightDir));
                half half_lambert = (NdotL+1.0)*0.5;
                //half half_lambert = half_lambert*ao //本来要乘上AO因为素材没有算了

                half3 tint_Base_color1 = base_col;

                //第一层ramp
                half2 uv_ramp1 = half2(half_lambert+_RampLayerOffset1,0.5);
                half toon_diffuse1 = tex2D(_DiffuseRamp,uv_ramp1).r;
                half3 tint_color1 = lerp(half3(1,1,1),__TintLayer1,toon_diffuse1*__TintLayer1.a);
                
                //第二层ramp
                half2 uv_ramp2 = half2(half_lambert+_RampLayerOffset2,1-i.VertexColor.g+_RampLayerSoftness2);//1-i.VertexColor.g可以用_RampLayerSoftness2代替越大越柔和
                half toon_diffuse2 = tex2D(_DiffuseRamp,uv_ramp2).g;
                half3 tint_color2 = lerp(half3(1,1,1),__TintLayer2,toon_diffuse2*__TintLayer2.a);
                
                //第三层ramp
                half2 uv_ramp3 = half2(half_lambert+_RampLayerOffset3,1-i.VertexColor.b+_RampLayerSoftness3);//1-i.VertexColor.b可以用_RampLayerSoftness3代替越大越柔和
                half toon_diffuse3 = tex2D(_DiffuseRamp,uv_ramp3).b;
                half3 tint_color3 = lerp(half3(1,1,1),__TintLayer3,toon_diffuse3*__TintLayer3.a);
                
                half3 final_diffuse = tint_Base_color1 * tint_color1 *tint_color2 *tint_color3  ;
                //高光
                //half3 half_R = normalize(lightDir+ViewDir);
                half3 R = normalize(reflect(-lightDir,normal_world));
                half specular_term = max(pow(dot(R,ViewDir),_Shineness),0.0001);//本来要乘上AO因为素材没有算了
                specular_term = smoothstep(0.5-_SpecularSmooth*0.5,0.5+_SpecularSmooth*0.5,specular_term);//风格化高光
            
                half3 final_specular = base_col  * specular_term *_SpecularColor *atten *_LightColor0.xyz *_SpecularIntensity  ;
           		half3 final_col = final_diffuse+final_specular ;

卖家秀

在这里插入图片描述

买家秀

在这里插入图片描述

边缘光,环境光

在这里插入图片描述

解释

_MatCap是我自己加上去的,有点多余

实现
Properties :
		_MatCap ("MatCap", 2D) = "Black" {}
        _Envmap ("Envmap", Cube) = "Black" {}
        [Header(Rim)]
        _RimMin("RimMin", Range(-2,2)) = 0.5
        _RimMax("RimMax", Range(-2,2)) = 0.5
        _Roughness("Roughness", Float) = 0
        _EnvIntensity("Env Intensity", Float) = 0
pass :
            sampler2D _MatCap;
            samplerCUBE _Envmap;
            float _RimMax;
            float _RimMin;
            float _Roughness;
            float _EnvIntensity;
            float4 _Envmap_HDR;
frag :
				//边缘光,环境光
                
                half fresnel = 1- dot(ViewDir,normal_world);
                half rim = smoothstep(_RimMin,_RimMax,fresnel);//本来要乘上AO因为素材没有算了
                half3 mc=tex2D(_MatCap,fresnel.xx);
                half3 r = reflect(-ViewDir,normal_world);
                half roughness = lerp(0,0.95,saturate(_Roughness));
                roughness = roughness * (1.7-0.7*roughness );
                half mip_level = roughness * 6.0;
                half4 color_cubemap = texCUBElod(_Envmap,half4(r,mip_level));
                half3 color_env = DecodeHDR(color_cubemap,_Envmap_HDR);
                half3 final_env =color_env * rim * _EnvIntensity;

总体代码

Character_Skin.shader

Shader "Character/Skin"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _Normal("Normal",2D) = "bump" {}
        _DiffuseRamp ("DiffuseRamp", 2D) = "Black" {}
        _Envmap ("Envmap", Cube) = "Black" {}


        [Header(Tint Base)]
        _TintBase ("Tint Base", COLOR) = (0,0,0,1)
        [Header(RampLayer1)]
        _RampLayerOffset1 ("RampLayerOffset1", Range(-0.5,0.5)) = 0.5
        _TintLayer1 ("Tint_Layer1", COLOR) = (0,0,0,1)
        [Header(RampLayer2)]
        _RampLayerOffset2 ("RampLayerOffset2", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness2 ("RampLayerSoftness2", Range(0,1)) = 0.5
        __TintLayer2 ("Tint Layer2", COLOR) = (0,0,0,1)
        [Header(RampLayer3)]
        _RampLayerOffset3 ("RampLayerOffset3", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness3 ("RampLayerSoftness3", Range(0,1)) = 0.5
        __TintLayer3 ("Tint Layer3", COLOR) = (0,0,0,1)
        [Header(Specular)]
         _Shineness ("Shineness", Float) = 0.5
         _SpecularColor ("Specular Color", COLOR) = (0,0,0,1)
         _SpecularIntensity ("SpecularIntensity", Float) = 1
         _SpecularSmooth ("Specular Smooth", Float) = 1


        [Header(Rim)]
        _RimMin("RimMin", Range(-2,2)) = 0.5
        _RimMax("RimMax", Range(-2,2)) = 0.5
        _Roughness("Roughness", Float) = 0
        _EnvIntensity("Env Intensity", Float) = 0

        [Header(Outline)]
        _Outline ("Outline", Float) = 0.6
        _OutlineColor ("OutlineColor", COLOR) = (0,0,0,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        
        Pass
        {
            Name "Skin"
            Tags { "LightMode" = "ForwardBase" }
            
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            // make fog work
            #pragma multi_compile_fog
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float4 normal :NORMAL;
                float4 tangent:TANGENT;
                float4 VertexColor:COLOR;
            };

            struct v2f
            {
                
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;

                float3 normalDir: TEXCOORD1;
                float3 tangentDir: TEXCOORD2;
                float3 binormalDir: TEXCOORD3;
                float4 VertexColor: TEXCOORD4;
                float3 pos_world: TEXCOORD5;
                LIGHTING_COORDS(6,7)
            };

            sampler2D _MainTex;
            sampler2D _Normal;
            sampler2D _DiffuseRamp;
            samplerCUBE _Envmap;

            float4 _LightColor0;

            float4 _MainTex_ST;
            float4 _TintBase;
            float4 __TintLayer1;
            float _RampLayerOffset1;

            float4 __TintLayer2;
            float _RampLayerOffset2;
            float _RampLayerSoftness2;
            
            float4 __TintLayer3;
            float _RampLayerOffset3;
            float _RampLayerSoftness3;

            float _Shineness;
            float _SpecularIntensity;
            float4 _SpecularColor;
            float _SpecularSmooth;

            float _RimMax;
            float _RimMin;
            float _Roughness;
            float _EnvIntensity;
            float4 _Envmap_HDR;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord0;
                o.normalDir=normalize( mul(unity_ObjectToWorld,v.normal).xyz);
                o.tangentDir=normalize( mul(unity_ObjectToWorld,v.tangent).xyz);
                o.pos_world=normalize( mul(unity_ObjectToWorld,v.vertex).xyz);
                o.binormalDir=normalize( cross(o.normalDir,o.tangentDir)*v.tangent.w);
                o.VertexColor = v.VertexColor;
                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;   
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half atten = LIGHT_ATTENUATION (i);
                half3 base_col = tex2D(_MainTex, i.uv).xyz;
                half3 normal_world= normalize(i.normalDir);
                half3 tangent_world= normalize(i.tangentDir);
                half3 binormal_world= normalize(i.binormalDir);
                half3 pos_world= normalize(i.pos_world);
                half3 normal_data = UnpackNormal(tex2D(_Normal,i.uv));
                half3x3 TBN = half3x3 (tangent_world,binormal_world,normal_world);
                
                normal_world = mul(normal_data,TBN);
                half3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-pos_world);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射
                half NdotL =  max(0,dot(normal_world,lightDir));
                half half_lambert = (NdotL+1.0)*0.5;
                //half half_lambert = half_lambert*ao //本来要乘上AO因为素材没有算了

                half3 tint_Base_color1 = base_col;

                //第一层ramp
                half2 uv_ramp1 = half2(half_lambert+_RampLayerOffset1,0.5);
                half toon_diffuse1 = tex2D(_DiffuseRamp,uv_ramp1).r;
                half3 tint_color1 = lerp(half3(1,1,1),__TintLayer1,toon_diffuse1*__TintLayer1.a);
                
                //第二层ramp
                half2 uv_ramp2 = half2(half_lambert+_RampLayerOffset2,1-i.VertexColor.g+_RampLayerSoftness2);//1-i.VertexColor.g可以用_RampLayerSoftness2代替越大越柔和
                half toon_diffuse2 = tex2D(_DiffuseRamp,uv_ramp2).g;
                half3 tint_color2 = lerp(half3(1,1,1),__TintLayer2,toon_diffuse2*__TintLayer2.a);
                
                //第三层ramp
                half2 uv_ramp3 = half2(half_lambert+_RampLayerOffset3,1-i.VertexColor.b+_RampLayerSoftness3);//1-i.VertexColor.b可以用_RampLayerSoftness3代替越大越柔和
                half toon_diffuse3 = tex2D(_DiffuseRamp,uv_ramp3).b;
                half3 tint_color3 = lerp(half3(1,1,1),__TintLayer3,toon_diffuse3*__TintLayer3.a);
                
                half3 final_diffuse = tint_Base_color1 * tint_color1 *tint_color2 *tint_color3  ;
                //高光
                //half3 half_R = normalize(lightDir+ViewDir);
                half3 R = normalize(reflect(-lightDir,normal_world));
                half specular_term = max(pow(dot(R,ViewDir),_Shineness),0.0001);//本来要乘上AO因为素材没有算了
                specular_term = smoothstep(0.5-_SpecularSmooth*0.5,0.5+_SpecularSmooth*0.5,specular_term);//风格化高光
            
                half3 final_specular = base_col  * specular_term *_SpecularColor *atten *_LightColor0.xyz *_SpecularIntensity  ;
                
                //边缘光,环境光
                half NDL = NdotL>0 ? 1:0;
                half fresnel = 1- dot(ViewDir,normal_world);
                half rim = smoothstep(_RimMin,_RimMax,fresnel);//本来要乘上AO因为素材没有算了
                rim = rim * NDL;

                half3 r = reflect(-ViewDir,normal_world);
                half roughness = lerp(0,0.95,saturate(_Roughness));
                roughness = roughness * (1.7-0.7*roughness );
                half mip_level = roughness * 6.0;
                half4 color_cubemap = texCUBElod(_Envmap,half4(r,mip_level));
                half3 color_env = DecodeHDR(color_cubemap,_Envmap_HDR);
                half3 final_env =color_env * rim * _EnvIntensity    ;

                half3 final_col = final_diffuse+final_specular + final_env;
                return half4(final_col,1);
                
            }
            ENDCG
        }
        Pass
        {
        Name "OUTLINE"
        Cull Front
		ZWrite On
		ColorMask RGB
		Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata {
		        float4 vertex : POSITION;
		        float3 normal : NORMAL;
		        float4 texCoord : TEXCOORD0;
		        float4 vertexColor : COLOR;
	        };
	        struct v2f {
	        	float4 pos : POSITION;
	        	float4 color : COLOR;
	        	float4 tex : TEXCOORD0;
	        };
            sampler2D _MainTex;
            float _Outline;
            float4 _OutlineColor;
            v2f vert(appdata v) {
		        // just make a copy of incoming vertex data but scaled according to normal direction
	        	v2f o;
	        	o.pos = UnityObjectToClipPos(v.vertex);
		        float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
		        float2 offset = TransformViewToProjection(norm.xy);
		       // float2 offset = (norm.xy);
	        	o.pos.xy += offset * _Outline*0.0001;
	        	o.tex = v.texCoord;
	        	o.color = v.vertexColor;
	        	return o;
	        }
            fixed4 frag (v2f i) : SV_Target
            {
                half4 base_col = tex2D(_MainTex,i.tex);
                return _OutlineColor*base_col;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}


眼睛

眼睛折射效果,我的模型不需要进行眼球移动,也不适合做这个,所以没弄
其中uworld和vworld是指UV线在世界空间下在方向。
在这里插入图片描述
以下也没搞清除
在这里插入图片描述

关于头发遮挡眼睛,解决办法

模板测试

在进行眼睛渲染的Pass时,将模板值设置为某一固定值:

Stencil {
   Ref 1
   Comp Always
   Pass Replace
   Fail Replace
}

头发等遮挡部分

Stencil {
     Ref 1
     Comp NotEqual
     Pass Keep
     Fail Keep
}

在这里插入图片描述

将遮挡脸部透明部分写入顶点色A通道

将头发渲染队列改为Transparent
让最后输出的颜色的A等于顶点色A

Eye.shader

Shader "Character/Eye"
{
    Properties
    {
       _MainTex ("MainTex", 2D) = "white" {}
       _Normal("Normal",2D) = "bump" {}
       

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        
        Pass
        {
          Stencil {
             Ref 1
             Comp Always
             Pass Replace
             Fail Replace
          }
           Name "Eye"
            Tags { "LightMode" = "ForwardBase" }
            
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            // make fog work
            #pragma multi_compile_fog
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float4 normal :NORMAL;
                float4 tangent:TANGENT;
                float4 VertexColor:COLOR;
            };

            struct v2f
            {
                
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;

                float3 normalDir: TEXCOORD1;
                float3 tangentDir: TEXCOORD2;
                float3 binormalDir: TEXCOORD3;
                float4 VertexColor: TEXCOORD4;
                float3 pos_world: TEXCOORD5;
                LIGHTING_COORDS(6,7)
            };

            sampler2D _MainTex;
            sampler2D _Normal;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord0;
                o.normalDir=normalize( mul(unity_ObjectToWorld,v.normal).xyz);
                o.tangentDir=normalize( mul(unity_ObjectToWorld,v.tangent).xyz);
                o.pos_world=normalize( mul(unity_ObjectToWorld,v.vertex).xyz);
                o.binormalDir=normalize( cross(o.normalDir,o.tangentDir)*v.tangent.w);
                o.VertexColor = v.VertexColor;
                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;   
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 base_col = tex2D(_MainTex, i.uv);
                half3 normal_world= normalize(i.normalDir);
                half3 tangent_world= normalize(i.tangentDir);
                half3 binormal_world= normalize(i.binormalDir);
                half3 normal_data = UnpackNormal(tex2D(_Normal,i.uv));
                half3x3 TBN = half3x3 (tangent_world,binormal_world,normal_world);
                normal_world = mul(TBN,normal_world);
                half3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-i.pos_world);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                half fresnel = 1- saturate(dot(normal_world ,ViewDir ));
                return base_col;
               // return half4(normal_world,1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

头发

各项异性高光
原理见移动人物渲染

使用shiftmap增强质感

在这里插入图片描述

half2 uv_shift = i.uv * _ShiftRamp_ST.xy + _ShiftRamp_ST.zw;
half3 shift_col = tex2D(_ShiftRamp,uv_shift);
binormal_world = normalize( binormal_world + (shift_col+_ShiftOffset)* normal_world);

_ShiftRamp_ST.x调节质感,_ShiftOffset调节头发的上下偏移

初版

通过shininess调节柔和度
调大shininess让过度变硬

half StrandSpecular(half3 T, half3 V, half3 L, half exponent)
{
     half3 H = normalize(L + V);
     half dotTH = dot(T, H);
     half sinTH = sqrt(1 - dotTH * dotTH);
     half dirAtten = smoothstep(-1.0, 0.0, dotTH);
     return dirAtten * pow(sinTH, exponent);
}
half3 final_specular =    StrandSpecular(binormal_world,lightDir,ViewDir,_Shineness)*_SpecularColor;

改进

把柔和度调整到0,1中通过shininess值

half NdotV = max(0.0 ,dot(normal_world , ViewDir));
half3 H = normalize(lightDir+ViewDir);
half NdotH = dot(normal_world ,H );
half TdotH = dot(tangent_world ,H);
half BdotH = dot(binormal_world ,H);

BdotH /=_Shineness;
half spec_term =exp(-(TdotH*TdotH+BdotH*BdotH)/(1.0+NdotH));
half spec_atten = saturate(sqrt(max(0.0,half_lambert/NdotV)));
half3 final_specular =    spec_term*spec_atten*_SpecularColor;

在第二层高光叠加的时候

再乘上一层shift_col当噪声

在这里插入图片描述

Hair.shader

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Character/Hair"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _Normal("Normal",2D) = "bump" {}
        _DiffuseRamp ("DiffuseRamp", 2D) = "Black" {}
        _Envmap ("Envmap", Cube) = "Black" {}
        _ShiftRamp("ShiftRamp",2D)="Black"{}

        [Header(Tint Base)]
        _TintBase ("Tint Base", COLOR) = (0,0,0,1)
        [Header(RampLayer1)]
        _RampLayerOffset1 ("RampLayerOffset1", Range(-0.5,0.5)) = 0.5
        _TintLayer1 ("Tint_Layer1", COLOR) = (0,0,0,1)
        [Header(RampLayer2)]
        _RampLayerOffset2 ("RampLayerOffset2", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness2 ("RampLayerSoftness2", Range(0,1)) = 0.5
        __TintLayer2 ("Tint Layer2", COLOR) = (0,0,0,1)
        [Header(RampLayer3)]
        _RampLayerOffset3 ("RampLayerOffset3", Range(-0.5,0.5)) = 0.5
        _RampLayerSoftness3 ("RampLayerSoftness3", Range(0,1)) = 0.5
        __TintLayer3 ("Tint Layer3", COLOR) = (0,0,0,1)
        [Header(Specular1)]
         _SpecularColor1 ("Specular Color1", COLOR) = (0,0,0,1)
         _SpecularIntensity1 ("SpecularIntensity1", Float) = 1
         _SpecularSmooth1 ("Specular Smooth1",  Range(0,1)) = 1
         _ShiftOffset1 ("Shift Offset1", Float) = 0
         [Header(Specular2)]
         _SpecularColor2 ("Specular Color2", COLOR) = (0,0,0,1)
         _SpecularIntensity2 ("SpecularIntensity2", Float) = 1
         _SpecularSmooth2 ("Specular Smooth2", Range(0,1)) = 1
         _ShiftOffset2 ("Shift Offset2", Float) = 0 
         


        [Header(Rim)]
        _RimMin("RimMin", Range(-2,2)) = 0.5
        _RimMax("RimMax", Range(-2,2)) = 0.5
        _Roughness("Roughness", Float) = 0
        _EnvIntensity("Env Intensity", Float) = 0

        [Header(Outline)]
        _Outline ("Outline", Float) = 0.6
        _OutlineColor ("OutlineColor", COLOR) = (0,0,0,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        
        Pass
        {
            Name "Skin"
            Tags { "LightMode" = "ForwardBase" }
            
            Stencil {
                 Ref 1
                 Comp NotEqual
                 Pass Keep
                 Fail Keep
            }

            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            // make fog work
            #pragma multi_compile_fog
            #include "AutoLight.cginc"
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float4 normal :NORMAL;
                float4 tangent:TANGENT;
                float4 VertexColor:COLOR;
            };

            struct v2f
            {
                
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;

                float3 normalDir: TEXCOORD1;
                float3 tangentDir: TEXCOORD2;
                float3 binormalDir: TEXCOORD3;
                float4 VertexColor: TEXCOORD4;
                float3 pos_world: TEXCOORD5;
                LIGHTING_COORDS(6,7)
            };

            sampler2D _MainTex;
            sampler2D _Normal;
            sampler2D _DiffuseRamp;
            samplerCUBE _Envmap;
            sampler2D _ShiftRamp;
            float4 _ShiftRamp_ST;

            float4 _LightColor0;

            float4 _MainTex_ST;
            float4 _TintBase;
            float4 __TintLayer1;
            float _RampLayerOffset1;

            float4 __TintLayer2;
            float _RampLayerOffset2;
            float _RampLayerSoftness2;
            
            float4 __TintLayer3;
            float _RampLayerOffset3;
            float _RampLayerSoftness3;

            
            float _ShiftOffset1;
            float _SpecularIntensity1;
            float4 _SpecularColor1;
            float _SpecularSmooth1;
            
            float _ShiftOffset2;
            float _SpecularIntensity2;
            float4 _SpecularColor2;
            float _SpecularSmooth2;

            float _RimMax;
            float _RimMin;
            float _Roughness;
            float _EnvIntensity;
            float4 _Envmap_HDR;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord0;
                o.normalDir=normalize( mul(unity_ObjectToWorld,v.normal).xyz);
                o.tangentDir=normalize( mul(unity_ObjectToWorld,v.tangent).xyz);
                o.pos_world=normalize( mul(unity_ObjectToWorld,v.vertex).xyz);
                o.binormalDir=normalize( cross(o.normalDir,o.tangentDir)*v.tangent.w);
                o.VertexColor = v.VertexColor;
                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;   
            }


            
            half StrandSpecular(half3 T, half3 V, half3 L, half exponent)
            {
                 half3 H = normalize(L + V);
                 half dotTH = dot(T, H);
                 half sinTH = sqrt(1 - dotTH * dotTH);
                 half dirAtten = smoothstep(-1.0, 0.0, dotTH);
                 return dirAtten * pow(sinTH, exponent);
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half atten = LIGHT_ATTENUATION (i);
                half3 base_col = tex2D(_MainTex, i.uv).xyz;
                half3 normal_world = normalize(i.normalDir);
                half3 tangent_world= normalize(i.tangentDir);
                half3 binormal_world= normalize(i.binormalDir);
               
                half3 normal_data = UnpackNormal(tex2D(_Normal,i.uv));
                half3x3 TBN = half3x3 (tangent_world,binormal_world,normal_world);
                
                normal_world = mul(normal_data,TBN);
                half3 ViewDir = normalize(_WorldSpaceCameraPos.xyz-i.pos_world);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                //漫反射
                half NdotL =  max(0,dot(normal_world,lightDir));
                half half_lambert = (NdotL+1.0)*0.5;
                //half half_lambert = half_lambert*ao //本来要乘上AO因为素材没有算了

                half3 tint_Base_color1 = base_col;

                //第一层ramp
                half2 uv_ramp1 = half2(half_lambert+_RampLayerOffset1,0.5);
                half toon_diffuse1 = tex2D(_DiffuseRamp,uv_ramp1).r;
                half3 tint_color1 = lerp(half3(1,1,1),__TintLayer1,toon_diffuse1*__TintLayer1.a);
                
                //第二层ramp
                half2 uv_ramp2 = half2(half_lambert+_RampLayerOffset2,1-i.VertexColor.g+_RampLayerSoftness2);//1-i.VertexColor.g可以用_RampLayerSoftness2代替越大越柔和
                half toon_diffuse2 = tex2D(_DiffuseRamp,uv_ramp2).g;
                half3 tint_color2 = lerp(half3(1,1,1),__TintLayer2,toon_diffuse2*__TintLayer2.a);
                
                //第三层ramp
                half2 uv_ramp3 = half2(half_lambert+_RampLayerOffset3,1-i.VertexColor.b+_RampLayerSoftness3);//1-i.VertexColor.b可以用_RampLayerSoftness3代替越大越柔和
                half toon_diffuse3 = tex2D(_DiffuseRamp,uv_ramp3).b;
                half3 tint_color3 = lerp(half3(1,1,1),__TintLayer3,toon_diffuse3*__TintLayer3.a);
                
                half3 final_diffuse = tint_Base_color1 * tint_color1 *tint_color2 *tint_color3  ;
                //各向异性高光
                half2 uv_shift = i.uv * _ShiftRamp_ST.xy + _ShiftRamp_ST.zw;
                half3 shift_col = tex2D(_ShiftRamp,uv_shift);
                half3 binormal_world1 = normalize( binormal_world + (shift_col+_ShiftOffset1)* normal_world);
                half3 binormal_world2 = normalize( binormal_world + (shift_col+_ShiftOffset2)* normal_world);
                half NdotV = max(0.00001 ,dot(normal_world , ViewDir));
                half3 H = normalize(lightDir+ViewDir);
                half NdotH = dot(normal_world ,H );
                half TdotH = dot(tangent_world ,H);
               
              
                half BdotH1 =dot(binormal_world1 ,H)/_SpecularSmooth1;
                half spec_term1 =exp(-(TdotH*TdotH+BdotH1*BdotH1)/(1.0+NdotH));
                half spec_atten1 = saturate(sqrt(max(0.0,half_lambert/NdotV)));
                half3 specular_col1 = spec_term1* spec_atten1 * _LightColor0.xyz * _SpecularIntensity1 * atten * _SpecularColor1.xyz;
                
                half BdotH2 =dot(binormal_world2 ,H)/_SpecularSmooth2;
                half spec_term2 =exp(-(TdotH*TdotH+BdotH2*BdotH2)/(1.0+NdotH));
                half spec_atten2 = saturate(sqrt(max(0.0,half_lambert/NdotV)));
                half3 specular_col2 = spec_term2* spec_atten2 * _LightColor0.xyz * _SpecularIntensity2 * atten *_SpecularColor1.xyz*shift_col;

                 half3 final_specular =   specular_col1 + specular_col2 ;

                //half3 half_R = normalize(lightDir+ViewDir);
                //half3 R = normalize(reflect(-lightDir,normal_world));
                //half specular_term = max(pow(dot(R,ViewDir),_Shineness),0.0001);//本来要乘上AO因为素材没有算了
                //specular_term = smoothstep(0.5-_SpecularSmooth*0.5,0.5+_SpecularSmooth*0.5,specular_term);//风格化高光
            
                //half3 final_specular = base_col  * specular_term *_SpecularColor *atten *_LightColor0.xyz *_SpecularIntensity  ;
                
                //边缘光,环境光
                half NDL = NdotL>0 ? 1:0;
                half fresnel = 1- dot(ViewDir,normal_world);
                half rim = smoothstep(_RimMin,_RimMax,fresnel);//本来要乘上AO因为素材没有算了
                rim = rim * NDL;

                half3 r = reflect(-ViewDir,normal_world);
                half roughness = lerp(0,0.95,saturate(_Roughness));
                roughness = roughness * (1.7-0.7*roughness );
                half mip_level = roughness * 6.0;
                half4 color_cubemap = texCUBElod(_Envmap,half4(r,mip_level));
                half3 color_env = DecodeHDR(color_cubemap,_Envmap_HDR);
                half3 final_env =color_env * rim * _EnvIntensity    ;

                half3 final_col = final_diffuse +final_specular +final_env;
                return half4(final_col,1);
                
            }
            ENDCG
        }
        Pass
        {
        Name "OUTLINE"
        Cull Front
		ZWrite On
		ColorMask RGB
		Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata {
		        float4 vertex : POSITION;
		        float3 normal : NORMAL;
		        float4 texCoord : TEXCOORD0;
		        float4 vertexColor : COLOR;
	        };
	        struct v2f {
	        	float4 pos : POSITION;
	        	float4 color : COLOR;
	        	float4 tex : TEXCOORD0;
	        };
            sampler2D _MainTex;
            float _Outline;
            float4 _OutlineColor;
            v2f vert(appdata v) {
		        // just make a copy of incoming vertex data but scaled according to normal direction
	        	v2f o;
	        	o.pos = UnityObjectToClipPos(v.vertex);
		        float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
		        float2 offset = TransformViewToProjection(norm.xy);
		       // float2 offset = (norm.xy);
	        	o.pos.xy += offset * _Outline*0.0001;
	        	o.tex = v.texCoord;
	        	o.color = v.vertexColor;
	        	return o;
	        }
            fixed4 frag (v2f i) : SV_Target
            {
                half4 base_col = tex2D(_MainTex,i.tex);
                return _OutlineColor*base_col;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

脸部

在皮肤的基础上提亮一点。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值