Unity
Shader

Unity、Zバッファとレンダリング順の仕組み


概要

3DCGで透明なオブジェクトを扱う上で抑えておきたいトピックです。

レンダリングの基礎。


透明なメッシュのレンダリング順

MakotoNoMegane.gif

このシーンは 赤・青・透明 の3種類のメッシュで構成されています。

しかし、透明なメッシュを青いメッシュより手前に描画しているにもかかわらず、透明なメッシュの後ろに青いメッシュが描画されてません。何故でしょうか?


CGで奥行きを認識する仕組み

Zバッファ.png

CGでは、オブジェクトの奥行きに関する位置をZバッファ(または深度バッファ)という方法で管理しています。

これはオブジェクトをレンダリングした時に、レンダリング結果とは別にオブジェクトの位置(深度情報)も保存しておきます。

再びスクリーンの同じ個所をレンダリングする時、今からレンダリングするオブジェクトと、前にレンダリングしたオブジェクトの深度を比較して、今からレンダリングオブジェクトが奥であるのならレンダリングをスキップします。

この工程をZテスト(または深度テスト)といいます。

この仕組みによって、オブジェクトの前後関係が正しいレンダリング結果を得る事ができます。

Zバッファ - Wikipedia


半透明の場合どうなるか?

Unity道場 2019.2 シェーダを書けるプログラマになろう #1 シェーダを理解しよう

上記動画の15:45~の解説が分かりやすいです。(そこから再生します)

端的に言うと…

不透明のオブジェクトしか存在しないの場合、Zブァッファの前後関係を比較すれば絵に矛盾は起きません。

しかし、半透明のオブジェクトが混ざってしまうと…半透明のオブジェクトの奥に、不透明のオブジェクトを描画してほしいのに、Zバッファは不透明のオブジェクトが奥になるので描画されないという事が起こりえるのです。

そのため、半透明のオブジェクトと不透明のオブジェクトはそれぞれ分けて管理して、不透明のものを全てレンダリングしてから半透明のものをレンダリングすると順番が決まっており、正しく表示できるわけです。

では、改めて先ほどのプロジェクトを見てみましょう。

RenderQueue.png

RenderQueueの値が若い順にレンダリングされるので、

「2000のred → 2001のMegane → 2002のBlue」の順番でレンダリングされることになります。

そのため、青をレンダリングする時に既にMeganeのZバッファがあるので…Meganeの部分だけBlueがレンダリングされずに、透過して見えてしまう。という仕組みなんですね。

これは、Zバッファの学習用にレンダリング順を変更したプロジェクトなわけです。

RenderQueue

Materialのレンダリング順に関する定義


透明オブジェクトのシェーダー


MakotoNoMegane.Shader

Shader "Custom/MakotoNoMegane"

{
Properties
{
_LensColor("Color", Color) = (0, 0, 0, 0.1)
}

SubShader
{
Tags
{
"RenderType" = "Opaque"
"Queue" = "Geometry+1"
}
LOD 100
Blend SrcAlpha OneMinusSrcAlpha

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

fixed4 _LensColor;

struct appdata
{
float4 vertex : POSITION;
};

struct v2f
{
float4 vertex : SV_POSITION;
};

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}

fixed4 frag(v2f i) : SV_Target
{
return _LensColor;
}
ENDCG
}
}
}


こちらが、透明オブジェクトのShaderです。色が設定できます。

実際にプロジェクトを再現してみて、よりZバッファとレンダリング順の理解を深めるために、自由にお使いください。