Unity三种物体溶解方法

Posted zczplus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity三种物体溶解方法相关的知识,希望对你有一定的参考价值。

Unity三种物体溶解方法

效果展示

Dissolve1-3

1. 利用Noise纹理进行溶解

该方法的效果好坏在于噪声纹理的分布。本例利用SD的Blend节点,将Noise贴图和渐变贴图进行正片叠底操作,并简单调整边缘边缘大小,得到如下所示的噪声贴图。

制作的遮罩为四周较暗,且对角线轴向向内亮度递增,使得最终的溶解效果从四周向内部溶解。

shader要点

  1. 通过一个噪声权重因子来控制Noise贴图的整体亮度;
  2. 利用saturate将其截断在0,1之间。通过改变噪声权重因子的大小影响Noise贴图的亮度,
  3. 将加权过的噪声图作为输出图片的alpha值,从而改变物体的透明度,结合Blend最终达到消融的目的。
  4. 通过判断透明度的值生成包边效果,通过改变透明度的阈值范围控制包边的宽度。
  5. 结合之前的Bloom后处理方法,通过包边所在的透明度范围对包边进行提取,达到局部bloom的效果。

shader代码

Shader "Custom/DissolveShader" 
	Properties 
		_MainTex ("Main Texture", 2D) = "white"  
		_DissolveMap ("Noise Map", 2D) = "white"  
		_DissolveFactor ("Dissolve Factor", Range(0, 18)) = 0.5
		_Diffuse ("Diffuse Color", Color) = (1, 1, 1, 1)
		_DissolveColor ("Dissolve Color", Color) = (1, 0, 0, 1)
		_DissolveBoundaryBegin ("Dissolve Boundary Begin", Range(0.3, 1)) = 0.4
		_DissolveBoundaryEnd ("Dissolve Boundary End", Range(0.3, 0.4)) = 0.3

		_DissolveBoundaryAlpha ("Dissolve Boundary Alpha", Range(0, 1)) = 0.5
	
	SubShader 
		Tags  "Queue" = "Geometry+1" "RenderType" = "Opaque" 

		pass 
			Blend SrcAlpha OneMinusSrcAlpha
			
			Tags  "LightMode" = "ForwardBase" 

			CGPROGRAM
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			sampler2D _MainTex;
			float4 _MainTex_ST;

			sampler2D _DissolveMap;
			float4 _DissoveMap_ST;
			float _DissolveFactor;

			fixed4 _DissolveColor;
			float _DissolveBoundaryBegin;
			float _DissolveBoundaryEnd;
			float _DissolveBoundaryAlpha;

			struct v2f 
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD2;
				float2 uv : TEXCOORD0;
				float4 screenPos : TEXCOORD1;
			;

			v2f vert(appdata_base v) 
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.screenPos = ComputeGrabScreenPos(o.pos);
				return o;
			

			fixed4 frag(v2f i) : SV_Target 
				// 采样噪声贴图的值
				fixed dissolveTex = tex2D(_DissolveMap, i.uv).r;

				// 计算光照
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 lambert = saturate(dot(worldNormal, worldLightDir));

				fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 color = albedo * tex2D(_MainTex, i.uv).rgb;

				// 利用时间实现一个简单的动态效果
				fixed dissolveFactor = _DissolveFactor * abs(lerp(-1, 1, frac(_Time.y * 0.1)));
				// 利用消融因子为消融贴图赋值,使其整体灰度值随着消融因子的变化而变化
				fixed alpha = saturate(dissolveTex * dissolveFactor * dissolveFactor);

				// 判断当前的透明度 若在设定范围内则更改颜色 并同时更改透明度方便后续bloom效果实现
				if (alpha < _DissolveBoundaryBegin && alpha > _DissolveBoundaryEnd) 
					return fixed4(_DissolveColor.rgb, _DissolveBoundaryAlpha);
				 else if (alpha < _DissolveBoundaryEnd) 
					alpha = 0;
				
				return fixed4(color, alpha);
			
			ENDCG
		
	
	FallBack "Diffuse"

2. 屏幕空间棋盘格

利用cllip方法强制中止当前渲染过程,达到透视效果。由于clip方法的使用,导致无法进行early-z等操作,在手机端会有较大的性能开销。

shader要点

  1. 该方法在屏幕空间进行操作,所以要得到屏幕空间中的具体坐标;
  2. 利用floor方法将x,y值乘以棋盘格控制因子的结果限制为整数;
  3. 通过乘以0.5再相加的方式使得结果带小数部分或者不带小鼠部分;
  4. 最后利用frac函数对小数部分进行提取,从而得到一系列值为0和0.5的数;
  5. 对这些值取负值并利用clip函数进行判断,当对应的值为-0.5时,渲染中断,当为0时,渲染继续;从而实现棋盘格效果。

shader代码

Shader "Custom/Dissolve2Shader" 
	Properties 
		_MainTex ("Albedo (RGB)", 2D) = "white"  
		_Diffuse ("Diffuse Color", Color) = (1, 1, 1, 1)
		_Range ("Range", Range(0, 1)) = 0.1
	
	SubShader 
		Tags  "RenderType" = "Opaque" 

		pass 
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			float _Range;
			sampler2D _MainTex;
			float4 _Diffuse;

			struct v2f 
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD2;
				float2 uv : TEXCOORD0;
				float4 screenPos : TEXCOORD1;
			;

			v2f vert(appdata_base v) 
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.screenPos = ComputeScreenPos(o.pos);
				return o;
			

			fixed4 frag(v2f i) : SV_Target 
				float2 wcoord = (i.screenPos.xy / i.screenPos.w);
				// 记得再乘以屏幕的像素个数,才能最终得到屏幕位置
				wcoord *= _ScreenParams.xy;

				// 随时间变化 * _Time.y * 3
				float changeRange = _Range;

				// screenPos *0.5 是为了方便当两数相加时有奇偶的区分,
				// 若和为奇数,则乘以0.5再取小数部分则小数部分必为0.5,若为偶数,再乘0.5则小数部分为0
				float2 screenPos = floor(wcoord.xy * changeRange) * 0.5;
				// 棋盘图案中 4x4 像素块的 checker 值为负
				float checker = -frac(screenPos.r + screenPos.g);
				// 如果clip函数中的值为负数,则停止渲染
				clip(checker);

				// 计算光照
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 lambert = saturate(dot(worldNormal, worldLightDir));

				fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;

				// 对于保留的像素,读取纹理并将其输出
				fixed3 color = albedo * tex2D(_MainTex, i.uv).rgb;
				return fixed4(color, 1);
			
			ENDCG
		
	
	FallBack "Diffuse"

3. 判断顶点距离摄像头的距离进行clip

该方法同样利用clip方法达到消融的目的,距离的运算在世界空间中完成,消融的实现在屏幕空间中完成。

这里最后一句话有点没看懂 再琢磨琢磨。

shader 代码

Shader "Custom/Dissolve3Shader" 
	Properties 
		_MainTex ("Albedo (RGB)", 2D) = "white"  
		_Diffuse ("Diffuse Color", Color) = (1, 1, 1, 1)
		_FogNear ("Fog Near", float) = 1.0
		_FogFar ("Fog Far", float) = 1.0
		_Test ("Test", float) = 1.0
	
	SubShader 
		Tags  "RenderType" = "Opaque" 

		pass 
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			sampler2D _MainTex;
			float4 _Diffuse;
			float _FogFar;
			float _FogNear;
			float _Test;

			struct appdata 
				float4 vertex : POSITION;
				float2 uv1 : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float3 normal : NORMAL;
			;

			struct v2f 
				float4 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float3 worldPos : TEXCOORD1;
				float4 screenPos : TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
			;

			v2f vert(appdata v) 
				v2f o;

				// 裁剪空间坐标
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv.xy = v.uv1;
				o.uv.zw = v.uv2;

				// 屏幕空间坐标
				o.screenPos = ComputeScreenPos(o.vertex);
				
				// 世界空间坐标
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				return o;
			

			fixed4 frag(v2f i) : SV_Target 
				// 计算光照
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
				fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;

				fixed4 color = fixed4(albedo * tex2D(_MainTex, i.uv).rgb,tex2D(_MainTex, i.uv).a);

				// 物体顶点距离相机距离
				float distanceRamp = distance(i.worldPos, _WorldSpaceCameraPos.xyz);

				// 利用Smoothstep对距离进行约束
				distanceRamp = smoothstep(_FogNear, _FogFar, distanceRamp);

				// 构建抖动顺序矩阵
				float4x4 thresholdMatrix = 
					1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
					13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
					4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
					16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
				;

				// 获取屏幕空间坐标(归一化
				float2 screenPos = i.screenPos.xy / i.screenPos.w;
				// 0-1的坐标范围乘以画面像素总数
				screenPos *= _ScreenParams.xy;
				// 依据动态抖动计算透明度
				half noise = distanceRamp - thresholdMatrix[fmod(screenPos.x, 4 * _Test)] * thresholdMatrix[fmod(screenPos.y, 4 * _Test)];
				clip(noise);

				return color;
			

			ENDCG
		
	
	FallBack "Diffuse"

unity深度查找某个子物体和遍历所有子物体方法

本文总结一下关于unity的查找子物体的方法

 

首先说明一下这里将讲三种查找子物体方法:

查找固定路径的某一个子物体的方法、通过名字深度查找某个子物体的方法、查找父物体下所有子物体的方法。

 

第一:查找固定路径的某一个子物体的方法

对于已知的路径可以直接用go.transform.FindChild方法来查找。

例如:在这样一个层级路径下,我们要找到最后那个plane物体。

技术分享图片

 

 1 using UnityEngine;
 2 using System.Collections;
 3  
 4 public class findchild : MonoBehaviour {
 5  
 6     // Use this for initialization
 7     void Start () {
 8     
 9     }
10     
11     // Update is called once per frame
12     void Update () {
13         if (Input.GetMouseButtonDown(1))
14         {
15             //查找物体方法
16             GameObject go = GameObject.Find("Cube");
17             //查找子物体,并且将得到的物体转换成gameobject
18           GameObject objname= go.transform.FindChild("Sphere/Cylinder/Plane").gameObject;
19  
20           Debug.Log("得到最终子物体的名字是:"+ objname.name);
21         }
22     }
23 }

 

然后是执行结果:

 

技术分享图片

 

 

==-------------------------------------------------------------------------------------------------------------

 

第二:通过名字深度查找某个子物体的方法

注意:要使用这个方法必须要满足两个条件:第一必须有你要查找的子物体的名字,第二必须要从一个父物体上开始查起

 

 

下面代码中,check代表从这个父物体开始查起,name为你要查找的目标子物体的名称。如return GetTransform(transform,"bone12");

 

该方法核心代码:

 

 

而下面是查找的具体方法:

 1 Transform GetTransform(Transform check, string name)
 2     {
 3         Transform forreturn = null;
 4  
 5         foreach (Transform t in check.GetComponentsInChildren<Transform>())
 6         {
 7             if (t.name == name)
 8             {
 9                 Debug.Log("得到最终子物体的名字是:" + t.name);
10                 forreturn = t;
11                 return t;
12                 
13             }        
14            
15         }
16         return forreturn;
17     }

 

再看完整的测试代码:还用上个的例子的,例如这次要查到Cylinder这个物体:

 

修改后的代码:

 1 using UnityEngine;
 2 using System.Collections;
 3  
 4 public class findchild : MonoBehaviour {
 5  
 6     // Use this for initialization
 7     void Start () {
 8     
 9     }
10     
11     // Update is called once per frame
12     void Update () {
13         if (Input.GetMouseButtonDown(1))
14         {
15             //  //查找物体方法
16            GameObject go = GameObject.Find("Cube");
17             //  //查找子物体,并且将得到的物体转换成gameobject
18             //GameObject objname= go.transform.FindChild("Sphere/Cylinder/Plane").gameObject;
19  
20             //Debug.Log("得到最终子物体的名字是:"+ objname.name);
21              
22  
23             GetTransform(go.transform, "Cylinder");
24             
25         }
26     }
27  
28     Transform GetTransform(Transform check, string name)
29     {
30         Transform forreturn = null;
31  
32         foreach (Transform t in check.GetComponentsInChildren<Transform>())
33         {
34             if (t.name == name)
35             {
36                 Debug.Log("得到最终子物体的名字是:" + t.name);
37                 forreturn = t;
38                 return t;
39                 
40             }        
41            
42         }
43         return forreturn;
44     }
45 }

 

测试结果:

 

技术分享图片

-----------------------------------------------------------------------------------------------------

第三:接下来我们将获取一个父物体下的所有子物体,然后销毁其下所有子物体

注意:所有子物体都是同级关系,在同一层里。如图:

技术分享图片

 

核心方法:

 1 List<Transform> lst = new List<Transform>();
 2             foreach (Transform child in transform)
 3             {
 4                 lst.Add(child);
 5                 Debug.Log(child.gameObject.name);
 6             }
 7             for (int i = 0; i < lst.Count; i++)
 8             {
 9                 Destroy(lst[i].gameObject);
10             }

 

上面的transform就是该父物体的transform。具体案例代码:

 1 using UnityEngine;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4  
 5 public class findchild : MonoBehaviour {
 6  
 7     // Use this for initialization
 8     void Start () {
 9     
10     }
11     
12     // Update is called once per frame
13     void Update () {
14         if (Input.GetMouseButtonDown(1))
15         {
16             //  //查找物体方法
17            GameObject go = GameObject.Find("Cube");
18             List<Transform> lst = new List<Transform>();
19             foreach (Transform child in go.transform)
20             {
21                 lst.Add(child);
22                 Debug.Log(child.gameObject.name);
23             }
24             for (int i = 0; i < lst.Count; i++)
25             {
26                 Debug.Log("销毁的物体是:"+ lst[i].gameObject);
27                 Destroy(lst[i].gameObject);
28             }
29  
30         }
31     }
32  
33    
34 }

测试结果,全被销毁了:

 

技术分享图片

 

以上就是我总结的常用的三种查找子物体的方法。

 

以上是关于Unity三种物体溶解方法的主要内容,如果未能解决你的问题,请参考以下文章

Unity鼠标带动物体运动的三种方法

Unity中获取物体的尺寸(size)的三种方法

翻译11 Unity 透明渲染

unity怎么设置物体坐标?

Unity3D中三种调用其他脚本函数的方法

unity怎么给物体上颜色