C for Graphic:网格描边特效
之后呢我会持续更新一些常用、炫酷的shader实现,从简单到复杂,从单一到复合,从我已经写过的到未来见识到的(基本上更新到不搞这一行了,上百篇应该是有的),这里来一篇最最最常用的shader。
——网格描边
开发们在unity中随便创建一个cube,在Scene面板就看得到网格描边的效果,pick到这个cube后就显示出来,真的是相当实用,如图:
这里说一下这个实现原理,我们可以将这种效果分开为:
1.中间的正常渲染的立方体
2.中间立方体网格顶点沿着法向量方向扩展x个单位后渲染一个橘黄色的立方体(也就是描边)
实现代码如下:
-
Shader "Unlit/OutlineUnlitShader"
-
{
-
Properties
-
{
-
_CubeColor("color",color) = (1,1,1,1)
-
_EdgeColor("edge",color) = (1,1,1,1)
-
_EdgeThick("thick",Range(0.01,1)) = 0.1
-
}
-
SubShader
-
{
-
Tags { "RenderType"="Opaque" }
-
LOD 100
-
-
//正常渲染一个立方体的pass
-
Pass
-
{
-
CGPROGRAM
-
#pragma vertex vert
-
#pragma fragment frag
-
-
#include "UnityCG.cginc"
-
-
struct appdata
-
{
-
float4 vertex : POSITION;
-
};
-
-
struct v2f
-
{
-
float4 vertex : SV_POSITION;
-
};
-
-
//渲染cube的颜色
-
float4 _CubeColor;
-
-
v2f vert (appdata v)
-
{
-
v2f o;
-
//只进行最简单的MVP变换
-
o.vertex = UnityObjectToClipPos(v.vertex);
-
return o;
-
}
-
-
fixed4 frag (v2f i) : SV_Target
-
{
-
//返回单色调
-
return _CubeColor;
-
}
-
ENDCG
-
}
-
-
//渲染描边的pass
-
Pass
-
{
-
//这里剔除正面渲染,是因为会遮挡掉cube渲染,所以剔除掉
-
Cull front
-
CGPROGRAM
-
-
#pragma vertex vert
-
#pragma fragment frag
-
-
#include "UnityCG.cginc"
-
-
struct appdata
-
{
-
float4 vertex : POSITION;
-
float4 normal : NORMAL; /*使用NORMAL语义绑定顶点法向量*/
-
};
-
-
struct v2f
-
{
-
float4 vertex : SV_POSITION;
-
float4 normal : TEXCOORD1;
-
};
-
-
float4 _EdgeColor; /*描边的颜色*/
-
float _EdgeThick; /*描边的粗细*/
-
-
v2f vert (appdata v)
-
{
-
v2f o;
-
//对顶点坐标进行法向量方向偏移
-
v.vertex += v.normal * _EdgeThick;
-
o.vertex = UnityObjectToClipPos(v.vertex);
-
return o;
-
}
-
-
fixed4 frag (v2f i) : SV_Target
-
{
-
//返回描边颜色
-
return _EdgeColor;
-
}
-
ENDCG
-
}
-
}
-
}
效果图如下:
给予了一个红色的外描边。
但是实际会发现一个问题,如下的gif:
一眼就看出问题所在了,因为我们只将顶点沿着法向量方向平移,所以在平移距离过大的情况下,给人的视觉感受就是“面片分离”了,同时也能看出,unity自带的cube并非是8个顶点12个三角面,而是24个顶点12个三角面,共用的顶点全部都是分离开的,这也和引擎理解共用顶点和法向量相关,同时也涉及到渲染优化的问题,如下图:
回到正题,为了解决上面那种“面片分离”的情况,我们给每个模型顶点一个“虚拟法向量”,法向量方向为模型中心点到顶点的朝向向量,示意图如下:
接着我们实现我们的“虚拟法向量”描边代码,如下:
-
//渲染描边的pass
-
Pass
-
{
-
//这里剔除正面渲染,是因为会遮挡掉cube渲染,所以剔除掉
-
Cull front
-
CGPROGRAM
-
-
#pragma vertex vert
-
#pragma fragment frag
-
-
#include "UnityCG.cginc"
-
-
struct appdata
-
{
-
float4 vertex : POSITION;
-
float4 normal : NORMAL; /*使用NORMAL语义绑定顶点法向量*/
-
};
-
-
struct v2f
-
{
-
float4 vertex : SV_POSITION;
-
};
-
-
vector _CubeCenter; /*cube的世界空间中心*/
-
float4 _EdgeColor; /*描边的颜色*/
-
float _EdgeThick; /*描边的粗细*/
-
-
v2f vert (appdata v)
-
{
-
v2f o;
-
//起算这个奇异的法向量
-
//首先将世界空间center变换到建模空间,然后计算“虚拟”法向量
-
float4 cube_model_center = mul(unity_WorldToObject,_CubeCenter);
-
float3 strange_normal = normalize(v.vertex.xyz - cube_model_center.xyz);
-
//计算法向量偏移量
-
float4 normal_offset = float4(stangne_normal * _EdgeThick,0);
-
v.vertex += normal_offset;
-
o.vertex = UnityObjectToClipPos(v.vertex);
-
return o;
-
}
-
-
fixed4 frag (v2f i) : SV_Target
-
{
-
//返回描边颜色
-
return _EdgeColor;
-
}
-
ENDCG
-
}
这里我只贴上描边的pass渲染,最后的效果图如下:
是不是正常了,美中不足的就是模型的世界空间中心点需要使用脚本实时传入。
顺便说一下,还有一种方法可以得到相同的效果,后面再来实现。
so,我闲着无聊就继续更新着色Shader。