夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

ゼロから始めるUntiyShader開発 #pragma Targetって何?

本日はShader枠です。

〇#pragma Targetとは?

ShadferターゲットはShaderLabでシェーダーを記述する際に使用できる#pragmaディレクティブです。

用途としてはシェーダーコンパイルターゲットレベルを指定します。

シェーダーコンパイルターゲットレベルとはプラットフォームによるサポートを制御する値です。

例えばモバイルプラットフォーム向けのシェーダーとPC向けのシェーダーではパフォーマンス等の理由で異なるシェーダーレベルが使用されることが一般的です。

またプラットフォームによって処理アルゴリズムが違うこともあります。一つの例としてDirectXとOpenGLESではUV座標の扱いが違うことが知られています。

同じシェーダーであってもプラットフォームによって最適な処理を行うことで対応するプラットフォームに最適なパフォーマンスを発揮するようになります。

このプラットフォームを制御する値がシェーダーコンパイルターゲットレベルの意味です。

一般的なコンパイルターゲットレベルには、2.0、3.0、4.0、4.5、5.0があります。

2.5がDefault値としてレベルが低いほどモバイルなどどのようなプラットフォームでも動作し、逆にレベルが高くになるほどハイエンドデバイスに適したシェーダーとなります。

〇#pragma Targetの使い方と機能

ShaderTargetが次のように他のディレクティブと同じように使用します。

Shader "Unlit/NewUnlitShader "
{
    Properties
    {
       ・・・
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
           # pragma target 3.5

            #include "UnityCG.cginc"

上記の場合3.5のターゲットが使用されます。

レベルとサポートするプラットフォームは次のようになります。(なお詳細はUnityのバージョンによって異なります。)

ターゲット プラットフォーム 備考
2.0 Unityがサポートするすべてのプラットフォーム DX9(シェーダーモデル2)
2.5 3.0と同じだがDX9シェーダーモデル2.0と3.0の間 Default
3.0 DirectX9(シェーダーモデル3.0),OpenGLES 3.0
3.5 OpnGL ES 3.0の機能を持ちDirectX9, DirextX11 ,OpnGLES2.0はサポートされない Andtoidモバイル向け
4.0 DirectX11(シェーダーモデル4.0),DirextX9,WindowsPhone,OpenGLES 2.0/3.0/2/1 ,Metalはサポートされていない Windows向け
4.5 OpenGLES 3.1 , MacやOpenGLES2.0/3.0のサポートはされていない ハイエンドAndorid向け
5.0 DirectX11(シェーダーモデル5.0)  エンドデバイス向け

シェーダーターゲットに応じてシェーダー内部では処理が分岐しています。

例えば法線マップの処理に当たるUnpackScaleNormalを見てみると次のようになります。

half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
{
    return UnpackScaleNormalRGorAG(packednormal, bumpScale);
}

half3 UnpackScaleNormalRGorAG(half4 packednormal, half bumpScale)
{
    #if defined(UNITY_NO_DXT5nm)
        half3 normal = packednormal.xyz * 2 - 1;
        #if (SHADER_TARGET >= 30)
            // SM2.0: instruction count limitation
            // SM2.0: normal scaler is not supported
            normal.xy *= bumpScale;
        #endif
        return normal;
    #elif defined(UNITY_ASTC_NORMALMAP_ENCODING)
        half3 normal;
        normal.xy = (packednormal.wy * 2 - 1);
        normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
        normal.xy *= bumpScale;
        return normal;
    #else
        // This do the trick
        packednormal.x *= packednormal.w;

        half3 normal;
        normal.xy = (packednormal.xy * 2 - 1);
        #if (SHADER_TARGET >= 30)
            // SM2.0: instruction count limitation
            // SM2.0: normal scaler is not supported
            normal.xy *= bumpScale;
        #endif
        normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
        return normal;
    #endif
}

ここでSHARDER_TARGETの値で処理が分岐していることがわかります。

#if (SHADER_TARGET >= 30)
            // SM2.0: instruction count limitation
            // SM2.0: normal scaler is not supported
            normal.xy *= bumpScale;
        #endif

以上がShaderTargetになります。

〇参考

docs.unity3d.com