環境マッピング
環境
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