Parallax Mapping
Bump Map通过对一个平面上的像素点上增加法向量信息,因此通过像素级的光照后,平面上就具有凹凸的表现,但他仍然是一个平面,呈现不出几何上的高低。而Parallax Map可以一定程度的弥补这种情况,同样Parallax Map仍然是一个平面。
如下图所示:
如果一个Plane上添加了高度信息(这个信息可以放在Normal Map的Alpha中),在Bump Map中,看到的将是V0点,而实际上应该看到的是V1点
最初的Parallax Map方法
如图所示(该图只表示了在u方向)
因为通过u点并不知道到实际的点有多远的off,所以就根据在u点的height和eye的角度,根据off = h*tan(a)来计算得到u',然后以新的uv值从新采样得到normal和detail。这只是一个近似。
存在的问题:
1:表现出的视差效果并不是很好,而且会出现浮动(Swimming Effect)
2:当Eye与Plane的角度很低的时候,off会出现的很大,这会出现很严重的错误
解决方法:
1:对于Swimming Effect,在Irrlicth中使用的方法是,对Height上面再乘上一个Normal.z。
From Irrlicth CD3D8ParallaxMapRenderer.cpp
- ; original parallax mapping: \n"\
- ";mul r3, r1_bx2.wwww, c6; ; r3 = (height, height, height) * scale \n"\
- ";mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
- " \n"\
- "; modified parallax mapping to reduce swimming effect: \n"\
- "mul r3, r1_bx2.wwww, r1_bx2.zzzz ; (nh,nh,nh,nh) = (h,h,h,h) * (n.z,n.z,n.z,n.z,) \n"\
- "mul r3, r3, c6; ; r3 = (nh, nh, nh) * scale \n"\
- "mad r2.xyz, r3, r4_bx2, r0 ; newTexCoord = height * eye + oldTexCoord \n"\
实际上他的off = h*h_scale*n.z
2:对于角度很低时候的问题,可以像Irrlicht中一样,根本就不乘上tan(a),因此就避开了角度的问题;另外一种方法是给定一个角度的极限,当角度超过了允许的极限,则使用极限值来替代这个角度。也就是说tan(a)存在一个最大值。
改进的Parallax Map
改进的方法是通过一定的Trace,找到实际的交点
如图所示
这个方法,首先根据u点,以最大的高度(就是1*height_scale)按照Eye的角度得到一个初始值u_s,然后以u_step为步调从u_s点开始依次往前找,每次将对应点的实际h值和当前的Eye视线的h值做比较,如果h_e>h_u,则h_e递减,u递增,直到找到h_e<=h_u的点或者到达u点。使用这种方法明显要比前面的方法要精确。并且该方法支持了Occlusion。
存在的问题:
1:迭代的消耗更大;当然,如果迭代越多,精度越大,但性能消耗越严重,反之则反之。
2:同样存在角度很小的时候,得到的u_s偏离u很大,而出现错误,这可以规定一个极限角度来解决。
Shader Code :
Vertex Shader
- ////////////////////////////////////////////////////////////////////////////////////////
- // Vertex Shader
- vs_3_0
- ; Input
- dcl_position v0
- dcl_tangent0 v1
- dcl_binormal0 v2
- dcl_normal v3
- dcl_texcoord0 v4.xy
- ; Output
- dcl_position o0
- dcl_color0 o1 ; light vector
- dcl_color1 o2 ; eye vector
- dcl_texcoord0 o3.xy ; tex uv
- ; c0~c3 Geometry matrix
- ; c4 Eye position in object space
- ; c5 Lit position in object space
- m4x4 o0, v0, c0
- ; Calculate the light vector
- sub r0, c5, v0
- m3x3 r1.xyz, r0, v1
- nrm r2, r1
- mov o1, r2
- ; Calculate the eye vector
- sub r0, c4, v0
- m3x3 r1.xyz, r0, v1
- nrm r2, r1
- mov o2, r2
- mov o3.xy, v4.xy
Original Pixel Shader with assembly shader
- ////////////////////////////////////////////////////////////////////////////////////////
- // Original with Assembly Shader
- ps_3_0
- ; Input
- dcl_color0 v0 ; Light vector
- dcl_color1 v1 ; Eye vector
- dcl_texcoord0 v2.xy
- dcl_2d s0 ; detail
- dcl_2d s1 ; normal
- ; Constant
- def c0, 2.0, 1.0, 0.03, 0.0
- def c1, 2.0, 0.0, 0.0, 0.0
- ; Texld the normal map, and the height is in the alpha channel
- texld r0, v2.xy, s1
- mad r0.xyz, r0.xyz, c0.x, -c0.y ; *2-1, transform to (-1, 1)
- ; Parallax mapping
- nrm r2, v1
- rcp r3.x, r2.z ; 1/Eye.z
- min r3.x, r3.x, c1.x ; Eye angle limitation
- mul r3.x, r3.x, r0.w ; h*scale/eye.z
- mul r3.x, r3.x, c0.z
- mad r3.xy, r2.xy, r3.x, v2.xy ; Get the true uv
- ; Texld the normal map
- texld r0, r3.xy, s1
- mad r0.xyz, r0.xyz, c0.x, -c0.y ; *2-1, transform to (-1, 1)
- nrm r1, r0
- ; Diffuse
- nrm r2, v0
- dp3_sat r0.w, r1, r2
-
- ; Load the detail texture
- texld r0.xyz, r3.xy, s0
- ; diffuse*detail
- mul oC0, r0.xyz, r0.w
Improved Pixel Shader with HLSL
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Improved with HLSL shader
- sampler NrmSampler : register(ps, s1);
- sampler DtlSampler : register(ps, s0);
- struct PS_INPUT
- {
- float3 litVec : COLOR0;
- float3 eyeVec : COLOR1;
- float2 tex0 : TEXCOORD0;
- };
- static const float scale = 0.04; // Height map scaling
- static const float n = 10; // Trace count
- static const float fHStep = 1.0/n;
- static const float fMaxUVStep = 2.0*scale; // Camera angle limitation
- float4 main(PS_INPUT input) : COLOR0
- {
- // Get the normal and height
- float3 eyeVec = normalize(input.eyeVec);
- float2 uvStep = eyeVec.xy*min(scale/eyeVec.z, fMaxUVStep); // Init as the most offet
- float4 uv = {input.tex0+uvStep, 0.0, 0.0}; // The start uv
- float4 nrm = tex2D(NrmSampler, uv.xy);
- float curHDiff = 0.0;
- float preHDiff = 1.0;
- if(nrm.a < 1.0) // if nrm.a == 1.0, it's the highest point
- {
- float curH = 1.0;
- float preH = 1.0-nrm.a;
- uvStep *= fHStep;
- [loop] for(int i=0; i
- {
- if(nrm.a >= curH)
- {
- curHDiff = nrm.a-curH;
- break;
- }
- preHDiff = curH-nrm.a;
- curH -= fHStep;
- uv.xy -= uvStep;
- nrm = tex2Dlod(NrmSampler, uv);
- }
- }
-
- uv.xy += curHDiff/(preHDiff+curHDiff)*uvStep; // Interpolate
- nrm = tex2D(NrmSampler, uv.xy);
-
- // Do the parallax mapping
- nrm.xyz = normalize(float3(nrm.xyz*2-1));
- float3 litVec = normalize(input.litVec);
-
- float4 output = saturate(dot(litVec.xyz, nrm.xyz))*tex2D(DtlSampler, uv.xy);
-
- return output;
- };
Samples :
Bump Mapping
Original Parallax Mapping
Improved Parallax Mapping
Reference
Kaneko, T., et al. "Detailed Shape Representation with Parallax Mapping"
阅读(2846) | 评论(1) | 转发(0) |