🌈

そろそろShaderをやるパート35 Diffuse(拡散反射)

2021/06/06に公開

そろそろShaderをやります

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

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

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

下準備

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

Diffuse(拡散反射)とは?

Diffuseは拡散させる,放散するという意味です.
私たちが普段目にしている物体のほとんどはこのDiffuseの影響を受けて立体的に見えています.
とある光源から放たれた光は,物体の表面で反射して私たちの目に入るため,私たちは物体を知覚することができます.
この時の物体の明るさは,光と表面の法線方向(Normal)が完全に反対向きの時に1番明るくなり,
逆に完全に一致すると1番暗くなります.
(光源方向に近い面は明るく,反対側は暗くなるということです)
この計算式をシェーダーにいれることで,べた塗りではなく立体的な(形状がはっきりとした)結果を得ることができます.

【引用元】:Diffuse(拡散反射)

物体をより自然に見せるには、光源(Light)の光を正しく物体に反映させる計算が必要で、
その手段の1つがDiffuseということですね。

デモ

実際にDiffuseの処理を適用すると下記GIFのようになります。
(画面と同じ向きに光源の向きを設定した場合)

Shaderサンプル

Shader "Custom/SimpleDiffuse"
{
    Properties
    {
        _MainColor ("Main Color", Color) = (0, 0, 0, 1)
        _DiffuseShade("Diffuse Shade",Range(0,1)) = 0.5
    }

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

            fixed4 _MainColor;
            float _DiffuseShade;

            struct appdata
            {
                float4 vertex:POSITION;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 worldNormal:TEXCOORD0;
            };

            //頂点シェーダー
            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //法線方向のベクトル
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            //フラグメントシェーダー
            fixed4 frag(v2f i) : SV_Target
            {
                //1つ目のライトのベクトルを正規化
                float3 L = normalize(_WorldSpaceLightPos0.xyz);
                //ワールド座標系の法線を正規化
                float3 N = normalize(i.worldNormal);
                //ライトベクトルと法線の内積からピクセルの明るさを計算 ランバートの調整もここで行う
                fixed4 diffuseColor = max(0, dot(N, L) * _DiffuseShade + (1 - _DiffuseShade));
                //色を乗算
                fixed4 finalColor = _MainColor * diffuseColor;
                return finalColor;
            }
            ENDCG
        }
    }
}

_WorldSpaceLightPos0という組み込みの変数を利用します。
これによりシーン上に存在するDirectional Lightの向きを利用できます。
_WorldSpaceLightPos0.wがDirectional Lightだったら0、それ以外は1の値を取るようです。
その他のLightの利用も可能ということですね。

【参考リンク】:【Unity】【シェーダ】Forward Renderingで複数のライトを取り扱う


内積の返す値を下記計算式で調整しています。
これにより暗すぎる影を良い感じに調整できます。
fixed4 diffuseColor = max(0, dot(N, L) * _DiffuseShade + (1 - _DiffuseShade));

内積について↓
【参考リンク】:そろそろShaderをやるパート31 内積を使う

参考リンク

UnityでForwardのライトに対応したLambert反射モデルのシェーダを作成する
[Unity] Cgでのライティングについてメモ
【Unityシェーダ入門】ランバート拡散照明モデルを試す
[Unity]シェーダでランバート反射
Unityでシェーダー描いてみたい
Unityシェーダー】よく使う手法・計算まとめ

Discussion