x光材质怎么调【Shader案例】透视高亮、X光效果

新闻资讯2026-04-21 00:55:12

原理:利用ZTest Greater深度测试大于条件,来为物体被遮挡的像素片元进行修改为透视时的颜色,颜色为视角向量和法线向量的余弦值乘上透视颜色和自定义的控制值。

Shader "透视描边效果/OverwatchOutlineShader"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_OutlineColor("Outline Color", Color) = (1,1,1,1)
		_Idensity("Idensity", Float) = 1 //用于调整透视颜色强弱
	}
		SubShader
		{
			Tags { "RenderType" = "Opaque" }
			LOD 100

			Pass
			{
				Blend SrcAlpha OneMinusSrcAlpha //关键:美观、肯定生效,我使用这种方式,外加一个float来控制透视颜色程度
				//Blend SrcAlpha One //关键:美观、但偶尔会失效(例如当画面的曝光度很高时,无法正常透视)
				ZTest Greater//关键 (深度测试为当前片元深度值大于屏幕缓冲区深度值则进行渲染,即深度值大于屏幕缓冲区深度值意味着这个片元是被遮挡着的
				Lighting Off //非必须
				ZWrite Off //关键:一定要关闭深度写入,不然渲染会不正常,因为深度测试通过后,
				//如果还写入深度的话,会让物体局部遮挡也算入透视 什么叫局部遮挡这个会在文章截图说明, 你也可以自己测试

				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "UnityCG.cginc"

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

				struct v2f
				{
					float4 vertex : SV_POSITION;
					float3 normal : TEXCOORD0;
					float4 worldPos : TEXCOORD1;
				};

				sampler2D _MainTex;
				float4 _MainTex_ST;
				fixed4 _OutlineColor;
				float _Idensity;

				v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.normal = UnityObjectToWorldNormal(v.normal);
					o.worldPos = mul(unity_ObjectToWorld, v.vertex);
					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos));
					float NdotV = 1 - dot(worldView, i.normal);
					return _OutlineColor * NdotV * (1 + _Idensity);
				}
				ENDCG
			}

			//第二个Pass用于正常的情况渲染, 正常情况是ZTest LEqual (你也可以不写ZTest Less)因为默认就是ZTest LEqual
			Pass
			{
				ZTest Less //可不写,可写ZTest LEqual
				//下面是你正常渲染情况的渲染shader代码,假如你有多个材质球的情况, 
				//PS:一定要注意不能出现ZTest Greater 或 ZTest 大于等于 同时存在ZWrite On 因为这样会造成局部透视高亮
				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;
				};

				sampler2D _MainTex;
				float4 _MainTex_ST;
				fixed4 _OutlineColor;

				v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.uv, _MainTex);
					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					fixed4 col = tex2D(_MainTex, i.uv);
					return col;
				}
				ENDCG
			}
		}
}

问题一:由于物体上有深度测试大于或大于等于 且开启深度写入的Shader代码,就可能会出现如下情况。

你看到的不正常部分是因为这部分的像素片元深度小于屏幕深度缓冲区的深度值,因为这部分区域不是一次渲染结束的,我推测起码经过了2次渲染,第一次渲染你看到的背面部分,此时深度值写入更新为背部部分的深度值,第二次渲染正面部分(不正常的部分)此时这部分的深度值肯定是小于背部深度值的,所以就按照正常渲染走了,所以就不正常了,正常应该还是大于屏幕深度值,而不是小于屏幕深度值,正是因为开启了深度写入,导致在第一次渲染背部时进行了更新屏幕深度缓冲区为背部深度值。

解决方法:检查这个物体所有的材质球上的Shader代码,把全部深度测试为大于或大于等于的Pass或SubShader部分的深度写入关闭即可。

问题二:物体身上有另一个材质球其Shader有深度测试大于条件的情况,会进行渲染出这个材质球的效果。

解决方法:深度测试改为小于或小于等于。(深度测试改成大于情况是极其少数情况的)

问题三:

原因:有另一个Unity内置材质球影响物体,材质球shader为Mobile/Diffuse 或 Mobile/Unlit(Supports Lightmap) 等都会造成这种物体局部部分也会作为透视情况。

解决方法:从官网下载同Unity版本的Shader源码,然后创建一个Shader,内容直接复制粘贴下载下来的Shader源码即可,例如创建一个MyDiffuse.shader,然后拷贝Mobile/Diffuse的源码到MyDiffuse.shader文件即可,用这个MyDiffuse.shader替换掉直接引用内置的Mobile/Diffuse (很懵逼不知道具体原因是什么,因为本人也是新手 知道的大佬可以说下为什么会造成这种情况)

如果解决方法无效,那么你就可以通过注释一部分Pass来测试,究竟是什么原因造成这个问题了,我怀疑还是因为深度写入问题,例如前部分像素先写入深度,后部分像素就算被遮挡,然后就造成了后部分像素透视了。