视差贴图
- 啥是视差贴图
- 视差贴图的原理
- 视差贴图咋用
1.啥是视差贴图
视差贴图(Parallax Mapping)技术和法线贴图差不多,但它有着不同的原则。和法线贴图一样视差贴图能够极大提升表面细节,使之具有深度感。它也是利用了视错觉,然而对深度有着更好的表达,与法线贴图一起用能够产生难以置信的效果。视差贴图和光照无关,我在这里是作为法线贴图的技术延续来讨论它的。视差贴图属于位移贴图(Displacement Mapping)技术的一种,它对根据储存在纹理中的几何信息对顶点进行位移或偏移。
2.视差贴图的原理
获得A和B,我们用向量V¯减去点A的纹理坐标得到P¯。我们通过在着色器中用1.0减去采样得到的高度贴图中的值来取得深度值,而不再是高度值,或者简单地在图片编辑软件中把这个纹理进行反色操作。
在3D空间中观察视差贴图的原理,P1为A,P2为B,这里因为方便所以两个平面都使用了平面。
在这里P2 = P1 + |P2 - P1|*viewDir 经过矢量化可以得到viewDir和平面的夹角sinθ = viewDir.z。
所以(X2, Y2, Z2) = (X1, Y1, Z1) - h/sinθ * viewDir
3.视差贴图咋用
Shader "ParallaxMap"
{
Properties
{
_MainTex ("_MainTex", 2D) = "white" {}
_ParallaxMap ("_ParallaxMap", 2D) = "white" {}
_HeightFactor("_Height Factor", Float) = 1 //高度系数
}
SubShader
{
Tags{"RenderType"="Opaque"}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float3 lightDir : TEXCOORD3;
float3 viewDir : TEXCOORD4;
float3 lightDir_tangent : TEXCOORD5;
float3 viewDir_tangent : TEXCOORD6;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _ParallaxMap;
float4 _ParallaxMap_ST;
float _HeightFactor;
// 利用视差贴图(高度图)计算某个像素UV偏移的量
inline float2 CaculateParallaxUV(v2f i)
{
//采样heightmap
float height = 1 - tex2D(_ParallaxMap, i.uv).r;
// 归一化切线空间的视线方向
float3 viewDir = normalize(i.viewDir_tangent);
//偏移值 = 切线空间的视线方向.xy(uv空间下的视线方向)* height * 控制系数
float2 offset = i.viewDir_tangent.xy/i.viewDir_tangent.z * height * _HeightFactor;
return offset;
}
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//TRANSFORM_TEX主要作用是拿顶点的uv去和材质球的tiling和offset作运算,确保材质球里的缩放和偏移设置是正确的
o.uv = v.uv;
/* 勘误 为什么这里不能用 UnityObjectToWorldDir
* 这地方我们要求的是这个像素点所在的坐标
o.worldPos = UnityObjectToWorldDir(v.vertex);
// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
return normalize(mul((float3x3)unity_ObjectToWorld, dir));
}
*/
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.lightDir = normalize(_WorldSpaceLightPos0.xyz);
o.viewDir = normalize(_WorldSpaceCameraPos.xyz - o.worldPos.xyz);
TANGENT_SPACE_ROTATION;
o.lightDir_tangent = normalize(mul(rotation,ObjSpaceLightDir(v.vertex)));
o.viewDir_tangent = normalize(mul(rotation,ObjSpaceViewDir(v.vertex)));
return o;
}
fixed4 frag(v2f i) : SV_Target
{
i.normalDir = normalize(i.normalDir);
float2 offsetUV = CaculateParallaxUV(i);
i.uv += offsetUV;
//clip(i.uv);
//clip(1 - i.uv);
fixed3 color = tex2D(_MainTex,i.uv);
return fixed4(color, 1);
}
ENDCG
}
}
}