Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果
目录
Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果
一、简单介绍
二、实现原理
三、注意事项
四、效果预览
五、实现步骤
六、关键代码
一、简单介绍
Shader Language的发展方向是设计出在便携性方面可以和C++、Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”同时“利用图形硬件的并行性,提高算法效率”。
本文介绍,实现黑洞再任意位置,被吸收物体再任意位置,黑洞吸收吞噬的简单效果。
二、实现原理
1、通过脚本求得物体的距离黑洞的最近顶点和最远顶点;
2、传给shader,得到最近顶点和最远顶点的范围,并计算各顶点到最近顶点的距离归一化比;
3、在通过一个参数,来控制物体被黑洞吸收吞噬或生成的状态的程度;
三、注意事项
1、通过一个参数,来控制物体吸收或生成的状态的程度,范围是 0 - 2(如果小于2 最远的点吸收不到);
2、因为控制参数再0-2范围,所以对超过黑洞的模型顶点做处理,让他一直处于黑洞位置;
3、_Control = sin(_Time.y * _Speed ) + 1; 用来代码模拟的,正式用的时候可以用其他方法代替;
4、注意顶点区分是世界坐标,还是模型坐标;
四、效果预览

五、实现步骤
原理图示:

具体步骤:
1、打开Unity,新建一个工程

2、在工程中,新建一个Shader,并新建对应材质,导入一张图片,再新建一个脚本来见识鼠标点击监控,并把相关信息传给shader

3、在场景中,添加两个 Sphere(一个作为黑洞) 和 Capsule,适当布局

4、把材质给 Sphere 和 Capsule,并挂载脚本,把黑洞赋给脚本

5、运行场景,效果如上

六、关键代码
1、BlackHoleEffeck.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class BlackHoleEffeck : MonoBehaviour { public Transform BlackHoleGO; private float nearDistance = float.MaxValue; private float farDistance = 0; void Start() { Material mat = GetComponent<Renderer>().material; Vector3 nearPos, farPos; CalculateMinMaxY(out nearPos, out farPos); mat.SetVector("_NearPos", nearPos); mat.SetVector("_FarPos", farPos); mat.SetVector("_BlackHolePos", BlackHoleGO.position); } /// <summary> /// 计算模型 距离黑洞的最近值顶点和最远值顶点 /// </summary> /// <param name="nearPos"></param> /// <param name="farPos"></param> void CalculateMinMaxY(out Vector3 nearPos, out Vector3 farPos) { nearPos = Vector3.zero; farPos = Vector3.zero; Vector3[] vertices = GetComponent<MeshFilter>().mesh.vertices; for (int i = 1; i < vertices.Length; i++) { Vector3 tmp = vertices[i] + transform.position; float distance = Vector3.Distance(tmp, BlackHoleGO.position); if (distance < nearDistance) { nearDistance = distance; nearPos = tmp; } if (distance > farDistance) { farDistance = distance; farPos = tmp; } } } } |
2、ShaderBlackHole.shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | Shader "Unlit/ShaderBlackHole" { Properties { _MainTex ("Texture", 2D) = "white" {} _NearPos("Near Postion",Vector) =(0,0,0,0) _FarPos("Far Position",Vector)=(0,0,0,0) _Control("Control Squash",Range(0,2))=0 _Speed("Speed",Range(0,10))=1 _ObjectWorldPos("Object World Position",Vector)=(0,0,0,0) _BlackHolePos("Black Hole Position",Vector)=(0,0,0,0) } SubShader { Tags { "RenderType"="Opaque" } 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; float3 worldPos:TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; float3 _NearPos; float3 _FarPos; float3 _BlackHolePos; float _Control; float _Speed; float rangeFar_Near; // 求顶点到最高顶点的距离比例 float GetNormalizedDist(float3 worldPos){ // 加上在世界坐标的位置 worldPos = worldPos ; // 最大最小范围 rangeFar_Near = length(_FarPos-_NearPos); // 以最高顶点为黑洞吸收点开始点 float3 border = _NearPos; // 求顶点据最高顶点的距离绝对值 float dist = length( worldPos-border); //归一化距离值 float normalizedDist = saturate(dist/rangeFar_Near); return normalizedDist; } v2f vert(appdata v){ v2f o; o.uv = TRANSFORM_TEX(v.uv,_MainTex); // 把顶点撞到世界坐标上 float3 worldPos = mul(unity_ObjectToWorld, v.vertex); // 得到顶点距离顶部的比例 float normalizedDist = GetNormalizedDist(worldPos); // sin 值范围为 (-1,1),+1 使得范围再(0,2)周期变化 _Control = sin(_Time.y * _Speed ) + 1; // 变化比例(至于为什么max 是因为变化normalizedDist 为0-1, _Control - 为负,说明未变化到,故取0,不然就,你可以试试不要 max 看看结果) float val = max(0,_Control-normalizedDist); float3 toBlackHole = mul(unity_WorldToObject, (_BlackHolePos - worldPos)).xyz; float3 srcVertex = v.vertex.xyz; // 向黑洞方向偏移顶点数值,渐渐被黑洞吸收 v.vertex.xyz += toBlackHole* val; // 因为 val 的值会大于1,所以当顶点偏移到黑洞就一直保持 // length(v.vertex.xyz - srcVertex) 当前顶点偏移的值 // _BlackHolePos - worldPos 顶点到黑洞的最大值 // 故当 当前顶点偏移的值 大于 顶点到黑洞的最大值,让顶点值设置为黑洞处 if(length(v.vertex.xyz - srcVertex) > length(_BlackHolePos - worldPos)){ v.vertex.xyz = srcVertex+ toBlackHole; } // 转到裁剪空间 o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i):SV_Target{ fixed4 col = tex2D(_MainTex,i.uv); return col; } ENDCG } } } |