Shader实现边缘自定角度高光,描边,闪烁

发表于2018-11-20
评论0 826浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
Shader可以实现的特效实在是太多了,这里就不给大家一一列举了。本篇中要给大家分享的是使用shader实现边缘自定角度高光、描边、闪烁效果,一起来看看吧。

最终效果

向量的旋转

事实上是初中知识,但我书读的不好,所以我要给自己讲下。

先弄清楚如何在一个二维空间旋转一个变量。
观察图中v和v`的关系是 v`=[Cosθ,Sinθ]v  (假设v是列向量)

但在二维空间中,每个物体都有2个轴且互相垂直,如果旋转一个物体,就相当于旋转这个物体的坐标系,需要同时旋转这个物体的2个轴向量,才能达到完全旋转一个物体的要求。

我们把上面的图扩展下。

这样,我们就成功的获得了一个二维旋转矩阵。
[cosθ,-sinθ]
[sinθ,cosθ]
用这个矩阵就可以任意旋转任何二维物体,如果矩阵乘以对应的物体的世界坐标那物体就会围绕世界原心旋转(因为物体的坐标在图形是以矩阵的形式存储的)

旋转的方向如图示,θ是弧度,可不是角度,弧度乘以180/π,才是角度。

现在会旋转二维物体了,其实也就会旋转三维物体了,因为二维旋转就是在绕三维空间的Z轴旋转,只不过Z轴与我们的坐标系垂直,看不到(这里我就不画图啦)。

把上面的矩阵扩展到三维就是
[cosθ,-sinθ,0]
[sinθ,cosθ,0]
[  0,   0,1]
我们在3行3列留1,保证相乘后Z轴不变。绕X轴,绕Y轴以此类推。

乒乓效果

其实这个很简单,shader中,_Time是unity通过cpu给gpu传的time值。我们可以取他的余弦实现乒乓效果。
float t = abs(cos(_Time.x*loopTime));
用时间取余你的间隔时间比较你的间隔中间时间来,判断到底是增还是减。

其他问题

关于描边,可以看代码中的注释。
sinθ和cosθ的正负只要成对改,依然能正常旋转,因为和你传入的角度依然是正确的函数关系。

Shader代码:
Shader"QQ/RimLight"{  
	Properties{  
		_Color("Color",Color)=(1,1,1,1)  
		_MainTex("纹理",2D)="white"{}  
	<spanstyle="white-space:pre">	</span>_LightColor("灯颜色",Color)=(1,1,1,1)  
		_LightDir("灯方向",Vector)=(0,1,0,1)  
		_OutLine("描边颜色",Color)=(1,1,1,1)  
		_EdgeChange("描边大小",Range(0,.1))=.05  
		_FlickerTime("闪烁时间,0为关闭",Range(0,2))=1  
	}  
	CGINCLUDE  
	#include"UnityCG.cginc"  
	#pragmavertexvert  
	#pragmafragmentfrag  
	#pragmatarget3.0  
	ENDCG  
	SubShader{  
	Tags{"RenderType"="Transparent"  
	"Queue"="Transparent"  
	"LightMode"="ForwardBase"}  
	LOD200  
	Pass  
	{  
		CullFront  
		ZWriteOff  
		BlendSrcAlphaOneMinusSrcAlpha  
		CGPROGRAM  
		fixed4_OutLine;  
		float_EdgeChange;  
		float_FlickerTime;  
	structa2v  
	{  
		float4vertex:POSITION;  
		float3normal:NORMAL;  
	};  
	structv2f  
	{  
		float4pos:POSITION;  
	};  
	v2fvert(a2vv)  
	{  
		v2fo;  
		//参考博客http://blog.csdn.net/candycat1992/article/details/45577749  
		o.pos=mul(UNITY_MATRIX_MV,v.vertex);  
		v.normal=mul((float3x3)UNITY_MATRIX_MV,v.normal);  
		v.normal.z=-.5;  
		o.pos.xyz+=v.normal*_EdgeChange;  
		o.pos=mul(UNITY_MATRIX_P,o.pos);  
		//参考官方教程  
		//v.vertex.xyz+=v.normal*_EdgeChange;  
		//o.pos=mul(UNITY_MATRIX_MVP,v.vertex);  
		returno;  
	}  
	fixed4frag(v2fi):COLOR  
	{  
		//闪烁的乒乓的速度,如果脚本传值可以把这段删掉  
		_OutLine.a=abs(cos(_Time.x*_FlickerTime));  
		return_OutLine;  
	}  
	ENDCG  
	}  
	Pass  
	{  
	CGPROGRAM  
	fixed4_Color;  
	sampler2D_MainTex;  
	fixed4_MainTex_ST;  
	fixed4_LightColor;  
	fixed4_LightDir;  
	structa2v  
	{  
		float4vertex:POSITION;  
		float3normal:NORMAL;  
		float4texcoord:TEXCOORD0;  
	};  
	structv2f{  
		float4pos:POSITION;  
		float2uv:TEXCOORD0;  
		float3normal:TEXCOORD1;  
		float3viewDir:TEXCOORD2;  
		UNITY_FOG_COORDS(3)  
	};  
	v2fvert(a2vv)  
	{  
		v2fo;  
		o.pos=mul(UNITY_MATRIX_MVP,v.vertex);  
		float4wPos=mul(_Object2World,v.vertex);  
		o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);  
		//不过分追求效果的情况下在这里normalize,如果追求效果,请在frag内normalize,否则插值会被归一。  
		o.normal=normalize(mul(v.normal,_World2Object).xyz);  
		//我们希望光是从摄像机对面过来的,所以就反减  
		o.viewDir=normalize(wPos.xyz-_WorldSpaceCameraPos);  
		//旋转矩阵  
		float3x3rotaX={1,0,0,0,cos(_LightDir.x),sin(_LightDir.x),0,-sin(_LightDir.x),cos(_LightDir.x)};  
		float3x3rotaY={cos(_LightDir.y),0,sin(_LightDir.y),0,1,0,-sin(_LightDir.y),0,cos(_LightDir.y)};  
		float3x3rotaZ={cos(_LightDir.z),sin(_LightDir.z),0,-sin(_LightDir.z),cos(_LightDir.z),0,0,0,1};  
		o.viewDir=mul(rotaX,o.viewDir);  
		o.viewDir=mul(rotaY,o.viewDir);  
		o.viewDir=mul(rotaZ,o.viewDir);  
		//矩阵相乘后结果不太对,可能写错了。  
		//float3x3rota={sin(_LightDir.y)*sin(_LightDir.z),sin(_LightDir.y)*cos(_LightDir.z),cos(_LightDir.y),  
		//-sin(_LightDir.y)*cos(_LightDir.x)*sin(_LightDir.z)-sin(_LightDir.x)*sin(_LightDir.z),-sin(_LightDir.y)*cos(_LightDir.x)*cos(_LightDir.z)+sin(_LightDir.x)*cos(_LightDir.z),cos(_LightDir.x)*cos(_LightDir.y),  
		//-sin(_LightDir.y)*cos(_LightDir.x)*sin(_LightDir.z)+sin(_LightDir.x)*sin(_LightDir.z),-sin(_LightDir.y)*cos(_LightDir.x)*cos(_LightDir.z)-sin(_LightDir.x)*cos(_LightDir.z),cos(_LightDir.x)*cos(_LightDir.y)  
		//};  
		//o.viewDir=mul(rota,o.viewDir);  
		returno;  
	}  
	fixed4frag(v2fi):COLOR  
	{  
		fixed4col;  
		fixed4tex=tex2D(_MainTex,i.uv);  
		//点乘normal和视线,判断相似度  
		floatdiff=max(0,dot(i.normal,i.viewDir));  
		col=tex*_Color+_LightColor*diff*_LightDir.w;  
		returncol;  
	}  
	ENDCG  
	}  
	}  
	FallBack"Diffuse"  
}  

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台