粗略的视差效果:
本文就不陈述视差贴图相关描述了, 此处只记录说明一下两点
(1) 在求uv的偏移量的时候 :在切线空间下的 viewDir.xy / viewDir.z , 这里为什么要除以 .z
(2)以及直接使用上面链接中的“改进3”的 RayMarching (光线步进)实现精确的视差效果
(一)viewDir.xy / viewDir.z 的解释:
(1)求出BC: 最大的UV偏移值
AE, AG: 归一化后的 viewDir
AC的高度为:1
相似三角形: EF/ AF = BC / AC
即: viewDir.xy / viewDir.z = BC / 1
根据以上条件: BC = (viewDir.xy / View.z) * 1
因此 ,uv的偏移量 uvOffset = BC
uvOffset乘以很小的偏移系数,误差就会非常小。
(为防止.z太小,造成误差太大 通常: view.xy / (view.z + 0.42) )
(2)精确的偏移值:BQ
人眼看到E点就被挡住了 B点的UV + BQ 才是 在Q点采样得到的高度 QE
(二)RayMatching 光线步进求相对精确的交点
Shader "Unlit/Parallax"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ParallaxMap("Parallax",2D) = "white"{}
_HeightScale("HeightScale",Range(0,0.1)) = 1
_BumpMap("BumpMap",2D) = "Bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#define PARALLAX_RAYMARCHING_STEPS 150
#define PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS 10
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 tangent : TANGENT;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float4 T2W0 : TEXCOORD1;
float4 T2W1 : TEXCOORD2;
float4 T2W2 : TEXCOORD3;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _ParallaxMap;
float _HeightScale;
sampler2D _BumpMap;
// unity 的内置函数
//ParallaxOffset( )
//-----------------RayMatching 封装的相关代码-----------------------------
// 在函数中用到多次 tex2D采样 和 for循环,代价相对较大。。。。。。
float GetParallaxHeight (float2 uv) {
return tex2D(_ParallaxMap, uv).r ;
}
float2 ParallaxRaymarching (float2 uv, float2 viewDir) {
#if !defined(PARALLAX_RAYMARCHING_STEPS)
#define PARALLAX_RAYMARCHING_STEPS 5
#endif
float2 uvOffset = 0;
float stepSize = 1.0 / PARALLAX_RAYMARCHING_STEPS;
float2 uvDelta = viewDir * stepSize;
float stepHeight = 1;
float surfaceHeight = GetParallaxHeight(uv);
for (int i = 0; i < PARALLAX_RAYMARCHING_STEPS && stepHeight > surfaceHeight; ++i)
{
uvOffset -= uvDelta;
stepHeight -= stepSize;
surfaceHeight = GetParallaxHeight(uv + uvOffset);
}
#if !defined(PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS)
#define PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS 2
#endif
for (int i = 0; i < PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS; i++) {
uvDelta *= 0.5;
stepSize *= 0.5;
if (stepHeight < surfaceHeight) {
uvOffset += uvDelta;
stepHeight += stepSize;
}else{
uvOffset -= uvDelta;
stepHeight -= stepSize;
}
surfaceHeight = GetParallaxHeight(uv + uvOffset);
}
return uvOffset;
}
float2 DoParallaxMap(float3 viewDir,float2 uv){
viewDir = normalize(viewDir);
viewDir.xy /=(viewDir.z + 0.41);
viewDir.xy *= _HeightScale;
uv += ParallaxRaymarching(uv.xy,viewDir.xy);
return uv;
}
//-----------------RayMatching end------------------------------
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 normal =UnityObjectToWorldNormal(v.normal);
float3 tangent = UnityObjectToWorldDir(v.tangent);
float3 binormal = cross(normal,tangent) * v.tangent.w;
float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
o.T2W0 = float4(tangent.x,binormal.x,normal.x,worldPos.x);
o.T2W1 = float4(tangent.y,binormal.y,normal.y,worldPos.y);
o.T2W2 = float4(tangent.z,binormal.z,normal.z,worldPos.z);
return o;
}
// 初始通用版本
fixed4 frag (v2f i) : SV_Target
{
float3 worldPos = float3(i.T2W0.w,i.T2W1.w,i.T2W2.w);
float3 normal = UnpackNormal(tex2D(_BumpMap,i.uv));
normal = float3(dot(i.T2W0.xyz,normal),dot(i.T2W1.xyz,normal),dot(i.T2W2.xyz,normal));
float3 view_ws =normalize(UnityWorldSpaceViewDir(worldPos));
float3 t = float3(i.T2W0.x,i.T2W1.x,i.T2W2.x);
float3 b = float3(i.T2W0.y,i.T2W1.y,i.T2W2.y);
float3 n = float3(i.T2W0.z,i.T2W1.z,i.T2W2.z);
float3 view_ts = float3(dot(t,view_ws),dot(b,view_ws),dot(n,view_ws));
在视差贴图中 R,G,B, 三个分量的值相等,所以随便用一个就好
float height = tex2D(_ParallaxMap,i.uv).r;
float类型的 ,如 1.0f , 不要写成1, 不然会被当成int处理,舍弃小数,造成精度不准;
/// 0.5分界线高度图 来达到上下各异的视差
height -= 0.5f;
// 此处的 .xy / .z 是利用相似三角形,求出精确的 xy 的偏移量
float2 offsetUV = height*( view_ts.xy/ (view_ts.z + 0.42)) * _HeightScale;
i.uv += offsetUV;
float4 col = tex2D(_MainTex,i.uv);
return col;
}
ENDCG
}
}
}