如何在Unity中實現水體互動?
1.標記水體碰撞的位置
2.計算水波的傳遞 通過波動公式,3D或者2D 波動公式都行
3.水面頂點取樣波動傳遞結果計算結果做頂點Y軸偏移
本文由Unity Connect博主從自身實踐經驗出發,為大家分享在Unity中如何實現水體互動?
相關公式:
根據公式可知波的下次一次傳遞 z(i,j,k+1) 為 當前波值+上一次波值+周圍波值
當前波值 *= (4-8*c^2*t^2/d^2/d^2)/(u*t)
上一次波值 *= (ut-2) / (ut + 2)
四周波值 *= (2c^2t^2/d^2) / (ut + 2)
其中各引數含義為 c 波速, u 粘度, d 波的遞進距離, t 為遞進時間
下面我們著手開始建立:
首先建立水面。這裡直接用Unity Wiki的已有輪子的建立平面,下載wiki上的程式碼,傳到專案中。
https://wiki.unity3d.com/index.php/CreatePlane
這裡我們直接建立一個寬10米,長10米,間隔100的平面,間隔越多,水體的顆粒感越小。對應本文開頭描述的三大步驟,建立3個紋理,對應水體碰撞標記,傳遞,渲染。
- m_waterWaveMarkTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
- m_waterWaveMarkTexture.name = "m_waterWaveMarkTexture";
- m_waveTransmitTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
- m_waveTransmitTexture.name = "m_waveTransmitTexture";
- m_prevWaveMarkTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
- m_prevWaveMarkTexture.name = "m_prevWaveMarkTexture";
標記水體碰撞位置
- void WaterPlaneCollider()
- {
- hasHit = false;
- if (Input.GetMouseButton(0))
- {
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
- RaycastHit hitInfo = new RaycastHit();
- bool ret = Physics.Raycast(ray.origin, ray.direction, out hitInfo);
- if (ret)
- {
- Vector3 waterPlaneSpacePos = WaterPlane.transform.worldToLocalMatrix * new Vector4(hitInfo.point.x, hitInfo.point.y, hitInfo.point.z, 1);
- float dx = (waterPlaneSpacePos.x / WaterPlaneWidth) + 0.5f;
- float dy = (waterPlaneSpacePos.z / WaterPlaneLength) + 0.5f;
- hitPos.Set(dx, dy);
- m_waveMarkParams.Set(dx, dy, WaveRadius * WaveRadius, WaveHeight);
- hasHit = true;
- }
- }
- }
由於我們預設Raycast 獲取的是碰撞的世界座標,我們期望的是直接獲取到 [0-1] 範圍的數值用來對映到uv空間,直接在 m_waterWaveMarkTexture 進行標記, 因此我們乘以一個 world2Local 矩陣變換到本地, 又因為CreatePlane預設建立的Pivot 位於中心,再除以寬高縮放到1區間時,值域落在[-0.5,0.5]上,因此我們還要做 + 0.5偏移。
標記水體碰撞Shader
- float dx = i.uv.x - _WaveMarkParams.x;
- float dy = i.uv.y - _WaveMarkParams.y;
- float disSqr = dx * dx + dy * dy;
- int hasCol = step(0, _WaveMarkParams.z - disSqr);
- float waveValue = DecodeHeight(tex2D(_MainTex, i.uv));
- if (hasCol == 1) {
- waveValue = _WaveMarkParams.w;
- }
根據傳入的_WaveMarkParams.xy 跟當前uv 對比,在筆刷範圍內的畫素標記位預設波高度。
波的傳遞Shader
- static const float2 WAVE_DIR[4] = { float2(1, 0), float2(0, 1), float2(-1, 0), float2(0, -1) };
- float dx = _WaveTransmitParams.w;
- float avgWaveHeight = 0;
- for (int s = 0; s < 4; s++)
- {
- avgWaveHeight += DecodeHeight(tex2D(_MainTex, i.uv + WAVE_DIR[s] * dx));
- }
- //(2 * c^2 * t^2 / d ^2) / (u * t + 2)*(z(x + dx, y, t) + z(x - dx, y, t) + z(x, y + dy, t) + z(x, y - dy, t);
- float agWave = _WaveTransmitParams.z * avgWaveHeight;
- // (4 - 8 * c^2 * t^2 / d^2) / (u * t + 2)
- float curWave = _WaveTransmitParams.x * DecodeHeight(tex2D(_MainTex, i.uv));
- // (u * t - 2) / (u * t + 2) * z(x,y,z, t - dt) 上一次波浪值 t - dt
- float prevWave = _WaveTransmitParams.y * DecodeHeight(tex2D(_PrevWaveMarkTex, i.uv));
- //波衰減
- float waveValue = (curWave + prevWave + agWave) * _WaveAtten;
最後就是水體的呈現,因為需要做頂點紋理取樣,因此需要至少ES3.0 硬體。
- v2f vert (appdata v)
- {
- v2f o;
- float4 localPos = v.vertex;
- float4 waveTransmit = tex2Dlod(_WaveResult, float4(v.uv, 0, 0));
- float waveHeight = DecodeFloatRGBA(waveTransmit);
- localPos.y += waveHeight * _WaveScale;
- float3 worldPos = mul(unity_ObjectToWorld, localPos);
- float3 worldSpaceNormal = mul(unity_ObjectToWorld, v.normal);
- float3 worldSpaceViewDir = UnityWorldSpaceViewDir(worldPos);
- o.vertex = mul(UNITY_MATRIX_VP, float4(worldPos, 1));
- o.uv = v.uv;
- o.worldSpaceReflect = reflect(-worldSpaceViewDir, worldSpaceNormal);
- return o;
* 本文參考的波動相關資料:
https://en.wikipedia.org/wiki/Wave_equation
https://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869 流體 章節
作者:dreamfairy
來源:Unity官方平臺
原地址:https://mp.weixin.qq.com/s/-sL54xgyX6mVMnVOMHnpVg
相關文章
- 如何在unity實現足夠快的2d動態光照Unity
- AngularJS中Directive間互動實現合成AngularJS
- 如何在鬆弛中建立互動式按鈕
- 基於Web實現遠端與硬體互動Web
- 在Unity中實現手部跟蹤Unity
- Unity中實現人形角色的攀爬Unity
- 如何在移動應用中實現AI畫圖?AI
- 如何在 UE4 移動端中實現 HZB?
- 如何在DataWindow中實現列的自動折行
- 如何在jdonframework中實現EventBus?Framework
- unity3d與android互動Unity3DAndroid
- Vue 3現實生活中的過渡和微互動Vue
- Android在WebView中載入HTML並實現互動AndroidWebViewHTML
- 如何在互動式環境中執行Python程式Python
- 物體識別桌利用智慧互動實現資訊傳播
- 【unity2022與html互動】UnityHTML
- 如何在Redis中實現事務Redis
- 如何在mysql中實現自然排序MySql排序
- 如何在TOMCAT中實現SSO?Tomcat
- 如何在Java中實現事件驅動的微服務架構Java事件微服務架構
- Unity實現“籠中窺夢”的渲染效果Unity
- 讓微信小程式開發如魚得水微信小程式
- 邊做遊戲邊划水: 基於淺水方程的水面互動、河道互動模擬方法遊戲
- 互動投影的幾種實現方式
- CSS 實現搜尋相關互動CSS
- RecyclerView 之使用 ItemTouchHelper 實現互動動畫View動畫
- Rust使用Sauron實現Web介面互動RustWeb
- C# 編寫 Windows 動態桌面軟體實現(一)之桌面互動功能C#Windows
- 在Flask程式中實現GitHub登入和GitHub資源互動FlaskGithub
- JS互動 點選WKWebView中的圖片實現預覽效果JSWebView
- 如何在 Java 中實現無向圖Java
- Unity學習-程式碼中動態生成預製體Unity
- 在Unity中實現2D光照系統Unity
- Unity3D中實現幀同步 - Part 2Unity3D
- Flask 運用Xterm實現互動終端Flask
- Servlet實現、與html的簡單互動ServletHTML
- .NET 實現的互動式 OA 系統
- 移動機器人如何在陌生環境中實現智慧導航?機器人