質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

Unityのfragmentシェーダーの色の出力について質問です

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,528

shauni

score 13

前提・実現したいこと

Unityのfragmentシェーダーの色の出力について質問です
0~255の整数で256段階に表現された色をfloatもしくはfixedに戻して出力したいのですがそのまま256で割るだけだとうまくいきませんでした
出力した色をスクショしてペイントソフトで調べてみたところ、元のfloatの値が0.1だったら89だったり、0.01だったら25だったり、どのような規則があるのかわかりませんでした

intの色とfloatかfixedの色を相互に変換するにはどうすれば良いのでしょうか

unityのバージョンは2017.4.15f1です

Shader "Unlit/color test"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col;
                col.rgb = 0.0;
                //128になってほしいがスクショすると188になる
                col.r = float(128.0 / 255.0);
                //25になってほしいがスクショすると88になる
//                col.r = float(25 / 255.0);

                col.a = 1.0;
                col.gb = 0.0;
                return col;
            }
            ENDCG
        }
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Bongo

    2018/12/14 16:08

    シェーダーのコードやC#スクリプトのコードもご提示いただけると参考になるかもしれません。また、ちょっと気になるのですが、メニューの「Edit」→「Project Settings」→「Player」の「Other Settings」の「Rendering」の「Color Space」はどのような設定になっているでしょうか?

    キャンセル

  • shauni

    2018/12/14 17:52 編集

    回答ありがとうございます
    ColorSpaceは Linerでした
    シェーダーはUnlitのシェーダーの色の出力を固定して適当なオブジェクトのマテリアルに設定しているだけなので、C#のスクリプトはありません
    ```
    Shader "Unlit/color test"
    {
    Properties
    {
    _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
    Tags { "RenderType"="Opaque" }
    LOD 100

    Pass
    {
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    // make fog work
    #pragma multi_compile_fog

    #include "UnityCG.cginc"

    struct appdata
    {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    };

    struct v2f
    {
    float2 uv : TEXCOORD0;
    UNITY_FOG_COORDS(1)
    float4 vertex : SV_POSITION;
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;

    v2f vert (appdata v)
    {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
    }

    float4 frag (v2f i) : SV_Target
    {
    float4 col;
    col.rgb = 0.0;
    //128になってほしいがスクショすると188になる
    col.r = float(128.0 / 255.0);
    //25になってほしいがスクショすると88になる
    // col.r = float(25 / 255.0);

    col.a = 1.0;
    col.gb = 0.0;
    return col;
    }
    ENDCG
    }
    }
    }
    ```

    キャンセル

  • shauni

    2018/12/14 17:54

    見づらくて申し訳ないです
    コードブロックというものは返信では使えないのでしょうか

    キャンセル

  • Bongo

    2018/12/14 18:33

    おお、ありがとうございます。今はUnityが手元にないのでのちほどよく見てみようと思います。何だかガンマ補正が関係しているように見えますね。((128.0/255.0)^(1.0/2.2)*255.0 = 186.4、((25.0/255.0)^(1.0/2.2)*255.0 = 88.7といった具合にまずまずの一致があるようです。先ほど申し上げた「Color Space」の「Linear」と「Gamma」を切り替えてみて比較すると何かわかるかもしれませんね。

    コードの投稿についてですが、質問文は後から編集することも可能なはずですので、最初の質問文の下に追記していただく形にしますと、コードブロックも使えますので好都合かと思います。残念ながら追記依頼欄やコメント欄ではコードブロックは使えないようですね...

    キャンセル

回答 1

checkベストアンサー

+1

確認してみましたところ、おそらくColor Spaceの設定によるのではないかと思われます。もし「Linear」になっているようでしたら、「Gamma」に切り替えればご質問者さんご提示のコードそのままで意図通りのスクリーンショットになりそうです。
Linearモードだと「シェーダー内ではリニア色空間を想定して計算を行っており、最後にガンマ補正を加える必要がある」と判断され、シェーダーから0.5の色を出力しても自動的に中間階調が持ち上げられて、最終結果が128よりも明るい188になったのだろうと考えられます。

下記のようにUV座標のxをそのまま出力するマテリアル(LinearGradient)と...

Shader "Unlit/LinearGradient"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv.xxx, 1.0);
            }
            ENDCG
        }
    }
}

UV座標のxの値をsRGB逆変換して出力するマテリアル(GammaGradient)を作り...

Shader "Unlit/GammaGradient"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            inline float3 srgbToLinear(float3 c)
            {
                return lerp(c / 12.92, pow((c + 0.055) / 1.055, 2.4), step(0.04045, c));
            }

            inline float3 linearToSrgb(float3 c)
            {
                return lerp(c * 12.92, 1.055 * pow(c, 1.0 / 2.4) - 0.055, step(0.0031308, c));
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(srgbToLinear(i.uv.xxx), 1.0);
            }
            ENDCG
        }
    }
}

両モードでどのように表示されるか比較しました。

LinearGradient、Gammaモード
linear-gamma

GammaGradient、Linearモード
gamma-linear

LinearGradient、Linearモード
linear-linear

GammaGradient、Gammaモード
gamma-gamma

Linearモードのままで0.5の値が128として得られるようにしたい場合、GammaGradientに例示したような感じで、シェーダーから色を出力する直前に中間階調を暗くしてやる必要があるでしょう。こうするとその後のガンマ補正で中間階調が明るくなり、GammaモードでLinearGradientを使ったときとほぼ同等の見た目になるかと思います。
ただし、計算精度の問題で白周辺・黒周辺は多少ずれてしまうかもしれません。Linearモードはシェーダー内の計算をリニア空間で行いたい(光の伝搬をより物理的に正確に計算し、リアルな映像を作りたい...など)場合には好都合でしょうが、今回のようなケースはGammaモード前提でやった方がよさそうです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/15 20:54

    ありがとうございました
    ガンマ補正というものを知らなかったのでとても参考になりました 

    自分のUnityでもgammaにすると意図した通りになりました
    ただ環境がVRChatというゲーム上で利用したくてLinerから変更することができないので
    Bongoさんの提示された関数を使わせて頂くことにします

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る