博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Shader入门精要】第十四章——卡通风格的渲染
阅读量:497 次
发布时间:2019-03-07

本文共 8383 字,大约阅读时间需要 27 分钟。

一、卡通风格的渲染(渐变纹理漫反射+分块的高光反射处理) 

Shader "MilkShader/14/ToonShading"{	Properties	{		_Color("Color Tint", Color) = (1,1,1,1)		_MainTex ("Main Tex", 2D) = "white"{}		_Ramp("Ramp Texture", 2D) = "white"{} //漫反射渐变纹理		_Outline("Outline", Range(0,1)) = 0.1 //轮廓线宽度		_OutlineColor("Outline Color", Color) = (1,1,1,1) 		_Specular ("Specular", Color) = (1,1,1,1)  //高光反射颜色		_SpecularScale ("Specular Scale", Range(0, 0.1)) = 0.01 //阈值	}	SubShader	{		Tags { "RenderType"="Opaque" "Queue"="Geometry"}		LOD 100		Pass		{		//第一个PASS是用法线进行向外扩展,然后只渲染背面,颜色输出是边缘颜色			NAME "OUTLINE"			Cull Front			CGPROGRAM			#pragma vertex vert			#pragma fragment frag			#include "UnityCG.cginc"			float _Outline;			fixed4 _OutlineColor;			struct a2v {				float4 vertex : POSITION;				float3 normal : NORMAL;			}; 						struct v2f {			    float4 pos : SV_POSITION;			};			v2f vert(a2v v){				v2f o;				//将顶点和法线转到视角空间下				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);  				//设置法线的z分量,对其归一化后在将顶点沿其方向扩张				//设置法线z的处理是为了尽可能避免背面扩张(现在我们渲染的是背面)后的顶点挡住正面的面片.				normal.z = -0.5;				//在视角空间下进行的一个顶点位置偏移(向法线方向)				pos = pos + float4(normalize(normal), 0) * _Outline;				//再转为裁剪空间				o.pos = mul(UNITY_MATRIX_P, pos);				return o;			}			float4 frag(v2f i) : SV_Target{				//直接输出边缘颜色					return float4(_OutlineColor.rgb, 1);			}			ENDCG		}		Pass{			//前向渲染			Tags{"LightMode"="ForwardBase"}			Cull Back			CGPROGRAM						#include "UnityCG.cginc"			#include "Lighting.cginc"//漫反射、高光反射需要的			#include "AutoLight.cginc"//这是阴影三剑客需要的头文件			#include "UnityShaderVariables.cginc"			#pragma vertex vert			#pragma fragment frag			#pragma multi_compile_fwdbase			sampler2D _MainTex;			float4 _MainTex_ST;			fixed4 _Color;			sampler2D _Ramp;			fixed4 _Specular;			fixed _SpecularScale;			struct a2v {				float4 vertex : POSITION;				float3 normal : NORMAL;				float4 texcoord : TEXCOORD0;				//float4 tangent : TANGENT; 为啥例子会有tangent			}; 					struct v2f {				float4 pos : POSITION;				float2 uv : TEXCOORD0;				float3 worldNormal : TEXCOORD1;				float3 worldPos : TEXCOORD2;				SHADOW_COORDS(3)			};			v2f vert (a2v v) {				v2f o;				o.pos = UnityObjectToClipPos( v.vertex);				o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);								o.worldNormal  = UnityObjectToWorldNormal(v.normal);				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;								TRANSFER_SHADOW(o);				return o;			}			float4 frag(v2f i) :SV_Target{			 	 fixed3 worldNormal = normalize(i.worldNormal);				 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));				 fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));				 fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);				 fixed4 c = tex2D (_MainTex, i.uv);				 fixed3 albedo = c.rgb * _Color.rgb;				 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;				 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);				 								 fixed diff = dot(worldNormal, worldLightDir);				 diff = (diff * 0.5 + 0.5) * atten; //先进行转为(0,1)范围,再乘以衰减值*阴影 				 //这里的漫反射是用渐变纹理方式,diff作为纹理坐标进行采样,通过这种方式采样出的漫反射颜色是卡通化的				 fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;				 fixed spec = dot(worldNormal, worldHalfDir);				 fixed w = fwidth(spec) * 2.0;//抗锯齿处理? 这种获取w阈值绝对值的方式很懵逼				 //下面lerp(...)是比较复杂的,先假设_SpecularScale为0.5,  spec是(0,1)的一个动态数值根据halfDir和normal而变化				 // spec + _SpecularScale - 1 是将 spec从(0,1)转为(-(1-_SpecularScale), _SpecularScale),现在是(-0.5, 0.5)				 // w实际上可以直接理解为是一个动态的阈值范围, 实际上可以直接看成固定的比如:[-0.3, 0.3]				 // 此时当spec + _SpecularScale - 1 小于 -0.3(-w)时就会返回0, 在-w,w之间时就会返回0~1, 大于w时会返回1				 // 这样这个高光反射系数就不会产生突变而是渐变效果				 // 书上直接说_SpecularScale是一个阈值,而我的理解是如上,不然太难理解了..				 // 这样我们得到的高光反射颜色是一个个块状的,而不是之前那样子的 之前是pow(dot(normal, half), x)				 // 这种lerp(...) 处理 如果觉得难以理解的话 简单的做法是 直接 step(阈值, dot(...)) 当点积大于阈值时会返回1,小于阈值时会返回0				 // 但是这种做法就是导致了突变,高光部分会产生锯齿,可以想象假设 阈值时 0.5,  那么当 (0.5, 1)时直接返回1,(0,0.5)时候返回0				 // 在0.49 时候还是0, 逐渐变为0.51时突变为1, 反过来也是一样,高光颜色会直接从0,突变为有色状态,锯齿就是由于突变导致的。				 // 上面那个阈值0.5,可看成一个分界线, 当dot()在0.5(左边)时就会返回0,在0.5的右边时返回1				 // 此时下面的做法就是 将这个分界线,变成了2个! 即 -w 和 w, 虽然这2个值得获取很难理解,但是就可以直接理解为就是一个很小的常数				 //    当dot() < -w 时,返回的是0, > w时候 返回的是1,  在(-w,w)中间时返回的是0~1的数值,所以效果就是渐变,解决了抗锯齿问题				 fixed3 specular = _Specular.rgb * lerp(0,1, smoothstep(-w,w,spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);				 				 return fixed4(ambient + diffuse + specular, 1.0);			}			ENDCG		}	}	Fallback "Diffuse"}

二、素描风格的渲染

Shader "MilkShader/14/Hatching"{	Properties	{				_Color ("Color Tint", Color) = (1,1,1,1) //控制颜色		_TileFactor ("Tile Factor", Float) = 1 //平铺系数(对纹理坐标的缩放系数
) //描边使用的参数 _Outline("Outline", Range(0,1)) = 0.1 _OutlineColor("Outline Color", Color) = (1,1,1,1) //六张素描纹理(逐渐增多横向线条的纹理) _Hatch0 ("Hatch 0", 2D) = "white"{} _Hatch1 ("Hatch 1", 2D) = "white"{} _Hatch2 ("Hatch 2", 2D) = "white"{} _Hatch3 ("Hatch 3", 2D) = "white"{} _Hatch4 ("Hatch 4", 2D) = "white"{} _Hatch5 ("Hatch 5", 2D) = "white"{} } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry"} UsePass "MilkShader/14/ToonShading/OUTLINE"//使用了之前的描边PASS Pass { Tags{ "LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" #include "UnityShaderVariables.cginc" fixed4 _Color; fixed _TileFactor; fixed _Outline; fixed4 _OutlineColor; sampler2D _Hatch0; sampler2D _Hatch1; sampler2D _Hatch2; sampler2D _Hatch3; sampler2D _Hatch4; sampler2D _Hatch5; struct appdata { float4 vertex : POSITION; float4 tangent : TANGENT; float2 texcoord : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; fixed3 hatchWeight0 : TEXCOORD1; fixed3 hatchWeight1 : TEXCOORD2; float3 worldPos : TEXCOORD3; SHADOW_COORDS(4) }; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord.xy * _TileFactor; fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal)); fixed3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex)); //法线和光源的点积 fixed diff = max(0, dot(worldNormal, worldLightDir)); //将diff转到[0,7]范围 float hatchFactor = diff * 7.0; o.hatchWeight0 = fixed3(0,0,0); o.hatchWeight1 = fixed3(0,0,0); //分别判断每一个区间[0,1] [1,2] [2,3] [3,4] [4,5] [5,6] [6,7]再计算对应区间的素描纹理权重值 //这个是固定的算法,记住是这样写就好了,如果想研究的话可以参考冯乐乐的第十四章 //实际上就是不同的区间时会对其中1个或2个素描纹理权重进行赋值,以此在片元着色器进行使用它们来进行与相应素描颜色进行相乘 //也就是决定在什么情况下,哪几个素描纹理产生多少影响 // hatchWeight0.x 是 第一个素描纹理的权重, 以此类推 hatchWeight1.z 是最后一个素描纹理的权重 // 初始化全为0是表示素描纹理完全没有任何作用 if(hatchFactor > 6.0){ }else if(hatchFactor > 5.0){ o.hatchWeight0.x = hatchFactor - 5.0; }else if(hatchFactor > 4.0){ o.hatchWeight0.x = hatchFactor - 4.0; o.hatchWeight0.y = 1 - o.hatchWeight0.x; }else if(hatchFactor > 3.0){ o.hatchWeight0.y = hatchFactor - 3.0; o.hatchWeight0.z = 1 - o.hatchWeight0.y; }else if(hatchFactor > 2.0){ o.hatchWeight0.z = hatchFactor - 2.0; o.hatchWeight1.x = 1 - o.hatchWeight0.z; }else if(hatchFactor > 1.0){ o.hatchWeight1.x = hatchFactor - 1.0; o.hatchWeight1.y = 1 - o.hatchWeight1.x; }else{ o.hatchWeight1.y = hatchFactor; o.hatchWeight1.z = 1 - o.hatchWeight1.y; } o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; TRANSFER_SHADOW(o); return o; } fixed4 frag (v2f i) : SV_Target { //从不同的素描纹理采样后乘以相应的素描纹理权重值(在顶点着色器我们就已经对它们进行了一个赋值) fixed4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeight0.x; fixed4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeight0.y; fixed4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeight0.z; fixed4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeight1.x; fixed4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeight1.y; fixed4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeight1.z; //非素描部分的颜色是白色(如果素描的纹理权重总和越大,非素描部分越黑,否则越白) fixed4 whiteColor = fixed4(1,1,1,1) * (1 - i.hatchWeight0.x - i.hatchWeight0.y - i.hatchWeight0.z - i.hatchWeight1.x - i.hatchWeight1.y - i.hatchWeight1.z); fixed4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5 + whiteColor; UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); return fixed4(hatchColor.rgb * _Color.rgb * atten, 1.0); } ENDCG } } Fallback "Diffuse"}

平铺系数越大素描的线越密集,否则反之。

中秋节快乐!

转载地址:http://xhucz.baihongyu.com/

你可能感兴趣的文章