日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在Unity中实现基于粒子的水模拟(三:混合屏幕)

發(fā)布時(shí)間:2024/1/8 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在Unity中实现基于粒子的水模拟(三:混合屏幕) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在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值。
代碼表示:

//獲取深度的公式,這個(gè)深度值就是深度圖中的深度值 //_ProjectionParams是Unity提供的數(shù)據(jù),y是攝像機(jī)的近平面、z是遠(yuǎn)平面 //i.pos是裁剪空間坐標(biāo),其中的w值存儲著視角空間的z值 depth = (_ProjectionParams.z * i.pos.w - _ProjectionParams.z * _ProjectionParams.y)/((_ProjectionParams.z - _ProjectionParams.y) * i.pos.w) //Unity會將深度圖值轉(zhuǎn)化,讓近平面的精度更高,遠(yuǎn)平面精度可以低一點(diǎn), //因?yàn)楦↑c(diǎn)數(shù)在接近0時(shí)精度會上升 depth = 1- depth;

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分別是水和空氣的折射率。
代碼:

float n_0 = pow( (_WaterData.y - _WaterData.x) / (_WaterData.y + _WaterData.x), 2 );float fresnel = ( n_0 + (1 - n_0) * pow( 1 - dot(viewDirection, waterNormal), 5 ) )* waterWidth;float3 finalCol = refrColor * (1 - fresnel) + fresnel * specular;

總結(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。