首先要知道,视差要采样一张视差图,通过这张视差图,才能判断这个像素在这个点应该偏移多少
本文使用陡峭视差映射
底下有全部代码
unity一开,优哉游哉,无需多言,我们直接开始
1.首先viewdir是要从世界空间转到切线空间去计算的,
这是因为如果用世界空间viewdir,随视角转动,凹凸效果也会随之改变
肯定要用切线空间,切线空间的Z轴(法线)决定了表面的“陡峭程度” ,只有在切线空间中,视角方向才能正确反映表面局部倾斜。
想想看我们的uv偏移,其实算的就是在这个t0点,向下偏移了多少
float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
float3 cammearDir = o.worldPos.xyz - _WorldSpaceCameraPos;
o.viewDir = float3(
dot(cammearDir, v.tangent.xyz),
dot(cammearDir, binormal),
dot(cammearDir, v.normal)
);
2.准备一个视差函数
接下来,我们的视角层层递进(写循环),直到探查到视差图的深度
需要的食材:
currentTexCoords;指当前uv,开始是正常的uv,每一次步进,都会让当前uv,增加一层视差uv
deltaDepthever =height / _maxstep 每一步走的深度是多少;
deltaTexCoords = viewdir.xy / (viewdir.z * ) 偏移uv,viewdir.xy/view.z是因为,离摄像机越近,那么偏移的程度就越大,离摄像机越远,偏移的程度就越小
currentLayerDepth;当前前进的深度
currentDepthMapValue;视差图采样的深度
想想看如果currentLayerDepth<currentDepthMapValue,是不是就可以结束循环了
接下来我们开始循环
while (currentLayerDepth < currentDepthMapValue && xxx < 100)
{
outuv = currentTexCoords;
currentTexCoords += deltaTexCoords;
currentDepthMapValue = tex2D(_DispTex, currentTexCoords);
currentLayerDepth += deltaDepth;
xxx += 1;
}
outuv;记录循环后偏移的uv,并作为函数的返回值;
currentTexcoords +=deltatexcoords;currentTexcoords 其实也就是最终的uv,也就是outuv,为什么uv要累计深度偏移
这是因为视差贴图通过“欺骗性”的UV偏移模拟表面高度差。例如:
高处(高度值大)的像素需要更大的UV偏移量(看起来更“远”)
低处(高度值小)的像素需要更小的UV偏移量(看起来更“近”)
用累计好的深度去采样贴图,哪里深哪里浅,视差就成功了
currentDepthMapValue = tex2D(_DispTex, currentTexCoords);用偏移后的uv去采样深度图,再去与当前步进累计的深度currentLayerDepth做判断,是不是当前深度已经抵达视差贴图深度,我们就可以停止循环了,这个就是循环的条件
好了现在给出全部代码,有些地方看着有点奇怪,问就是这样效果好,有什么问题可以留言,其实本来就比较简单,难得我也不会,哈哈
Shader"Unlit/csdnshicha"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_maxstep("MaxStep", Int) = 1
_height_scale("height-scale",range(0,5))=3
_DispTex("displace",2D)= "white"{}
_scale("scale",range(0,255))=1
_Heightl("height",range(-1,1)) = 0.01
_LayerStep("cengshu",Range(1,256)) = 16
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
sampler2D _MainTex;
sampler2D _DispTex;
float4 _MainTex_ST;
int _maxstep;
float _height_scale;
float _scale;
float _Heightl;
float _LayerStep;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
float3 cammearDir = o.worldPos.xyz - _WorldSpaceCameraPos;
o.viewDir = float3(
dot(cammearDir, v.tangent.xyz),
dot(cammearDir, binormal),
dot(cammearDir, v.normal)
);
return o;
}
float2 par(float3 viewdir, float2 uv)
{
float deltaDepth = _height_scale / _maxstep;//每一步前进的深度
float3 viewDir = normalize(viewdir);
//获得深度距离的绝对值
viewdir.z = abs(viewdir.z);
//增加视角方向UV,增加距离感觉
viewdir.xy *= _Heightl;
float2 deltaTexCoords = viewdir.xy / (viewdir.z * _LayerStep); //
float2 currentTexCoords = uv * _scale;
float2 outuv = currentTexCoords;
float currentDepthMapValue = tex2D(_DispTex, currentTexCoords);
float currentLayerDepth = 0;
float xxx = 0; //防止循环次数过大
while (currentLayerDepth < currentDepthMapValue && xxx < 100)
{
outuv = currentTexCoords;
currentTexCoords += deltaTexCoords;
currentDepthMapValue = tex2D(_DispTex, currentTexCoords);
currentLayerDepth += deltaDepth;
xxx += 1;
}
return outuv;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
// apply fog
float3 viewdir = i.viewDir;
float2 uv = i.uv;
float2 outuv = par(viewdir,uv );
float4 col = tex2D(_MainTex, outuv);
return col;
}
ENDCG
}
}
}