带顶点动画的护盾效果——UnityShader学习笔记


自言自语

最近又是很久没有更新笔记了。原因有二。
一、最近一直再啃一个看起来酷炫的护盾效果 啃了好久啊。直至效果满意 也理解了。 这个是一个同事传授给我的 我算是偷师。
二、前不久难得出去旅游了一趟。五六年了,第一次出去旅游。真开心。不想回来。
所以导致很久没更新笔记。今日就接着更新吧

一、效果

GIF限制大小,就不穿了。 放一个静态图,方便以后能快速查找区分。

在这里插入图片描述

二、C#

与上一笔记代码一样。就不重复贴了

三、Shader

同样源码贴出。并对上个笔记中不理解的地方 重新理解后做了更详细的注释笔记。 谁让我脑子笨呢 不写详细点以后就又看不懂了。

Shader "ShaderFX/ShaderPractiseFX_BeeSphere"
{
    Properties
    {
		[Header(BeeShape  IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
        _EdgeMask ("EdgeMask", 2D) = "White" {}
        [HDR]_EdgeColor("BeeColor",Color) = (1,1,1,1)
        _BeeNoiseMap("EdgeNoiseMap",2D)="white"{}
        [HDR]_BeeColor("BeeEmissive",Color)=(1,1,1,1)

		[Space(10)]
		[Header(RimColor  IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
        [HDR]_rimColor("RimColor",Color)=(1,1,1,1)
        _rimSmooth("RimSmooth",Range(0.001,4))=0.5
        _rimStrenth("RimStrenth",Float)=1
        _ShieldEdgeSmooth("ShieldEdgeSmooth",Range(0,10)) = 0.5
        _ShieldEdgePow("ShieldEdgePow",Range(0,2)) =1

		[Space(10)]
		[Header(Distortion  IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
		[HDR]_DistortionColor("DistortionColor",Color) = (1,1,1,1)
	    _NoiseIntensityDistor("NoiseDistortion",Float) =1
		_DistortionFactor("DistortionFactor",Float) = 1
		_DistortionSmooth ("DistortionSmooth",Float) =1
		_PositionOffset("DistortionPos",Vector) = (0,0,0,0)

		[Space(10)]
		[Header(HitWave  IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
		_rampmap("FadeRampTex",2D) = "white"{}
		[HDR]_WaveColor("WaveColor",Color) =(1,1,1,1)
        _NoiseMap ("NoiseMap",2D)="white"{}
		_WaveNoiseIntensity("WaveNoiseIntensity",Float) = 1
		_FadeScale ("WaveFadeScale",Float) = 1
		_FadeSmoosth("FadeSmooth",Float) = 0.5
		_FadeIntensity("FadeIntensity",Float) = 1
        _OffsetDistance("OffsetDistance",Float) = 1
        _OffsetWidth("OffsetWidth",Float) = 1
        [HDR]_NewVertexColor("OffsetColor",Color) = (1,1,1,1)

		[Space(10)]
		[Header(FlowMap IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
        [HDR]_FlowColor("FlowColor",Color) = (1,1,1,1)
        _FlowLightMap("FlowLightMap",2D)="white"{}
        _FlowMap("FlowMap",2D)="white"{}
        _FlowSpeed ("FlowSpeed",Float) = 0.2
        _FlowIntensityDirectional("FlowIntensityDirectional",Vector)=(0,0,0,0)

		[Space(10)]
		[Header(Specular  IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)]
        [HDR]_specularColor("SpecularColor",Color) = (1,1,1,1)
        _Gloss("_Gloss",Float) = 100

    }
    SubShader
    {
		//Transparent+1 是为了在透明物体后边渲染 不然看不见透明物体
        Tags { "RenderType"="Opaque"  "Queue" = "Transparent+1"}
		
        LOD 100

        Pass
        {
			//因为是透明队列,所以要注意混合模式 否则不会与前一帧进行有效的混合哦 
            Blend SrcAlpha OneMinusSrcAlpha
			//背面的应该先渲染 所以先剔除正面
            Cull Front
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float2 texcoord1 : TEXCOORD1;
                float2 texcoord2 : TEXCOORD2;
                float2 texcoord3 : TEXCOORD3;
                half3 normal : NORMAL;
            //实例化需要用到的UNITY宏
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float2 uv2 : TEXCOORD2;
                float2 uv3 : TEXCOORD3;
                float4 pos : SV_POSITION;
                float4 posWS : TEXCOORD5;
                float4 scrPos : TEXCOORD6;
                half3 normalWS : TEXCOORD4;
                float newVertexPos : TEXCOORD7;
				float3 newVertPos : TEXCOORD8;
            //实例化需要用到的UNITY宏
                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _EdgeMask;
            float4 _EdgeMask_ST;		 
            sampler2D _NoiseMap;
            float4 _NoiseMap_ST;   
            sampler2D _BeeNoiseMap;
            float4 _BeeNoiseMap_ST;


			float4  _EdgeColor;
            float4 _rimColor;
            float _rimSmooth;
            float _rimStrenth;
            float _ShieldEdgeSmooth;
            float _ShieldEdgePow;
            float4 _BeeColor;

			sampler2D _rampmap;
			float4 _WaveColor;
			float _WaveNoiseIntensity;
			float _FadeScale ;
			float _FadeSmoosth ;
			float _FadeIntensity  ;
            float _OffsetDistance;
			float _OffsetWidth;
            float4 _NewVertexColor;

            float _Gloss;
            float4 _specularColor;

            sampler2D _FlowMap;
            float4 _FlowMap_ST; 
            sampler2D _FlowLightMap;
            float4 _FlowLightMap_ST;
            float4 _FlowIntensityDirectional;
            float4 _FlowColor;
            float _FlowSpeed;

			float4 _DistortionColor;
			float _NoiseIntensityDistor;
			float _DistortionFactor;
			float _DistortionSmooth;
			float3 _PositionOffset;

			//这3个参数时要传进来的
			float MaxParticle;
			float waveSize[100];
		    float4 circleCenter[100];
			//这3个参数时要传进来的

			//声明深度图,拿到深度图算边界
			UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
			float4 _CameraDepthTexture_TexelSize;

            //三平面映射  为的是让冲击波更好的能映射到模型表面 不受UV影响
			half4 TriPlanarnotime (float3 posWS,half3 normal,float smooth,sampler2D textures,float2 flow,float2 uv)
			{
				half3 normalWS = normalize(normal);
				half3 weight = pow(abs(normalWS),smooth);
				//就是一个平均算法 记下就好了
				half3 uvWeight = weight /(weight.x+weight.y+weight.z);
				

				//flow是flowmap的干扰 可以要可以不要  *uv值 是为了做tiling缩放  其实可以不用
				half4 col0 = tex2D(textures,posWS.xy*uv+flow)*uvWeight.z;
                half4 col1 = tex2D(textures,posWS.xz*uv+flow)*uvWeight.y;
                half4 col2 = tex2D(textures,posWS.zy*uv+flow)*uvWeight.x;

                return col0+col1+col2;
			}

        //FlowMap
			half4 FlowMapFunction (float4 posws,float2 uv,float2 uv2,half Wave)
            {
                //先算个时间
                float2 timeDelta = frac(_Time.y*_FlowSpeed)*_FlowIntensityDirectional;
                //再算个间隔帧时间
                float2 timeNext =frac(_Time.y*_FlowSpeed+0.5)*_FlowIntensityDirectional;
                //再算个两个时间插值的插值因子
                float timeLerp = abs(frac(_Time.y*_FlowSpeed)*2-1);

                half2 flowDirection = 0.5-tex2D(_FlowMap,uv*_FlowMap_ST.xy+_FlowMap_ST.zw).rg+Wave;

                //前一状态的流动效果
                half2 FlowMap = flowDirection*timeDelta;
				half4 mainTexpre = tex2D(_FlowLightMap,uv*_FlowLightMap_ST.xy+_FlowLightMap_ST.zw+FlowMap);
                //后一状态的流动效果
                half2 flowMapNext = flowDirection*timeNext; 
				half4 mainTex = tex2D(_FlowLightMap,uv*_FlowLightMap_ST.xy+_FlowLightMap_ST.zw+flowMapNext);
                //插值两个流动效果 减少顿挫感
                half4 col =  lerp(mainTexpre,mainTex,timeLerp);
				//做个MASK 是为了遮挡UV拉伸的地方
				float mask  = 1-saturate(pow(1-smoothstep(uv2.y,1,0.98),1));
				col *= 	mask;
                //输出最终效果
                return col;            
            }

        //计算可以驱动整块多边形进行顶点动画的方法
			float3 VertexToFaceCenterFirst (float3 worldPos,float3 normalWS)
            {
                   //利用模型中心点,顶点法线 等数据算出顶点到多边形面中心点的向量,以此来移动顶点到模型法向量的中心点位置 
                   //从而使得多边形的顶点能够统一朝一个方向变化
                   float3 centerPositionWS = mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
                   float3 comparePositionWS = worldPos - centerPositionWS;
				   //Rejection 算法
                   float3 newPos = comparePositionWS -normalWS* dot(comparePositionWS,normalWS)/dot(normalWS,normalWS);  
				   return newPos;
            }

            float3 VertexToFaceCenter (float3 worldPos,float3 normalWS)
            {
                   //利用模型中心点,顶点法线 等数据算出顶点到多边形面中心点的向量,以此来移动顶点到模型法向量的中心点位置 
                   //从而使得多边形的顶点能够统一朝一个方向变化
				   float3 temp = VertexToFaceCenterFirst (worldPos,normalWS);
				   float3 newVertexPosition = worldPos.xyz -temp;
                   return newVertexPosition;
            }

	    //点击冲击波,这个直接copy上个笔记内容的东西
			half FinalWave (float3 worldPos,float2 uv)
			{
				half ramp;
				for (int j = 0;  j<MaxParticle; j++)
				{
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
				//1 大概思路就是先求点击生成粒子穿过来的冲击波范围的圆形大小,用世界坐标点来算. 																																										|
				//2 利用粒子传过来的大小来算出这个圆形区域内形成的冲击波的基本圈儿																																															|
				//3 再利用一个参数模拟一下淡出淡入的效果. 与算平滑的方法一样  用除法  由于淡入其实是从小到大的缩放 并且很快速 所以这里相当于只是做了淡出效果																    |
				//4 最后看需求是否添加一个贴图来影响一下刚求出的这个圈儿的外形  当然 噪声图和三平面映射也都是看情况加																																    |
				//5 需要注意的是,由于C#脚本传进来的是两个数组,所以需要用for循环来遍历每个数组里的位置和大小信息, 才能让生成的每个粒子的信息 生成所有的像素 C#要传数组进来 这个还得去研究理解下.	    |
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

					//求距离  函数作用是求空间中任意一点到物体表面的最小距离。此间我们假设坐标原点为000中心点
					//中心点到任意一点像素的最小距离可有如下两个函数求出. 任意一点越接近000点则返回值越小越黑.减去半径后则可以求画出相应的圆形.
					//float dis = length(circleCenter - i.posWS.xyz);
					float dis = distance(circleCenter[j],worldPos);
					//再用dis -  传进来的粒子大小,得到一个圈儿的大小
					float circleSize = dis - waveSize[j];
					float noisemap = tex2D(_NoiseMap, uv).r;
					//三平面映射方法调用		如果需要调用flowmap 则还要给方法增加参数 这里先不用
					//float noisemap = TriPlanarnotime(worldPos,normal,0.5,noise,0,uv2).r;

					//淡出范围和淡出平滑																				 
					float fadesize =1- dis /_FadeScale;
					float fadeSmooth =saturate( fadesize * _FadeIntensity);
				    //以上内容今后看可能就不理解了. 那就操作一遍  一行一行return输出看过程
					float circleCol = saturate((circleSize + noisemap*_WaveNoiseIntensity)/_FadeSmoosth);
				    float wave = tex2D (_rampmap,float2(circleCol,0.5)).r;
					float wavefade = wave *fadeSmooth;
					ramp += wavefade;
				}
				return clamp(0,1,ramp);
			}

        //计算texLOD的点击冲击波函数 用于顶点着色器中
			half FinalWaveVertex (float3 worldPos,float smooth,float2 uv2)
			{
				half ramp;
				for (int j = 0;  j<MaxParticle; j++)
				{
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
				//1 大概思想就是先求点击生成粒子穿过来的冲击波范围的圆形大小,用世界坐标点来算. 																																										|
				//2 利用粒子传过来的大小来算出这个圆形区域内形成的冲击波的基本圈儿																																															|
				//3 再利用一个参数模拟一下淡出淡入的效果. 与算平滑的方法一样  用除法  由于淡入其实是从小到大的缩放 并且很快速 所以这里相当于只是做了淡出效果																    |
				//4 最后看需求是否添加一个贴图来影响一下刚求出的这个圈儿的外形  当然 噪声图和三平面映射也都是看情况加																																    |
				//5 需要注意的是,由于C#脚本传进来的是两个数组,所以需要用for循环来遍历每个数组里的位置和大小信息, 才能让生成的每个粒子的信息 生成所有的像素 C#要传数组进来 这个还得去研究理解下.	    |
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

					//求距离  函数作用是求空间中任意一点到物体表面的最小距离。此间我们假设坐标原点为000中心点
					//中心点到任意一点像素的最小距离可有如下两个函数求出. 任意一点越接近000点则返回值越小越黑.减去半径后则可以求画出相应的圆形.
					//float dis = length(circleCenter - i.posWS.xyz);

                    float dis = distance(circleCenter[j],worldPos);
					//再用dis -  传进来的粒子大小,得到一个圈儿的大小
					float circleSize = dis - waveSize[j];
					float noisemap = tex2Dlod(_NoiseMap, float4(uv2,0,0)).r;
					//三平面映射方法调用		如果需要调用flowmap 则还要给方法增加参数 这里先不用
					//float noisemap = TriPlanarlod(worldPos,normal,0.5,noise,0,uv2).r;

					//淡出范围和淡出平滑																				 
					float fadesize =1- dis /_FadeScale;
					float fadeSmooth =saturate( fadesize * _FadeIntensity);

					float circleCol = saturate((circleSize + noisemap*_WaveNoiseIntensity*0.2)/smooth);
				    float wave = tex2Dlod (_rampmap,float4(circleCol,0.5,0,0)).r;
					float wavefade = wave *fadeSmooth;
					ramp += wavefade;
				}
				return saturate(ramp);
			}

		//溶解算法 重新定义函数 用于顶点着色器 溶解时缩放多边形
			half DistortionFunction (float3 newWorldPos,float2 uv)
			{
				//先算出球体中心点的相对位置	   用顶点世界坐标 减去 模型中心点的世界坐标空间下的位置	 模型中心点一般在DCC软件里居中 也就是000点的位置  点的位置有意义不是矢量 所以W值为1
				float3 centerpos =newWorldPos - mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
				//传入溶解起始点位置  
				float3 localPos = _PositionOffset;
				//计算一个动画的noise干扰
				half distortionNoise = tex2Dlod(_NoiseMap,float4(uv+_Time.y*0.05,0,0)).r*_NoiseIntensityDistor;
				//通过distance函数 算出溶解黑白遮罩范围
				half dispos = distance(centerpos,localPos);
				//再用范围减去一个值算出边界 减去noise干扰值 增加扰动效果  最后除以一个值来控制溶解变宽的平滑过渡范围
				half distortion = saturate((dispos - _DistortionFactor-distortionNoise)/_DistortionSmooth);
				return distortion;
			}

            v2f vert (a2v v)
            {
                v2f o;
            //实例化需要用到的UNITY宏
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                UNITY_TRANSFER_INSTANCE_ID(v,o);

                o.normalWS = (UnityObjectToWorldNormal(v.normal));
                o.posWS = mul(unity_ObjectToWorld,v.vertex);
                o.uv0 = v.texcoord0;
                o.uv1 = v.texcoord1;
                o.uv2 = v.texcoord2;
                o.uv3 = v.texcoord3;

			//计算点击效果对顶点的偏移影响
				//算出点击的中心点位置
                float3 newVertexPositionWS =  VertexToFaceCenter(o.posWS.xyz,o.normalWS);
				//算出此位置下点击对顶点的位移变化值
                half waveVertex = FinalWaveVertex (newVertexPositionWS,_OffsetWidth,o.uv2);

			//溶解顶点缩放
				float3 newVertPosCenter = VertexToFaceCenterFirst(o.posWS,o.normalWS);
			    half distortionVertex  = DistortionFunction(newVertexPositionWS,o.uv2);
				//用负号做个反向,来表示出现消失的正负方向
				float3 distortionNewPos = -distortionVertex*newVertPosCenter+o.posWS.xyz;
				//再将顶点溶解转换到模型空间下  好做相对位置的位移缩放
				float3 localDistiortion = mul (unity_WorldToObject,distortionNewPos);

				//用算好的偏移值 去在他的法线方向上做扰动。 这里注意对法线扰动是用乘法来做 与outline的xy轴做乘法计算是一样的  然后把扰动值 作为顶点偏移值 累加到vertex上
                v.normal *= waveVertex*0.01;
				//这一步就是相当于把顶点按照法线方向外拓 也就是按照发现方向位移
                float3 newVertex =v.vertex.xyz+v.normal*_OffsetDistance+localDistiortion;
				//因为之前都是在相对坐标下进行 所以要再减去原本的顶点坐标 来转换成绝对坐标  原理同世界空间坐标 减去 模型顶点自身坐标在世界空间下的表示一样	以防止起始位置会随模型移动而相对于模型本身位置发生改变
			    newVertex -=v.vertex.xyz;
				//最后再把相应的计算结果存到插槽中 传到片元着色器
                o.pos = UnityObjectToClipPos(newVertex);
                o.scrPos = ComputeScreenPos(o.pos);
                o.newVertexPos = waveVertex;
				o.newVertPos = newVertexPositionWS;

                return o;
            }

			
            half4 frag (v2f i) : SV_Target
            {

			//点击释放冲击波  
				half wave =  saturate(FinalWave (i.posWS , i.uv3));
				//给冲击波加个颜色 
				half4 waveCol = wave *_WaveColor;
                //把受顶点动画影响的片也进行着色 最后加到最终结果中
                half4 newVertexColor = i.newVertexPos*_NewVertexColor;

            //多边形遮罩
                half beeTex = tex2D(_EdgeMask,i.uv0).r;
                //噪声扰动图
				half beeNoise=tex2D(_BeeNoiseMap,i.uv3*_BeeNoiseMap_ST.xy+_BeeNoiseMap_ST.zw*_Time.y*0.01);
                //把噪声一加完成
                half4 beeColor = beeTex*_EdgeColor+beeTex*beeNoise*_BeeColor;
				
            //求交叉边缘	   这一套乱七八糟的操作 只能背了
				//除以分量w来得到屏幕空间的顶点坐标位置
				float4 screenPos  = i.scrPos/i.scrPos.w;
				//这里需要做个近裁剪面的正负值判断 来修正Z值
				screenPos.z = UNITY_NEAR_CLIP_VALUE>=0 ? screenPos.z : screenPos.z*0.5+0.5;
				//利用屏幕空间中XY坐标也就是像素位置来采样深度图 得到实际深度值
				float depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,screenPos.xy));
				//这步推测是为了根据深度值在屏幕空间下的交叉边界的像体现
				float distanceDepth =saturate(abs((depth - LinearEyeDepth(screenPos.z))));
				//然后根据结果算出边界的和非边界的地方. 这里这么计算总觉得就把平滑过渡的计算给干掉了 气氛上感觉还要除以一个smooth值 所以我擅自加上看看
				float EdgeSmooth =(1-saturate(smoothstep(0,_ShieldEdgePow,distanceDepth)))/_ShieldEdgeSmooth;

			//溶解
				float3 centerpos =i.newVertPos - mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
				float3 localPos = _PositionOffset;
				//用uv2 来模拟闪烁效果
				half distortionNoise = tex2D(_NoiseMap,i.uv2+_Time.y*0.05).r*_NoiseIntensityDistor;
				half dispos = distance(localPos,centerpos);
				half distortion = saturate((dispos - _DistortionFactor+distortionNoise)/_DistortionSmooth);
				half distortionStep = step(0,saturate((dispos - _DistortionFactor+distortionNoise)));
				half4 distorCol = (distortion+distortion*distortionStep)*_DistortionColor;

            //边缘光 NV 计算 要复杂点的话就用 shlick的 近似公式来计算
                 //shlick近似等式 为   fresnel = f0 +(1-f0)*pow(1-NV,5);
                half3 viewDir = normalize(UnityWorldSpaceViewDir(i.posWS));
                half3 normalDir = normalize(i.normalWS);
                half3 lightDir = normalize(UnityWorldSpaceLightDir(i.posWS));
                half NV = abs(dot(viewDir,normalDir));
                //把边缘交叉的部分算边缘光里  
                half rimArea = saturate(1 - NV);
                half4 rimColor =saturate(((rimArea+EdgeSmooth)+_rimStrenth)/_rimSmooth)*_rimColor;

            //再加个表面高光吧 看起来可能更精致点
                half3 Hdir = normalize(viewDir+lightDir);
                half NH = saturate(dot(normalDir,Hdir));
                half spec = abs(pow(NH,_Gloss));
                half4 speccolor = spec*_specularColor;

            //护盾流动FlowMap
                half2 flowDirectionalIntensity = _FlowIntensityDirectional.xy;
                half4 FlowMapColor = FlowMapFunction(i.posWS,i.uv3,i.uv3,wave)*_FlowColor;    
                half4 finalCol = rimColor+beeColor+FlowMapColor+waveCol+newVertexColor+speccolor;

		   //溶解边缘光插值合并
				//beeColor.rgb*4 为个人调整亮度值的经验系数
				half3 col = lerp(finalCol.rgb,distorCol.rgb+distorCol.rgb*beeTex*beeColor.rgb*4,saturate(distortion));
				//Alpha计算 在透明物体计算的过程中 尤为重要 不要搞错最终输出的alpha值到底会受到哪些方面的影响
                half finalAlpha =saturate(1-distortion)*finalCol.a;
                //将这个pass由于是先剔除正面的 放在背面渲染  所以亮度相对降低一点,做出点前后层次感 0.65也是一个经验值  所以计算在内 也可以直接给finalAlpha值进行一个缩小
                return  float4(col*0.65,finalAlpha);

            }
            ENDCG
        }
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Back
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float2 texcoord1 : TEXCOORD1;
                float2 texcoord2 : TEXCOORD2;
                float2 texcoord3 : TEXCOORD3;
                half3 normal : NORMAL;
            //实例化需要用到的UNITY宏
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float2 uv2 : TEXCOORD2;
                float2 uv3 : TEXCOORD3;
                float4 pos : SV_POSITION;
                float4 posWS : TEXCOORD5;
                float4 scrPos : TEXCOORD6;
                half3 normalWS : TEXCOORD4;
                float newVertexPos : TEXCOORD7;
				float3 newVertPos : TEXCOORD8;
            //实例化需要用到的UNITY宏
                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _EdgeMask;
            float4 _EdgeMask_ST;		 
            sampler2D _NoiseMap;
            float4 _NoiseMap_ST;   
            sampler2D _BeeNoiseMap;
            float4 _BeeNoiseMap_ST;


			float4  _EdgeColor;
            float4 _rimColor;
            float _rimSmooth;
            float _rimStrenth;
            float _ShieldEdgeSmooth;
            float _ShieldEdgePow;
            float4 _BeeColor;

			sampler2D _rampmap;
			float4 _WaveColor;
			float _WaveNoiseIntensity;
			float _FadeScale ;
			float _FadeSmoosth ;
			float _FadeIntensity  ;
            float _OffsetDistance;
			float _OffsetWidth;
            float4 _NewVertexColor;

            float _Gloss;
            float4 _specularColor;

            sampler2D _FlowMap;
            float4 _FlowMap_ST; 
            sampler2D _FlowLightMap;
            float4 _FlowLightMap_ST;
            float4 _FlowIntensityDirectional;
            float4 _FlowColor;
            float _FlowSpeed;

			float4 _DistortionColor;
			float _NoiseIntensityDistor;
			float _DistortionFactor;
			float _DistortionSmooth;
			float3 _PositionOffset;

			//这3个参数时要传进来的
			float MaxParticle;
			float waveSize[100];
		    float4 circleCenter[100];
			//这3个参数时要传进来的

			//声明深度图,拿到深度图算边界
			UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
			float4 _CameraDepthTexture_TexelSize;

            //三平面映射  为的是让冲击波更好的能映射到模型表面 不受UV影响
			half4 TriPlanarnotime (float3 posWS,half3 normal,float smooth,sampler2D textures,float2 flow,float2 uv)
			{
				half3 normalWS = normalize(normal);
				half3 weight = pow(abs(normalWS),smooth);
				half3 uvWeight = weight /(weight.x+weight.y+weight.z);
				

				//flow是flowmap的干扰 可以要可以不要  *uv值 是为了做tiling缩放  其实可以不用
				half4 col0 = tex2D(textures,posWS.xy*uv+flow)*uvWeight.z;
                half4 col1 = tex2D(textures,posWS.xz*uv+flow)*uvWeight.y;
                half4 col2 = tex2D(textures,posWS.zy*uv+flow)*uvWeight.x;

                return col0+col1+col2;
			}

        //FlowMap
			half4 FlowMapFunction (float4 posws,float2 uv,float2 uv2,half Wave)
            {
                //先算个时间
                float2 timeDelta = frac(_Time.y*_FlowSpeed)*_FlowIntensityDirectional;
                //再算个间隔帧时间
                float2 timeNext =frac(_Time.y*_FlowSpeed+0.5)*_FlowIntensityDirectional;
                //再算个两个时间插值的插值因子
                float timeLerp = abs(frac(_Time.y*_FlowSpeed)*2-1);

                half2 flowDirection = 0.5-tex2D(_FlowMap,uv*_FlowMap_ST.xy+_FlowMap_ST.zw).rg+Wave;

                //前一状态的流动效果
                half2 FlowMap = flowDirection*timeDelta;
				half4 mainTexpre = tex2D(_FlowLightMap,uv*_FlowLightMap_ST.xy+_FlowLightMap_ST.zw+FlowMap);
                //后一状态的流动效果
                half2 flowMapNext = flowDirection*timeNext; 
				half4 mainTex = tex2D(_FlowLightMap,uv*_FlowLightMap_ST.xy+_FlowLightMap_ST.zw+flowMapNext);
                //插值两个流动效果 减少顿挫感
                half4 col =  lerp(mainTexpre,mainTex,timeLerp);
				float mask  = 1-saturate(pow(1-smoothstep(uv2.y,1,0.98),1));
				col *= 	mask;
                //输出最终效果
                return col;            
            }

        //计算可以驱动整块多边形进行顶点动画的方法
			float3 VertexToFaceCenterFirst (float3 worldPos,float3 normalWS)
            {
                   //利用模型中心点,顶点法线 等数据算出顶点到多边形面中心点的向量,以此来移动顶点到模型法向量的中心点位置 
                   //从而使得多边形的顶点能够统一朝一个方向变化
                   float3 centerPositionWS = mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
                   float3 comparePositionWS = worldPos - centerPositionWS;
                   float3 newPos = comparePositionWS -normalWS* dot(comparePositionWS,normalWS)/dot(normalWS,normalWS);  
				   return newPos;
            }

            float3 VertexToFaceCenter (float3 worldPos,float3 normalWS)
            {
                   //利用模型中心点,顶点法线 等数据算出顶点到多边形面中心点的向量,以此来移动顶点到模型法向量的中心点位置 
                   //从而使得多边形的顶点能够统一朝一个方向变化
				   float3 temp = VertexToFaceCenterFirst (worldPos,normalWS);
				   float3 newVertexPosition = worldPos.xyz -temp;
                   return newVertexPosition;
            }

	    //点击冲击波,这个直接copy原来的
			half FinalWave (float3 worldPos,float2 uv)
			{
				half ramp;
				for (int j = 0;  j<MaxParticle; j++)
				{
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
				//1 大概思路就是先求点击生成粒子穿过来的冲击波范围的圆形大小,用世界坐标点来算. 																																										|
				//2 利用粒子传过来的大小来算出这个圆形区域内形成的冲击波的基本圈儿																																															|
				//3 再利用一个参数模拟一下淡出淡入的效果. 与算平滑的方法一样  用除法  由于淡入其实是从小到大的缩放 并且很快速 所以这里相当于只是做了淡出效果																    |
				//4 最后看需求是否添加一个贴图来影响一下刚求出的这个圈儿的外形  当然 噪声图和三平面映射也都是看情况加																																    |
				//5 需要注意的是,由于C#脚本传进来的是两个数组,所以需要用for循环来遍历每个数组里的位置和大小信息, 才能让生成的每个粒子的信息 生成所有的像素 C#要传数组进来 这个还得去研究理解下.	    |
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

					//求距离  函数作用是求空间中任意一点到物体表面的最小距离。此间我们假设坐标原点为000中心点
					//中心点到任意一点像素的最小距离可有如下两个函数求出. 任意一点越接近000点则返回值越小越黑.减去半径后则可以求画出相应的圆形.
					//float dis = length(circleCenter - i.posWS.xyz);
					float dis = distance(circleCenter[j],worldPos);
					//再用dis -  传进来的粒子大小,得到一个圈儿的大小
					float circleSize = dis - waveSize[j];
					float noisemap = tex2D(_NoiseMap, uv).r;
					//三平面映射方法调用		如果需要调用flowmap 则还要给方法增加参数 这里先不用
					//float noisemap = TriPlanarnotime(worldPos,normal,0.5,noise,0,uv2).r;

					//淡出范围和淡出平滑																				 
					float fadesize =1- dis /_FadeScale;
					float fadeSmooth =saturate( fadesize * _FadeIntensity);

					float circleCol = saturate((circleSize + noisemap*_WaveNoiseIntensity)/_FadeSmoosth);
				    float wave = tex2D (_rampmap,float2(circleCol,0.5)).r;
					float wavefade = wave *fadeSmooth;
					ramp += wavefade;
				}
				return clamp(0,1,ramp);
			}

        //计算texLOD的点击冲击波函数 用于顶点着色器中
			half FinalWaveVertex (float3 worldPos,float smooth,float2 uv2)
			{
				half ramp;
				for (int j = 0;  j<MaxParticle; j++)
				{
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
				//1 大概思想就是先求点击生成粒子穿过来的冲击波范围的圆形大小,用世界坐标点来算. 																																										|
				//2 利用粒子传过来的大小来算出这个圆形区域内形成的冲击波的基本圈儿																																															|
				//3 再利用一个参数模拟一下淡出淡入的效果. 与算平滑的方法一样  用除法  由于淡入其实是从小到大的缩放 并且很快速 所以这里相当于只是做了淡出效果																    |
				//4 最后看需求是否添加一个贴图来影响一下刚求出的这个圈儿的外形  当然 噪声图和三平面映射也都是看情况加																																    |
				//5 需要注意的是,由于C#脚本传进来的是两个数组,所以需要用for循环来遍历每个数组里的位置和大小信息, 才能让生成的每个粒子的信息 生成所有的像素 C#要传数组进来 这个还得去研究理解下.	    |
				//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

					//求距离  函数作用是求空间中任意一点到物体表面的最小距离。此间我们假设坐标原点为000中心点
					//中心点到任意一点像素的最小距离可有如下两个函数求出. 任意一点越接近000点则返回值越小越黑.减去半径后则可以求画出相应的圆形.
					//float dis = length(circleCenter - i.posWS.xyz);

                    float dis = distance(circleCenter[j],worldPos);
					//再用dis -  传进来的粒子大小,得到一个圈儿的大小
					float circleSize = dis - waveSize[j];
					float noisemap = tex2Dlod(_NoiseMap, float4(uv2,0,0)).r;
					//三平面映射方法调用		如果需要调用flowmap 则还要给方法增加参数 这里先不用
					//float noisemap = TriPlanarlod(worldPos,normal,0.5,noise,0,uv2).r;

					//淡出范围和淡出平滑																				 
					float fadesize =1- dis /_FadeScale;
					float fadeSmooth =saturate( fadesize * _FadeIntensity);

					float circleCol = saturate((circleSize + noisemap*_WaveNoiseIntensity*0.2)/smooth);
				    float wave = tex2Dlod (_rampmap,float4(circleCol,0.5,0,0)).r;
					float wavefade = wave *fadeSmooth;
					ramp += wavefade;
				}
				return saturate(ramp);
			}

		//溶解算法 重新定义函数 用于顶点着色器 溶解时缩放多边形
			half DistortionFunction (float3 newWorldPos,float2 uv)
			{
				float3 centerpos =newWorldPos - mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
				float3 localPos = _PositionOffset;
				half distortionNoise = tex2Dlod(_NoiseMap,float4(uv+_Time.y*0.05,0,0)).r*_NoiseIntensityDistor;
				half dispos = distance(centerpos,localPos);
				half distortion = saturate((dispos - _DistortionFactor-distortionNoise)/_DistortionSmooth);
				return distortion;
			}

            v2f vert (a2v v)
            {
                v2f o;
            //实例化需要用到的UNITY宏
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                UNITY_TRANSFER_INSTANCE_ID(v,o);

                o.normalWS = (UnityObjectToWorldNormal(v.normal));
                o.posWS = mul(unity_ObjectToWorld,v.vertex);
                o.uv0 = v.texcoord0;
                o.uv1 = v.texcoord1;
                o.uv2 = v.texcoord2;
                o.uv3 = v.texcoord3;

			//计算点击效果对顶点的偏移影响
                float3 newVertexPositionWS =  VertexToFaceCenter(o.posWS.xyz,o.normalWS);
                half waveVertex = FinalWaveVertex (newVertexPositionWS,_OffsetWidth,o.uv2);

			//溶解顶点缩放
				float3 newVertPosCenter = VertexToFaceCenterFirst(o.posWS,o.normalWS);
			    half distortionVertex  = DistortionFunction(newVertexPositionWS,o.uv2);
				float3 distortionNewPos = -distortionVertex*newVertPosCenter+o.posWS.xyz;
				float3 localDistiortion = mul (unity_WorldToObject,distortionNewPos);

			//用算好的偏移值 去在他的法线方向上做扰动。  然后把扰动值 作为顶点偏移值 累加到vertex上
                v.normal *= waveVertex*0.01;
                float3 newVertex =v.vertex.xyz+v.normal*_OffsetDistance+localDistiortion;

			    newVertex -=v.vertex.xyz;

                o.pos = UnityObjectToClipPos(newVertex);
                o.scrPos = ComputeScreenPos(o.pos);
                o.newVertexPos = waveVertex;
				o.newVertPos = newVertexPositionWS;

                return o;
            }

			
            half4 frag (v2f i) : SV_Target
            {

			//点击释放冲击波  
				half wave =  saturate(FinalWave (i.posWS , i.uv3));
				half4 waveCol = wave *_WaveColor;
                //把受顶点动画影响的片也进行着色 最后加到最终结果中
                half4 newVertexColor = i.newVertexPos*_NewVertexColor;

            //多边形遮罩
                half beeTex = tex2D(_EdgeMask,i.uv0).r;
                //噪声扰动图
				half beeNoise=tex2D(_BeeNoiseMap,i.uv3*_BeeNoiseMap_ST.xy+_BeeNoiseMap_ST.zw*_Time.y*0.01);
                //把噪声一加完成
                half4 beeColor = beeTex*_EdgeColor+beeTex*beeNoise*_BeeColor;
				
            //求交叉边缘	  
				float4 screenPos  = i.scrPos/i.scrPos.w;
				screenPos.z = UNITY_NEAR_CLIP_VALUE>=0 ? screenPos.z : screenPos.z*0.5+0.5;
				float depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,screenPos.xy));
				float distanceDepth =saturate(abs((depth - LinearEyeDepth(screenPos.z))));
				float EdgeSmooth =(1-saturate(smoothstep(0,_ShieldEdgePow,distanceDepth)))/_ShieldEdgeSmooth;

			//溶解
				float3 centerpos =i.newVertPos - mul(unity_ObjectToWorld,float4(0,0,0,1)).xyz;
				float3 localPos = _PositionOffset;
				//用uv2 来模拟闪烁效果
				half distortionNoise = tex2D(_NoiseMap,i.uv2+_Time.y*0.05).r*_NoiseIntensityDistor;
				half dispos = distance(localPos,centerpos);
				half distortion = saturate((dispos - _DistortionFactor+distortionNoise)/_DistortionSmooth);
				half distortionStep = step(0,saturate((dispos - _DistortionFactor+distortionNoise)));
				half4 distorCol = (distortion+distortion*distortionStep)*_DistortionColor;

            //边缘光 NV 计算 要复杂点的话就用 shlick的 近似公式来计算
                 //shlick近似等式 为   fresnel = f0 +(1-f0)*pow(1-NV,5);
                half3 viewDir = normalize(UnityWorldSpaceViewDir(i.posWS));
                half3 normalDir = normalize(i.normalWS);
                half3 lightDir = normalize(UnityWorldSpaceLightDir(i.posWS));
                half NV = abs(dot(viewDir,normalDir));
                //把边缘交叉的部分算边缘光里  
                half rimArea = saturate(1 - NV);
                half4 rimColor =saturate(((rimArea+EdgeSmooth)+_rimStrenth)/_rimSmooth)*_rimColor;

            //再加个表面高光吧 看起来可能更精致点
                half3 Hdir = normalize(viewDir+lightDir);
                half NH = saturate(dot(normalDir,Hdir));
                half spec = abs(pow(NH,_Gloss));
                half4 speccolor = spec*_specularColor;

            //护盾流动FlowMap
                half2 flowDirectionalIntensity = _FlowIntensityDirectional.xy;
                half4 FlowMapColor = FlowMapFunction(i.posWS,i.uv3,i.uv3,wave)*_FlowColor;    
                half4 finalCol = rimColor+beeColor+FlowMapColor+waveCol+newVertexColor+speccolor;

		   //溶解边缘光插值合并
				half3 col = lerp(finalCol.rgb,distorCol.rgb+distorCol.rgb*beeTex*beeColor.rgb*4,saturate(distortion));
                half finalAlpha =saturate(1-distortion)*finalCol.a;
                return  float4(col,finalAlpha);

            }
            ENDCG
        }
    }
}

总结

其实在写这个笔记的时候,已经是做好效果一礼拜之后了。果不其然,很多我都看不懂额。所以重新对每一行进行理解再进行注释。没办法,自己记忆力和脑子越来越不好。年龄和压力摆在这儿。只能用这样的笨办法强迫自己反复去记忆理解吧。 虽然现在学习进度越来越慢,但我还是不会放弃的。 加油老男孩儿。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值