Unityな日々(Unity Geek)

Unityで可視化アプリを開発するための試行錯誤の覚書

ShaderLabの基礎:Culling & Depth Testingを理解する

参考:ShaderLab syntax: Culling & Depth Testing

f:id:yasuda0404:20150214204213p:plain

英語の'cull'は「選びとる・間引く」という意味だが、CGの世界では「除外する」ととらえるとわかりすい。たとえば、「バックフェース・カリング」はポリゴンの裏面を表示しない処理になる。また、オブジェクトのもっとも近いサーフィス以外を表示しない「デプス・テスト」もカリングの一種。


Cull Back|Front|Off ポリゴンのどの面をカリングするかの指定。 デフォルトは'Back'で、裏面を表示しない。'Off'は両面を表示。

ZWrite On | Off このオブジェクトのからのピクセルをDepth Bufferに書き込むかどうか。デフォルトはOn。

ZTest Less | Greater | LEqual | GEqual | Equal | NotEqual | Always Depth Testの方法。デフォルトは'LEequal' = 存在するオブジェクトより近いものと同じ距離にあるものは表示、それより背後にあるものは非表示)

Offset Factor, Units Depthパラメータのオフセット。FactorはZの最大値を、X,Y値に対してスケーリングする。UnitsはDepth Buffer値の最小値をスケーリングする。 これによって、あるポリゴンを常に上側に表示できる。たとえば、'Offset 0, -1'は、ポリゴンの傾きを無視してポリゴンをカメラに近づける。一方、'Offset -1, -1'は、ポリゴンをさらに近くに表示する。


サンプル

1. オブジェクトの裏面のみをレンダリングする

Shader "Show Insides" {
    SubShader {
        Pass {
            Material {
                Diffuse (1,1,1,1)
            }
            Lighting On
            Cull Front
        }
    }
}


2. 深度を考慮したトランスペアレント・シェーダ

通常のトランスペアレント・シェーダではDepthを考慮せず、単純に描画順で前後が決まるらしい。これを回避するには、深度情報を考慮したトランスペアレント・シェーダを作る必要がある。下がその例。 参考:Unity で Transparent/Diffuse で描画順が崩れてしまう際の対処法

ZWrite OnでDepth Bufferに書き込み、ColorMask 0レンダリングはしない。そのかわりに

Shader "Transparent/Diffuse ZWrite" {
Properties {
    _Color ("Main Color", Color) = (1,1,1,1)
    _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
    Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    LOD 200

    // extra pass that renders to depth buffer only
    Pass {
        ZWrite On
        ColorMask 0
    }

    // paste in forward rendering passes from Transparent/Diffuse
    UsePass "Transparent/Diffuse/FORWARD"
}
Fallback "Transparent/VertexLit"
}


3. 法線ベクトルの修正

最初のパスでは通常のVertex Lightingでレンダリングし、2番めのパスでオブジェクトの背面をピンク色でレンダリングする。裏向きポリゴンを発見する際に便利なシェーダ。

Shader "Reveal Backfaces" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }
    SubShader {
        // Render the front-facing parts of the object.
        // We use a simple white material, and apply the main texture.
        Pass {
            Material {
                Diffuse (1,1,1,1)
            }
            Lighting On
            SetTexture [_MainTex] {
                Combine Primary * Texture
            }
        }

        // Now we render the back-facing triangles in the most
        // irritating color in the world: BRIGHT PINK!
        Pass {
            Color (1,0,1,1)
            Cull Front
        }
    }
}


4. グラス・カリング

ガラスのような半透明なオブジェクトでは、オブジェクトの裏面が透けて見えるようにしたい。そのためのシェーダ。裏面と表面を2パスでレンダリングする。 spheres, cubes, car windscreensなどの凸形状のオブジェクト適用できる。

Shader "Simple Glass" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }

    SubShader {
        // We use the material in many passes by defining them in the subshader.
        // Anything defined here becomes default values for all contained passes.
        Material {
            Diffuse [_Color]
            Ambient [_Color]
            Shininess [_Shininess]
            Specular [_SpecColor]
            Emission [_Emission]
        }
        Lighting On
        SeparateSpecular On

        // Set up alpha blending
        Blend SrcAlpha OneMinusSrcAlpha

        // Render the back facing parts of the object.
        // If the object is convex, these will always be further away
        // than the front-faces.
        Pass {
            Cull Front
            SetTexture [_MainTex] {
                Combine Primary * Texture
            }
        }
        // Render the parts of the object facing us.
        // If the object is convex, these will be closer than the
        // back-faces.
        Pass {
            Cull Back
            SetTexture [_MainTex] {
                Combine Primary * Texture
            }
        }
    }
}