Shader笔记二十——带噪声的能量盾实现

先上效果: 


该效果的实现主要分为两个部分:

  • 能量盾与场景相交处的高光处理
  • 能量盾的纹理变化

能量盾与场景相交处的高光处理

能量盾与场景中其他物体的相交部分的高光,主要是通过比较能量盾自身的深度值与场景内相交的部分物体的深度值,通过两者深度值的差异大小来决定高亮颜色的显示比例插值:

float diff =pow((1-saturate(sceneZ-i.scrPos.z))/1.03,_EdgePow);
finalColor = lerp(finalColor, _HighlightColor, diff);

这里的 i.scrPos.z 为能量盾自身的深度值,而 Scene.z 则是场景内的深度值信息。能量盾自身的深度信息可以通过

//ComputeScreenPos函数,得到归一化前的视口坐标xy,z分量为裁剪空间的z值,范围[-Near,Far] 
o.scrPos = ComputeScreenPos(o.pos);
//COMPUTE_EYEDEPTH函数,将z分量范围[-Near,Far]转换为[Near,Far]
COMPUTE_EYEDEPTH(o.scrPos.z);  

场景内的深度值信息是通过场景深度图进行纹理采样并转化到对应范围:

//获取深度纹理,通过LinearEyeDepth函数将采样的深度纹理值转换为对应的深度范围[Near~Far]
float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)));

为了得到场景中的深度纹理,在变量声明阶段,需要声明该纹理:

//获取深度纹理
sampler2D _CameraDepthTexture;

同时需要对相机进行设置(C#代码):

[RequireComponent(typeof(Camera))]
public class SetCameraDepthMode : MonoBehaviour {
private Camera mainCamera;
public Camera Camera {
    get {
        if (mainCamera == null) {
            mainCamera = GetComponent<Camera>();
        }
        return mainCamera;
    }
}

private void OnEnable()
{
    Camera.depthTextureMode |= DepthTextureMode.Depth;
}
}	

能量盾的纹理变化

纹理变化主要为主纹理和使用噪声纹理控制部分显示与隐藏,主纹理直接采样,噪声纹理的采样值主要用来控制主纹理的透明度,可以直接使用一张噪声纹理,并加上时间变量增加变化效果,这里使用了程序噪声和分形处理,关于 程序噪声 可以参考专栏之前的笔记 程序噪声
主要代码:

//纹理和Mask部分,主要作用是实现基本纹理和噪声变化遮罩
fixed mainTex = 1 - tex2D(_MainTex , i.uv).a;
//fixed mask = tex2D(_MaskTex , i.uv + float2(0,(_Time.y)*_FlowSpeed)).r;
float mask=FlowFBM(i.uv*_GridSize);
fixed4 finalColor = lerp(_MainColor , _HighlightColor , mainTex);
finalColor=lerp(fixed4(0,0,0,0),finalColor,mask);
finalColor.a*=max(0,mask-cos(_AlphaZone*_Time.y))*0.4;  

完整Shader代码:

Shader "Other/ForceField" {
Properties {
	_MainColor ("Color" , Color) = (1,1,1,1)
	_HighlightColor("HighlightColor" ,Color) = (0,0,1,1)
	_EdgePow("Threshold" , Range(1 , 10)) = 1.0
	_RimNum("Rim" , Range(0 , 5)) = 1
	_MainTex("Main Tex" , 2D) = "white"{}
	_MaskTex("Mask Tex" ,  2D) = "white" {}
	_GridSize("GridSize",Float)=30
	_FlowSpeed("Speed" ,Range(1 , 3)) = 2
	_AlphaZone("AlphaZone",Range(0,1))=0.5
}

SubShader {

Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}

Pass{
	Tags { "LightMode"="ForwardBase" }	
	
	Blend SrcAlpha OneMinusSrcAlpha
	ZWrite Off
	Cull Off

	CGPROGRAM

	#include "UnityCG.cginc"
	#include "Assets/Shaders/ShaderLibs/FBM.cginc"

	#pragma vertex vert
	#pragma fragment frag

	#define UNITY_PASS_FORWARDBASE
    #pragma multi_compile_fwdbase

	float4 _MainColor;
	float4 _HighlightColor;
	//获取深度纹理
	sampler2D _CameraDepthTexture;
	float _EdgePow;
	sampler2D _MainTex;
	float4 _MainTex_ST;
	sampler2D _MaskTex;
	float _FlowSpeed;
	float _RimNum;
	float _GridSize;
	float _AlphaZone;


	struct a2v{
		float4 vertex:POSITION;
		float3 normal:NORMAL;
		float2 tex:TEXCOORD0;
	};

	struct v2f{
		float4 pos:POSITION;
		float4 scrPos:TEXCOORD0;
		half3 worldNormal:TEXCOORD1;
		half3 worldViewDir:TEXCOORD2;
		float2 uv:TEXCOORD3;
	};

	float _Noise(in float2 x){return VNoise(x*0.75);}

	float2 Gradn(float2 p){
			float ep=0.09;
			float gradx=_Noise(float2(p.x+ep,p.y))-_Noise(float2(p.x-ep,p.y));
			float grady=_Noise(float2(p.x,p.y+ep))-_Noise(float2(p.x,p.y-ep));
			return float2(gradx,grady);
		}

	float FlowFBM(in float2 p){
			float z=2.;
			float rz=0.;
			float2 bp=p;
			//FBM 8层分形叠加
			for(float i=1.;i<9.;i++){
				//不同的层添加不同的速度 进行不同程度细节分形叠加
				p+=_Time.x*0.006;
				bp+=_Time.x*0.0006;
				//获取梯度,得到当前点的变化趋势(需在叠加前进行计算)
				float2 gr=Gradn(i*p*1.54+_Time.y*0.14*_FlowSpeed)*4.;
				p+=gr*.5;
				//FBM计算
				rz+=(sin(_Noise(p)*7.)*0.5+0.5)/z;
				//插值调整每层之间的效果
				p=lerp(bp,p,0.7);
				//FBM系数变化
				z*=1.4;
				p*=2.;
				bp*=1.9;
			}
			return rz;
		}


	v2f vert (a2v v )
	{
		v2f o;
		o.pos = UnityObjectToClipPos ( v.vertex );
		//ComputeScreenPos函数,得到归一化前的视口坐标xy,z分量为裁剪空间的z值,范围[-Near,Far] 
		o.scrPos = ComputeScreenPos(o.pos);

		float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; 
	
		o.worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
		o.worldNormal = UnityObjectToWorldNormal(v.normal); 

		o.uv = TRANSFORM_TEX(v.tex , _MainTex);
		//COMPUTE_EYEDEPTH函数,将z分量范围[-Near,Far]转换为[Near,Far]
		COMPUTE_EYEDEPTH(o.scrPos.z);
		return o;
	}

	fixed4 frag ( v2f i ) : SV_TARGET
	{
		//纹理和Mask部分,主要作用是实现基本纹理和噪声变化遮罩
		fixed mainTex = 1 - tex2D(_MainTex , i.uv).a;
		//fixed mask = tex2D(_MaskTex , i.uv + float2(0,(_Time.y)*_FlowSpeed)).r;
		float mask=FlowFBM(i.uv*_GridSize);
		fixed4 finalColor = lerp(_MainColor , _HighlightColor , mainTex);
		finalColor=lerp(fixed4(0,0,0,0),finalColor,mask);
		finalColor.a*=max(0,mask-cos(_AlphaZone*_Time.y))*0.4;
	
		//获取深度纹理,通过LinearEyeDepth函数将采样的深度纹理值转换为对应的深度范围[Near~Far]
		float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)));
		float partZ = i.scrPos.z;

		//diff为比较两个深度值,rim在能量盾自身边缘位置加上一层_HighlightColor的颜色
		float diff =pow((1-saturate(sceneZ-i.scrPos.z))/1.03,_EdgePow);
		half rim = pow(1 - abs(dot(normalize(i.worldNormal),normalize(i.worldViewDir))) , _RimNum);

		//最后通过插值混合颜色
		finalColor = lerp(finalColor, _HighlightColor, diff);
		finalColor = lerp(finalColor, _HighlightColor, rim);
		return finalColor;
	}

	ENDCG
	}
}
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值