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