Mesh Renderer & Filter
Mesh Filter と Mesh Renderer が 3D 形状を表示します——Filter がメッシュデータを供給し、Renderer が Material・影・ライティングで描画します。手続きメッシュ、バッチ、PropertyBlock の基礎を学べます。図解とシミュレーターで構造を確認できます。
想像してみてください...
Mesh Filter は型——3D 形状(頂点、辺、面)を保持します。 Mesh Renderer は塗り手——その型に Material(塗料・テクスチャ)を載せ GPU に送ります。 対で動きます:Mesh Filter がなければ Renderer は描くものがなく、Renderer がなければメッシュは見えません。 ビルド時、Unity は何千もの Mesh Renderer を draw call batches にまとめ GPU を効率よく保ちます。
概念の詳細
MeshFilter は Mesh アセットへの参照——形状データ:
vertices、triangles(インデックス三つ組)、normals(ライティング用)、
UVs(テクスチャマッピング)、任意で tangents と colors。メッシュは
インポート資産(.fbx/.obj)でも、コードから手続き的に作っても構いません。
MeshRenderer は Materials 配列(サブメッシュごと)、Shadow
Casting/Receiving、Lightmap baking、Probe Anchor、そして重要な Layer
と Rendering Layer Mask(URP ライティング)を持ちます。bounds(境界ボックス)が
視錐台カリングを駆動——境界がカメラ視錐台の外なら描画をスキップします。
Unity のバッチ手法:Static Batching(物体を “Static” に)、 Dynamic Batching(300 頂点未満の移動物体)、 GPU Instancing(同じメッシュ + 同じマテリアルで多数を一つのドローコール)。 Material で GPU Instancing を有効にします。
Unity 2022+ では Mesh API 2.0 でボクシングなしに手続きメッシュを作成・編集できます:
Mesh.SetVertexBufferData、Mesh.SetIndexBufferData —
マネージ配列を飛ばして GPU バッファへ直接書き込み。
Mesh から画面へ
MeshFilter
Mesh Asset
vertices, triangles
normals, UVs
MeshRenderer
Materials[ ]
Shadow settings
Lightmap/Probe
Batching / Culling
Static/Dynamic Batch
Frustum Culling
Occlusion Culling
GPU Draw Calls
Vertex Shader
Fragment Shader
→ Pixels
Sub-mesh & Materials
Mesh (2 sub-mesh)
Materials[ ]
ハンズオン手順
モデルをインポート——MeshFilter は自動追加
.fbx/.obj を Project に入れ、Scene へ——Unity が MeshFilter + MeshRenderer と Material スロットを作ります。
Shadow Casting を設定
MeshRenderer → Cast Shadows: On/Off/Two Sided。他の影を受ける床は Receive Shadows をオン。
静的環境に Static Batching
Inspector → Static ドロップダウン → Batching Static。同じ Material を共有する静的物体が一つのドローコールにまとまります。
手続き的にメッシュを構築
new Mesh()、vertices[]、triangles[]、normals[]、uv[] を設定 → GetComponent<MeshFilter>().mesh に割り当て。
Frame Debugger でドローコールを数える
Window → Analysis → Frame Debugger:各ドローコールを検査し、コストの高い物体を確認。
インタラクティブシミュレーター
手続き的にメッシュを構築——トポロジを選び、ワイヤーフレーム / 法線を検査。
コード例
基本三角形メッシュを手続き的に作り、MeshFilter + MeshRenderer で表示。
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class ProceduralTriangle : MonoBehaviour
{
void Start()
{
Mesh mesh = new Mesh();
mesh.name = "Procedural Triangle";
// 3 vertices of the triangle
mesh.vertices = new Vector3[] {
new Vector3(0f, 0f, 0f),
new Vector3(1f, 0f, 0f),
new Vector3(0.5f, 1f, 0f)
};
// Clockwise indices (viewed from the front)
mesh.triangles = new int[] { 0, 1, 2 };
// UV mapping: one texture coord per vertex
mesh.uv = new Vector2[] {
new Vector2(0f, 0f),
new Vector2(1f, 0f),
new Vector2(0.5f, 1f)
};
mesh.RecalculateNormals(); // Compute normals automatically
GetComponent<MeshFilter>().mesh = mesh;
}
}コード例
上級実行時に Material を差し替え、MaterialPropertyBlock でバッチを崩さずに色付け。
using UnityEngine;
public class MeshRendererUtils : MonoBehaviour
{
MeshRenderer rend;
MaterialPropertyBlock mpb;
void Awake()
{
rend = GetComponent<MeshRenderer>();
mpb = new MaterialPropertyBlock();
}
// Read mesh bounds in world space
void LogMeshInfo()
{
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
Debug.Log($"Vertices: {mesh.vertexCount}");
Debug.Log($"Triangles: {mesh.triangles.Length / 3}");
Debug.Log($"Bounds: {rend.bounds.size}");
}
// Swap the Material on a specific sub-mesh (index 1 = window glass)
public void SwapSubMesh(int index, Material mat)
{
Material[] mats = rend.materials; // Get a copy
mats[index] = mat;
rend.materials = mats; // Assign back
}
// Per-instance color via PropertyBlock — keeps GPU Instancing
public void SetInstanceColor(Color c)
{
rend.GetPropertyBlock(mpb);
mpb.SetColor("_BaseColor", c);
rend.SetPropertyBlock(mpb);
}
}📌 要点
- ▸MeshFilter = 形、MeshRenderer = 描き方
- ▸sharedMesh はクローンしない;.mesh は私有コピーを作る
- ▸Static + 同じ Material → Static Batching
- ▸triangles[] は正面から見て時計回り(CW)
- ▸頂点変更後は RecalculateNormals()
⚠️ よくあるミス
❌ メッシュが裏返り(面がカリングされる)
三角形インデックスが反時計回り → Unity がその面をカリング
✅ インデックス順を反転:{0,1,2} → {0,2,1}、または Double Sided シェーダー
❌ Play Mode で meshFilter.mesh を何度も代入
毎回新しい Mesh → メモリリーク
✅ Awake() で一度作り、Update() では頂点だけ更新