夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

Unityでメッシュの見た目をフラットシェーディングとスムースシェーディングで自在に変える

本日はUnity枠です。

現在勉強もかねてBlenderとUnityアプリケーションを連携させるパッケージを作っています。

github.com

こちらはまだ基礎機能ができただけで現時点では本ブログで紹介できるようなボリュームがないのですが今回はこの中からメッシュの見た目をフラットシェーディング、スムースシェーディングで自在に変える点を紹介します。

〇シェーディングとは?

3Dモデルではオブジェクトの表面に対する光の挙動をシミュレートし、見た目を変えることができます。

この際メッシュの持つ法線(ノーマル)を計算していますが、このシュミレートをシェーディングと呼び、ポリゴン数が同じでもかくかくした見た目のフラットシェーディングに滑らかな見た目となるスムースシェーディングが代表的です。

フラットシェーディング

スムースシェーディング

ゲーム用モデルなどでは容量を削減するためにポリゴン数を割けないことがあります。

このような場合ポリゴン数は低いもののスムースシェーディングを使用することでポリゴンが目立たないようにして最適化を行うことが一般的です。

〇スムースシェーディングとフラットシェーディングを切り替える。

今回は冒頭で紹介したパッケージについての実装になります。

このパッケージ(MixedRealityModelingTools(仮))ではBlenderからメッシュのデータをUnityに送信しています。

その際にMessagePackを使用することで送られたバイナリデータはJsonとして格納されます。

    [DataContract]
    public class MeshData
    {
        [DataMember]
        public List<float> vertices;
    
        [DataMember]
        public List<int> triangles;
    
        [DataMember]
        public List<float> normals;
    }

データを基に3Dモデルを構築しています。

using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
using MixedRealityModelingTools.Core;

/// <summary>
/// This class constructs a mesh based on the data received.
/// </summary>
public class ObjectBuilder : MonoBehaviour
{
   private void Start()
   {
         // Rotate to Blender Axis
         _targetObject.transform.rotation = Quaternion.Euler(-90, 0, 0);
      _meshFilter = _targetObject.GetComponent<MeshFilter>();
      _meshRenderer = _targetObject.GetComponent<MeshRenderer>();

   }

   private void CreateMesh()
   {
         mesh = new Mesh();

         List<Vector3> vertices = ConvertToVector3List(meshData.vertices);
         mesh.SetVertices(vertices);
         mesh.SetTriangles(meshData.triangles, 0);

      
         _meshFilter.mesh = mesh;
         _isGetMeshData = false;
         if (_isFlatShading)
            FlatShading ();

         if (_defaultMaterial != null) 
            _meshRenderer.material = _defaultMaterial;


   }
   ・・・   
}

この時に法線の計算によってUnity側でスムースシェーディングを行うか、フラットシェーディングを行うかを任意に指定できるようにしています。

〇フラットシェーディング

こちらはおもちゃラボさんの記事を参考にさせていただいています。

nn-hokuson.hatenablog.com

void FlatShading ()
   {
      //https://discussions.unity.com/t/flat-shading/117837 
      //ReBuild the mesh with flat shading
      Mesh mesh = Instantiate (_meshFilter.sharedMesh) as Mesh;
      _meshFilter.sharedMesh = mesh;

      Vector3[] oldVerts = mesh.vertices;
      int[] triangles = mesh.triangles;
      Vector3[] vertices = new Vector3[triangles.Length];

      for (int i = 0; i < triangles.Length; i++) 
      {
         vertices[i] = oldVerts[triangles[i]];
         triangles[i] = i;
      }

      mesh.vertices = vertices;
      mesh.triangles = triangles;
      mesh.RecalculateNormals();
   }

メッシュクラスで再度メッシュを再生成しています。

注目点は面の向きである法線の計算で、頂点を取得し、新しいメッシュの頂点座標に元のメッシュの頂点情報をコピーしています。

これにより、各三角形の頂点座標がフラットシェーディングに適した形式になります。

なお頂点数とノーマル数は一致している必要があります。

これを実行することでメッシュのシェーディングがスムースになります。

〇スムースシェーディング

フラットシェーディングの処理を行わない場合はスムースシェーディングとなります。

法線の情報に関してはもう少し筆者自身も勉強する必要があるのでまた更新していきます。

本日は以上です。