老師:
"注意觀察這張圖片..."
小明:
"嗯葵孤,我看到了鼻由,它一臉嫌棄的看著我..."
老師:
"不是讓你看這個小精靈暇榴,你看到后面的房子沒有,只能看到一個房頂蕉世,研究一下蔼紧,看看怎么實現(xiàn)"
小明:
"好的老師,這兩個蘋果真不錯..."
彎曲地表
大家玩動森的時候都發(fā)現(xiàn)了狠轻,角色移動奸例,地表會有一個彎曲的視差效果,看起來很酷向楼,我們一起來分析一下實現(xiàn)原理查吊。
假設(shè)一個人前面有一個平板,他看過去就是我們的正常視角湖蜕,如果想讓它變成曲面逻卖,就得把平板做彎曲,如下圖所示:
看到平板彎曲的形狀昭抒,是不是想起了高中學(xué)過的一元二次多項式曲線评也?
當(dāng)a=-1,b=c=0的時候炼杖,就是我們要的曲線了。也可以看做平板彎曲的程度(Y坐標(biāo)位移值)和距離你操縱的角色(藍(lán)框選中的小人)的距離的平方成正比盗迟。
DeltaY = curvature * Distance * Distance坤邪, 其中curvature是一個常量。
好了罚缕,數(shù)學(xué)公式有了艇纺,下面我們就可以動手改寫VertexShader了,首先要動態(tài)的把你操縱的角色的世界坐標(biāo)傳給Shader,然后在頂點(diǎn)Shader里面計算當(dāng)前頂點(diǎn)到角色的距離的平方(因為向量求距離平方更高效,所以不用求出距離來再相乘), 然后乘上常量curvature邮弹,這個常量也開放給用戶設(shè)置喂饥,以便控制彎曲的程度。封裝一個函數(shù)給VertexShader調(diào)用:
//實現(xiàn)球面地形添加的代碼
#if defined(_SPHERE_TERRAIN_ON)
uniform float _Curvature; //曲率
uniform float3 _AnchorPosWS; //地形的錨點(diǎn)
// 對外接口肠鲫,得到彎曲后的裁切空間坐標(biāo)
inline float4 GetFixedClipPos(float4 objVertex)
{
// 《動森》中只考慮z軸距離的彎曲
#ifdef _SPHERE_TERRAIN_ONLY_Z
//頂點(diǎn)由本地坐標(biāo)轉(zhuǎn)換到世界坐標(biāo)
float3 worldPos = mul(unity_ObjectToWorld, objVertex);
//攝像機(jī)的方向向量
float3 viewDir = GetViewForwardDir();
viewDir.y = 0.0f;
float dist2Player = pow(dot(worldPos - _AnchorPosWS, normalize(viewDir)), 2);
//根據(jù)離著玩家的遠(yuǎn)近,向下彎曲不同的坐標(biāo)
worldPos.y -= dist2Player * _Curvature;
return mul(UNITY_MATRIX_VP, float4(worldPos, 1));
#else
float3 worldPos = mul(unity_ObjectToWorld, objVertex);
// 沒有必要得到精確的距離或粮,因此不計算開方
float2 tempFloat2 = pow(worldPos.xz - _AnchorPosWS.xz, 2);
float dist2Player = tempFloat2.x + tempFloat2.y;
// 根據(jù)系數(shù)降低頂點(diǎn)"高度"导饲,實現(xiàn)視差效果
worldPos.y -= dist2Player * _Curvature;
// 世界空間到剪裁空間
return mul(UNITY_MATRIX_VP, float4(worldPos, 1));
#endif
}
#endif
這里提供了一個可選項 _SPHERE_TERRAIN_ONLY_Z,指明是僅僅彎曲攝像機(jī)向前的方向(攝像機(jī)空間Z軸方向在世界空間xz平面的投影方向),還是真的彎曲成一個球形氯材。
然后修改VertexShader:
#if defined(_SPHERE_TERRAIN_ON)
output.positionCS = GetFixedClipPos(input.positionOS);
#else
output.positionCS = vertexInput.positionCS;
#endif
用_SPHERE_TERRAIN_ON宏區(qū)分開是否受彎曲參數(shù)影響渣锦。到此,Shader就準(zhǔn)備好了氢哮,下面就要寫腳本動態(tài)設(shè)置角色的位置到Shader了袋毙。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereTerrainSetting : MonoBehaviour
{
public Transform anchor;
public float curvature;
public bool sphereTerrainOnlyZ;
private static int AnchorPosKeyId = Shader.PropertyToID("_AnchorPosWS");
// Update is called once per frame
void Update()
{
if (anchor != null)
{
Shader.SetGlobalVector(AnchorPosKeyId, anchor.position);
}
}
#if UNITY_EDITOR
void OnValidate()
{
if (Application.isPlaying)
{
Shader.SetGlobalFloat("_Curvature", curvature);
if (sphereTerrainOnlyZ)
Shader.EnableKeyword("_SPHERE_TERRAIN_ONLY_Z");
else
Shader.DisableKeyword("_SPHERE_TERRAIN_ONLY_Z");
}
else
{
Shader.SetGlobalFloat("_Curvature", 0.0f);
}
}
#endif
}
其中anchor就是你操縱的角色,curvature是你希望的曲率(彎曲程度),sphereTerrainOnlyZ是球形還是單向彎曲冗尤。把腳本掛載到任意GameObject上听盖,勾選材質(zhì)的選項以便打開_SPHERE_TERRAIN_ON宏。運(yùn)行看看效果吧:
好了裂七,彎曲地表到這里就基本實現(xiàn)了皆看,實際應(yīng)用過程中可能要比這個復(fù)雜一些,要考慮裁切問題背零,需要一步步完善腰吟,感興趣的朋友可以試試看。
注
本教程采用了Unity Assets store上的免費(fèi)資源包徙瓶,特對作者表示感謝毛雇!
【轉(zhuǎn)載請注明出處】