unity 继承会调用start吗_【浅入浅出】Unity 雾效
大家好,我是Shawn。如何實現“霧效”?”在Unity中,是有自帶的霧效的,在Lighting窗口下,在other Settings就可以找到fog選項了,啟用fog就能使用Unity自帶的霧效了,但是,需要注意的是,只有在向前渲染下才能實現Unity自帶的霧效,Unity有三種模式:Linear,Exp 和 Exp2 分別對應,線性、指數、和指數的平方的增長模式。
霧效因子分別對應:
、 、E是end、S是start、c是coordinate、d就是density,通過上述式子我們不難發現這是一個關于c的減函數,即距離越遠,霧效因子越小。
我們已經知道Unity是如何實現霧效的了,其實就是計算霧效因子,然后進行差值就行了
現在我們試著創建一個自己的霧效。
首先需要添加#pragma multi_compile_fog讓Unity為我們生成正確的變體。然后我們還需要
設置一個宏控制霧效的開啟。
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)#define APPLY_FOG 1 #endif在頂點著色器之前,編寫一個自定義的函數:
float4 ApplyFog(float4 color , v2f i){float viewDistance = length(_WorldSpaceCameraPos - i.worldPos);UNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}這個函數計算了頂點到攝像機的距離,并且我們用Unity自帶的宏為我們計算霧效因子,由于霧效因子的范圍不在0-1之間所以我們用需要限定一下,這個宏可以在UnityCG.cginc里面查看到:
#if defined(FOG_LINEAR)// factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w #elif defined(FOG_EXP)// factor = exp(-density*z)#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor) #elif defined(FOG_EXP2)// factor = exp(-(density*z)^2)#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor) #else#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0 #endif其實就是Unity判斷我們選擇的模式,傳入c就算霧效因子,unity_FogParams可以在UnityShaderVariables里查看,這里我們直接給出:
unity_FogParams:(density / sqrt(ln(2)), density / ln(2), –1/(end-start), end/(end-start))
在片元著色器中代碼就更簡單了,只需要調用即可:
fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fog#if APPLY_FOGcol = col*ApplyFog(col ,i);#endifreturn col;}返回Unity查看效果,我們沒有用光照模型,只輸出顏色:
其中一個球是標準材質,用來做對比在上一個方法中我們使用的c是物體到攝像機的距離,下面我們將基于深度來實現霧效。
我們需要用一個變量來儲存深度值,這里我們把頂點的世界坐標改成float4類型,用w儲存深度值:
float4 worldPos : TEXCOORD2;在頂點著色器中添加一段代碼:
#if APPLY_FOGo.worldPos.w = o.vertex.z; #endif我們記錄了clip space下的深度值,由于平臺的差異我們取到的z值的范圍也不同,我們使用
宏UNITY_Z_0_FAR_FROM_CLIPSPACE來針對不同平臺做不同處理,把深度轉換為從近切面到遠切面線性增大的值。
float4 ApplyFog(float4 color , v2f i){//float viewDistance = length(_WorldSpaceCameraPos - i.worldPos.xyz);float viewDistance =UNITY_Z_0_FAR_FROM_CLIPSPACE(i.worldPos.w);UNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}其他代碼不變,我們得到的效果幾乎和上面的一樣。
但其實還是差別的,當我們基于深度來實現霧效,我們不需要開根號,運行速度更快了,但是,我們還會發現一些問題,當我們的攝像機旋轉時,我們的深度變了,從而影響了霧效,盡管他在邏輯上是不會變化的
源自catlike coding我們可以查看做一個簡單的測試,查看旋轉時攝像機的深度圖:
我們可以發現雖然是同一個位置,長方體的顏色卻有細微的不同
下面是完整代碼:(同時包含了基于距離和深度的方法)
Shader "Unlit/TestFog" {Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{//Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#define FOG_DISTANCE#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)#if !defined(FOG_DISTANCE)#define FOG_DEPTH 1#endif#define APPLY_FOG 1 #endifstruct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float4 worldPos : TEXCOORD2;};sampler2D _MainTex;float4 _MainTex_ST;float4 ApplyFog(float4 color , v2f i){float viewDistance = length(_WorldSpaceCameraPos - i.worldPos.xyz);#if FOG_DEPTHviewDistance =UNITY_Z_0_FAR_FROM_CLIPSPACE(i.worldPos.w);#endifUNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldPos.xyz = mul(unity_ObjectToWorld , v.vertex).xyz;#if APPLY_FOGo.worldPos.w = o.vertex.z;#endifreturn o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fog#if APPLY_FOGcol = col*ApplyFog(col ,i);#endifreturn col;}ENDCG}} }前文提到:這種方法只能用在向前渲染中,在延遲渲染中我們就要使用屏幕后處理。
我們先寫一個最基本的后處理腳本:
[ExecuteInEditMode] public class PostScreenEffect : MonoBehaviour {public Shader shader;Material material;[ImageEffectOpaque]private void OnRenderImage(RenderTexture source, RenderTexture destination){if (material == null){material= new Material(shader);}Graphics.Blit(source, destination, material);}然后創建一個Imageeffect的shader文件,思路也很簡單,就是通過采樣深度紋理來獲取深度值,從而計算距離。我們要聲明_CameraDepthTexture來采樣深度紋理,然后再采樣他,核心代碼如下:
fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);depth = Linear01Depth(depth);if(depth<1){float distance = depth * _ProjectionParams.z-_ProjectionParams.y;UNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);col.rgb = lerp(unity_FogColor.rgb,col.rgb,unityFogFactor);}return col;}我們用Linear01Depth函數是為了能夠得到一個0-1范圍的線性的深度值,你可能已經不止一次聽到類似“MVP矩陣的P導致了非線性”,所以我再嘮叨一遍,哈哈。然后轉換為距離,同樣你也可以在UnityShaderVariables里查看_ProjectionParams. 這里z就代表遠裁面,y代表近裁面。
我們把深度值限定在小于1的范圍內是因為我們不想渲染天空盒。
返回Unity查看效果:
最后,我們再用真正的距離實現屏幕后處理實現霧效:
這里的距離我們可以從攝像機發射一道光線。如果沒有沒有物體的話,這道光線可以一直延伸最后達到遠裁面,如果有物體,我們我們只要記錄下它的深度值與遠裁面的比,然后乘這個光線向量得到一個新的向量,就是一個簡單的相似三角形的知識~最后計算這個向量的模,我們就得到了距離了。
理論上來說要為每個像素發射一次射線,但是,我們這里我們只計算遠裁面的四個角的射線,剩下的就交給差值去做吧。
幸運的是,我們不用自己手動計算這四個射線,Unity有給定函數方便了我們的計算。
點擊這里了解這個函數。
我們在camera的腳本中添加一些代碼:
Camera cam = null;Vector3[] frustumCorners; Vector4[] vectorArray; /****下面代碼添加至OnRenderImage函數中****/ if (material == null){material= new Material(shader);cam = GetComponent<Camera>();frustumCorners = new Vector3[4];}cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), cam.farClipPlane, cam.stereoActiveEye, frustumCorners);vectorArray[0] = frustumCorners[0];vectorArray[1] = frustumCorners[3];vectorArray[2] = frustumCorners[1];vectorArray[3] = frustumCorners[2];material.SetVectorArray("_FrustumCorners", vectorArray);在calculateFrustumCorners函數里我們傳入的是Vector3,而我們SetVectorArray需要傳入Vector4 ,所以我們變化一下,這樣我們的數值就能正確被傳入了 ;
在我們的shader文件下我們聲明一個宏,來控制是否啟用distance的算法。
#define FOG_DISTANCE接著要在頂點結構體聲明ray向量:
#ifdef FOG_DISTANCE float3 ray : TEXCOORD1; #endif最后我們修改片元著色器中的核心語句:
if(depth<1){float distance = depth * _ProjectionParams.z-_ProjectionParams.y;#ifdef FOG_DISTANCEdistance = length(depth*i.ray);#endifUNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);col.rgb = lerp(unity_FogColor.rgb,col.rgb,unityFogFactor);}效果:
以上就是霧效的所有內容了
最后的最后(真的是最后了)我們來實現一個類似firewatch的效果:
代碼如下:
Shader "Custom/GradientColor" {Properties{_MainTex ("Texture", 2D) = "white" {}_FogAmount("Fog amount", float) = 1_ColorRamp("Color ramp", 2D) = "white" {}_FogIntensity("Fog intensity", float) = 1}SubShader{// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float4 scrPos : TEXCOORD1;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;o.scrPos = ComputeScreenPos(o.vertex);return o;}sampler2D _MainTex;sampler2D _CameraDepthTexture;sampler2D _ColorRamp;float _FogAmount;float _FogIntensity;fixed4 frag (v2f i) : SV_Target{fixed4 orCol = tex2D(_MainTex, i.uv);float depthValue = Linear01Depth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)));//float depthValue = Linear01Depth (SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv));float distance = depthValue*_ProjectionParams.z-_ProjectionParams.y;UNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);float depthValueMul = depthValue * _FogAmount;fixed4 fogCol = tex2D(_ColorRamp, (float2(depthValueMul, 0)));if(depthValue<1){#if !defined(FOG_LINEAR) && !defined(FOG_EXP) && !defined(FOG_EXP2)orCol = lerp(orCol, fogCol, fogCol.a * _FogIntensity) ;#elseunityFogFactor =1-unityFogFactor;orCol = lerp(orCol,fogCol,unityFogFactor*_FogIntensity);#endif}return orCol;}ENDCG}} }我們通過深度對一張梯度圖采樣,這樣不同的深度就能顯示不同的顏色,如果你覺的這樣很不自然的話,當然也可以使用霧效因子來對他進行采樣。
如果你的渲染路徑是向前渲染的話,你就需要在腳本中加入Camera.main.depthTextureMode = DepthTextureMode.Depth;
這樣你就可以訪問深度圖了
我們找一張梯度圖看看效果:
也可以這樣:
當然如果你找的顏色是彩虹色的話:
Enjoy!
總結
以上是生活随笔為你收集整理的unity 继承会调用start吗_【浅入浅出】Unity 雾效的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 没有猫如何安装无线路由器我是长城宽带用户
- 下一篇: small用于不连续数组_Excel公式