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

20 篇文章 1 订阅

效果如下:

在这里插入图片描述
在这里插入图片描述

或者只显示线框,如下:

在这里插入图片描述
在这里插入图片描述

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

实现思路:

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

在这里插入图片描述

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

在这里插入图片描述

红色范围则是我们要找的边缘附近的部分,比如这里的边长是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
		}
    }
}

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity实现描边效果,可以通过编写Shader实现。具体实现方法如下: 1. 首先,在Shader中需要先声明一个描边颜色的变量: ```csharp // 描边颜色 _Color ("Outline Color", Color) = (0,0,0,1) ``` 2. 接着,需要在Shader中将表面进行放大,然后将描边颜色填充到放大后的表面上: ```csharp // 放大表面,并填充描边颜色 float2 borderOffset[4] = {float2(1,0), float2(-1,0), float2(0,1), float2(0,-1)}; float border = _OutlineWidth * (1.0 / _ScreenParams.z); float4 borderColor = _Color; float4 c; for (int i = 0; i < 4; i++) { c = tex2D(_MainTex, i.uv + border * borderOffset[i]); borderColor.a *= c.a; o.Normal += borderColor.a * borderOffset[i]; } ``` 这里,`borderOffset`表示描边的偏移量,`border`表示描边的宽度,`borderColor`表示描边的颜色,`c`表示当前像素点的颜色值。在循环中,依次对当前像素点的上、下、左、右四个方向进行采样,并将采样到的颜色值和描边颜色进行叠加。 3. 最后,在Shader中将表面的颜色和描边颜色进行混合: ```csharp // 混合表面颜色和描边颜色 o.Albedo = lerp(_Color.rgb, o.Albedo, o.Normal); ``` 这里使用了`lerp`函数来对表面颜色和描边颜色进行混合,根据描边的程度不同,可以调整`_Color`的alpha值来控制描边显示程度。 综上所述,完整的描边Shader代码如下: ```csharp Shader "Custom/Outline" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Outline Color", Color) = (0,0,0,1) _OutlineWidth ("Outline Width", Range(0, 2)) = 1 _ScreenParams ("Screen Params", Vector) = (0,0,0,0) } SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float4 Normal : TEXCOORD1; }; sampler2D _MainTex; float4 _Color; float _OutlineWidth; float4 _ScreenParams; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.Normal = float4(0,0,0,0); return o; } fixed4 frag (v2f i) : SV_Target { float2 borderOffset[4] = {float2(1,0), float2(-1,0), float2(0,1), float2(0,-1)}; float border = _OutlineWidth * (1.0 / _ScreenParams.z); float4 borderColor = _Color; float4 c; for (int j = 0; j < 4; j++) { c = tex2D(_MainTex, i.uv + border * borderOffset[j]); borderColor.a *= c.a; i.Normal += borderColor.a * borderOffset[j]; } i.Normal.a = 1.0; i.Normal = normalize(i.Normal); fixed4 col = tex2D(_MainTex, i.uv); col.rgb = lerp(_Color.rgb, col.rgb, i.Normal); col.a = col.a * i.Normal.a; return col; } ENDCG } } FallBack "Diffuse" } ``` 使用该Shader,你可以在游戏中对需要描边的物体进行材质替换,从而实现描边效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值