Unity5 使用Projector实现纹理投射
发表于2018-12-04
游戏中,我们经常需要实现将纹理投射到场景中其他物体上的效果,如地上的光环、石块上的logo、水印等。很多情况下我们可以通过灯光或者其他方式达到我们想要的效果,但是Unity已经为我们提供了一种更加便捷高效的实现方式,那就是Projector组件。本篇我们就来看看如何使用Projector实现纹理投射。
最终效果预览
准备工作
首先创建工程,通过Assets->Import Package->Effects导入标准资源库中的特效素材,如下图所示。如果安装Unity时没有安装标准资源库,可以到这里单独下载并安装。
导入资源后,Project中将出现下图中的结构:
接下来就可以在场景中加入必要的元素了。
应用Projector组件
将BlobShadowProjector(Standard Shader Gallery -> Effects -> Projectors -> Prefabs)拖入场景中,Inspector面板如图:
BlobShadowProjector实现的是通过Projector组件在物体上投射一个圆形的阴影。将上图中Cookie改成我们需要的图片(注意图片属性中Alpha from Grayscale需要勾选,Wrap mode选择Clamp,当然你可以尝试不同选择看实际效果),但仍然存在不少问题,如png图片中的透明部分会被渲染成黑色,场景中有不明条纹等。
Projector的原理是为其frustum范围内的物体增加一个Pass,将我们在Projector中定义的材质渲染出来。为了解决上面所说的问题,我们可以创建自己的Shader和材质。
首先,复制Standard Shader Gallery -> Effects -> Projectors -> ProjectorMultiply,另存为ProjectorCustom,修改如下:
Shader "Projector/Custom" { Properties{ _ShadowTex("Cookie", 2D) = "gray" {} _FalloffTex("FallOff", 2D) = "white" {} } Subshader{ Tags{ "Queue" = "Transparent" } Pass{ ZWrite Off ColorMask RGB //Blend DstColor Zero Blend SrcAlpha OneMinusSrcAlpha Offset -1, -1 CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" struct v2f { float4 uvShadow : TEXCOORD0; float4 uvFalloff : TEXCOORD1; UNITY_FOG_COORDS(2) float4 pos : SV_POSITION; }; float4x4 _Projector; float4x4 _ProjectorClip; v2f vert(float4 vertex : POSITION) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, vertex); o.uvShadow = mul(_Projector, vertex); o.uvFalloff = mul(_ProjectorClip, vertex); UNITY_TRANSFER_FOG(o,o.pos); return o; } sampler2D _ShadowTex; sampler2D _FalloffTex; fixed4 frag(v2f i) : SV_Target { fixed4 texS = tex2Dproj(_ShadowTex, UNITY_PROJ_COORD(i.uvShadow)); //texS.a = 1.0-texS.a; //fixed4 texF = tex2Dproj(_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff)); //lerp(fixed4(1, 1, 1, 0), texS, texF.a); fixed4 res = texS; UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1)); return res; } ENDCG } } }
注释的部分为修改之前的代码
新建一个材质并将Shader选择为Projector/Custom,勾选图片的Alpha is Transparent,将新材质作为Projector组件的Material,并勾选Projector组件的Orthographic复选框,得到如下效果:
我们发现图片不仅投射到地面和方块上,也投射到了人物上,这显然不是我们想要的效果,不过这个问题处理起来很简单,只需要新建一个Player的Layer,将人物的Layer选成Player,然后将Projector的Ignore Layers设置成Player得到了我们的最终效果了。
总结
Projector是一个非常有用的组件,除了我们上面实现的效果,还能用来廉价的实现投影(如我们最开始使用的BlobShadowProjector)、特殊光效(可以用来代替Cookie或者范围光,但是更加高效)等效果,通过尝试不同的设置和不同的Shader,我们还会得到更多有意思的效果。