C#運用Shader完成夜幕來臨倒計時的效果。本站提示廣大學習愛好者:(C#運用Shader完成夜幕來臨倒計時的效果)文章只能為提供參考,不一定能成為您想要的結果。以下是C#運用Shader完成夜幕來臨倒計時的效果正文
最近火爆全球的PC游戲Battlerite(和平典禮)在倒計時的會生成一品種似夜幕來臨的效果,會以戰場中心為圓心,某個長度為半徑的范圍外是暗的,而這個半徑會逐步減少,而圓之外的陰暗局部是附著地形的,本文就嘗試運用屏幕後處置的手腕來完成這種效果。
(暫時短少Battlerite的截圖,稍後會補上)
首先看效果圖:

注:本文參考了Tasharen Fog of War插件
創立一個C#腳本,命名為NightFall.cs,為NightFall類創立一些公共變量(nightColor,center和radius),另外還需求一個NightFall.shader。
首先,我們要確定這個效果是在場景渲染之後還未送到屏幕顯示之前的完成的,所以,NightFall腳本是要掛載到主Camera上的(添加特性[RequireComponent(typeof(Camera))]),並要完成OnRenderImage辦法。
其次,在OnRenderImage辦法裡,我們最終需求調用Graphics.Blit辦法,而這個辦法的第三個參數是Material類型,所以我們需求在代碼裡創立一個暫時材質,這個材質運用了NightFall.shader。
再次,我們需求在Shader外面將屏幕坐標轉換為世界坐標,來計算與世界中心的坐標,所以我們需求MVP的逆矩陣(參考Shader山下(十六)坐標空間與轉換矩陣)。
最後,為了附著地形,我們需求在Shader計算深度,也就是坐標點與攝像機的絕對間隔,所以需求攝像機的地位。
C#的代碼:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class NightFall : MonoBehaviour
{
public Shader shader;
public Color nightColor = new Color(0.05f, 0.05f, 0.05f, 0.5f);
public Vector3 center = Vector3.zero;
public float radius = 10;
Camera mCam;
Matrix4x4 mInverseMVP;
Material mMat;
/// The camera we're working with needs depth.
void OnEnable ()
{
mCam = GetComponent<Camera>();
mCam.depthTextureMode = DepthTextureMode.Depth;
if (shader == null) shader = Shader.Find("Image Effects/NightFall");
}
/// Destroy the material when disabled.
void OnDisable () { if (mMat) DestroyImmediate(mMat); }
/// Automatically disable the effect if the shaders don't support it.
void Start ()
{
if (!SystemInfo.supportsImageEffects || !shader || !shader.isSupported)
{
enabled = false;
}
}
// Called by camera to apply image effect
void OnRenderImage (RenderTexture source, RenderTexture destination)
{
print (nightColor);
print (destination);
// Calculate the inverse modelview-projection matrix to convert screen coordinates to world coordinates
mInverseMVP = (mCam.projectionMatrix * mCam.worldToCameraMatrix).inverse;
if (mMat == null)
{
mMat = new Material(shader);
mMat.hideFlags = HideFlags.HideAndDontSave;
}
Vector4 camPos = mCam.transform.position;
// This accounts for Anti-aliasing on Windows flipping the depth UV coordinates.
// Despite the official documentation, the following approach simply doesn't work:
// http://docs.unity3d.com/Documentation/Components/SL-PlatformDifferences.html
if (QualitySettings.antiAliasing > 0)
{
RuntimePlatform pl = Application.platform;
if (pl == RuntimePlatform.WindowsEditor ||
pl == RuntimePlatform.WindowsPlayer ||
pl == RuntimePlatform.WindowsWebPlayer)
{
camPos.w = 1f;
}
}
mMat.SetVector("_CamPos", camPos);
mMat.SetMatrix("_InverseMVP", mInverseMVP);
mMat.SetColor("_NightColor", nightColor);
mMat.SetVector ("_Center", center);
mMat.SetFloat ("_Radius", radius);
Graphics.Blit(source, destination, mMat);
}
}
Shader代碼:
Shader "Image Effects/NightFall"
{
Properties
{
_NightColor ("Night Color", Color) = (0.05, 0.05, 0.05, 0.05)
_Center ("Center", Vector) = (0,0,0,0)
_Radius ("Radius", float) = 10
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Fog { Mode off }
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag vertex:vert
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _CameraDepthTexture;
uniform float4x4 _InverseMVP;
uniform float4 _CamPos;
uniform half4 _NightColor;
uniform half4 _Center;
uniform half _Radius;
struct Input
{
float4 position : POSITION;
float2 uv : TEXCOORD0;
};
void vert (inout appdata_full v, out Input o)
{
o.position = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
}
float3 CamToWorld (in float2 uv, in float depth)
{
float4 pos = float4(uv.x, uv.y, depth, 1.0);
pos.xyz = pos.xyz * 2.0 - 1.0;
pos = mul(_InverseMVP, pos);
return pos.xyz / pos.w;
}
fixed4 frag (Input i) : COLOR
{
#if SHADER_API_D3D9 || SHADER_API_D3D11
float2 depthUV = i.uv;
depthUV.y = lerp(depthUV.y, 1.0 - depthUV.y, _CamPos.w);
float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, depthUV));
float3 pos = CamToWorld(depthUV, depth);
#else
float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv));
float3 pos = CamToWorld(i.uv, depth);
#endif
// Limit to sea level
if (pos.y < 0.0)
{
// This is a simplified version of the ray-plane intersection formula: t = -( N.O + d ) / ( N.D )
float3 dir = normalize(pos - _CamPos.xyz);
pos = _CamPos.xyz - dir * (_CamPos.y / dir.y);
}
half4 col;
float dis = length(pos.xz - _Center.xz);
if (dis < _Radius)
{
col = fixed4(0,0,0,0);
}
else
{
col = _NightColor;
}
return col;
}
ENDCG
}
}
Fallback off
}
需求闡明的幾個點:
1、由於平台差別性,為了兼容Direct3D,所以在C#和shader裡經過CamPos(_CamPos)的w重量來調整uv坐標。
2、這裡雖然沒有聲明_MainTex,但是_MainTex實踐上就是行將成像的屏幕圖像,所以這裡的i.uv也就是指屏幕圖像的紋理坐標。
3、_CameraDepthTexture是攝像機的深度紋理,經過UNITY_SAMPLE_DEPTH辦法獲取深度。
4、CamToWorld外面,先是依據uv坐標和深度depth創立了一個float4的坐標值pos,然後對pos乘2減1是將這個坐標范圍從[0,1]轉換到了[-1,1],對應世界坐標。然後運用傳入的MVP逆矩陣_InverseMVP乘以這個坐標值,就失掉了屏幕點的世界坐標。最後將pos的xyz重量除以w重量,這裡w重量表示由於遠近而發生的縮放值。
5、在計算過世界坐標之後,關於y小於0的坐標要做一下處置,將效果限制在海立體(sea level)之上,運用射線立體相交方程(ray-plane intersection formula)的簡化版原本處置。
6、最後依據間隔前往顏色值。
假如要完成夜幕來臨倒計時的效果,只需求在控制腳本(C#)中獲取Camera上的NightFall組件,依據時間修正radius變量即可。
以上所述是給大家引見的C#運用Shader完成夜幕來臨倒計時的效果,希望對大家有所協助,假如大家有任何疑問請給我留言,會及時回復大家的。在此也十分感激大家對網站的支持!