欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity随记(八) shader实现立方体Cube显示边框,描边

程序员文章站 2024-02-14 10:11:52
...

效果如下:

Unity随记(八) shader实现立方体Cube显示边框,描边
Unity随记(八) shader实现立方体Cube显示边框,描边

或者只显示线框,如下:

Unity随记(八) shader实现立方体Cube显示边框,描边
Unity随记(八) shader实现立方体Cube显示边框,描边

注意此效果仅仅针对部分模型生效,比如unity内置的立方体Cube,因为采用的实现方式比较依赖顶点UV值。

实现思路:

通过UV值判断,处在立方体边的边缘附近(接近0和接近1的部分)的部分可见,而其他部分透明度设为0,作为不可见部分,而具体要怎么判断是否在边附近。要从内置的cube顶点参数来找线索了,如果仅仅是为了显示一个立方体模型的话,用八个顶点就够了,也就是说立方体的每个角就一个顶点,一个面两个三角形就总共十二个三角形就搞定,的确,如果自己手动尝试构造一个立方体Mesh八个顶点是足够的,但是如果需要贴图,并且要求每个面都能正常且完整的显示这张贴图的话,就会发现八个顶点是基本上达不到要求的(就是立方体的每个面都像一个Quad一样的显示贴图,反正我是没有摸索出这样的妙招),可以观察到unity内置的Cube构造的立方体Mesh用了24个顶点,才能达到立方体的每个面都像一个Quad一样的显示贴图,当然多出来的顶点不仅仅是解决UV问题,还有normal等,这里用不到所以只关注UV即可。

Unity随记(八) shader实现立方体Cube显示边框,描边

额,说了这么多其实就是为了引出一个结论,Cube的Mesh的每个面的UV情况其实就是和一个Quad是基本一致的,也和普通的UI控件的Image一样,均匀的沿着U和V方向从0->1,如果不是很确定的话可以用代码获取到Mesh对象,并打印他的顶点中的UV即可验证。

Unity随记(八) shader实现立方体Cube显示边框,描边

红色范围则是我们要找的边缘附近的部分,比如这里的边长是0.1,则边缘范围就是:

uv.x < 0.1 || uv.x > 1 - 0.1 || uv.y < 0.1 || uv.y > 1 - 0.1

撸代码

到此就可以开始着手撸shader代码了。Vertex Shader中就中规中矩的一个MVP转换,传递一个UV即可,主要来看下Fragment Shader中,其实就上面找范围的一个判断,代码如下:

float4 frag(v2f i) : SV_Target 
{
	fixed4 col = fixed4(0,0,0,0);
	if (i.uv.x < _Width || i.uv.x > 1 - _Width || i.uv.y < _Width || i.uv.y > 1 - _Width) 
	{
		col = _Color;
	}
	return  col;
}

不过有点不太满意的是,用到了一个if分支,如果能避免分*就更优雅了。还是Step大法好呀,上面的if分支语句可以这样,去掉分支语句,舒服多了~

col += saturate(step(i.uv.x, _Width) + step(1 - _Width, i.uv.x) + step(i.uv.y, _Width) + step(1 - _Width, i.uv.y)) * _Color;

完整代码如下:

Shader "Vitens/CubeOutline"
{
    Properties
    {
		_Color("Color", color) = (1,1,1,1)
		_Width("Width", range(0,0.5)) = 0.1
    }
    SubShader
    {
		Tags { "Queue"="Transparent" }
		Pass {

  //如果要显示背面的线框,取消下面两个注释即可
			//cull off
			//ZWrite off
			blend srcalpha oneminussrcalpha
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			fixed4 _Color;
			fixed _Width;

			struct a2v {
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			v2f vert(a2v v) {
				v2f o;

				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;

				return o;
			}

			float4 frag(v2f i) : SV_Target {

				fixed4 col = fixed4(0,0,0,0);//Cube的基础颜色
				
				col += saturate(step(i.uv.x, _Width) + step(1 - _Width, i.uv.x) + step(i.uv.y, _Width) + step(1 - _Width, i.uv.y)) * _Color;

				//if (i.uv.x < _Width || i.uv.x > 1 - _Width || i.uv.y < _Width || i.uv.y > 1 - _Width) 
				//{
				//	col = _Color;
				//}

				return  col;
			}

			ENDCG
		}
    }
}