【Unity Shader】 消融效果的实现

1.前言

参加腾讯2018游戏岗校招结果出师未捷身先死,连面试机会都没有(-_-||),想想笔试自己三道编程题0个ac也就释怀了233,忙着实习实在没精力复习算法题,精力有限啊...

吐槽完毕回归主题

咱最近在玩wy的神都夜行录,这款手游画面还是挺不错的,就是肝疼(万恶的wy策划)。

刷本的时候看见了如下图这个消融效果,就想着怎么用unity shader来实现,毕竟消融效果在游戏中是非常常见的。

 

2.实现思路

1.使用clip函数对片元进行裁剪来实现模型的消融

clip(value):当value值小于0时,裁减掉该片元,否则保留。

2.使用下面的噪声图来实现伪随机消融,这样能使效果看起来更加自然。关于什么是噪声,可以去看乐乐大师姐(在下小迷弟一枚)写的关于噪声的博文-【图形学】谈谈噪声

3.关于颜色侵蚀过渡的实现

从上面的效果图中可以看见模型在消融时,有明显的色彩侵蚀过程,这种色彩变化为消融提供了很好的过渡效果,为了实现这种效果,我们可以设置一个侵蚀颜色的阈值,根据这个阈值来判断和返回侵蚀过程中的色彩变化。

//控制侵蚀程度
float _Erode;
//控制侵蚀的颜色阈值
float _ErodeThreshold;

4.光照

直接使用Lambert光照模型,光照的衰减交给unity内置宏来处理

5.关于展示用的模型

手残党的福音,给大家推荐一款小巧的免费体素建模软件-MagicaVoxel, 上手简单,不好用不要钱~

来给大家展示一下我的作品:

怎么样,是不是神还原,哈哈哈哈

3.代码

下面是完整的shader代码,关键部分加了注释:

Shader "Unlit/Melt"
{
	Properties{
		_MainTex("Base(rgb)", 2D) = "white"{}
		_NoiseMap("NoiseMap", 2D) = "white"{}
		_StartColor("StarColor", Color) = (0,0,0,0)
		_EndColor("EndColor", Color) = (0,0,0,0)
		_MeltThreshold("MeltThreshold", Range(0, 1)) = 0
		_Erode("Erode", Range(0.0, 1.0)) = 0.98
		_ErodeThreshold("ErodeThreshold", Range(0.0, 1.0)) = 0.71
	}


	SubShader{

		CGINCLUDE

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NoiseMap;
			//消融边缘起始颜色
			fixed4 _StartColor;
			//最终颜色
			fixed4 _EndColor;
			//消融阈值
			float _MeltThreshold;
			//控制侵蚀程度
			float _Erode;
            //控制侵蚀颜色阈值
			float _ErodeThreshold;

			struct a2v{
				float4 vertex : POSITION;
    			float3 normal : NORMAL;
    			float4 texcoord : TEXCOORD0;
			};

			struct v2f{
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
				SHADOW_COORDS(3)
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				TRANSFER_SHADOW(o);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target{

				//使用噪声图采样
				fixed3 melt = tex2D(_NoiseMap, i.uv).rgb;

				//采样阈值与设定阈值比较,小于设定的阈值就裁剪掉该片元
				clip(melt.r - _MeltThreshold);

				//光照计算部分,使用兰伯特漫反射光照模型

				//纹理采样得到反射率
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb;
				//世界法线
				fixed3 worldNormal = normalize(i.worldNormal);
				//入射光
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				//计算环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				//漫反射
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, -worldLightDir));
				//最终光照
				fixed3 lightColor = diffuse * atten + ambient;
				//侵蚀计算部分
				float result = _MeltThreshold / melt.r;

				if(result > _Erode){
					//如果结果大于消融颜色的阈值,则返回消融结束部分的颜色,否则返回初始颜色
					if(result > _ErodeThreshold) {
						return _EndColor;
					}
					
					return _StartColor;

				}
				//直接返回光照后颜色
				return fixed4(lightColor, 1);
			}

		ENDCG


		Pass{

			Tags{ "RenderType" = "Opaque"}
			Cull off
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			ENDCG
		}
	}

	FallBack Off
}

 

为了看见整个模型的消融过程,我选择关闭了剔除

Cull Off  //关闭遮挡剔除

创建一个脚本来控制消融的程度,实现自动播放的效果

  Melt.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Melts : MonoBehaviour {

	public Material material;

	[Range(0.01f, 1.0f)]
	public float meltSpeed = 0.2f;

	private float meltThreshold = 0.0f;

	void Start(){
		material.SetFloat("_MeltThreshold", 0);
	}

	void Update(){
		//使用时间控制消融阈值
		meltThreshold = Mathf.Repeat(Time.time * meltSpeed, 6.0f);
		material.SetFloat("_MeltThreshold", meltThreshold);

	}

}

4.实现效果

来看看实现效果:

emmm,好像颜色有什么地方不对劲....

截图与游戏效果图对比看看

通过对比可以发现,游戏截图中的色彩明显亮于我所使用的颜色。

这五毛钱特效可没脸拿出去见人啊 QAQ

5.修改后的效果

想起从乐乐学姐那里偷师学来的bloom效果,翻出之前写的代码加上去试试看效果咋样

!!! 瞬间感觉高大上了,并且有一种金属感,效果的还原度也很高

关于bloom效果

bloom效果属于图像处理的一种,这种图像处理技术在渲染中常被称为后处理,由脚本和shader共同实现。

bloom的原理简单的来说就是对图像中亮度较高的区域进行高斯模糊处理,由于篇幅的原因我这里不贴出具体代码,对bloom感兴趣的可以看看我的github,我把该效果的源码放在了GitHub上。

链接:我的GitHub

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章