環境
Unity2021.3.4f1
概要
視差遮蔽マッピング(ParallaxOcclusionMapping)です。
Depthを書き換えないシンプルなものです。
Shader "Custom/ParallaxOcclusionMapping" { Properties { _MainTex ("MainTex", 2D) = "white" {} _NormalTex("NormalTex",2D) = "white"{} _HeightTex("DepthTex", 2D) = "white" {} _Height ("Height", Range(0.01, 0.3)) = 0.3 _MinSamples ("MinSamples", int) = 16 _MaxSamples ("MaxSamples", int) = 128 _SpecularColor("Specular Color", Color) = (1,1,1,1) _SpecularExp ("SpecularExp", Range(32, 256)) = 64 } CGINCLUDE #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 position : POSITION; float2 texCoord : TEXCOORD0; float3 normal : NORMAL; float4 tangent : TANGENT; }; struct v2f { float4 position : POSITION; float2 texCoord : TEXCOORD0; float3 lightTS : TEXCOORD1; float3 viewTS : TEXCOORD2; float2 parallaxOffsetTS : TEXCOORD3; float3 normalWS : TEXCOORD4; float3 viewWS : TEXCOORD5; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; sampler2D _HeightTex; float4 _HeightTex_TexelSize; float _Height; int _MinSamples; int _MaxSamples; float4 _SpecularColor; float _SpecularExp; v2f vert(appdata v) { v2f o = (v2f)0; o.position = UnityObjectToClipPos(v.position); o.texCoord = TRANSFORM_TEX(v.texCoord, _MainTex); o.normalWS = UnityObjectToWorldNormal(v.normal); o.viewWS = WorldSpaceViewDir(v.position); float3 lightWS = WorldSpaceLightDir(v.position); float3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz); float3 binormalWS = cross(o.normalWS, tangentWS) * v.tangent.w; float3x3 worldToTangent = float3x3(tangentWS, binormalWS, o.normalWS); o.lightTS = mul(worldToTangent, lightWS); o.viewTS = mul(worldToTangent, o.viewWS); float2 parallaxDirection = normalize(o.viewTS.xy); float len = length(o.viewTS); float parallaxLength = sqrt(len * len - o.viewTS.z * o.viewTS.z) / o.viewTS.z; o.parallaxOffsetTS = parallaxDirection * parallaxLength; o.parallaxOffsetTS *= _Height; return o; } fixed4 frag(v2f i) : COLOR0 { float3 viewTS = normalize(i.viewTS); float3 viewWS = normalize(i.viewWS); float3 lightTS = normalize(i.lightTS); float3 normalWS = normalize(i.normalWS); float2 dx, dy; dx = ddx(i.texCoord); dy = ddy(i.texCoord); float2 texSample = i.texCoord; int numSteps = (int)lerp(_MaxSamples, _MinSamples, dot(viewWS, normalWS)); float currHeight = 0.0; float stepSize = 1.0 / (float)numSteps; float prevHeight = 1.0; float2 texOffsetPerStep = stepSize * i.parallaxOffsetTS; float2 texCurrentOffset = i.texCoord; float currentBound = 1.0; for(int step = 0; step < numSteps && currHeight < currentBound; step++) { texCurrentOffset -= texOffsetPerStep; prevHeight = currHeight; currHeight = tex2Dgrad(_HeightTex, texCurrentOffset, dx, dy).r; currentBound -= stepSize; } float2 pt1 = float2(currentBound, currHeight); float2 pt2 = float2(currentBound + stepSize, prevHeight); float delta2 = pt2.x - pt2.y; float delta1 = pt1.x - pt1.y; float parallaxAmount = (pt1.x * delta2 - pt2.x * delta1) / (delta2 - delta1); float2 parallaxOffset = i.parallaxOffsetTS * (1 - parallaxAmount); float2 texSampleBase = i.texCoord - parallaxOffset; texSample = texSampleBase; fixed4 baseColor = tex2Dgrad(_MainTex, texSample, dx, dy); float3 normalTS = normalize(UnpackNormal(tex2Dgrad(_NormalTex, texSample, dx, dy))); fixed3 diffuse = _LightColor0.rgb * baseColor.rgb * saturate(dot(normalTS, lightTS)); fixed3 halfDirTS = normalize(lightTS + viewTS); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(halfDirTS, normalTS)), _SpecularExp); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * baseColor; fixed4 finalColor = fixed4(ambient + diffuse + specular, 1.0); return finalColor; } ENDCG SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags {"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }
感想
処理負荷が高いのでディスプレースメントマッピングのほうが良いでしょう。
参考
http://maverickproj.web.fc2.com/pg97.html
DepthOffset付き
こちらはDepthを書き換えるタイプのものです。
Shader "Custom/ParallaxOcclusionMappingDepth" { Properties { _MainTex ("MainTex", 2D) = "white" {} _NormalTex("NormalTex",2D) = "white"{} _HeightTex("DepthTex", 2D) = "white" {} _Height ("Height", Range(0.01, 0.3)) = 0.3 _MinSamples ("MinSamples", int) = 16 _MaxSamples ("MaxSamples", int) = 128 _SpecularColor("Specular Color", Color) = (1,1,1,1) _SpecularExp ("SpecularExp", Range(32, 256)) = 64 _DepthBufferScale("DepthBufferScale", Range(1.0, 5.0)) = 1.2 } CGINCLUDE #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 texCoord : TEXCOORD0; float3 normal : NORMAL; float4 tangent : TANGENT; }; struct v2f { float4 position : POSITION; float2 texCoord : TEXCOORD0; float3 lightTS : TEXCOORD1; float3 viewTS : TEXCOORD2; float2 parallaxOffsetTS : TEXCOORD3; float3 normalWS : TEXCOORD4; float3 viewWS : TEXCOORD5; float3 posWS : TEXCOORD6; }; struct fout { float4 col : SV_Target; float depth : SV_Depth; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _NormalTex; sampler2D _HeightTex; float4 _HeightTex_TexelSize; float _Height; int _MinSamples; int _MaxSamples; float4 _SpecularColor; float _SpecularExp; float _DepthBufferScale; v2f vert(appdata v) { v2f o = (v2f)0; o.position = UnityObjectToClipPos(v.vertex); o.texCoord = TRANSFORM_TEX(v.texCoord, _MainTex); o.normalWS = UnityObjectToWorldNormal(v.normal); o.viewWS = WorldSpaceViewDir(v.vertex); float3 lightWS = WorldSpaceLightDir(v.vertex); float3 tangentWS = UnityObjectToWorldDir(v.tangent.xyz); float3 binormalWS = cross(o.normalWS, tangentWS) * v.tangent.w; float3x3 worldToTangent = float3x3(tangentWS, binormalWS, o.normalWS); o.lightTS = mul(worldToTangent, lightWS); o.viewTS = mul(worldToTangent, o.viewWS); float2 parallaxDirection = normalize(o.viewTS.xy); float len = length(o.viewTS); float parallaxLength = sqrt(len * len - o.viewTS.z * o.viewTS.z) / o.viewTS.z; o.parallaxOffsetTS = parallaxDirection * parallaxLength; o.parallaxOffsetTS *= _Height; o.posWS = mul(UNITY_MATRIX_M, v.vertex); return o; } inline float ComputeDepth(float4 posPS) { float z = posPS.z / posPS.w; #if defined(SHADER_API_GLCORE) || defined(SHADER_API_OPENGL) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) return z * 0.5 + 0.5; #else return z; #endif } fout frag(v2f i) { fout o; float3 viewTS = normalize(i.viewTS); float3 viewWS = normalize(i.viewWS); float3 lightTS = normalize(i.lightTS); float3 normalWS = normalize(i.normalWS); float2 dx, dy; dx = ddx(i.texCoord); dy = ddy(i.texCoord); float2 texSample = i.texCoord; int numSteps = (int)lerp(_MaxSamples, _MinSamples, dot(viewWS, normalWS)); float stepSize = 1.0 / (float)numSteps; float currHeight = 0.0; float prevHeight = 1.0; float2 texOffsetPerStep = stepSize * i.parallaxOffsetTS; float2 texCurrentOffset = i.texCoord; float currentBound = 1.0; for(int step = 0; step < numSteps && currHeight < currentBound; step++) { texCurrentOffset -= texOffsetPerStep; prevHeight = currHeight; currHeight = tex2Dgrad(_HeightTex, texCurrentOffset, dx, dy).r; currentBound -= stepSize; } float2 pt1 = float2(currentBound, currHeight); float2 pt2 = float2(currentBound + stepSize, prevHeight); float delta2 = pt2.x - pt2.y; float delta1 = pt1.x - pt1.y; float parallaxAmount = (pt1.x * delta2 - pt2.x * delta1) / (delta2 - delta1); float2 parallaxOffset = i.parallaxOffsetTS * (1 - parallaxAmount); float2 texSampleBase = i.texCoord - parallaxOffset; float3 posWS = i.posWS; float depthScale = _DepthBufferScale; float parallaxLength = length(i.parallaxOffsetTS / _Height); depthScale += depthScale * parallaxLength; float3 offsetWS = -viewWS * (1 - parallaxAmount) * depthScale; posWS += offsetWS; float4 posPS = mul(UNITY_MATRIX_VP, float4(posWS, 1.0)); o.depth = ComputeDepth(posPS); texSample = texSampleBase; fixed4 baseColor = tex2Dgrad(_MainTex, texSample, dx, dy); float3 normalTS = normalize(UnpackNormal(tex2Dgrad(_NormalTex, texSample, dx, dy))); fixed3 diffuse = _LightColor0.rgb * baseColor.rgb * saturate(dot(normalTS, lightTS)); fixed3 halfDirTS = normalize(lightTS + viewTS); fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(halfDirTS, normalTS)), _SpecularExp); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * baseColor; o.col = fixed4(ambient + diffuse + specular, 1.0); return o; } ENDCG SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags {"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }