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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

在Unity中實現基于粒子的水模擬(三:混合屏幕)


文章目錄

  • 在Unity中實現基于粒子的水模擬(三:混合屏幕)
  • 前言
  • 一、著色算法介紹
    • 1.折射
    • 2.反射
  • 二、準備紋理
    • 1.獲取紋理
    • 2.模糊紋理
    • 2.混合主紋理上
  • 總結
    • 1.現有問題


前言

經過了前面的紋理,我們就到了最后的混合到屏幕階段了,這個階段的邏輯不是很難,本來是要基于這篇文章的公式來往上套的,不過由于本人水平問題并沒有看懂作者的具體含義,于是就大概的實現了一下,估計出入挺大的。
效果看圖:

項目鏈接:注意是在其中的master分支中

一、著色算法介紹

我們將顏色分為兩部分,也就是折射反射。

1.折射

折射要有最好的效果應該是要根據當前像素執行SSR算法,也就是計算出折射法線后,在通過深度重建的世界坐標中,用最接近折射位置的那個像素的顏色值作為折射的顏色值,但是這種方式太昂貴,所以沒有采用這種方式。

本文用的方式是最簡單的折射采樣方式,根據法線以及液體寬度指進行uv偏移來獲取折射的顏色值(因為實在看不懂作者到底什么意思,所以就這樣簡單實現了 )。

2.反射

反射的計算比較簡單,直接傳遞一個Cube貼圖,然后根據反射方向進行紋理采樣即可。

最后將兩個顏色根據透光度進行混合就計算完成了。

二、準備紋理

1.獲取紋理

在SRP中可以通過設置CommandBuffer的SetRenderTarget將寬度、法線、深度圖專門用特定的ShaderTagId指定,這樣可以渲染兩次就得到所有3張紋理,因為法線可以和深度一次得到。

但是在Build-in中我沒有找到指定深度圖的方法,導致后面模糊時不能很好的將深度值提取出來模糊,不過因為我們這個計算方式只使用了xy的法線值,可以將深度值存儲在法線紋理的b通道中。
裁剪空間獲取深度的公式:

其中f是遠裁剪面、n是近裁剪面、z是視角空間z值。
代碼表示:

//獲取深度的公式,這個深度值就是深度圖中的深度值 //_ProjectionParams是Unity提供的數據,y是攝像機的近平面、z是遠平面 //i.pos是裁剪空間坐標,其中的w值存儲著視角空間的z值 depth = (_ProjectionParams.z * i.pos.w - _ProjectionParams.z * _ProjectionParams.y)/((_ProjectionParams.z - _ProjectionParams.y) * i.pos.w) //Unity會將深度圖值轉化,讓近平面的精度更高,遠平面精度可以低一點, //因為浮點數在接近0時精度會上升 depth = 1- depth;

2.模糊紋理

模糊紋理使用的算法是雙邊濾波,這個算法可以讓球體的邊界混合起來,同時不會像高斯模糊一樣讓顏色值與周圍混合,導致顏色不能準確對應該像素。

核心代碼:

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); }

雙邊濾波也需要像高斯模糊一樣執行兩次,對垂直以及水平進行模糊,具體實現可以參考這篇文章。

2.混合主紋理上

這部分就和正常的后處理流程一樣了,具體后處理如何實現就不贅述了,這里只描寫著色的核心代碼。

首先第一步,進行深度對比,判斷該像素是否需要進行液體著色,也就是是否被遮擋。

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;

總結

導致這個粒子水系列就正式完成了,我覺得這個實現的水不適合作為“水”,不過如果拿來做流體模擬的話還是可以的,因為有深度、法線圖,用來實現牛奶等BSDF等渲染效果是很不錯的,畢竟BSDF的一個難點是寬度計算,有了寬度其他計算就和BRDF沒什么區別了。

1.現有問題

目前本場景的粒子并沒有進行軟粒子處理,如果有必要的話可以在渲染寬度時將深度圖傳入,在深度相近時進行透明,而且可以根據深度值進行Hi-z剔除,優化效果。

本項目的根據都是基于Unity的物理檢測的,這個部分是損耗最大的部分,也是最容易出bug的部分,如果之后場景需要的話建議直接設置固定的流動方向,不進行真正的時時物理檢測,刷新流動方向,這樣CPU占用太大了。

理論上這些粒子著色都是要用ComputeShader寫的,但是我在寫這個系列時還不懂這些,在最近研究SRP時才知道有這個東西。
不過由于模擬時數據量太大了,這種直接將數據傳遞到頂點的方式說不定更適合物理模擬,不過之前實現的粒子系統可能就真的需要更新了,之后應該會更新Compute Shader進行剔除的粒子系統,更加全面的實現Unity新版粒子系統。

總結

以上是生活随笔為你收集整理的在Unity中实现基于粒子的水模拟(三:混合屏幕)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。