unity 继承会调用start吗_【浅入浅出】Unity 雾效
大家好,我是Shawn。如何實(shí)現(xiàn)“霧效”?”在Unity中,是有自帶的霧效的,在Lighting窗口下,在other Settings就可以找到fog選項(xiàng)了,啟用fog就能使用Unity自帶的霧效了,但是,需要注意的是,只有在向前渲染下才能實(shí)現(xiàn)Unity自帶的霧效,Unity有三種模式:Linear,Exp 和 Exp2 分別對(duì)應(yīng),線性、指數(shù)、和指數(shù)的平方的增長模式。
霧效因子分別對(duì)應(yīng):
、 、E是end、S是start、c是coordinate、d就是density,通過上述式子我們不難發(fā)現(xiàn)這是一個(gè)關(guān)于c的減函數(shù),即距離越遠(yuǎn),霧效因子越小。
我們已經(jīng)知道Unity是如何實(shí)現(xiàn)霧效的了,其實(shí)就是計(jì)算霧效因子,然后進(jìn)行差值就行了
現(xiàn)在我們?cè)囍鴦?chuàng)建一個(gè)自己的霧效。
首先需要添加#pragma multi_compile_fog讓Unity為我們生成正確的變體。然后我們還需要
設(shè)置一個(gè)宏控制霧效的開啟。
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)#define APPLY_FOG 1 #endif在頂點(diǎn)著色器之前,編寫一個(gè)自定義的函數(shù):
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 ;}這個(gè)函數(shù)計(jì)算了頂點(diǎn)到攝像機(jī)的距離,并且我們用Unity自帶的宏為我們計(jì)算霧效因子,由于霧效因子的范圍不在0-1之間所以我們用需要限定一下,這個(gè)宏可以在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其實(shí)就是Unity判斷我們選擇的模式,傳入c就算霧效因子,unity_FogParams可以在UnityShaderVariables里查看,這里我們直接給出:
unity_FogParams:(density / sqrt(ln(2)), density / ln(2), –1/(end-start), end/(end-start))
在片元著色器中代碼就更簡單了,只需要調(dià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;}返回Unity查看效果,我們沒有用光照模型,只輸出顏色:
其中一個(gè)球是標(biāo)準(zhǔn)材質(zhì),用來做對(duì)比在上一個(gè)方法中我們使用的c是物體到攝像機(jī)的距離,下面我們將基于深度來實(shí)現(xiàn)霧效。
我們需要用一個(gè)變量來儲(chǔ)存深度值,這里我們把頂點(diǎn)的世界坐標(biāo)改成float4類型,用w儲(chǔ)存深度值:
float4 worldPos : TEXCOORD2;在頂點(diǎn)著色器中添加一段代碼:
#if APPLY_FOGo.worldPos.w = o.vertex.z; #endif我們記錄了clip space下的深度值,由于平臺(tái)的差異我們?nèi)〉降膠值的范圍也不同,我們使用
宏UNITY_Z_0_FAR_FROM_CLIPSPACE來針對(duì)不同平臺(tái)做不同處理,把深度轉(zhuǎn)換為從近切面到遠(yuǎn)切面線性增大的值。
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 ;}其他代碼不變,我們得到的效果幾乎和上面的一樣。
但其實(shí)還是差別的,當(dāng)我們基于深度來實(shí)現(xiàn)霧效,我們不需要開根號(hào),運(yùn)行速度更快了,但是,我們還會(huì)發(fā)現(xiàn)一些問題,當(dāng)我們的攝像機(jī)旋轉(zhuǎn)時(shí),我們的深度變了,從而影響了霧效,盡管他在邏輯上是不會(huì)變化的
源自catlike coding我們可以查看做一個(gè)簡單的測(cè)試,查看旋轉(zhuǎn)時(shí)攝像機(jī)的深度圖:
我們可以發(fā)現(xiàn)雖然是同一個(gè)位置,長方體的顏色卻有細(xì)微的不同
下面是完整代碼:(同時(shí)包含了基于距離和深度的方法)
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}} }前文提到:這種方法只能用在向前渲染中,在延遲渲染中我們就要使用屏幕后處理。
我們先寫一個(gè)最基本的后處理腳本:
[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);}然后創(chuàng)建一個(gè)Imageeffect的shader文件,思路也很簡單,就是通過采樣深度紋理來獲取深度值,從而計(jì)算距離。我們要聲明_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函數(shù)是為了能夠得到一個(gè)0-1范圍的線性的深度值,你可能已經(jīng)不止一次聽到類似“MVP矩陣的P導(dǎo)致了非線性”,所以我再嘮叨一遍,哈哈。然后轉(zhuǎn)換為距離,同樣你也可以在UnityShaderVariables里查看_ProjectionParams. 這里z就代表遠(yuǎn)裁面,y代表近裁面。
我們把深度值限定在小于1的范圍內(nèi)是因?yàn)槲覀儾幌脘秩咎炜蘸小?/p>
返回Unity查看效果:
最后,我們?cè)儆谜嬲木嚯x實(shí)現(xiàn)屏幕后處理實(shí)現(xiàn)霧效:
這里的距離我們可以從攝像機(jī)發(fā)射一道光線。如果沒有沒有物體的話,這道光線可以一直延伸最后達(dá)到遠(yuǎn)裁面,如果有物體,我們我們只要記錄下它的深度值與遠(yuǎn)裁面的比,然后乘這個(gè)光線向量得到一個(gè)新的向量,就是一個(gè)簡單的相似三角形的知識(shí)~最后計(jì)算這個(gè)向量的模,我們就得到了距離了。
理論上來說要為每個(gè)像素發(fā)射一次射線,但是,我們這里我們只計(jì)算遠(yuǎn)裁面的四個(gè)角的射線,剩下的就交給差值去做吧。
幸運(yùn)的是,我們不用自己手動(dòng)計(jì)算這四個(gè)射線,Unity有給定函數(shù)方便了我們的計(jì)算。
點(diǎn)擊這里了解這個(gè)函數(shù)。
我們?cè)赾amera的腳本中添加一些代碼:
Camera cam = null;Vector3[] frustumCorners; Vector4[] vectorArray; /****下面代碼添加至OnRenderImage函數(shù)中****/ 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函數(shù)里我們傳入的是Vector3,而我們SetVectorArray需要傳入Vector4 ,所以我們變化一下,這樣我們的數(shù)值就能正確被傳入了 ;
在我們的shader文件下我們聲明一個(gè)宏,來控制是否啟用distance的算法。
#define FOG_DISTANCE接著要在頂點(diǎn)結(jié)構(gòu)體聲明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);}效果:
以上就是霧效的所有內(nèi)容了
最后的最后(真的是最后了)我們來實(shí)現(xiàn)一個(gè)類似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}} }我們通過深度對(duì)一張?zhí)荻葓D采樣,這樣不同的深度就能顯示不同的顏色,如果你覺的這樣很不自然的話,當(dāng)然也可以使用霧效因子來對(duì)他進(jìn)行采樣。
如果你的渲染路徑是向前渲染的話,你就需要在腳本中加入Camera.main.depthTextureMode = DepthTextureMode.Depth;
這樣你就可以訪問深度圖了
我們找一張?zhí)荻葓D看看效果:
也可以這樣:
當(dāng)然如果你找的顏色是彩虹色的話:
Enjoy!
總結(jié)
以上是生活随笔為你收集整理的unity 继承会调用start吗_【浅入浅出】Unity 雾效的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 没有猫如何安装无线路由器我是长城宽带用户
- 下一篇: 男生长相十大评分标准(男生1-10分颜值