Shader实现Unity中物体轮廓外发光效果,效果如图
上述效果分两个pass实现,就是每一个pass会绘制一次,至于为什么更改Shader以及相关GPU绘制物体基本流程可以自行搜索,这里只做代码实现以及实现原理,这里更改顶点Shader以及片元着色Shader,这也是我们自己可以代码干预的。
在Assets目录下创建Shader:Create->Shader->Unlit Shader
再创建材质球:Create->Material,将材质球拖入你需要外发光的物体上
创建好后单机材质球在Inspector中找到Shader,更换为Shader->Unlit->你创建好的Shader然后双击打开shader
一、物体边缘黑色光
Pass { cull front CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float4 w_vertex:TEXTURE2; float3 w_normal:TEXTURE1; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; float4 new_vert=v.vertex+float4(v.normal,1)*float4(0.1,0.1,0.1,1); o.vertex = UnityObjectToClipPos(new_vert); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.w_normal=UnityObjectToWorldNormal(v.normal); o.w_vertex=mul(unity_ObjectToWorld,v.vertex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = fixed4(0,0,0,1); UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG }
在第一个pass后直接添加第二个pass,下面讲解上述代码原理:
1.原理
网格包含有顶点坐标,发现,纹理坐标等信息,我们利用法线扩大物体顶点后计算相机与物体顶点法线的之间,法线与相机的角度cos,作为每个顶点都不同的值,并将他作为混合模式的值进行光照计算
2.实现
2.1CGPROGRAM
CGPROGRAM表示Cg program,cg代码开始,最后会有ENDCG表示cg结束
2.2顶点、片元着色器
#pragma vertex vert #pragma fragment frag
顶点着色器函数名为vert
片元着色器函数名为frag
#include "UnityCG.cginc"
shader的库函数
v2f vert (appdata v)
这里开始顶点绘制,在上面struct v2f中拿到需要的参数
v2f vert (appdata v) { v2f o; float4 new_vert=v.vertex+float4(v.normal,1)*float4(0.1,0.1,0.1,1); o.vertex = UnityObjectToClipPos(new_vert); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.w_normal=UnityObjectToWorldNormal(v.normal); o.w_vertex=mul(unity_ObjectToWorld,v.vertex); UNITY_TRANSFER_FOG(o,o.vertex); return o; }
将顶点沿着法线方向放大0.1,
并将新顶点模型坐标转为世界坐标再转为相对相机为原点的坐标。法线向量从对象空间转换到世界空间,返回数据给渲染管道。
fixed4 frag (v2f i) : SV_Target { fixed4 col = fixed4(0,0,0,1); UNITY_APPLY_FOG(i.fogCoord, col); return col; }
这里我只需要黑色外轮廓所以直接返回黑色
注意cull front剔除前面
上面把效果图中黑色外轮廓实现,注意unity写shader并没有提示,小心错误
二、物体边缘轮廓发亮
Pass { cull front blend SrcAlpha One CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float4 w_vertex:TEXTURE2; float3 w_normal:TEXTURE1; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; float4 new_vert=v.vertex+float4(v.normal,1)*float4(0.5,0.5,0.5,1); o.vertex = UnityObjectToClipPos(new_vert); o.w_normal=UnityObjectToWorldNormal(v.normal); o.w_vertex=mul(unity_ObjectToWorld,v.vertex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float3 view_dir=i.w_vertex-_WorldSpaceCameraPos; float3 w_normal=i.w_normal; view_dir=normalize(view_dir); w_normal=normalize(w_normal); float value=dot(view_dir,w_normal); value=(value<0)?0:value; value=pow(value,2); fixed4 col =fixed4(0,1,0,value); UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG }
blend SrcAlpha One 这种混合模式通常用于实现一些半透明的效果,其他混合模式可以去unity官方手册查看,下面提供跳转链接
Unity 用户手册 2020.3 (LTS) - Unity 手册
frag中的参数用于计算value值
这样写完之后会导致边缘可能发黑,因为剔除前面但可能网格包含前面,
所以value=(value<0)?0:value;将前面的删除。其他原理与上述差不多