C for Graphic:网格描边特效

      实际上如果我们完整经历过之前的学习,可以说shader着色器对于大家来说完全就算入门了。后面的着色器效果实现,可以自行研究,或看书或看源码等学习。

      之后呢我会持续更新一些常用、炫酷的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渲染,最后的效果图如下:

      

      是不是正常了,美中不足的就是模型的世界空间中心点需要使用脚本实时传入。

      想起来还可以使用缩放矩阵进行扩展,所以继续来实现,pass渲染代码如下:

      

Pass {
			Cull front
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata {
				float4 vertex : POSITION;
				float4 normal : NORMAL;
			};

			struct v2f {
				float4 vertex : SV_POSITION;
			};

			vector _CubeCenter;
			float4 _EdgeColor;
			float _EdgeScale;		/*缩放系数*/

			v2f vert (appdata v) {
				v2f o;
				//构建缩放矩阵
				float4x4 _matscale = float4x4(_EdgeScale, 0, 0, 0,
					0, _EdgeScale, 0, 0,
					0, 0, _EdgeScale, 0,
					0, 0, 0, 1);
				float4 vs = mul(_matscale, v.vertex);
				o.vertex = UnityObjectToClipPos(vs);
				return o;
			}

			fixed4 frag (v2f i) : SV_Target {
				return _EdgeColor;
			}
			ENDCG
		}

      效果图如下:

     

      顺便说一下,还有一种方法可以得到相同的效果,后面再来实现。

      so,我闲着无聊就继续更新着色Shader。

      

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值