在Unity中实现基于粒子的水模拟(三:混合屏幕)
在Unity中實(shí)現(xiàn)基于粒子的水模擬(三:混合屏幕)
文章目錄
- 在Unity中實(shí)現(xiàn)基于粒子的水模擬(三:混合屏幕)
- 前言
- 一、著色算法介紹
- 1.折射
- 2.反射
- 二、準(zhǔn)備紋理
- 1.獲取紋理
- 2.模糊紋理
- 2.混合主紋理上
- 總結(jié)
- 1.現(xiàn)有問題
前言
經(jīng)過了前面的紋理,我們就到了最后的混合到屏幕階段了,這個(gè)階段的邏輯不是很難,本來是要基于這篇文章的公式來往上套的,不過由于本人水平問題并沒有看懂作者的具體含義,于是就大概的實(shí)現(xiàn)了一下,估計(jì)出入挺大的。
效果看圖:
項(xiàng)目鏈接:注意是在其中的master分支中
一、著色算法介紹
我們將顏色分為兩部分,也就是折射和反射。
1.折射
折射要有最好的效果應(yīng)該是要根據(jù)當(dāng)前像素執(zhí)行SSR算法,也就是計(jì)算出折射法線后,在通過深度重建的世界坐標(biāo)中,用最接近折射位置的那個(gè)像素的顏色值作為折射的顏色值,但是這種方式太昂貴,所以沒有采用這種方式。
本文用的方式是最簡單的折射采樣方式,根據(jù)法線以及液體寬度指進(jìn)行uv偏移來獲取折射的顏色值(因?yàn)閷?shí)在看不懂作者到底什么意思,所以就這樣簡單實(shí)現(xiàn)了 )。
2.反射
反射的計(jì)算比較簡單,直接傳遞一個(gè)Cube貼圖,然后根據(jù)反射方向進(jìn)行紋理采樣即可。
最后將兩個(gè)顏色根據(jù)透光度進(jìn)行混合就計(jì)算完成了。
二、準(zhǔn)備紋理
1.獲取紋理
在SRP中可以通過設(shè)置CommandBuffer的SetRenderTarget將寬度、法線、深度圖專門用特定的ShaderTagId指定,這樣可以渲染兩次就得到所有3張紋理,因?yàn)榉ň€可以和深度一次得到。
但是在Build-in中我沒有找到指定深度圖的方法,導(dǎo)致后面模糊時(shí)不能很好的將深度值提取出來模糊,不過因?yàn)槲覀冞@個(gè)計(jì)算方式只使用了xy的法線值,可以將深度值存儲在法線紋理的b通道中。
裁剪空間獲取深度的公式:
其中f是遠(yuǎn)裁剪面、n是近裁剪面、z是視角空間z值。
代碼表示:
2.模糊紋理
模糊紋理使用的算法是雙邊濾波,這個(gè)算法可以讓球體的邊界混合起來,同時(shí)不會像高斯模糊一樣讓顏色值與周圍混合,導(dǎo)致顏色不能準(zhǔn)確對應(yīng)該像素。
核心代碼:
float CompareColor(float4 col1, float4 col2) {float l1 = LinearRgbToLuminance(col1.rgb);float l2 = LinearRgbToLuminance(col2.rgb);return smoothstep(_BilaterFilterFactor, 1.0, 1.0 - abs(l1 - l2)); }float4 BilateralFilterFragment (Varyings input) : SV_TARGET{float2 delta = _PostFxEffectSource_TexelSize.xy * _BlurRadius.xy;//采集Normal的顏色值float4 col = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV);float4 col0a = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV - delta);float4 col0b = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV + delta);float4 col1a = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV - 2.0 * delta);float4 col1b = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV + 2.0 * delta);float4 col2a = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV - 3.0 * delta);float4 col2b = SAMPLE_TEXTURE2D(_PostFxEffectSource, sampler_linear_clamp, input.screenUV + 3.0 * delta);float w = 0.37004405286;float w0a = CompareColor(col, col0a) * 0.31718061674;float w0b = CompareColor(col, col0b) * 0.31718061674;float w1a = CompareColor(col, col1a) * 0.19823788546;float w1b = CompareColor(col, col1b) * 0.19823788546;float w2a = CompareColor(col, col2a) * 0.11453744493;float w2b = CompareColor(col, col2b) * 0.11453744493;float3 result;result = w * col.rgb;result += w0a * col0a.rgb;result += w0b * col0b.rgb;result += w1a * col1a.rgb;result += w1b * col1b.rgb;result += w2a * col2a.rgb;result += w2b * col2b.rgb;result /= w + w0a + w0b + w1a + w1b + w2a + w2b;return float4(result, 1); }雙邊濾波也需要像高斯模糊一樣執(zhí)行兩次,對垂直以及水平進(jìn)行模糊,具體實(shí)現(xiàn)可以參考這篇文章。
2.混合主紋理上
這部分就和正常的后處理流程一樣了,具體后處理如何實(shí)現(xiàn)就不贅述了,這里只描寫著色的核心代碼。
首先第一步,進(jìn)行深度對比,判斷該像素是否需要進(jìn)行液體著色,也就是是否被遮擋。
if(currentDepth >= waterDepth)return float4(currentColor, 1); //返回原本像素顏色獲得折射以及反射的顏色值:
float3 viewDirection = normalize( _WorldSpaceCameraPos - worldPos );float3 reflectDir = normalize(-viewDirection + 2 * waterNormal); //反射方向//反射顏色float3 specular = SAMPLE_TEXTURECUBE_LOD( _WaterReflectCube, sampler_WaterReflectCube, reflectDir, 0 ).rgb;float2 ofssetUV = (-viewDirection - 0.2 * waterNormal).xy * waterWidth * 0.2 + input.screenUV;//折射顏色float3 refrColor = SAMPLE_TEXTURE2D_LOD(_PostFxEffectSource, sampler_linear_clamp, ofssetUV, 0).rgb;//混合液體顏色,transLight是透光度refrColor = lerp(refrColor * _WaterColor.rgb, refrColor, transLight);按照大佬文章的公式:
其中R1和R2分別是水和空氣的折射率。
代碼:
總結(jié)
導(dǎo)致這個(gè)粒子水系列就正式完成了,我覺得這個(gè)實(shí)現(xiàn)的水不適合作為“水”,不過如果拿來做流體模擬的話還是可以的,因?yàn)橛猩疃取⒎ň€圖,用來實(shí)現(xiàn)牛奶等BSDF等渲染效果是很不錯的,畢竟BSDF的一個(gè)難點(diǎn)是寬度計(jì)算,有了寬度其他計(jì)算就和BRDF沒什么區(qū)別了。
1.現(xiàn)有問題
目前本場景的粒子并沒有進(jìn)行軟粒子處理,如果有必要的話可以在渲染寬度時(shí)將深度圖傳入,在深度相近時(shí)進(jìn)行透明,而且可以根據(jù)深度值進(jìn)行Hi-z剔除,優(yōu)化效果。
本項(xiàng)目的根據(jù)都是基于Unity的物理檢測的,這個(gè)部分是損耗最大的部分,也是最容易出bug的部分,如果之后場景需要的話建議直接設(shè)置固定的流動方向,不進(jìn)行真正的時(shí)時(shí)物理檢測,刷新流動方向,這樣CPU占用太大了。
理論上這些粒子著色都是要用ComputeShader寫的,但是我在寫這個(gè)系列時(shí)還不懂這些,在最近研究SRP時(shí)才知道有這個(gè)東西。
不過由于模擬時(shí)數(shù)據(jù)量太大了,這種直接將數(shù)據(jù)傳遞到頂點(diǎn)的方式說不定更適合物理模擬,不過之前實(shí)現(xiàn)的粒子系統(tǒng)可能就真的需要更新了,之后應(yīng)該會更新Compute Shader進(jìn)行剔除的粒子系統(tǒng),更加全面的實(shí)現(xiàn)Unity新版粒子系統(tǒng)。
總結(jié)
以上是生活随笔為你收集整理的在Unity中实现基于粒子的水模拟(三:混合屏幕)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 497D Ge
- 下一篇: 新天绿色能源与建投国融续签温室气体减排项