🌈

そろそろShaderをやるパート77 視差マッピング

2022/09/03に公開

そろそろShaderをやります

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

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

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

下準備

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

デモ

平面であるはずのPlaneに凹凸があるように見える表現です。

前回のノーマルマッピングと組み合わせることで真価を発揮します。
【参考リンク】:そろそろShaderをやるパート76 ノーマルマッピング

Textureは以下かお借りしました。
【参考リンク】:【Unity】視差マッピング

Shaderサンプル

Shader "Custom/ParallaxMapping"
{
    Properties
    {
        //ここに書いたものがInspectorに表示される
        _MainColor("MainColor",Color) = (1,1,1,1)
        _Reflection("Reflection", Range(0, 10)) = 1
        _Specular("Specular", Range(0, 10)) = 1
        _HeightFactor ("Height Factor", Range(0.0, 0.1)) = 0.02
        _NormalMap ("Normal map", 2D) = "bump" {}
        _HeightMap ("HeightMap map", 2D) = "white" {}
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
            "LightMode"="ForwardBase"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal: NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 lightDir : TEXCOORD1;
                float3 viewDir : TEXCOORD2;
            };

            //変数の宣言 Propertiesで定義した名前と一致させる
            float4 _MainColor;
            float _Reflection;
            float _Specular;
            float _HeightFactor;
            sampler2D _NormalMap;
            sampler2D _HeightMap;

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

                // 接空間の行列を取得
                TANGENT_SPACE_ROTATION;
                // ライトの方向ベクトルを接空間に変換
                o.lightDir = normalize(mul(rotation, ObjSpaceLightDir(v.vertex)));
                // カメラの方向ベクトルを接空間に変換
                o.viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex)));
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                //ハイトマップをサンプリングしてUVをずらす
                float4 height = tex2D(_HeightMap, i.uv);
                i.uv += i.viewDir.xy * height.r * _HeightFactor;

                //ノーマルマップから法線を取得
                float3 normal = UnpackNormal(tex2D(_NormalMap, i.uv));

                //ライトベクトルと法線ベクトルから反射ベクトルを計算
                float3 refVec = reflect(-i.lightDir, normal);
                //視線ベクトルと反射ベクトルの内積を計算
                float dotVR = dot(refVec, i.viewDir);
                //0以下は利用しないように内積の値を再計算
                dotVR = max(0, dotVR);
                dotVR = pow(dotVR, _Reflection);
                //スペキュラー
                float3 specular = _LightColor0.xyz * dotVR * _Specular;
                //内積を補間値として塗分け
                float4 finalColor = _MainColor + float4(specular, 1);
                return finalColor;
            }
            ENDCG
        }
    }
}

視差マッピング

物体の凹凸による視界の変化を、メッシュの変形を使わずに擬似的に実現する手法です。
難しそうな言葉使いましたが、視線に応じてUVを変化させるだけで実現できます。

//ハイトマップをサンプリングしてUVをずらす
float4 height = tex2D(_HeightMap, i.uv);
i.uv += i.viewDir.xy * height.r * _HeightFactor;

参考リンク

【Unity】視差マッピング

Discussion