本文共 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/