-100p

-10p

+10p

+100p

シェーダー内の数値を可視化するシェーダー

Unity用、シェーダー内の数値を可視化するシェーダー。
(A shader to visualize variables in Unity shader)

関連ページ 参考URL
Shaderを開発する上で問題になるのが、Shader内ではログ出しが出来ないこと。
このためShader開発の始まりから終わりに至るまで開発者はコード内の変数の値を想像しながら作り切るしかない。

こういった問題を解決するため、Unity環境では既に有志の方が Shader内変数を可視化するシェーダー を作っている。
このシェーダーは充分な機能を備えてるが、3D Object上に表示する設計をしていて、ほぼuGUIしか使わない自分には少し使いづらかった。
勉強のためにもこれをuGUI用にカスタマイズ+αしたシェーダーを作ってみた。

数値可視化シェーダーの使い方

今回のシェーダーは特定の画像がセットで必要なので、画像とシェーダーとマテリアルをUnityPackageにまとめておいた。
こちらのアドレス から取得できる。

落としたファイルをダブルクリックすると必要なファイルがImportされる。Project上はAssets直下に配置されるはず。


このシェーダーはuGUI Image用に作ってあるので、ImageコンポーネントのMaterial部分にImportしたマテリアルをアタッチして使う。
RectTransofmのWidthは500、Heightは50が推奨となってるが、目視できる大きさであれば何でも良い。


ImportされたDebugShader.shaderを開き、フラグメントシェーダーのnumの部分に数値を代入すると、それが画面上に可視化される。

注意点として、整数部分が大きすぎると、小数点以下の数値の精度が著しく落ちる。floatの性質上しょうがないかも。

コード全文と補足

一応コード全文もここに載せておく。
自分としては珍しく頂点シェーダーも弄っていて、スクリーン上に表示されるImageの横幅を操作してる。
Shader "Test/DebugShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _IntergerCount ("IntergerCount", Range(1, 6)) = 6
        _DecimalCount ("DecimalCount", Range(0, 6)) = 6
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            sampler2D _MainTex;
            int _IntergerCount;
            int _DecimalCount;

            
//アタッチした画像の数字記号の桁数、0123456789+-.で全部で13桁

            static fixed textureDigit = 13;
            
//スクリーン上に表示されるImageのwidthを、数字一桁分のwidthに圧縮するために使う

            static fixed toSoloWidth = 12;

            
//スクリーン上に表示する数字と記号の桁数。符号、整数、小数点記号、小数点数に分かれる

            fixed viewDigitCount()
            {
                return 1 + _IntergerCount + step(1, _DecimalCount) + _DecimalCount;
            }
   
            v2f vert (appdata v)
            {
                v2f o;
                
                
//画像の横幅を数字一桁まで圧縮した上で、必要な数字の桁分まで広げる

                fixed soloWidth = v.vertex.x / toSoloWidth;
                v.vertex.x = soloWidth * viewDigitCount();
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.color = v.color;
                return o;
            }

            
//num = Textrue上の表示したい数字もしくは記号の座標、0~12までの可能性がある

            
//placeIdx = 表示したいスクリーン上の座標のidx, 一番左が0

            fixed4 getNumber(v2f i, int num, int placeIdx)
            {
                fixed oneCellWidth = 1 / textureDigit * toSoloWidth / viewDigitCount();

                
//表示したい数字の座標にuvを修正

                fixed2 fixUv;
                fixUv.x = i.uv.x + num * oneCellWidth - placeIdx * oneCellWidth;
                fixUv.x = fixUv.x / toSoloWidth * viewDigitCount();
                fixUv.y = i.uv.y;

                
//対象の桁と関係ない部分は切り捨てて画像を返す

                fixed cutRight = step(i.uv.x, oneCellWidth + placeIdx * oneCellWidth);
                fixed cutLeft = 1 - step(i.uv.x, placeIdx * oneCellWidth);
                fixed4 col = tex2D(_MainTex, fixUv) * cutRight * cutLeft;
                return col;
            }

            
//渡された数値の指定の桁の値を取得

            int getDigit(float num, float digit)
            {
                return (int)(abs(num) * digit) % 10;
            }

            
//指定の桁の整数の画像を取得

            
//addIdx = 大きいほど整数の桁が小さくなる

            fixed4 getIntegerTextureNumber(v2f i, float num, int baseIdx, int addIdx)
            {
                float digit = pow(10, -1 * (_IntergerCount - addIdx - 1));
                int digitNum = getDigit(num, digit);
                return step(addIdx, _IntergerCount - 1) * getNumber(i, digitNum, baseIdx + addIdx);
            }

            
//指定の桁の小数点数の画像を取得

            
//addIdx = 大きいほど小数点数の桁が小さくなる

            fixed4 getDecimalTextureNumber(v2f i, float num, int baseIdx, int addIdx)
            {
                float digit = pow(10, addIdx + 1);
                int digitNum = getDigit(num, digit);
                return step(addIdx, _DecimalCount - 1) * getNumber(i, digitNum, baseIdx + addIdx);
            }

            
//渡した数字を画像に変換

            fixed4 convertNumver(v2f i, float num)
            {
                fixed4 col = 0.0;

                
//最初に+か-の符号を描画

                int placeIdx = 0;
                fixed isSignMinus = 1 - step(0, num);
                col += getNumber(i, 10 + isSignMinus, placeIdx);

                
//整数部分を表示

                placeIdx += 1;
                for(int cnt = 0; cnt < _IntergerCount; cnt++)
                {
                    col += getIntegerTextureNumber(i, num, placeIdx, cnt);
                }

                
//小数点記号を表示

                placeIdx += _IntergerCount;
                col += step(1, _DecimalCount) * getNumber(i, 12, placeIdx);

                
//小数点数表示

                placeIdx += 1;
                
//何故か小数点以下でfor文を使った計算を行うと大きく値がずれるので1つずつ実行

                col += getDecimalTextureNumber(i, num, placeIdx, 0);
                col += getDecimalTextureNumber(i, num, placeIdx, 1);
                col += getDecimalTextureNumber(i, num, placeIdx, 2);
                col += getDecimalTextureNumber(i, num, placeIdx, 3);
                col += getDecimalTextureNumber(i, num, placeIdx, 4);
                col += getDecimalTextureNumber(i, num, placeIdx, 5);
                /*
                for(int cnt = 0; cnt < _DecimalCount; cnt++)
                {
                    col += getDecimalTextureNumber(i, num, placeIdx, cnt);
                }     
                */
                
                return col;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                
//整数値が大きいと小数点以下の数値が大きくバグる

                float num = _ScreenParams.x;
                return convertNumver(i, num);
            }
            ENDCG
        }
    }
}

プロパティは2つ用意してある。
IntegerCountのスライダーを弄ることで、整数部分の数値の表示桁数を1~6の範囲で制御できる。
DecimalCountのスライダーを弄ることで、小数点数以下の数値の表示桁数を0~6の範囲で制御できる。

ただ作った後に気付いたが、全く無駄な機能で両方とも最大値の6固定で良いと思う。
1
1

-100p

-10p

+10p

+100p