unity shader卡通渲染(描边)+阴影+多光源处理

说道卡通渲染,应该都会想到描边:
我所学的描边有三种:
一种是计算边缘深度检测描边
一种是色差检测描边
一种是利用顶点法线向外扩展返回单色pass,使用正面裁剪
我用的第三种:

pass
{
		//剔除前面 (朝向摄像机的面)保留内部渲染
		Cull Front
		CGPROGRAM
		#pragma fragment frag

		float4 _LineColor;
		float _Line;
		struct a2v {
			float4 pos:POSITION;
			float3 normal:NORMAL;
		};
		struct v2f {
			float4 pos:POSITION;
		};
		v2f vert(a2v i)
		{
			v2f v;
			UNITY_INITIALIZE_OUTPUT(v2f, v);
			//UNITY_MATRIX_MV模型视图矩阵转置
			float4 pos = float4(UnityObjectToViewPos(i.pos), 1);
			//UNITY_MATRIX_IT_MV中,模型视图逆转置,加IT是专门用来变换法线用的,将模型空间转换到相机空间
			float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, i.normal);
			normal.z = -0.5f;
			pos = pos + float4(normalize(normal), 0)*_Line;//法线向外扩展向量*线宽,得到轮廓的宽度
			//在将其转换到裁剪空间
			v.pos = mul(UNITY_MATRIX_P, pos);
			return v;
		}
		fixed4 frag(v2f i) :SV_Target
		{
			//返回纯色
			return fixed4(_LineColor.rgb, 1);
		}
		ENDCG
	}

阴影计算:

需要添加:
#include "AutoLight.cginc"
#pragma multi_compile_fwdbase
#pragma	multi_compile_shadowcaster


struct v2f {

	......
	//阴影
	SHADOW_COORDS(4) //声明阴影坐标,参数2表示下一个可用的插值寄存器的索引值
};
v2f vert(a2v v)
{
	......
	//阴影计算
	TRANSFER_SHADOW(o);//计算上一步声明发阴影纹理坐标
}
fixed4 fragLig(v2f i) :SV_Target
{
	......
	//阴影计算atten
	UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
	得到的atten应该乘以漫反射和高光
	......
}
必须要有的FallBack "Specular"

完整代码:

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

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

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

Shader "Custom/katong"
{
	Properties
	{
		[Header(Light Setting)]
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Color("Color物体本身颜色(自发光)",Color) = (1,1,1,1)
		_Ambiel_K("Ambiel_K(环境光系数)",Float) = 1
		_Diff_K("Diff_K(漫反射系数)", Float) = 1
		_Diff_Color("Diff_Color(漫反射颜色)",Color) = (1,1,1,1)
		_HLight_K("HLight_K(高光系数)", Float) = 1
		_Specular_Color("Specular_Color(镜面反射颜色)", Color) = (1, 1, 1, 1)
		_ShadowThreshold("ShadowThreshold(漫反射光影分界阈值)", Range(-1.0, 1.0)) = 0.2
		_ShadowColor("ShadowColor(漫反射暗处颜色)", Color) = (1.0, 1.0, 1.0, 1.0)
		[Header(Line Setting)]
		[Space(5)]
		_LineColor("LineColor:轮廓颜色",Color) = (1,1,1,1)
		_Line("Line:轮廓宽度",Range(0,0.1)) = 0.002

	}
	SubShader
	{	
		CGINCLUDE
		#include "UnityCG.cginc"
		#include "Lighting.cginc"
		#include "AutoLight.cginc"
		#pragma vertex vert
		ENDCG
		
		pass {
			
			//使用光照模型
			Tags{"LightingMode" = "ForwardBase"}
			//剔除背面
			Cull Back
			CGPROGRAM
			#pragma fragment fragLig
			#pragma multi_compile_fwdbase
			#pragma	multi_compile_shadowcaster

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float4 _Color;
			float _Ambiel_K;
			float _Diff_K;
			float4 _Diff_Color;
			float _HLight_K;
			float4 _Specular_Color;
			float _ShadowThreshold;
			float4 _ShadowColor;

			struct a2v {
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
				float3 normal:NORMAL;
				float4 color:COLOR0;
			};
			struct v2f {
				float4 pos:POSITION;
				float2 uv:TEXCOORD0;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos:TEXCOORD2;
				float3 color:TEXCOORD3;
				//阴影
				SHADOW_COORDS(4) //声明阴影坐标,参数2表示下一个可用的插值寄存器的索引值
			};
			v2f vert(a2v v)
			{
				v2f o;
				UNITY_INITIALIZE_OUTPUT(v2f,o);
				//坐标转换到裁剪空间
				o.pos = UnityObjectToClipPos(v.vertex);
				//贴图
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.color = v.color;
				//阴影计算
				TRANSFER_SHADOW(o);//计算上一步声明发阴影纹理坐标
				return o;
			}
			fixed4 fragLig(v2f i) :SV_Target
			{
				float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos));
				float3 N = normalize(i.worldNormal);
				float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos));

				//纹理采样
				float3 color = tex2D(_MainTex, i.uv).rgb*_Color.rgb;

				fixed spec = dot(N, normalize(L + V)) + (i.color.g - 0.5) * 2;
				fixed w = fwidth(spec)*2.0;
				//环境光
				float3 Ambiel = UNITY_LIGHTMODEL_AMBIENT.xyz*_Ambiel_K*color;
				//阴影计算atten
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				//半兰伯特光照的漫反射
				fixed halfdiff = dot(L, N) + (i.color.r - 0.5 * 4);
				fixed diffStep = smoothstep(-w + _ShadowThreshold, w + _ShadowThreshold, halfdiff);
				fixed4 light = _LightColor0 * 0.5 + 0.5;
				//漫反射
				fixed3 Diff = light * color*(diffStep + (1 - diffStep)*_ShadowColor)*_Diff_Color*_Diff_K;
			

				//镜面反射-高光
				float3 Specul = _Specular_Color.rgb * lerp(0, 1, smoothstep(-w, w, dot(N, normalize(L + V)) + _HLight_K - 1))*step(0.0001, _HLight_K);

				return fixed4(Ambiel + Diff + Specul, 1.0);
			}
			ENDCG
		}
		pass
		{
			Tags{ "LightMode" = "ForwardAdd" } //ForwardAdd :多灯混合//
			Blend One One//混合模式,表示该Pass计算的光照结果可以在帧缓存中与之前的光照结果进行叠加,否则会覆盖之前的光照结果
			CGPROGRAM
			#pragma fragment fragLight
			#pragma multi_compile_fwdadd//

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float4 _Color;
			float _Ambiel_K;
			float _Diff_K;
			float4 _Diff_Color;
			float _HLight_K;
			float4 _Specular_Color;
			float _ShadowThreshold;
			float4 _ShadowColor;

			struct a2v {
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
				float3 normal:NORMAL;
				float4 color:COLOR;
			};
			struct v2f {
				float4 pos:POSITION;
				float2 uv:TEXCOORD0;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos:TEXCOORD2;
				float3 color:TEXCOORD3;
				//点光源需要的衰减
				LIGHTING_COORDS(6, 7)
			};
			v2f vert(a2v v)
			{
				v2f o;
				UNITY_INITIALIZE_OUTPUT(v2f, o);
				//坐标转换到裁剪空间
				o.pos = UnityObjectToClipPos(v.vertex);
				//贴图
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.color = v.color;
				return o;
			}
			fixed4 fragLight(v2f i) :SV_Target
			{
				fixed3 N = normalize(i.worldNormal);
				#ifdef USING_DIRECTIONAL_LIGHT  //平行光下可以直接获取世界空间下的光照方向
							fixed3 L = normalize(_WorldSpaceLightPos0.xyz);
				#else  //其他光源下_WorldSpaceLightPos0代表光源的世界坐标,与顶点的世界坐标的向量相减可得到世界空间下的光照方向
							fixed3 L = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
				#endif
				//相机坐标减去顶点坐标
				fixed3 viewdir = normalize(UnityWorldSpaceViewDir(i.worldPos));


				fixed spec = dot(N, normalize(L + viewdir)) + (i.color.g - 0.5) * 2;
				fixed w = fwidth(spec)*2.0;
				//半兰伯特光照的漫反射
				fixed halfdiff = dot(L, N) + (i.color.r - 0.5 * 4);
				fixed diffStep = smoothstep(-w + _ShadowThreshold, w + _ShadowThreshold, halfdiff);
				fixed4 light = _LightColor0 * 0.5 + 0.5;


				//漫反射
				fixed3 Diff = light *(diffStep + (1 - diffStep)*_ShadowColor)*_Diff_Color*_Diff_K;
				//镜面反射-高光
				float3 Specul = _Specular_Color.rgb * lerp(0, 1, smoothstep(-w, w, dot(N, normalize(L + viewdir)) + _HLight_K - 1))*step(0.0001, _HLight_K);

				//点光源需要的衰减
				fixed atten;
				#ifdef USING_DIRECTIONAL_LIGHT//平行光下不存在光照衰减,恒值为1
					atten = 1.0;
				#else
					#if defined(POINT)//点光源的光照衰减计算
						//unity_WorldToLight内置矩阵,世界空间到光源空间变换矩阵。与顶点的世界坐标相乘可得到光源空间下的顶点坐标
						float3 lightcoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
						/*利用Unity内置函数tex2D对Unity内置纹理_LightTexture0进行纹理采样计算光源衰减,获取其衰减纹理,
						再通过UNITY_ATTEN_CHANNEL得到衰减纹理中衰减值所在的分量,以得到最终的衰减值*/
						atten = tex2D(_LightTexture0, dot(lightcoord, lightcoord).rr).UNITY_ATTEN_CHANNEL;
					#elif defined(SPOT)//聚光灯的光照衰减
						float4 lightcoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
						/*(lightCoord.z > 0):聚光灯的深度值小于等于0时,则光照衰减为0
						_LightTextureB0:如果该光源使用了cookie,则衰减查找纹理则为_LightTextureB0*/
						atten = (lightcoord.z > 0) * tex2D(_LightTexture0, lightcoord.xy / lightcoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightcoord, lightcoord).rr).UNITY_ATTEN_CHANNEL;
					#else
						atten = 1;
					#endif
				#endif

				fixed4 col = fixed4((Diff + Specul)*atten, 1);
				return col;
			}
			ENDCG
		}
		pass
		{
			//剔除前面 (朝向摄像机的面)保留内部渲染
			Cull Front
			CGPROGRAM
			#pragma fragment frag

			float4 _LineColor;
			float _Line;
			struct a2v {
				float4 pos:POSITION;
				float3 normal:NORMAL;
			};
			struct v2f {
				float4 pos:POSITION;
			};
			v2f vert(a2v i)
			{
				v2f v;
				UNITY_INITIALIZE_OUTPUT(v2f, v);
				//UNITY_MATRIX_MV模型视图矩阵转置
				float4 pos = float4(UnityObjectToViewPos(i.pos), 1);
				//UNITY_MATRIX_IT_MV中,模型视图逆转置,加IT是专门用来变换法线用的,将模型空间转换到相机空间
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, i.normal);
				normal.z = -0.5f;
				pos = pos + float4(normalize(normal), 0)*_Line;//法线向外扩展向量*线宽,得到轮廓的宽度
				//在将其转换到裁剪空间
				v.pos = mul(UNITY_MATRIX_P, pos);
				return v;
			}
			fixed4 frag(v2f i) :SV_Target
			{
				//返回纯色
				return fixed4(_LineColor.rgb, 1);
			}
			ENDCG
		}
	}
	FallBack "Specular"
}

效果:
在这里插入图片描述
我的参数:可以根据自己情况调节(我的是参考)
在这里插入图片描述

学习《shader入门精粹》笔记:
参考:《shader入门精粹》

猜你喜欢

转载自blog.csdn.net/QO_GQ/article/details/119616656