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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

在Unity中实现基于粒子的水模拟(二:开始着色)

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

在Unity中實(shí)現(xiàn)基于粒子的水模擬(二:開(kāi)始著色)


文章目錄

  • 在Unity中實(shí)現(xiàn)基于粒子的水模擬(二:開(kāi)始著色)
  • 前言
  • 一、生成頂點(diǎn)
  • 二、偏移模擬
    • 1.接收細(xì)分著色器輸出的頂點(diǎn)
    • 2.根據(jù)數(shù)據(jù)調(diào)用對(duì)應(yīng)的處理方法
    • 3.曲線擬合
    • 4.碰撞后的模擬
  • 三、著色
    • 1.片元著色器輸入
    • 2.生成寬度數(shù)據(jù)
    • 3.生成法線和深度數(shù)據(jù)
  • 總結(jié)
    • 1.一點(diǎn)小問(wèn)題
    • 2.補(bǔ)充


前言

筆者最近在研究Unity的可編程渲染管線,參考的文章地址,之后的項(xiàng)目應(yīng)該都會(huì)基于該渲染管線進(jìn)行拓展。
同時(shí)本文是基于這篇文章的Unity實(shí)現(xiàn),只是一種實(shí)現(xiàn)的參考。
由于粒子進(jìn)行模擬時(shí)會(huì)有恐怖的Overdraw,所以其實(shí)在游戲中實(shí)時(shí)運(yùn)行還是有點(diǎn)奢侈了,但是如果只是作為游戲中固定的流體模擬,不需要場(chǎng)景改變效果的話還是可以通過(guò)定制來(lái)實(shí)現(xiàn)很好的效果的。

不過(guò)本文還是實(shí)時(shí)模擬的,在一些細(xì)節(jié)上并沒(méi)有實(shí)現(xiàn)的很好,比如液體碰撞到物體后的效果實(shí)現(xiàn),因?yàn)楦锢淼牧W訉?shí)現(xiàn)太奢侈了,因此只是簡(jiǎn)單的實(shí)現(xiàn),想要更好的效果就自己定制吧。

同時(shí)這里將物理幀的數(shù)據(jù)刷新?lián)Q為了實(shí)時(shí)幀,讓液體噴出時(shí)更連續(xù),同時(shí)將開(kāi)頭的循環(huán)刪去,換為了不能刷新就等待,而不是循環(huán)一遍,因?yàn)檫@個(gè)循環(huán)會(huì)導(dǎo)致幀數(shù)變得很不穩(wěn)定,更新后的效果:

在自定義渲染管線中實(shí)現(xiàn)噴水效果


一、生成頂點(diǎn)

粒子的生成是通過(guò)曲面細(xì)分生成的頂點(diǎn)來(lái)生成的,也就是在我的這篇文章生成粒子的格式生成的,同時(shí)將生成粒子的頂點(diǎn)部分全部放到了一個(gè)文件中處理,讓整體更加模塊化,不像一開(kāi)始將整個(gè)流程放在了一個(gè)文件中。

首先在曲面細(xì)分的結(jié)構(gòu)體中要有我們傳入的所有數(shù)據(jù),同時(shí)為了讓輸入與輸出區(qū)分開(kāi)定義了兩個(gè)結(jié)構(gòu)體,但是實(shí)際上這兩個(gè)結(jié)構(gòu)體的數(shù)據(jù)是一致的,因?yàn)樾枰獋鬟f給幾何著色器,由幾何著色器進(jìn)行數(shù)據(jù)計(jì)算。

這里提一下,之前搜API時(shí)看少了,因?yàn)閁nity只提供了設(shè)置float2格式的uv坐標(biāo),但是實(shí)際上是可以支持float4的坐標(biāo)的,需要的話的可以換一下設(shè)置數(shù)據(jù)的方法,更充分的利用每一個(gè)數(shù)據(jù)。

struct TessVertex_All{float4 vertex : POSITION;float4 color : COLOR;float3 normal : NORMAL;float4 tangent : TANGENT;float2 uv0 : TEXCOORD0;float2 uv1 : TEXCOORD1;float2 uv2 : TEXCOORD2;float2 uv3 : TEXCOORD3;float2 uv4 : TEXCOORD4;float2 uv5 : TEXCOORD5;float2 uv6 : TEXCOORD6; };struct TessOutput_All{float4 vertex : Var_POSITION;float4 color : Var_COLOR;float3 normal : Var_NORMAL;float4 tangent : Var_TANGENT;float2 uv0 : Var_TEXCOORD0;float2 uv1 : Var_TEXCOORD1;float2 uv2 : Var_TEXCOORD2;float2 uv3 : Var_TEXCOORD3;float2 uv4 : Var_TEXCOORD4;float2 uv5 : Var_TEXCOORD5;float2 uv6 : Var_TEXCOORD6; };

然后就是曲面細(xì)分的標(biāo)準(zhǔn)格式了,還是那套流程,不過(guò)需要注意的是在SRP中的曲面細(xì)分支持檢測(cè)的宏需要自己定義,為了方便我直接刪除了,畢竟大部分機(jī)器都能夠支持了。

//頂點(diǎn)著色器的輸入值,直接傳遞不進(jìn)行操作 void tessVertAll (inout TessVertex_All v){}//細(xì)分參數(shù)控制著色器,細(xì)分的前置準(zhǔn)備 OutputPatchConstant hullconst(InputPatch<TessVertex_All, 3>v){OutputPatchConstant o = (OutputPatchConstant)0;float size = _TessDegree;//獲得三個(gè)頂點(diǎn)的細(xì)分距離值float4 ts = float4(size, size, size, size);//本質(zhì)上下面的賦值操作是對(duì)細(xì)分三角形的三條邊以及里面細(xì)分程度的控制//這個(gè)值本質(zhì)上是一個(gè)int值,0就是不細(xì)分,每多1細(xì)分多一層//控制邊緣的細(xì)分程度,這個(gè)邊緣程度的值不是我們用的,而是給Tessllation進(jìn)行細(xì)分控制用的o.edge[0] = ts.x;o.edge[1] = ts.y;o.edge[2] = ts.z;//內(nèi)部的細(xì)分程度o.inside = ts.w;return o; }[domain("tri")] //輸入圖元的是一個(gè)三角形 //確定分割方式 [partitioning("fractional_odd")] //定義圖元朝向,一般用這個(gè)即可,用切線為根據(jù) [outputtopology("triangle_cw")] //定義補(bǔ)丁的函數(shù)名,也就是我們上面的函數(shù),hull函數(shù)的返回值會(huì)傳到這個(gè)函數(shù)中,然后進(jìn)行曲面細(xì)分 [patchconstantfunc("hullconst")] //定義輸出圖元是一個(gè)三角形,和上面對(duì)應(yīng) [outputcontrolpoints(3)] TessOutput_All hull (InputPatch<TessVertex_All, 3> v, uint id : SV_OutputControlPointID){return v[id]; }[domain("tri")] TessOutput_All domain_All (OutputPatchConstant tessFactors, const OutputPatch<TessOutput_All, 3> vi, float3 bary : SV_DomainLocation){TessOutput_All v = (TessOutput_All)0;v.vertex = vi[0].vertex * bary.x + vi[1].vertex*bary.y + vi[2].vertex * bary.z;v.normal = vi[0].normal * bary.x + vi[1].normal*bary.y + vi[2].normal * bary.z;v.tangent = vi[0].tangent * bary.x + vi[1].tangent*bary.y + vi[2].tangent * bary.z;v.color = vi[0].color * bary.x + vi[1].color*bary.y + vi[2].color * bary.z;v.uv0 = vi[0].uv0 * bary.x + vi[1].uv0*bary.y + vi[2].uv0 * bary.z;v.uv1 = vi[0].uv1 * bary.x + vi[1].uv1*bary.y + vi[2].uv1 * bary.z;v.uv2 = vi[0].uv2 * bary.x + vi[1].uv2*bary.y + vi[2].uv2 * bary.z;v.uv3 = vi[0].uv3 * bary.x + vi[1].uv3*bary.y + vi[2].uv3 * bary.z;v.uv4 = vi[0].uv4 * bary.x + vi[1].uv4*bary.y + vi[2].uv4 * bary.z;v.uv5 = vi[0].uv5 * bary.x + vi[1].uv5*bary.y + vi[2].uv5 * bary.z;v.uv6 = vi[0].uv6 * bary.x + vi[1].uv6*bary.y + vi[2].uv6 * bary.z;return v; }

二、偏移模擬

1.接收細(xì)分著色器輸出的頂點(diǎn)

代碼如下(示例):

[maxvertexcount(30)] void geom(triangle TessOutput_All IN[3], inout TriangleStream<FragInput> tristream) {LoadWater(IN[0], tristream);LoadWater(IN[1], tristream);LoadWater(IN[2], tristream); }

LoadWater函數(shù)是用來(lái)確定該頂點(diǎn)狀態(tài)的函數(shù),用來(lái)進(jìn)行數(shù)據(jù)準(zhǔn)備以及調(diào)用對(duì)應(yīng)的處理函數(shù)。

2.根據(jù)數(shù)據(jù)調(diào)用對(duì)應(yīng)的處理方法

LoadWater是判斷該頂點(diǎn)的運(yùn)動(dòng)階段是碰撞前(曲線階段)還是碰撞后(自定義偏移階段),因?yàn)椴煌A段對(duì)數(shù)據(jù)處理的方式不同,因此采用不同的處理方式。

先確定頂點(diǎn)的啟動(dòng)時(shí)間以及頂點(diǎn)的階段,在同一批的粒子(一個(gè)三角面)不是同一時(shí)間輸出的,而是有一個(gè)輸出的時(shí)間范圍,因此需要確定該頂點(diǎn)是否在輸出時(shí)間。

//這批粒子的啟動(dòng)時(shí)間,IN.uv6.x是移動(dòng)時(shí)間 float beginTime = IN.tangent.w - _OutTime - _OffsetTime - IN.uv6.x; //這個(gè)頂點(diǎn)的發(fā)出時(shí)間,ramdom是一個(gè)根據(jù)頂點(diǎn)隨機(jī)的float4數(shù)據(jù) float outTime = _OutTime * ramdom.w + beginTime; //不在頂點(diǎn)發(fā)出時(shí)間,不進(jìn)行射出 if(_Time.y < outTime ) return;

接著根據(jù)數(shù)據(jù)判斷一下屬于哪個(gè)階段以及數(shù)據(jù)情況,調(diào)用對(duì)應(yīng)的處理方法。

//偏移階段,也就是碰撞后的一段時(shí)間if(partiTime >= 1){float3 end = (float3)0;if(step(0.5, IN.color.x))end = float3(IN.uv3.xy, IN.uv4.x);elseend = IN.tangent.xyz;Offset(IN, (_Time.y - outTime - IN.uv6.x) / _OffsetTime,tristream, ramdom, end );return;}if(step(0.5, IN.color.x)){ //第一條射線射中OnePointEnd(IN, partiTime, tristream);}else{ //第二條射線射中TwoPointEnd(IN, partiTime, tristream);}

3.曲線擬合

通過(guò)設(shè)置的數(shù)據(jù)進(jìn)行該頂點(diǎn)位于的位置獲取,也就是通過(guò)貝塞爾曲線進(jìn)行曲線確定該時(shí)間上頂點(diǎn)應(yīng)該位于的世界坐標(biāo)。擬合結(jié)束后輸出到頂點(diǎn)生成平面的方法(Move_outOnePoint)。

//當(dāng)?shù)谝粭l射線就碰到物體時(shí)執(zhí)行的方法 void OnePointEnd(TessOutput_All IN, float moveTime, inout TriangleStream<FragInput> tristream){float3 begin = float3(IN.uv0.xy, IN.uv1.x);float3 center = float3(IN.uv2.xy, IN.uv1.y);float3 end = float3(IN.uv3.xy, IN.uv4.x);IN.vertex.xyz = Get3PointBezier(begin, center, end, moveTime);Move_outOnePoint(tristream, IN, moveTime, end); }//第二條射線碰撞到的情況 void TwoPointEnd(TessOutput_All IN, float moveTime, inout TriangleStream<FragInput> tristream){float3 begin = float3(IN.uv0.xy, IN.uv1.x);float3 center = float3(IN.uv2.xy, IN.uv1.y);float3 end = float3(IN.uv3.xy, IN.uv4.x);float3 target1 = Get3PointBezier(begin, center, end, moveTime);begin = end;center = float3(IN.uv5.xy, IN.uv4.y);end = IN.tangent.xyz;float3 target2 = Get3PointBezier(begin, center, end, moveTime);IN.vertex.xyz = target1 + (target2 - target1) * moveTime;Move_outOnePoint(tristream, IN, moveTime, end); }

4.碰撞后的模擬

碰撞后的模擬目前實(shí)現(xiàn)的很隨便,也就是這篇文章的實(shí)現(xiàn)的移動(dòng)方式,內(nèi)容很少,因此這個(gè)部分是肯定需要根據(jù)項(xiàng)目重新設(shè)置的。

//偏移階段 void Offset(TessOutput_All IN, float offsetTime, inout TriangleStream<FragInput> tristream, float4 random, float3 begin){if(offsetTime > 1 || offsetTime < 0) return;float3 dir0 = lerp(_VerticalStart, _VerticalEnd, random.xyz);float3 normal = IN.normal;//確定旋轉(zhuǎn)矩陣float cosVal = dot(normalize( normal ), float3(0, 1, 0));float sinVal = sqrt(1 - cosVal * cosVal);float3x3 xyMatrix = float3x3(-cosVal, sinVal, 0,-sinVal, -cosVal, 0,0, 0, 1);float3x3 yzMatrix = float3x3(1, 0, 0,0, -cosVal, sinVal,0, -sinVal, -cosVal);float3x3 xzMatrix = float3x3(-cosVal, 0, -sinVal,0, 1, 0,sinVal, 0, -cosVal);float3 targetDir = mul(xzMatrix, mul( yzMatrix, mul(xyMatrix, dir0) ));IN.vertex.xyz = begin + targetDir * offsetTime;Offset_outOnePoint(tristream, IN, offsetTime); }

三、著色

1.片元著色器輸入

首先描述一下片源著色器的輸入結(jié)構(gòu)體,因?yàn)檫@個(gè)輸出的處理方式和這篇文章是一樣的,因此只描述一下輸入的數(shù)據(jù)。

struct FragInput{ //這個(gè)粒子平面的uv坐標(biāo),和particle system的粒子uv分布一樣float2 uv : TEXCOORD0; float4 pos : SV_POSITION;//目前沒(méi)有用到該數(shù)據(jù),因此目前就是粒子總時(shí)間float time : TEXCOORD2;//x為0時(shí)是曲線階段,1時(shí)為碰撞后//zyw存儲(chǔ)了這個(gè)粒子的球面中心float4 otherDate : TEXCOORD3;//存儲(chǔ)世界空間位置float3 worldPos : TEXCOORD4;float4 otherDate2 : TEXCOORD5; //預(yù)留數(shù)據(jù) };

2.生成寬度數(shù)據(jù)

生成寬度數(shù)據(jù)的格式很簡(jiǎn)單,直接根據(jù)紋理顏色值返回就行了,我用的紋理是一張帶透明通道的圓形圖,采集紋理顏色后根據(jù)對(duì)應(yīng)的階段來(lái)乘以其的透明度就行了。

float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * _Color;#ifdef _CURVE_ALPHAif(i.otherDate.x < 0.1){col *= saturate( LoadCurveTime( i.time.x, _MoveAlphaPointCount, _MoveAlphaPointArray ) );}else {col *= saturate( LoadCurveTime( i.time.x, _OffsetAlphaPointCount, _OffsetAlphaPointArray ) );}#endif

實(shí)際上寬度數(shù)據(jù)最重要的是紋理的混合模式,因?yàn)橐寯?shù)據(jù)疊加,因此要使用的混合模式是One One,不過(guò)為了更好的定義,可以通過(guò)設(shè)置混合模式為選項(xiàng)來(lái)控制混合效果。

[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1 [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0 [Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 1

疊加模式的寬度圖:

3.生成法線和深度數(shù)據(jù)

由于在渲染深度時(shí)需要深度寫入,因此我順便將法線數(shù)據(jù)也一同寫入了。
在粒子中生成法線的方式很簡(jiǎn)單,因?yàn)槭紫任覀冃枰@得當(dāng)這個(gè)粒子為球時(shí)的球心,其實(shí)就是生成粒子的根據(jù)點(diǎn)沿?cái)z像機(jī)方向原理球的半徑。

//worldVer是根據(jù)頂點(diǎn)的世界坐標(biāo),paritcleLen為半徑 float3 sphereCenter = worldVer - UNITY_MATRIX_V[2].xyz * paritcleLen;

通過(guò)圓心的位置與該像素的世界坐標(biāo)的差來(lái)得到法線數(shù)據(jù),不過(guò)直接這樣會(huì)在圓的外部也有數(shù)據(jù)(因?yàn)榱W邮且粋€(gè)平面),因此需要根據(jù)透明度剔除。

float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv); #ifdef _CURVE_ALPHAif(i.otherDate.x < 0.1){col *= saturate( LoadCurveTime( i.time.x, _MoveAlphaPointCount, _MoveAlphaPointArray ) );}else col *= saturate( LoadCurveTime( i.time.x, _OffsetAlphaPointCount, _OffsetAlphaPointArray ) ); #endif clip(col.a - 0.5); //剔除的位置float3 normal = normalize(i.worldPos - i.otherDate.yzw); //由于紋理不能存儲(chǔ)負(fù)數(shù),需要進(jìn)行數(shù)據(jù)映射 return float4(normal * 0.5 + 0.5, 1);

生成的法線圖,深度圖因?yàn)榫葐?wèn)題,就不顯示了,反正都是全黑


總結(jié)

1.一點(diǎn)小問(wèn)題

到此就完成了3個(gè)紋理的渲染了,這里需要說(shuō)明一下,我這里的渲染的調(diào)用是通過(guò)可編程渲染管線調(diào)用的,因?yàn)槠淇梢宰寯?shù)據(jù)渲染到我想要的位置,但是如果是在默認(rèn)管線中是不能這么操作的。
因?yàn)槟J(rèn)管線渲染只能通過(guò)指定Camera的TargetTexture來(lái)渲染,通過(guò)指定兩個(gè)攝像機(jī)渲染,可以渲染出這兩張圖片,不過(guò)這里有一個(gè)小問(wèn)題,就是我沒(méi)有找到Unity中傳遞紋理默認(rèn)的深度數(shù)據(jù)的方式,導(dǎo)致一開(kāi)始還用了一個(gè)攝像機(jī)來(lái)渲染深度。

2.補(bǔ)充

實(shí)際上我這里的粒子模擬是有很大問(wèn)題的,因?yàn)閮蓚€(gè)階段導(dǎo)致有交叉問(wèn)題,比如邊緣的法線頂替了曲線的法線,但是實(shí)際上邊緣的法線的水不怎么厚,因此我覺(jué)得這種模式的粒子模擬方式不好,不過(guò)如果是通過(guò)粒子來(lái)模擬水流之類的效果應(yīng)該會(huì)好很多,成本也低。

同時(shí)可以因?yàn)樗泻穸戎?#xff0c;如果是模擬牛奶之類的使用BTDF效果模擬也會(huì)很不錯(cuò)。

總結(jié)

以上是生活随笔為你收集整理的在Unity中实现基于粒子的水模拟(二:开始着色)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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

主站蜘蛛池模板: 日韩亚洲国产精品 | 美女裸体网站久久久 | 亚洲一区二区视频 | 欧美一级二级三级 | 丝袜一区二区三区 | 亚洲欧美中文日韩在线观看 | 精品免费在线观看 | 动漫精品一区一码二码三码四码 | 日本五十熟hd丰满 | 女同久久另类69精品国产 | 天天激情| 精品无码久久久久久久久 | 调教丰满的已婚少妇在线观看 | 天天操天天碰 | 性色av浪潮 | 免费看黄在线网站 | 在线播放无码后入内射少妇 | www久久 | 日韩人妻精品在线 | 亚洲欧美一区二区三区在线观看 | 午夜av一区 | 亚洲国产成人综合 | 色婷婷国产精品久久包臀 | 成人国产免费 | 五月婷婷色 | 天堂久久久久久 | 成人一级黄色 | 99激情| 亚洲中字在线 | 日韩三级视频在线观看 | 在线免费看av的网站 | 久久婷婷一区 | 久久成人福利视频 | 特黄一区| 久久精品噜噜噜成人 | 伊人成人在线观看 | 国产一区二区在 | 久久日韩 | 国产色播 | 理论片中文 | 隔壁人妻偷人bd中字 | 亚洲精品97久久中文字幕无码 | 国产一级片a | 亚洲精品在线视频 | 成人福利视频 | 日韩在线黄色 | 久久久久免费精品 | 美日韩av| 午夜成人免费电影 | 亚洲精品无码久久 | 精品视频一二三区 | 波多一区二区 | 老司机精品视频在线播放 | 日本黄色片网址 | 都市激情麻豆 | 午夜av剧场 | 手机在线永久免费观看av片 | 中文字幕一区二区人妻在线不卡 | 成年在线视频 | 国产精品高潮呻吟AV无码 | 丁香五色月 | wwwww国产| 日韩五码| 少妇人禽zoz0伦视频 | 国产不卡一 | 孕妇爱爱视频 | 国产av人人夜夜澡人人爽麻豆 | 亚洲综合三区 | 奇米第四色在线 | 欧美精品久久久久 | 欧美成年人 | 国产不卡一区二区视频 | 日韩精品一区二区三 | 日韩电影在线观看一区二区 | 国产熟妇一区二区三区四区 | h片网站在线观看 | 清清草在线视频 | 99久久99久久精品国产片果冻 | 五月婷色| 日少妇b | 秋霞午夜鲁丝一区二区老狼 | 欧洲金发美女大战黑人 | 激情五月婷婷在线 | 在线免费av片 | 亚洲图片在线观看 | 国产麻豆久久 | 色婷五月天 | 99视频导航 | 青青草综合在线 | 日韩黄色片免费看 | 亚洲黄色激情视频 | 国产亚洲欧美在线视频 | 爱爱网视频 | 日韩一区免费 | 日本在线第一页 | 成人无高清96免费 | 古代黄色一级片 | 日韩乱码人妻无码中文字幕 | 午夜精品一区二 |