ミップマップのLODレベルによって処理を振り分ける

環境

Unity2021.2.18f1

概要

処理の重いシェーダを作成するときにミップマップのLODレベルで処理を切り替えたい事があります。 そんな場合は、CalculateLevelOfDetailを使用します。

このシェーダはLODレベルに応じてカラーを変えているだけのものです。

Shader "Custom/MipmapLodVisualize"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    CGINCLUDE
    #pragma target 4.5
    #include "UnityCG.cginc"
    #define UNITY_LOD_TEX2D(tex,coord) tex.CalculateLevelOfDetail(sampler##tex,coord)
    #define UNITY_SAMPLE_TEX2D_GRAD(tex,coord,dx,dy) tex.SampleGrad(sampler##tex,coord,dx,dy)
    ENDCG

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

            Texture2D _MainTex;
            SamplerState sampler_MainTex;
            float4 _MainTex_ST;
            float4 _MainTex_TexelSize;

            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
            {
                float mipLevel;
                float mipLevelInt;
                float mipLevelFrac;
                float2 dx, dy;
#if 0   //ポイントサンプルなら合う。こちらを使いたい場合もあるかも
                float2 texCoordsPerSize = i.uv * _MainTex_TexelSize.zw;
                float2 dxSize, dySize;
                float4(dxSize, dx) = ddx(float4(texCoordsPerSize, i.uv));
                float4(dySize, dy) = ddy(float4(texCoordsPerSize, i.uv));
                float minTexCoordDelta;
                float2 texCoords;
                texCoords = dxSize * dxSize + dySize * dySize;
                minTexCoordDelta = max(texCoords.x, texCoords.y);
                mipLevel = max(0.5 * log2(minTexCoordDelta), 0);
#else
                mipLevel = UNITY_LOD_TEX2D(_MainTex, i.uv);
                dx = ddx(i.uv);
                dy = ddy(i.uv);
#endif
                mipLevelFrac = modf(mipLevel, mipLevelInt);

                fixed4 col;
                fixed4 cols[] = {fixed4(1,0,0,1), fixed4(0,1,1,1), fixed4(1,0,1,1), fixed4(0,1,0,1), fixed4(0,0,1,1), fixed4(1,1,0,1), fixed4(1,1,1,1)};
                if(mipLevel > 5)
                    col = fixed4(1,1,1,1);
                else
                {
                    fixed4 col0 = cols[mipLevelInt];
                    fixed4 col1 = cols[mipLevelInt + 1];
                    col = lerp(col0, col1, mipLevelFrac);
                }
                return col;
                //return UNITY_SAMPLE_TEX2D_GRAD(_MainTex, i.uv, dx, dy);
            }
            ENDCG
        }
    }
}

参考

textureQueryLod/CalculateLevelOfDetail in Fragment Shader - Unity Answers

コースティクス

環境

Unity2021.2.18f1

概要

テクスチャを使用したコースティクスです。

参考リンクのものを実装しました。ルミナンスマスクは実装していません。

以下のサイトのテクスチャを使用させていただいています。

Water Caustics Effect (Small) | OpenGameArt.org

Shader "Custom/Caustics"
{
    Properties
    {
        _CausticsTexture("Caustics Texture", 2D) = "white" {}
        _CausticsSpeed("CausticsSpeed", float) = 0.05
        _CausticsScale("CausticsScale", float) = 3
        _CausticsStrength("CausticsStrength", float) = 1
        _CausticsSplit("CausticsSplit", float) = 0.002
        _CausticsFadeRadius("CausticsFadeRadius", float) = 0.3
        _CausticsFadeStrength("CausticsFadeStrength", float) = 0.6
    }
    SubShader
    {
        Tags
        { 
            "RenderType" = "Transparent"
            "Queue" = "Transparent"
            //"LightMode" = "ForwardBase"
        }
        Pass
        {
            ZWrite Off
            ZTest Always
            Cull Front
            Lighting Off
            Cull Back
            Blend SrcAlpha One//OneMinusSrcAlpha

            CGPROGRAM
            //#pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            //#include "AutoLight.cginc"
            //#include "Lighting.cginc"

            half4 _Color;
            sampler2D _CameraDepthTexture;
            float _CausticsSpeed;
            float _CausticsScale;
            float _CausticsStrength;
            float _CausticsSplit;
            float _CausticsFadeRadius;
            float _CausticsFadeStrength;

            struct VSInput
            {
                float4 vertex : POSITION;
                float3 texcoord : TEXCOORD0;
            };
            struct VSOut
            {
                float4 positionCS :SV_POSITION;
                float4 positionSS : TEXCOORD0;
                float3 rayVS : TEXCOORD1;
                float radius : TEXCOORD3;
            };

            half4x4 _MainLightDirection;
            sampler2D _CausticsTexture;

            VSOut vert(VSInput v)
            {
                VSOut o;
                o.positionCS = UnityObjectToClipPos(v.vertex);
                o.positionSS = ComputeScreenPos(o.positionCS);
                o.rayVS = UnityObjectToViewPos(v.vertex).xyz;
                o.rayVS.z *= -1;
                o.radius = length(UNITY_MATRIX_M._m01_m11_m21) * 0.5;
                return o;
            }

            half2 Panner(half2 uv, half speed, half tiling)
            {
                return (half2(1, 0) * _Time.y * speed) + (uv * tiling);
            }

            half3 SampleCaustics(half2 uv, half split)
            {
                half2 uv1 = uv + half2(split, split);
                half2 uv2 = uv + half2(split, -split);
                half2 uv3 = uv + half2(-split, -split);

                half r = tex2D(_CausticsTexture, uv1).r;
                half g = tex2D(_CausticsTexture, uv2).r;
                half b = tex2D(_CausticsTexture, uv3).r;

                return half3(r, g, b);
            }

            fixed4 frag(VSOut i) : Color
            {
                const float far = _ProjectionParams.z;
                i.rayVS = i.rayVS * (far / i.rayVS.z);
                float depth;
                float3 normalVS;
                float2 positionSS = i.positionSS.xy / i.positionSS.w;
                depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, positionSS);
                depth = Linear01Depth(depth);
                float4 positionVS = float4(i.rayVS * depth, 1);
                float3 positionWS = mul(unity_CameraToWorld, positionVS).xyz;
/*
                half4 color = half4(frac(positionWS.xyz), 1.0);
                #if UNITY_REVERSED_Z
                    if (depth < 0.0001) return half4(0,0,0,1);
                #else
                    if (depth > 0.9999) return half4(0,0,0,1);
                #endif
*/
                // calculate position in object-space coordinates
                float3 positionOS = mul(unity_WorldToObject, float4(positionWS, 1.0)).xyz;
/*
                // create bounding box mask
                float boundingBoxMask = all(step(positionOS, 0.5) * (1 - step(positionOS, -0.5)));
                color.rgb = boundingBoxMask;
*/              
                half2 uv = mul(positionWS, _MainLightDirection).xy;

                half2 uv1 = Panner(uv, 0.75 * _CausticsSpeed, 1 / _CausticsScale);
                half2 uv2 = Panner(uv, 1 * _CausticsSpeed, -1 / _CausticsScale);

                half3 tex1 = SampleCaustics(uv1, _CausticsSplit);
                half3 tex2 = SampleCaustics(uv2, _CausticsSplit);

                half edgeFadeMask = 1 - saturate((distance(positionOS, 0) - _CausticsFadeRadius) / (1 - _CausticsFadeStrength));
                half3 caustics = min(tex1, tex2) * _CausticsStrength * edgeFadeMask;

                return fixed4(caustics, 1);
            }
            ENDCG
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class Caustics : MonoBehaviour
{
    private Material _causticsMaterial = null;
    private void OnEnable()
    {
        var renderer = GetComponent<Renderer>();
        _causticsMaterial = renderer.sharedMaterial;
    }
    private void Update()
    {
        var sunMatrix = RenderSettings.sun.transform.localToWorldMatrix;
        _causticsMaterial.SetMatrix("_MainLightDirection", sunMatrix);
    }
}

感想

色収差の部分のテクスチャフェッチは重いので、パラメータの値が決まったら、あらかじめテクスチャのUVにチャンネルごとにオフセットを入れておくと良いと思いました。

参考

Rendering realtime caustics

2DLookupLighting(FakeBRDF)

環境

Unity2021.2.18f1

概要

Lookupテクスチャを使用したライティングです。

以下のページの画像に対応するようにしています。 BRDF - Wakapon http://wiki.nuaj.net/images/0/0e/MERL100Slices.jpg

Shader "Unlit/2DLookupLighting"
{
    Properties
    {
        _NormalTex("Normal Texture", 2D) = "white" {}
        _Lookup("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct v2f
            {
                float4 positionCS : SV_POSITION;
                float2 uv     : TEXCOORD0;
                fixed3 TSToWS0 : TEXCOORD1;
                fixed3 TSToWS1 : TEXCOORD2;
                fixed3 TSToWS2 : TEXCOORD3;
                half3 viewDirWS: TEXCOORD4;
                half3 lightDirWS: TEXCOORD5;
            };

            sampler2D _NormalTex;
            float4 _NormalTex_ST;
            sampler2D _Lookup;

            v2f vert(appdata v)
            {
                v2f o;
                o.positionCS = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _NormalTex);
                fixed3 normalWS = UnityObjectToWorldNormal(v.normal);
                fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 binormalWS = cross(normalWS, tangentWS) * v.tangent.w;
                o.TSToWS0 = fixed3(tangentWS.x, binormalWS.x, normalWS.x);
                o.TSToWS1 = fixed3(tangentWS.y, binormalWS.y, normalWS.y);
                o.TSToWS2 = fixed3(tangentWS.z, binormalWS.z, normalWS.z);
                o.viewDirWS = WorldSpaceViewDir(v.vertex);
                o.lightDirWS = WorldSpaceLightDir(v.vertex);
                return o;
            }

            fixed3 LightingLookup(half3 lightDir, half3 viewDir, half3 normal)
            {
#define HALF_VEC 1
#if HALF_VEC
                half3 HalfDir = normalize(lightDir + viewDir);
                float NdotL = dot(normal, HalfDir);
#else
                float NdotL = dot(normal, viewDir);
#endif
                float NdotV = dot(normal, viewDir);

                float2 lookupUV = float2(1.0 - (NdotL * 0.5 + 0.5), 1.0 - saturate(NdotV));
                fixed3 exitRadiance = tex2D(_Lookup, lookupUV).rgb;
                float atten = 1;
                return _LightColor0.rgb * (exitRadiance * atten * 2);
            }

            fixed4 frag(v2f i) : SV_Target
            {
                half3 normalTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half3 normalWS;
                normalWS.x = dot(i.TSToWS0.xyz, normalTS);
                normalWS.y = dot(i.TSToWS1.xyz, normalTS);
                normalWS.z = dot(i.TSToWS2.xyz, normalTS);
                normalWS = normalize(normalWS);

                half3 lightDirWS = normalize(i.lightDirWS);
                half3 viewDirWS = normalize(i.viewDirWS);
                fixed3 diffuse = LightingLookup(lightDirWS, viewDirWS, normalWS);

                return fixed4(diffuse, 1);
            }
            ENDCG
        }
    }
}

参考

Problem with BRDF Shader - Unity Forum

Writing Surface Shaders in Unity3D: BRDF Fake - YouTube

Index of /datahoarder/Walt Disney Animation Studios/BRDF Explorer

Dropbox - BRDFDatabase - Simplify your life

環境マッピング

環境

Unity2021.2.18f1

概要

環境マッピングです。 Cube、FakeSpherical(Matcap)、Equirectangular、FishEyeで行ってみました。 BumpMap付です。

Shader "Custom/Cube-BumpedEnv"
{
    Properties
    {
        _Color("Main Color", Color) = (1,1,1,1)
        _MainTex("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
        _NormalTex("Normal Texture", 2D) = "white" {}
        _EnvTex("Env Texture", CUBE) = "white" {}
    }

    SubShader
    {
        Pass
        {
            Tags
            {
                "RenderType" = "Opaque"
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                half3 lightDirWS : TEXCOORD1;
                fixed3 TSToWS0 : TEXCOORD2;
                fixed3 TSToWS1 : TEXCOORD3;
                fixed3 TSToWS2 : TEXCOORD4;
                half3 viewDirWS : TEXCOORD5;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            UNITY_DECLARE_TEXCUBE(_EnvTex);

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.lightDirWS = WorldSpaceLightDir(v.vertex);

                fixed3 normalWS = UnityObjectToWorldNormal(v.normal);
                fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 binormalWS = cross(normalWS, tangentWS) * v.tangent.w;
                o.TSToWS0 = fixed3(tangentWS.x, binormalWS.x, normalWS.x);
                o.TSToWS1 = fixed3(tangentWS.y, binormalWS.y, normalWS.y);
                o.TSToWS2 = fixed3(tangentWS.z, binormalWS.z, normalWS.z);

                o.viewDirWS = WorldSpaceViewDir(v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : COLOR
            {
                half3 normalTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half3 normalWS;
                normalWS.x = dot(i.TSToWS0.xyz, normalTS);
                normalWS.y = dot(i.TSToWS1.xyz, normalTS);
                normalWS.z = dot(i.TSToWS2.xyz, normalTS);
                normalWS = normalize(normalWS);

                half3 lightDirWS = normalize(i.lightDirWS);
                fixed4  diffuse = (dot(normalWS, lightDirWS) * 0.5 + 0.5) * _LightColor0;
                fixed4 col = diffuse * tex2D(_MainTex, i.uv);

                half3 viewDirWS = normalize(i.viewDirWS);
                half3 reflDirWS = reflect(-viewDirWS, normalWS);
                fixed4 envCol = UNITY_SAMPLE_TEXCUBE(_EnvTex, reflDirWS);

                return col + envCol;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}
Shader "Custom/FakeSpherical-BumpedEnv"
{
    Properties
    {
        _Color("Main Color", Color) = (1,1,1,1)
        _MainTex("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
        _NormalTex("Normal Texture", 2D) = "white" {}
        _EnvTex("Env Texture", 2D) = "white" {}
    }

    SubShader
    {
        Pass
        {
            Tags
            {
                "RenderType" = "Opaque"
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

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

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float2 uv : TEXCOORD;
            };
#define MAPPING_TYPE0 1
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                half3 lightDirWS : TEXCOORD1;
                fixed3 TSToWS0 : TEXCOORD2;
                fixed3 TSToWS1 : TEXCOORD3;
                fixed3 TSToWS2 : TEXCOORD4;
#if MAPPING_TYPE0
                half3 viewDirWS : TEXCOORD5;
#endif
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            sampler2D _EnvTex;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.lightDirWS = WorldSpaceLightDir(v.vertex);

                fixed3 normalWS = UnityObjectToWorldNormal(v.normal);
                fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 binormalWS = cross(normalWS, tangentWS) * v.tangent.w;
                o.TSToWS0 = fixed3(tangentWS.x, binormalWS.x, normalWS.x);
                o.TSToWS1 = fixed3(tangentWS.y, binormalWS.y, normalWS.y);
                o.TSToWS2 = fixed3(tangentWS.z, binormalWS.z, normalWS.z);
#if MAPPING_TYPE0
                o.viewDirWS = WorldSpaceViewDir(v.vertex);
#endif
                return o;
            }

            inline float2 Mapping(float3 normal)
            {
#if MAPPING_TYPE0
                half3 normalVS = mul((float3x3)UNITY_MATRIX_V, normal);
                normalVS.z += 1;
                return normalVS.xy / length(normalVS) * 0.5 + 0.5;
#else
                return normal.xy * 0.5 + 0.5;
#endif
            }

            fixed4 frag(v2f i) : COLOR
            {
                half3 normalTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half3 normalWS;
                normalWS.x = dot(i.TSToWS0.xyz, normalTS);
                normalWS.y = dot(i.TSToWS1.xyz, normalTS);
                normalWS.z = dot(i.TSToWS2.xyz, normalTS);
                normalWS = normalize(normalWS);

                half3 lightDirWS = normalize(i.lightDirWS);
                fixed4  diffuse = (dot(normalWS, lightDirWS) * 0.5 + 0.5) * _LightColor0;
                fixed4 col = diffuse * tex2D(_MainTex, i.uv);

#if MAPPING_TYPE0
                half3 viewDirWS = normalize(i.viewDirWS);
                half3 reflDirWS = reflect(-viewDirWS, normalWS);
                fixed4 envCol = tex2D(_EnvTex, Mapping(reflDirWS));
#else
                half3 normalVS = mul((float3x3)UNITY_MATRIX_V, normalWS);
                fixed4 envCol = tex2D(_EnvTex, Mapping(normalVS));
#endif
                return col + envCol;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}
Shader "Custom/Equirectangular-BumpedEnv"
{
    Properties
    {
        _Color("Main Color", Color) = (1,1,1,1)
        _MainTex("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
        _NormalTex("Normal Texture", 2D) = "white" {}
        _EnvTex("Env Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass 
        {
            Tags
            {
                "RenderType" = "Opaque"
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                half3 lightDirWS : TEXCOORD1;
                fixed3 TSToWS0 : TEXCOORD2;
                fixed3 TSToWS1 : TEXCOORD3;
                fixed3 TSToWS2 : TEXCOORD4;
                half3 viewDirWS : TEXCOORD5;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            sampler2D _EnvTex;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.lightDirWS = WorldSpaceLightDir(v.vertex);

                fixed3 normalWS = UnityObjectToWorldNormal(v.normal);
                fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 binormalWS = cross(normalWS, tangentWS) * v.tangent.w;
                o.TSToWS0 = fixed3(tangentWS.x, binormalWS.x, normalWS.x);
                o.TSToWS1 = fixed3(tangentWS.y, binormalWS.y, normalWS.y);
                o.TSToWS2 = fixed3(tangentWS.z, binormalWS.z, normalWS.z);

                o.viewDirWS = WorldSpaceViewDir(v.vertex);
                return o;
            }

            inline float2 Mapping(half3 normal)
            {
                float2 longlat = float2(atan2(normal.x, normal.z) + UNITY_PI, acos(-normal.y));
                float2 uv = longlat / float2(2.0 * UNITY_PI, UNITY_PI);
                return uv;
            }

            fixed4 frag(v2f i) : COLOR
            {
                half3 normalTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half3 normalWS;
                normalWS.x = dot(i.TSToWS0.xyz, normalTS);
                normalWS.y = dot(i.TSToWS1.xyz, normalTS);
                normalWS.z = dot(i.TSToWS2.xyz, normalTS);
                normalWS = normalize(normalWS);

                half3 lightDirWS = normalize(i.lightDirWS);
                fixed4  diffuse = (dot(normalWS, lightDirWS) * 0.5 + 0.5) * _LightColor0;
                fixed4 col = diffuse * tex2D(_MainTex, i.uv);

                half3 viewDirWS = normalize(i.viewDirWS);
                half3 reflDirWS = reflect(-viewDirWS, normalWS);
                fixed4 envCol = tex2Dlod(_EnvTex, float4(Mapping(reflDirWS), 0, 0));

                return col + envCol;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}
Shader "Custom/FishEye-BumpedEnv"
{
    Properties
    {
        _Color("Main Color", Color) = (1,1,1,1)
        _MainTex("Diffuse (RGB) Alpha (A)", 2D) = "gray" {}
        _NormalTex("Normal Texture", 2D) = "white" {}
        _EnvTex("Env Texture", 2D) = "white" {}
    }

    SubShader
    {
        Pass
        {
            Tags
            {
                "RenderType" = "Opaque"
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                half3 lightDirWS : TEXCOORD1;
                fixed3 TSToWS0 : TEXCOORD2;
                fixed3 TSToWS1 : TEXCOORD3;
                fixed3 TSToWS2 : TEXCOORD4;
                half3 viewDirWS : TEXCOORD5;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            sampler2D _EnvTex;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.lightDirWS = WorldSpaceLightDir(v.vertex);

                fixed3 normalWS = UnityObjectToWorldNormal(v.normal);
                fixed3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 binormalWS = cross(normalWS, tangentWS) * v.tangent.w;
                o.TSToWS0 = fixed3(tangentWS.x, binormalWS.x, normalWS.x);
                o.TSToWS1 = fixed3(tangentWS.y, binormalWS.y, normalWS.y);
                o.TSToWS2 = fixed3(tangentWS.z, binormalWS.z, normalWS.z);

                o.viewDirWS = WorldSpaceViewDir(v.vertex);
                return o;
            }

            inline float2 Mapping(float3 normal)
            {
                normal.z *= -1;
                float m = 2.0 * sqrt(
                    pow(normal.x, 2.0) +
                    pow(normal.y, 2.0) +
                    pow(normal.z + 1.0, 2.0)
                );
                return normal.xy / m + 0.5;
            }

            fixed4 frag(v2f i) : COLOR
            {
                half3 normalTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half3 normalWS;
                normalWS.x = dot(i.TSToWS0.xyz, normalTS);
                normalWS.y = dot(i.TSToWS1.xyz, normalTS);
                normalWS.z = dot(i.TSToWS2.xyz, normalTS);
                normalWS = normalize(normalWS);

                half3 lightDirWS = normalize(i.lightDirWS);
                fixed4  diffuse = (dot(normalWS, lightDirWS) * 0.5 + 0.5) * _LightColor0;
                fixed4 col = diffuse * tex2D(_MainTex, i.uv);

                half3 viewDirWS = normalize(i.viewDirWS);
                half3 reflDirWS = reflect(-viewDirWS, normalWS);
                fixed4 envCol = tex2Dlod(_EnvTex, float4(Mapping(reflDirWS), 0, 0));

                return col + envCol;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}

感想

Equirectangular、FishEyeはtex2Dlodでミップマップを使わないようにしないと線が出てしまいます。全天で行いたい場合は素直にCubeマップを使用した方が良いと思いました。

参考

Distinctive Derivative Differences | by Ben Golus | Medium

c# - Map points on a sphere to pixel locations on fish eye image - Stack Overflow

フェイクポイントライト

環境

Unity2021.2.18f1

概要

フェイクポイントライトの実装テストです。 法線を考慮してないシンプルなものです。

Hierarchy>3d Object >Sphareで球体モデルを生成して、マテリアルに設定します。

コード

Shader "Custom/FakePointLight"
{
    Properties{
        [HDR]_Color("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
        Pass
        {
            ZWrite Off
            Lighting Off
            Cull Front
            ZTest Always
            Blend SrcAlpha One

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

            half4 _Color;
            sampler2D _CameraDepthTexture;

            struct VSInput
            {
                float4 vertex : POSITION;
                float3 texcoord : TEXCOORD0;
            };
#define SSTOWS_TYPE0 1
            struct VSOut
            {
                float4 positionCS :SV_POSITION;
                float4 positionSS : TEXCOORD0;
#if SSTOWS_TYPE0
                float3 rayVS : TEXCOORD1;
#else
                float3 positionWS : TEXCOORD2;
#endif
                float radius : TEXCOORD3;
            };

            VSOut vert(VSInput v)
            {
                VSOut o;
                o.positionCS = UnityObjectToClipPos(v.vertex);
                o.positionSS = ComputeScreenPos(o.positionCS);
#if SSTOWS_TYPE0
                o.rayVS = UnityObjectToViewPos(v.vertex).xyz;
                o.rayVS.z *= -1;
#else
                o.positionWS = mul(unity_ObjectToWorld, v.vertex).xyz;
#endif
                o.radius = length(UNITY_MATRIX_M._m01_m11_m21) * 0.5;
                return o;
            }

            fixed4 frag(VSOut i) : Color
            {
#if SSTOWS_TYPE0
                const float far = _ProjectionParams.z;
                i.rayVS = i.rayVS * (far / i.rayVS.z);
                float depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.positionSS.xy / i.positionSS.w));
                float4 positionVS = float4(i.rayVS * depth, 1);
                float3 positionWS = mul(unity_CameraToWorld, positionVS).xyz;
#else
                float4 positionSS = i.positionSS / i.positionSS.w;
                float eyeDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, positionSS.xy));
                float3 cameraForwardDir = -UNITY_MATRIX_V._m20_m21_m22;
                float3 viewDirWS = normalize(UnityWorldSpaceViewDir(i.positionWS));
                float3 positionWS = ((eyeDepth * viewDirWS * (1.0 / dot(cameraForwardDir, viewDirWS))) + _WorldSpaceCameraPos);
#endif
                float len = length(positionWS - UNITY_MATRIX_M._m03_m13_m23);
                float power = 1.0 - smoothstep(0, 1, pow(saturate(len / i.radius), 0.5));
                //float power = 1.0 - smoothstep(0, 1, len / i.radius);
                return fixed4(_Color.rgb, _Color.a * power);
            }
            ENDCG
        }
    }
}

参考

bshishov/SimpleDecal.shader

UnityでC#7.2を使用できるようにしてVisualStudioに対応させる

この記事は2018年09月05日にqiitaに投稿した内容です。

環境

Unity2018.2.2f1 Incrementalcompiler 0.0.42-preview.19

概要

Incrementalcompilerをインストールしたら、VisualStudioにc#のバージョンが違うと怒られました。 その対処方法です。

Incrementalcompilerのインストール

・Unityを開く ・「window」>「PackageManager」を開く ・「All」タブでIncrementalcompilerを選択し、「install」を押す

VisualStudioがエラーを出さないようにする

https://github.com/zoon/C-Sharp-Latest-for-Unity/releases から「csharplatest-0.1.0.18.unitypackage」をインストールする

参照

https://forum.unity.com/threads/unity-incremental-c-compiler.523993/page-7

OpticalFlowによるテクスチャアニメ補間のテスト

この記事は2018年07月02日にqiitaに投稿した内容です。

環境

Unity2017.3.0p4 Windows10 Home

概要

OpenCv3のOpticalFlowでFlowMapを作成して補間するテストです

OpenCV

OpenCvSharp3を使用しました

テクスチャ

使用した画像です https://blogs.unity3d.com/jp/2016/11/28/free-vfx-image-sequences-flipbooks/

動作

左側がflowmapで右側が線形補間です。flowmapのほうは補間は綺麗ですが、全体的に揺れている。 テクスチャを均等分割したけど、中心がずれてるスプライトがあるのかも。 GifCapture-201807022226392970.gif

flowmapです。 Explosion01-light-nofire_5x5_flow.png

コード

多いのでgithubにアップ Tools/OpticalFlowで生成 https://github.com/fukaken5050/OpticalFlow

ツール

市販のVFXツールでも作成できます

Slate Editor https://www.facedownfx.com/

Houdini https://www.sidefx.com/ja/

参考

http://www.klemenlozar.com/frame-blending-with-motion-vectors/