HowTo:High precision heightfields
The following is a tentative method of generating 24 and 32-bit heightfields in POV-Ray using standard image output formats such as PNG. It is based on some discussion that occurred in povray.general toward the end of 2008.
The technique involves encoding different levels of heightfield precision in each color channel. The first 8 bits of precision are in the blue channel; the second 8 bits in the green channel; and the third 8 bits in the red channel. The channels are then concatenated to produce a single 24 bit number.
The benefit of this technique is that it results in standard 24-bit bitmaps that can be imported into regular image editing software. (By regular image editing software, I actually mean Photoshop and a few other select programs. More on that below.) I think that Leveller uses this format to export its heightfields. Of course, this technique can be extended to 32 bits as well (as you'll see below).
A sample scene with the 24-bit function applied as a pigment to a one-unit-high cone is as follows. You should be able to render the scene and import the resulting image back into POV-Ray to generate a new heightfield.
camera { orthographic location -z * 10 direction +z up +y * 2 right +x * 2 * image_width/image_height rotate +x * 90 } #local hf_pigment_function_pow = function { y * pow(256,3) } #local hf_pigment_function_b = function { mod(hf_pigment_function_pow(x,y,z), pow(256,1)) } #local hf_pigment_function_g = function { mod(hf_pigment_function_pow(x,y,z) - hf_pigment_function_b(x,y,z), pow(256,2)) } #local hf_pigment_function_r = function { mod(hf_pigment_function_pow(x,y,z) - hf_pigment_function_b(x,y,z) - hf_pigment_function_g(x,y,z), pow(256,3)) } #local hf_pigment_b = pigment { function {hf_pigment_function_b(x,y,z) / pow(256,1)} color_map {[0 rgb 0][1 rgb z * 3]} } #local hf_pigment_g = pigment { function {hf_pigment_function_g(x,y,z) / pow(256,2)} color_map {[0 rgb 0][1 rgb y * 3]} } #local hf_pigment_r = pigment { function {hf_pigment_function_r(x,y,z) / pow(256,3)} color_map {[0 rgb 0][1 rgb x * 3]} } // This is the one you want #local hf_pigment_rgb = pigment { average pigment_map { [hf_pigment_b] [hf_pigment_g] [hf_pigment_r] } } cone { 0, 1, y, 0 texture { pigment {hf_pigment_rgb} finish {ambient 1} } }
The 32-bit version of the function (which I haven't tested by importing back into POV-Ray yet) is as follows. Make sure to turn on the alpha channel in the output image by modifying the corresponding INI setting.
camera { orthographic location -z * 10 direction +z up +y * 2 right +x * 2 * image_width/image_height rotate +x * 90 } #local hf_pigment_function_pow = function { y * pow(256,4) } #local hf_pigment_function_t = function { mod(hf_pigment_function_pow(x,y,z), pow(256,1)) } #local hf_pigment_function_b = function { mod(hf_pigment_function_pow(x,y,z) - hf_pigment_function_t(x,y,z), pow(256,2)) } #local hf_pigment_function_g = function { mod(hf_pigment_function_pow(x,y,z) - hf_pigment_function_t(x,y,z) - hf_pigment_function_b(x,y,z), pow(256,3)) } #local hf_pigment_function_r = function { mod(hf_pigment_function_pow(x,y,z) - hf_pigment_function_t(x,y,z) - hf_pigment_function_b(x,y,z) - hf_pigment_function_g(x,y,z), pow(256,4)) } #local hf_pigment_t = pigment { function {hf_pigment_function_t(x,y,z) / pow(256,1)} color_map {[0 rgbt 0][1 rgbt <0,0,0,4,>]} } #local hf_pigment_b = pigment { function {hf_pigment_function_b(x,y,z) / pow(256,2)} color_map {[0 rgbt 0][1 rgbt <0,0,4,0,>]} } #local hf_pigment_g = pigment { function {hf_pigment_function_g(x,y,z) / pow(256,3)} color_map {[0 rgbt 0][1 rgbt <0,4,0,0,>]} } #local hf_pigment_r = pigment { function {hf_pigment_function_r(x,y,z) / pow(256,4)} color_map {[0 rgbt 0][1 rgbt <4,0,0,0,>]} } // This is the one you want #local hf_pigment_rgbt = pigment { average pigment_map { [hf_pigment_t] [hf_pigment_b] [hf_pigment_g] [hf_pigment_r] } } cone { 0, 1, y, 0 open texture { pigment {hf_pigment_rgbt} finish {ambient 1} } }
You may notice that when importing the rendered image back into POV-Ray as a heightfield, the heightfield looks less smooth than it really should. This is not due to a deficiency in the technique as far as I can tell, but probably rather due to the gamut POV-Ray (as well as most image editing programs, but excluding Photoshop and a few others) uses when outputting bitmap images. This issue is discussed in more detail here. Heightfield images outputted by Leveller don't seem to suffer from this problem, and I suspect the 16-bit "hf_gray_16" format already natively supported by POV-Ray gets around this issue somehow as well.
Criticisms of this technique include:
- While such images are easy to view in standard imaging software, they are more difficult to modify than a simple grayscale image.
- This level of precision is not necessary. 16-bit heightfields are sufficient.