【Cg Programming/Unity】Transparent Surfaces ~ Cutaways【順番にやっていく】
今回はこちら
en.wikibooks.org
この節では描画するピクセルの一部を破棄することについて学ぶ。
ピクセルの一部を表示しない
Shader "Custom/Cutaways" { SubShader { pass { Cull Off CGPROGRAM #pragma vertex v #pragma fragment f struct vertexOutput { float4 position : SV_POSITION; float4 vertexPos : TEXCOORD0; }; vertexOutput v(float4 vertexPos : POSITION) { vertexOutput output; output.position = UnityObjectToClipPos(vertexPos); output.vertexPos = vertexPos; return output; } float4 f(vertexOutput input) : COLOR { //頂点のローカル座標のyが0以上の場合(半分より上の部分) //ピクセルを描画しない if(input.vertexPos.y > 0) { discard; } return input.vertexPos + float4(0.5, 0.5, 0.5, 0); } ENDCG } } }
(Cull Offというキーワードについては後述)
discard
注目すべきはフラグメントシェーダf
関数でdiscard
と書かれている箇所だ。
input.vertexPos.y
という条件が真の場合にdiscard
が実行される。
discard
が実行された場合、そのピクセルの描画は破棄され、何も描画されない。
(つまり後ろに何か別のオブジェクトがあった場合透けて見える)
ただし、このdiscard
を使ってしまうと一部の最適化が利かなくなってしまうため、パフォーマンスのためになるべく使用しないほうがいいらしい。
ではピクセルの破棄をdiscard
無しでどのようにやるのかはまあそのうちあるでしょう。
Culling
さて、上のコードにはdiscard
のほかにもCull Off
という見慣れないキーワードがあった。
これは「Culling、つまり間引きをしない」ことを表している。
3Dオブジェクト上の面には必ず(カメラから見て)表と裏があり、裏面の描画をされても役に立たない場合が多い。
3Dモデルでは大抵の裏面はモデルの後ろ側に存在し、表を向いている面で隠されてしまうだろう。
したがって、特にこれといった事情が無い限りは裏を向いている面は描画しないということにすれば大幅にGPUの計算コストを下げることができる。
このようにモデルの面がカメラから見て表裏どちらを向いているかによって描画を省略してしまうための機能がこのCullingである。
Cull Back
デフォルトではCull Back
となっており、裏を向いている面の描画は省略されている。
Cull Front
逆に、表を向いている面の描画を省略したい場合はCull Front
という風に指定してやる。
Cull Off
描画の省略をしたくないという場合はCull Off
と指定する。
今回の場合、オブジェクトの上から半分の描画を破棄したことによりオブジェクトの中身が見えるようになってしまっている。そこで、不自然な描画とならないようにCull Off
しているのだ。
もしこれを指定せずにCull Back
のままであれば、以下のように描画されてしまう。
Passの追加
上記でCullingについて学んだ。
表面と裏面で描画の方法を変えてやりたい場合はCull Front
した描画の上にCull Back
した描画を重ねてやればよい。
このように1つのオブジェクトに対して複数の描画を行えるようにするため、UnityではPassを複数書けるようになっている。
Passは上から順に実行される。
例えば、表を向いた面の色は白っぽくするといったことができる。
具体的なコードは以下である。
Shader "Custom/Cutaways" { SubShader { //Passは書いた順番で実行される pass { Cull Front //まず裏面の描画から CGPROGRAM #pragma vertex v #pragma fragment f struct vertexOutput { float4 position : SV_POSITION; float4 vertexPos : TEXCOORD0; }; vertexOutput v(float4 vertexPos : POSITION) { vertexOutput output; output.position = UnityObjectToClipPos(vertexPos); output.vertexPos = vertexPos; return output; } float4 f(vertexOutput input) : COLOR { //頂点のローカル座標のyが0以上の場合(半分より上の部分) //ピクセルを描画しない if(input.vertexPos.y > 0) { discard; } return input.vertexPos + float4(0.5, 0.5, 0.5, 0); } ENDCG } pass { Cull Back //表面の描画 CGPROGRAM #pragma vertex v #pragma fragment f struct vertexOutput { float4 position : SV_POSITION; float4 vertexPos : TEXCOORD0; }; vertexOutput v(float4 vertexPos : POSITION) { vertexOutput output; output.position = UnityObjectToClipPos(vertexPos); output.vertexPos = vertexPos; return output; } float4 f(vertexOutput input) : COLOR { //頂点のローカル座標のyが0以上の場合(半分より上の部分) //ピクセルを描画しない if(input.vertexPos.y > 0) { discard; } return input.vertexPos + float4(1, 1, 1, 1); } ENDCG } } }
次回はこちら。
spi8823.hatenablog.com