本日はチュートリアルお試し枠です。
いつも通り、以下ブログの記事を参考に実施します。
azure-recipe.kc-cloud.jp
今回は空間マッピングによるオクルージョンを確認します。
記事の通りアプリを修正してプログラムを実行してみます。
前回同様、カメラ近くの床に地球のオブジェクトが表示されます。
これを壁を通して見てみると……。
青色の地球の輪郭が透過して見えるようになります。
また、他の惑星は以下のように格子状のメッシュで表示されます。
さて今回追加したコードは以下のコードです。
・PlanetOcclusion.cs
// Update is called once per frame void Update() { /* TODO: 5.a DEVELOPER CODING EXERCISE 5.a */ // Check to see if any of the planet's boundary points are occluded. for (int i = 0; i < checkPoints.Length; i++) { // 5.a: Convert the current checkPoint to world coordinates. // Call gameObject.transform.TransformPoint(checkPoints[i]). // Assign the result to a new Vector3 variable called 'checkPt'. Vector3 checkPt = gameObject.transform.TransformPoint(checkPoints[i]); // 5.a: Call Vector3.Distance() to calculate the distance // between the Main Camera's position and 'checkPt'. // Assign the result to a new float variable called 'distance'. float distance = Vector3.Distance(Camera.main.transform.position, checkPt); // 5.a: Take 'checkPt' and subtract the Main Camera's position from it. // Assign the result to a new Vector3 variable called 'direction'. Vector3 direction = checkPt - Camera.main.transform.position; // Used to indicate if the call to Physics.Raycast() was successful. bool raycastHit = false; // 5.a: Check if the planet is occluded by a spatial mapping surface. // Call Physics.Raycast() with the following arguments: // - Pass in the Main Camera's position as the origin. // - Pass in 'direction' for the direction. // - Pass in 'distance' for the maxDistance. // - Pass in SpatialMappingManager.Instance.LayerMask as layerMask. // Assign the result to 'raycastHit'. raycastHit = Physics.Raycast(Camera.main.transform.position, direction, distance, SpatialMappingManager.Instance.LayerMask); if (raycastHit) { // 5.a: Our raycast hit a surface, so the planet is occluded. // Set the occlusionObject to active. occlusionObject.SetActive(true); // At least one point is occluded, so break from the loop. break; } else { // 5.a: The Raycast did not hit, so the planet is not occluded. // Deactivate the occlusionObject. occlusionObject.SetActive(false); } } }
そもそも、このPlanetOcclusion.csが何処にアタッチされているかですが、各惑星のオブジェクト毎にアタッチされています。
動きとしてはカメラとチェックポイントの相対的な距離と方向を計算し、その直線状にSpatioalMappingの障害物があれば格子オブジェクトを有効化しています。
一方、Earthに割り当てたシェーダの輪郭表示部分は以下です。
・OccusionRim.shader
struct v2f { float4 viewPos : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; float3 viewDir : TEXCOORD2; }; v2f vert(appdata_tan v) { v2f o; o.viewPos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.normal = UnityObjectToWorldNormal(v.normal); o.viewDir = normalize(WorldSpaceViewDir(v.vertex)); return o; } half4 frag(v2f i) : COLOR { half Rim = 1 - saturate(dot(normalize(i.viewDir), i.normal)); half4 RimOut = _RimColor * pow(Rim, _RimPower); return RimOut; } ENDCG
オブジェクトの法線方向とカメラへのワールド方向の内積を求め、反転させることで輪郭部分を抽出しているようです。
www.opengl-tutorial.org
docs.unity3d.com
saturate (DirectX HLSL)