本文轉(zhuǎn)自Unity Connect博主 dreamfairy
制作可交互的水體蝶押,大致分為三步
1.標(biāo)記水體碰撞的位置
2.計(jì)算水波的傳遞 通過波動(dòng)公式,3D或者2D 波動(dòng)公式都行
3.水面頂點(diǎn)采樣波動(dòng)傳遞結(jié)果計(jì)算結(jié)果做頂點(diǎn)Y軸偏移
本文參考的波動(dòng)相關(guān)資料https://en.wikipedia.org/wiki/Wave_equationhttps://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869 流體 章節(jié)
相關(guān)公式
根據(jù)公式可知波的下次一次傳遞 z(i,j,k+1) 為 當(dāng)前波值+上一次波值+周圍波值
當(dāng)前波值 *= (4-8*c^2*t^2/d^2/d^2)/(u*t)
上一次波值 *= (ut-2) / (ut + 2)
四周波值 *= (2c^2t^2/d^2) / (ut + 2)
其中各參數(shù)含義為 c 波速竭沫, u 粘度, d 波的遞進(jìn)距離, t 為遞進(jìn)時(shí)間
ok~ 我們重頭開始
首先要建立水面
這里直接用Unity Wiki的已有輪子的創(chuàng)建平面膀捷,下載wiki上的代碼撕捍,傳到項(xiàng)目中https://wiki.unity3d.com/index.php/CreatePlane
這里我們直接創(chuàng)建一個(gè)寬10米拿穴,長(zhǎng)10米,間隔100的平面忧风, 間隔越多默色,水體的顆粒感越小
對(duì)應(yīng)本文開頭描述的三大步驟
創(chuàng)建3個(gè)紋理
對(duì)應(yīng)水體碰撞標(biāo)記,傳遞狮腿,渲染
? ? ? ? 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";
標(biāo)記水體碰撞位置
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;
? ? ? ? ? ? }
? ? ? ? }
? ? }
由于我們默認(rèn)Raycast 獲取的是碰撞的世界坐標(biāo)腿宰,我們期望的是直接獲取到 [0-1] 范圍的數(shù)值用來(lái)映射到uv空間,直接在 m_waterWaveMarkTexture 進(jìn)行標(biāo)記缘厢, 因此我們乘以一個(gè) world2Local 矩陣變換到本地吃度, 又因?yàn)镃reatePlane默認(rèn)創(chuàng)建的Pivot 位于中心,再除以寬高縮放到1區(qū)間時(shí)贴硫,值域落在[-0.5,0.5]上椿每,因此我們還要做 + 0.5偏移
標(biāo)記水體碰撞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;
? ? ? ? ? ? ? ? }
根據(jù)傳入的_WaveMarkParams.xy 跟當(dāng)前uv 對(duì)比,在筆刷范圍內(nèi)的像素標(biāo)記位默認(rèn)波高度
波的傳遞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;
最后就是水體的呈現(xiàn),因?yàn)樾枰鲰旤c(diǎn)紋理采樣间护,因此需要至少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;
? ? ? ? ? ? }
github地址https://github.com/dreamfairy/interactivity-waterplane
原文鏈接:https://connect.unity.com/p/zai-unityzhong-shi-xian-shui-ti-jiao-hu?app=true
歡迎戳上方原文鏈接删壮,下載Unity官方技術(shù)社區(qū)app,發(fā)現(xiàn)更多資源干貨~