Not logged in, Join Here! or Log In Below:  
News Articles Search    

 Home / 3D Theory & Graphics / Improvement to parallax mapping Account Manager
Archive Notice: This thread is old and no longer active. It is here for reference purposes. This thread was created on an older version of the flipcode forums, before the site closed in 2005. Please keep that in mind as you view this thread, as many of the topics and opinions may be outdated.

March 25, 2005, 12:59 PM

I've been playing with parallax mapping a bit, and came up with a simple way to improve the quality of the output. The basic parallax mapping equation is:

offset = eye_vector.xy * height;

where eye_vector is in tangent space. I sample the surface normal at the same point at which height is sampled (that is, at the texture coordinates passed to the shader), and then take:

offset = eye_vector.xy * height * normal.z;

This doesn't require any extra texture fetches if you have the normal map and the height map in a single RGBA texture. Geometrically, this equation takes into account the slope of the surface at the sample point, sort of like doing a single iteration of Newton's method.

This is nowhere near as good as relief mapping or 'steep' parallax mapping, which actually raytrace into the height map - but it's exceedingly simple to implement, and definitely improves the appearance at shallow viewing angles.

ordinary parallax mapping -
improved parallax mapping -

Gerald Knizia (cgk)

March 26, 2005, 03:10 PM

Nice trick. Thanks for sharing.


March 26, 2005, 05:29 PM

Where did you get the images that you used for the parallax effect? I had written a shader to compute this, but had trouble finding any decent images readily available... and since i dont really know max or maya I cant just make my own.


March 26, 2005, 09:18 PM

The rockwall texture was from the "original" parallax mapping demo by Terry Welsh, available at: The other textures are ripped from Half-Life 1, and I authored the height map myself using Photoshop.


March 27, 2005, 09:00 AM

Nice work!

Technically, it should be:

offset = eye_vector.xy * height * normal.z / eye_vector.z;

but one usually leaves off the normalization to avoid swim. I wonder if it is ok with your method?



March 27, 2005, 09:50 AM

Since you're going to multiply the offset by the bump height at the same pixel anyway, this can be precomputed as a filter on the bump height, avoiding the runtime multiply instruction.



March 27, 2005, 10:30 AM

In my implementation dividing by z still causes swim.

Do you normalize the tangent space eye vector per-pixel?



March 27, 2005, 03:24 PM

Yes, I normalize the tangent space eye vector per pixel.

Geometrically, the correct equation for a single iteration of Newton's method would be:

offset = E.xy * h * N.z / dot(N, E);

which calculates the intersection of the tangent plane and the eye ray (note: my eye vector points from the surface toward the eye; some implementations use the opposite convention). However, I find that leaving off the denominator gives better results visually - analogous to the way that ordinary parallax mapping should take offset = E.xy * h / E.z, but leaves off the denominator to perform automatic offset limiting.

Good point about pre-filtering the height map; I haven't done this since I want to keep experimenting with some other methods.


March 27, 2005, 06:49 PM

I still get a little texture swim-- the texture moves slightly faster than the surface under parallax. This is probably because we're leaving off the denominator. The effect is somewhat unsettling for very steep maps. Any ideas on how to correct that?



March 27, 2005, 07:49 PM

The swim is reduced if one biases the height values so that instead of falling in the range [0, height_scale] they fall in [-height_scale/2, height_scale/2]. Using this with a height_scale of 0.04, I can't see any swimming in the rockwall texture. Swimming still happens with a very steep map, but no more than it does for ordinary parallax mapping.

I am still investigating, but I don't think this improvement makes high-frequency maps look any better than with ordinary parallax mapping - its main effect is to improve the appearance at shallow viewing angles, where swimming can be seen even with low-frequency maps.


March 27, 2005, 10:29 PM

The reason that biasing makes the surface look better is that you are reducing the size of the offset. Your technique also reduces swimming by making the scale smaller (since n.z < 1)-- I assume that you're increasing the bump scale to compensate-- when I increased the scale the swim was the same on the surface but improved on edges.

I wonder if we can't figure a way to reduce the swim on the surface itself. I think that's the major drawback to parallax right now. It looks great in still images but in motion is kind of wierd when the bump scale gets large.


This thread contains 11 messages.
Hosting by Solid Eight Studios, maker of PhotoTangler Collage Maker.