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