• 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"
    }
    
    • 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

    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"
    }
    
    • 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

    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"
    }
    
    • 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
  • 相关阅读:
    socket与Linux TCP
    Linux部署安装docker环境&配置docker镜像加速
    通过usb串口发送接收数据
    【Mock】Neo4j知识图谱数据集Mock、问答训练数据集mock
    1.4.15 实验15:ospf多区域NSSA
    【SpringBoot】多环境配置和启动
    SpringBoot整合websockt实现消息对话
    vue项目开发环境工具-node
    微信小程序组件、web-view、h5之间交互
    计算机毕业设计之java+javaweb的外婆家网上订餐平台
  • 原文地址:https://blog.csdn.net/weixin_42221907/article/details/126322700