本日はUnity枠です。
Unityではレイキャストと呼ばれる機能を使用することである地点からあるベクトルで不可視のビームを飛ばし、途中ぶつかったコライダーの情報を取得できます。
今回はこの機能を使用してUnityでレイが当たっているポリゴンの法線ベクトルを取得します。
用途としては例えば床にはオブジェクトが配置できるけど、壁や天井にはできないといった処理を行うときに便利です。
床と天井の差別化を行う際に最もシンプルな判定が法線の向きになります。
〇レイを飛ばす
マウスをクリックした際にレイを飛ばしコライダーとのヒットを検出するコードは以下のようになります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NormalVectorDetector : MonoBehaviour
{
void OnMouseDown()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
//レイがコライダーにあたった場合の処理
}
}
// Update is called once per frame
void Update()
{
// マウスの左クリックでOnMouseDownを呼び出す
if (Input.GetMouseButtonDown(0))
{
OnMouseDown();
}
}
}
ここではマウスの右クリックでイベントを発火させています。
また、rayはメインカメラのスクリーン座標からのベクトルを使用しています。
もしコライダーを含んだオブジェクトをクリックした場合はPhysics.Raycast(ray, out hit)がTrueになります。
またhitにあたったオブジェクトの情報が渡されます。
〇ポリゴンの法線ベクトル
前提としてレイキャストはコライダーもしくはTagに判定を行いますが、ポリゴンを取得するためにはmeshColliderを使用する必要があります。

コライダーからSharedMeshを取得します。次のようなコードになります。
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
Vector3 normal = Vector3.zero;
for (int i = 0; i < triangles.Length; i += 3)
{
if (triangles[i] == hit.triangleIndex)
{
normal = normals[triangles[i]];
break;
}
}
Debug.Log("normal: " + normal);
取得したメッシュから頂点を取得し、頂点の法線からメッシュの法線を取得しています。
これによって法線の取得ができました。

本日は以上です。
〇コード全文
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NormalVectorDetector : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// レイを飛ばし当たったポリゴンの法線ベクトルを取得してログに出力
void OnMouseDown()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
Vector3 normal = Vector3.zero;
for (int i = 0; i < triangles.Length; i += 3)
{
if (triangles[i] == hit.triangleIndex)
{
normal = normals[triangles[i]];
break;
}
}
Debug.Log("normal: " + normal);
}
}
// Update is called once per frame
void Update()
{
// マウスの左クリックでOnMouseDownを呼び出す
if (Input.GetMouseButtonDown(0))
{
OnMouseDown();
}
}
}