お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

【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が実行された場合、そのピクセルの描画は破棄され、何も描画されない。
(つまり後ろに何か別のオブジェクトがあった場合透けて見える)
f:id:spi_8823:20200505185454p:plain

ただし、この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のままであれば、以下のように描画されてしまう。
f:id:spi_8823:20200505191858p:plain

Passの追加

上記でCullingについて学んだ。
表面と裏面で描画の方法を変えてやりたい場合はCull Frontした描画の上にCull Backした描画を重ねてやればよい。
このように1つのオブジェクトに対して複数の描画を行えるようにするため、UnityではPassを複数書けるようになっている。
Passは上から順に実行される。
例えば、表を向いた面の色は白っぽくするといったことができる。
f:id:spi_8823:20200505192636p:plain
具体的なコードは以下である。

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