【Unity大气渲染】Unity Shader中实现大气散射(半成品)
寫在前面
這是之前在做天空盒的時候同步寫的分析博客,結果后面寫到一半就忘了繼續了,這里先貼出當時寫的半成品,有小伙伴問我怎么做的,這里只能盡力把之前的半成品先放出來了(寫得很亂,勿怪orz),,后面有機會會完善好的!希望能幫到大家~
前置知識學習
【Unity大氣渲染】關于單次大氣散射的理論知識
關于卡通渲染的大氣散射
因為我想實現的是跟Unity 卡通渲染 程序化天空盒 晝夜變化,而作者實現過程的文章Unity 卡通渲染 程序化天空盒里說了只是想實現一個日出日落的效果,大概做了如下兩點簡化:
- 只計算了Mie散射
- 光學距離部分只計算了,省去了計算
既然不是基于物理的,那【Unity大氣散射】GAMES104:3A中如何實現大氣散射里整理的LUT優化方式等等內容就派不上用場了,但也無妨!多了一次學習~
那么接下來計算會盡量參考著這兩點去做~?很大部分的過程參考【實戰】從零實現一套完整單次大氣散射_四 - 知乎
分析原神的日出
截圖來自【原神】好美,原神一天中天空的變化_嗶哩嗶哩_bilibili
早晨,是地平線的顏色Bloom超突出,伴隨著緩緩升起的Mie散射的日出效果:?
隨著太陽升起,地平線Bloom褪去,留下明顯的Mie散射:?
日出結束,進入白天時間,太陽始終伴隨著一個炫光(猜測是直接疊加了一個炫光圖案),加上有隨太陽高度角變化的鏡頭光暈效果:?
關于地平線Bloom和太陽炫光、鏡頭光暈部分這里不提,先實現如此的Mie散射。
1 整體思路
我們需要模擬出人站在地面上看向天空的過程,其實就是人眼發出射線與天空相交的過程,這個過程還須判斷是否被物體擋住,擋住了返回黑色;沒擋住就調用一個單次散射函數,返回散射的值。
1.2?構建坐標系
- 首先要有一個明確的坐標系:這里將地球頂部作為世界原點建立坐標系,換句話來說,就按照Unity的世界坐標就行!
- 人眼此時就是A了,那么在Unity中相機的位置就是A的世界坐標,從A點出發發出射線與天空相交于點B,這時射線AB就是整個模擬過程中的“視線”
下圖是解釋圖,我覺得畫的不錯hhh
1.2 函數1:射線與球體求交
數學原理沒什么好說的,隨便找了個解釋原理的文章:
射線與球體/三角面片求交、重心坐標、插值_csu_xiji的博客-CSDN博客_射線求交
函數返回值
返回的是±t,rayOrigin+t*rayDir就能得到兩個交點啦。
//------------------------------------- // RaySphereIntersection //------------------------------------- float2 RaySphereIntersection(float3 rayOrigin, float3 rayDir, float3 sphereCenter, float sphereRadius) {rayOrigin -= sphereCenter;float a = dot(rayDir, rayDir);float b = 2.0 * dot(rayOrigin, rayDir);float c = dot(rayOrigin, rayOrigin) - (sphereRadius * sphereRadius);float d = b * b - 4 * a * c;if (d < 0){return -1;}else{d = sqrt(d);return float2(-b - d, -b + d) / (2 * a);} }1.3 計算的部分
就是主要的單次散射函數,主要有兩個關鍵:
- 計算
2 計算T(PA)
參考開頭的簡化方法,我們不計算,那么整體就是按照下式計算了!其中,海平面的散射系數是已知的,相位函數和光學距離(累加相對大氣密度)都需要計算。
2.1 函數2:計算大氣密度
//--------------------------------------------------------------------------- // GetAtmosphereDensity //--------------------------------------------------------------------------- void GetAtmosphereDensity(float3 position, float3 planetCenter, float3 lightDir, out float dpa, out float dpc) {// ρ(h)= exp(-h/H)float height = length(position - planetCenter) - _PlanetRadius;dpa = exp(-height / _DensityScaleHeight);// we don't computer the D(PC)dpc = 0; }其中,_DensityScaleHeight為大氣厚度。
2.2 函數3:相位函數
//--------------------------------------------------------------------------- // ApplyPhaseFunction //--------------------------------------------------------------------------- void ApplyPhaseFunction(inout float scatterMie, float cosAngle) {// only Miefloat g = _MieG;float g2 = g * g;float phase = (1.0 / (4.0 * PI)) * ((3.0 * (1.0 - g2)) / (2.0 * (2.0 + g2))) * ((1 + cosAngle * cosAngle) / (pow((1 + g2 - 2 * g * cosAngle), 3.0 / 2.0)));scatterMie *= phase; }2.3 函數3:計算總散射
//--------------------------------------------------------------------------- // IntegrateInscattering //--------------------------------------------------------------------------- float4 IntegrateInscattering(float3 rayStart, float3 rayDir, float rayLength, float3 planetCenter, float3 lightDir, float sampleCount) {float3 stepVector = rayDir * (rayLength / sampleCount);float stepSize = length(stepVector);float2 prevDPA = 0;float prevTransmittance = 0;float densityPA = 0;float densityCP = 0;float localDPA = 0;float scatterMie = 0;GetAtmosphereDensity(rayStart, planetCenter, lightDir, localDPA, densityCP);densityPA += localDPA * stepSize;prevDPA = localDPA;// local Inscattering, densityCP=0float Transmittance = exp(-(densityCP + densityPA) * _ExtinctionM) * localDPA;prevTransmittance = Transmittance;[loop]for (float i = 1.0; i < sampleCount; i += 1.0){float3 p = rayStart + stepVector * i;GetAtmosphereDensity(p, planetCenter, lightDir, localDPA, densityCP);densityPA += (prevDPA + localDPA) * (stepSize / 2.0); Transmittance = exp(-(densityCP + densityPA) * _ExtinctionM) * localDPA;scatterMie += (prevTransmittance + Transmittance) * stepSize / 2;prevTransmittance = Transmittance;prevDPA = densityPA;}// phase functionApplyPhaseFunction(scatterMie, dot(rayDir, -lightDir.xyz));float3 lightInscatter = _ScatteringM * scatterMie;return float4(lightInscatter, 1); }其中,為了方便用戶調試,cpp里對于Mie散射的S項和T項分別提供可控參數MieScatterCoef和MieExtinctionCoef:
Vector4 MieSct = new Vector4(2.0f, 2.0f, 2.0f, 0.0f) * 0.00001f;_ExtinctionM = MieSct * MieExtinctionCoef; _ScatteringM = MieSct * MieScatterCoef;也就是_ExtinctionM是計算所取的S項海平面處的散射系數,_ScatteringM是計算所取的M項海平面處的散射系數,都是由cpp直接傳入,MieSct取值參考:
1.1
總結
以上是生活随笔為你收集整理的【Unity大气渲染】Unity Shader中实现大气散射(半成品)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python在哪些省份加入高考加分项目_
- 下一篇: 私法英语表达尔雅答案_公款,私法