力场效果(本来就渣,录屏效果是更渣~~)
(1)这里冲击波,主要是利用粒子particle系统,每次点击交互 发射一个粒子,
(2)每个粒子在自己的生命周期中大小都是可以控制的: 由小变大 或者 由大变小 , 总之就是曲线可控。
(3)每个冲击波的大小 和 每个粒子的大小 同步, 粒子决定冲击波
效果中用到知识点 在前面的章节中已经详细介绍了
水波纹
三平面映射
溶解
流光原理
下面直接贴上代码:
Shader "Unlit/ForceFiled_first_HitWave"
{
Properties
{
_NoiseTex ("_NoiseTex", 2D) = "white" {}
_RampTex ("_RampTex", 2D) = "white" {}
// _HitPos("HitPos",vector) = (0,0,0,0)
// _HitSize("_HitSize",float) = 1
_HitSpread("_HitSpread",float) = 1
_NoiseIntensity("_NoiseIntensity",float) = 1
_HitFadeDistance("扩散的距离限制,最后到末尾时会消隐_HitFadeDistance",float) = 5
_HitFadePower("_HitFadePower",float) = 1
_AffectorAmount("_AffectorAmount",float) = 20
/// 溶解值的计算:
_DissolveRampTex("_DissolveRampTex",2D) = "white"{}
_DissolvePoint("溶解开始点_DissolvePoint",vector) = (0,0,0,0)
_DissolveNoisePower("_DissolveNoisePower",float) = 1
_DissolveAmount("_DissolveAmoun溶解程度",float) = 0.5
_DissolveSpread("_DissolveSpread",float) = 1
_DissolveEdgeIntensity("_DissolveEdgeIntensity",float) = 1
// RimColod
_FresnalPower("Fresnal Power",float) = 3
_FresnalScale("_FresnalScale",float) = 0.8
_FresnalBias("_FresnalBias",float) = 0.16
// _FlowMap
_FlowLightTex("_FlowLightTex",2D) = "white"{}
_FlowMap("_FlowMap",2D) = "white"{}
_FlowSpeed("_FlowSpeed",float) = 0.2
_FlowStrength("_FlowStrength",vector) = (0.2,0.2,0,0)
// 三平面映射相关
_Contrast("_Contrast法线遮罩的对比度",float) = 1
_TriPlanarTilling("_TriPlanarTilling 三平面映射的缩放平移",vector) = (1,1,0,0)
// 冲击波强度
_HitWaveIntensity("_HitWaveIntensity",float) = 1.3
//自发光
_EmissColor("_EmissColor",color) = (1,1,1,1)
_EmissIntensity("_EmissIntensity",float) = 2
}
SubShader
{
// 半透明 三件套 : Queue修改 Blend Zwrite
// 计算完的 向量 在 使用前 归一化!!!!
Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
Cull back
Zwrite off
Blend SrcAlpha OneMinusSrcAlpha
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 PivotPos : TEXCOORD2;
float2 uv_FlowMap : TEXCOORD3;
float3 worldNormal : TEXCOORD4;
};
sampler2D _NoiseTex,_RampTex;
float4 _NoiseTex_ST;
float _HitFadeDistance,_HitFadePower,_HitSpread,_NoiseIntensity;
float _AffectorAmount;
uniform float HitSize[20];
uniform float4 HitPosition[20];
/ 溶解相关参数:
sampler2D _DissolveRampTex;
float3 _DissolvePoint;
float _DissolveNoisePower, _DissolveAmount, _DissolveSpread, _DissolveEdgeIntensity;
/// 菲涅尔相关
float _FresnalPower, _FresnalScale,_FresnalBias;
sampler2D _FlowMap,_FlowLightTex;
float4 _FlowMap_ST;
float4 _FlowStrength;
float _FlowSpeed;
float _HitWaveIntensity,_EmissIntensity;
float4 _EmissColor;
// 三平面映射相关参数
float4 _TriPlanarTilling;
float _Contrast;
// 根据法线的三个轴 生成遮罩
float3 NorMalMask(float3 wNormal)
{
float3 tempNormal = pow(abs(wNormal),_Contrast);
float3 mask = tempNormal /(tempNormal.x + tempNormal.y + tempNormal.z);
return mask;
}
三平面映射
float4 TriPlanar(float3 maskValue,float3 worldPos,float3 worldPivot,sampler2D mainTex)
{
float3 tempPos = ((worldPos - worldPivot) * _TriPlanarTilling.xyz).xyz;
// xy 平面的像素正常显示 但是 其他轴对应平面的值不正确,所以 乘以 maskValue.z,屏蔽掉其它轴面的值。
float4 colorZ = tex2D(mainTex,tempPos.xy) * maskValue.z;
float4 colorY = tex2D(mainTex,tempPos.xz) * maskValue.y;
float4 colorX = tex2D(mainTex,tempPos.yz) * maskValue.x;
// 三个轴面的正确结果相加 , 归一化
return normalize(colorX + colorY + colorZ);
}
/// 计算冲击波
float HitWave(float3 worldPos,float waveNoise)
{
float hitResult=0;
for(int j=0;j<_AffectorAmount; j++)
{
float distanceMask = distance(HitPosition[j].xyz,worldPos);
float hitRange = -clamp( (distanceMask - HitSize[j] + waveNoise) / _HitSpread,-1.0,0.0);
float2 mapUV = float2(hitRange,0.5);
float2 hitWave = tex2D(_RampTex,mapUV).r;
float hitFade = saturate((1.0 - distanceMask / _HitFadeDistance) * _HitFadePower);
hitResult = hitResult + hitFade * hitWave;
}
return hitResult;
}
// 返回值是两个: x alphaMask遮罩; y edge溶解边缘的两边
float2 DissolveFactor(float3 worldPos,float3 PivotPos,float noiseData)
{
float dissolveX = 1 - clamp(((_DissolvePoint - (worldPos - PivotPos)) - noiseData ) / _DissolveSpread,0.0,1.0);
float dissolveAlpha = smoothstep(0.0,0.1,dissolveX);
float dissolveEdge = tex2D(_DissolveRampTex,float2(dissolveX,0.5)).r * _DissolveEdgeIntensity;
return float2(dissolveAlpha, dissolveEdge);
}
// 流光贴图采样计算结果
float4 flowMap(float flowSpeed,float2 flowIntensity,sampler2D mainTex,sampler2D flowMap,float2 maintexUV,float2 flowmapUV)
{
//
float2 timeDetla = frac(_Time.y * flowSpeed) * flowIntensity;
//
float2 timeNext = frac(_Time.y * flowSpeed + 0.5) * flowIntensity;
// 两个时间的插值因子
float timeLerp = abs(frac(_Time.y * flowSpeed)* 2 -1);
// 此处flowMap中存的值范围 (0,1), 但是向量的方向是(-1,1), 可以转化*2-1 ; 0.5- tex2D() 将值范围限制在(-0.5,0.5)之间,这样看起来效果比较舒服!!!
float2 flowDir = (0.5 - tex2D(flowMap,flowmapUV).rg);
float2 uvOffset1 = flowDir * timeDetla;
float4 mainDetlaCol = tex2D(mainTex,maintexUV + uvOffset1);
float2 uvOffset2 = flowDir * timeNext;
float4 mainNextCol = tex2D(mainTex,maintexUV + uvOffset2);
float4 endCol = lerp(mainDetlaCol,mainNextCol,timeLerp);
return endCol;
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _NoiseTex);
o.uv_FlowMap = TRANSFORM_TEX(v.uv, _FlowMap);
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
o.PivotPos = mul(unity_ObjectToWorld,float4(0,0,0,1));
o.worldNormal = UnityObjectToWorldDir(v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 worldNormal = normalize(i.worldNormal);
/// -------三平面映射
float3 normalMask = NorMalMask(worldNormal);
float waveNoise = TriPlanar(normalMask, i.worldPos, i.PivotPos,_NoiseTex);
-------冲击波---------
float hitWave = HitWave(i.worldPos,waveNoise);
--------- 溶解参数-------
float tempData = 1 -clamp( ( ( distance(_DissolvePoint.xyz , i.worldPos.xyz - i.PivotPos.xyz) - _DissolveAmount - (waveNoise * _DissolveNoisePower)) / _DissolveSpread ),0.0,1.0);
float dissolveAlphaMask = smoothstep(tempData,0.0,0.1);
float dissolveEdge = tex2D(_DissolveRampTex,float2(tempData,0.5)).r * _DissolveEdgeIntensity;
/// ------- 菲涅尔 边缘光-------
float3 worldView = normalize(UnityWorldSpaceViewDir(i.worldPos));
float fresnal = _FresnalBias + _FresnalScale * pow(1 - (dot( worldNormal,worldView)),_FresnalPower);
float4 emissionCol = _EmissColor * _EmissIntensity;
///----------------流光-----
float2 flowMapUV = float2(i.uv_FlowMap + hitWave);
float4 flowColor = flowMap(_FlowSpeed,_FlowStrength.xy,_FlowLightTex,_FlowMap,i.uv,flowMapUV);
/// ---------最后的颜色结果计算 ---------
float4 mainCol = fresnal * flowColor;
// 柔性叠加 加上0.5是为了提亮一点底色,不然冲击波太淡了
mainCol = (mainCol + 0.5) * ( hitWave * _HitWaveIntensity ) + mainCol + dissolveEdge;
float endAlpha = clamp(Luminance(mainCol.a) * dissolveAlphaMask ,0,1) ;
float3 endCol = mainCol * emissionCol;
return float4(endCol,endAlpha);
}
ENDCG
}
}
}
C#脚本 点击交互:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
[ExecuteInEditMode]
public class ForceFiled : MonoBehaviour
{
public ParticleSystem ps;
public string triggerTag = "ForceFiled";
public float clickPerSecond = 0.1f;
private float clickTimer = 0;
public int AffectorAmount = 20;
private ParticleSystem.Particle[] particles;
private Vector4[] positions;
private float[] sizes;
private Material mat;
private void Start()
{
mat = GetComponent<MeshRenderer>().sharedMaterial;
}
void DoRayCast()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray,out hitInfo,1000))
{
if (hitInfo.transform.CompareTag(triggerTag))
{
ps.transform.position = hitInfo.point;
ps.Emit(1);
}
}
}
void Update () {
if (Input.GetMouseButton(0))
{
clickTimer += Time.deltaTime;
if (clickTimer >= clickPerSecond)
{
clickTimer = 0;
DoRayCast();
}
}
particles = new ParticleSystem.Particle[AffectorAmount];
positions = new Vector4[AffectorAmount];
sizes = new float[AffectorAmount];
ps.GetParticles(particles);
for (int i = 0; i < AffectorAmount; i++)
{
positions[i] = particles[i].position;
sizes[i] = particles[i].GetCurrentSize(ps);
}
mat.SetVectorArray("HitPosition", positions);
mat.SetFloatArray("HitSize", sizes);
mat.SetFloat("AffectorAmount", AffectorAmount);
// Shader.SetGlobalVectorArray("HitPosition", positions);
// Shader.SetGlobalFloatArray("HitSize", sizes);
// Shader.SetGlobalFloat("AffectorAmount", AffectorAmount);
}
}
下载链接:力场