そろそろShaderをやるパート22 Skyboxで疑似太陽

3 min読了の目安(約3500字TECH技術記事

そろそろShaderをやります

そろそろShaderをやります。そろそろShaderをやりたいからです。
パート100までダラダラ頑張ります。10年かかってもいいのでやります。
100記事分くらい学べば私レベルの初心者でもまあまあ理解できるかなと思っています。

という感じでやってます。

※初心者がメモレベルで記録するので
 技術記事としてはお力になれないかもしれません。

下準備

下記参考
そろそろShaderをやるパート1 Unite 2017の動画を見る(基礎知識~フラグメントシェーダーで色を変える)

デモ

計算式で疑似的な太陽を表現しています。

今回は下記記事の内容の意味を理解することに努めました。
【参考リンク】:【Unity】Skyboxシェーダーの書き方

Shaderサンプル

Shader "Custom/SunSky"
{
   Properties {
        _BGColor ("Background Color", Color) = (0.05, 0.9, 1, 1)
        _SunColor ("Color", Color) = (1, 0.8, 0.5, 1)
        _SunDir ("Sun Direction", Vector) = (0, 0.5, 1, 0)
        _SunStrength("Sun Strengh", Range(0, 200)) = 30
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Background" //最背面に描画するのでBackground
            "Queue"="Background" //最背面に描画するのでBackground
            "PreviewType"="SkyBox" //設定すればマテリアルのプレビューがスカイボックスになるらしい
        }

        Pass
        {
            ZWrite Off //常に最背面に描画するので深度情報の書き込み不要

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            fixed3 _BGColor;
            fixed3 _SunColor;
            float3 _SunDir;
            float _SunStrength;

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

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

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

            fixed4 frag (v2f i) : SV_Target
            {
                float3 dir = normalize(_SunDir); //太陽の位置ベクトル正規化
                float angle = dot(dir, i.uv); //太陽の位置ベクトル と 描画されるピクセルの位置ベクトル の内積
                //pow(x,y)はxをy乗する 
                //0 < max(0, angle) < 1 なので _SunStrengthを大きくするほど計算結果は0に近づく
                fixed3 c = _BGColor + _SunColor * pow(max(0, angle), _SunStrength);
                return fixed4(c, 1);
            }
            ENDCG
        }
    }
}

内積の計算を図解したものが下記です。
下記の計算を各ピクセルごとに行うことで太陽を疑似的に再現しています。


太陽の色が加算なので濃い色で反映したい場合は下記のように
Lerpで計算を行えば色を混ぜることなく計算できました。

//pow(x,y)はxをy乗する
//0 < max(0, angle) < 1 なので _SunStrengthを大きくするほど計算結果は0に近づく
fixed3 c = lerp(_BGColor,_SunColor,pow(max(0, angle), _SunStrength));

pow

pow(x,y)はxのy乗を返すそうです。
xが少数ならyが大きくなるにつれて返す値も小さくなります。

ZWrite

ピクセル単位で深度情報を保存するバッファをオン、オフできるみたいです。

オンにすると各ピクセルがZバッファを保持し、
新たに描画予定のピクセルの深度値との比較が行われます。
その比較の結果、深度値が小さいピクセルが前面に表示されます。

これはZバッファ法(デプスバッファ法)という手法であり、
ピクセル単位で深度情報を保存し、前後関係を正しく描画しやすくするらしいです。

【参考リンク】:Zバッファ法(デプスバッファ法)

Skyboxは常に最背面に描画されるものなので、
前後を特定する深度情報は必要ではなく、Queueの描画順序のみで十分ということでしょう。

参考リンク

Unity でShaderの勉強 その3
【Unity(C#)】Rayではなく内積(Vector3.Dot)で視線判定を行う
GLSLについてのメモ
ShaderLab culling and depth testing