Wednesday, January 25, 2012

HDR texture sampling (pt 1)

One of the things I discovered while working on environment importance sampling is that Cycles encodes all of its textures to 32-bit byte-per-component RGBA format before uploading to the compute device.  Essentially, this means all textures are low dynamic range.

This is fine for masks and reflectance maps (e.g. most surface textures), but won't cut the mustard for at least a couple of scenarios: HDR backgrounds/environments, and interesting compositing techniques within shader networks.  You can imagine bringing in a map whose range is not 0.0 to 1.0, and then using it as a multiplier or other effect on shading for a surface shader.  Or, you can imagine using an HDR texture for albedos in volumetric rendering, as those values go from above 0.0 to essentially infinity.

Long story short, Cycles needs the range, at least for some textures.  The primary issue is that of memory usage; on GPUs with limited memory, taking a 32-bit RGBA texture to a float RGBA texture quadruples its memory usage.  A single 2k x 2k texture currently takes 16 MB of RAM; stored as floats it will take 64 MB.  When all you have is, say, 1 GB of RAM, 64 MB for a single texture is quite a big chunk of the budget.

There is an alternative format, which is half float; it takes obviously half the space, but doesn't have quite as good of fidelity.  Honestly, it's probably good enough for texture sampling.  The drawback is that, while GPUs natively support computations in half format natively, CPUs do not.  It would likely end up being a performance penalty too large to ignore when CPU rendering.

An obvious answer is to only use float textures where it is needed.  This approach is difficult because the texture slots for GPUs must be known beforehand, and on CUDA there is a 128 texture limit.  Cycles uses the last 28 for internal purposes, so there are 100 to work with.  Siphoning any portion of those off to become float textures impacts that aspect of the budget.

Suffice it to say, this'll be an interesting acrobatic exercise to see how to impact texture budgets the least, while providing the HDR texture benefit.  I went ahead and switched all of the texture slots to float textures just to see the benefit on a small scene.  Here is a comparison of the Grace Cathedral lightprobe lighting the same scene I used for the environment importance sampling testing, with the LDR (current Cycles) results first, both at 100 paths/pixel:

Low dynamic range (0.0 to 1.0 clamp) -- Grace Cathedral light probe
High dynamic range -- Grace Cathedral light probe

To say the lighting difference is dramatic is an understatement.  There is particularly a very bright light almost directly up in the environment texture, and in the LDR range that is almost completely lost relative to the other bright regions of the environment.  The HDR version preserves that, and produces much more natural lighting to boot.  Note that the area of the map with the bright light is relatively small, and the environment-lit scene almost looks like it's lit with a point light!  That is the advantage of HDR environments combined with importance sampling.

I have yet to figure out how to not blow texture budgets, but until I do, for anyone who wants a full-float texture environment, let me know and I can give you a patch.  Just keep in mind the impact it has on RAM usage; you may have to switch to CPU rendering depending on your scene.


  1. Extremely important! I'd appreciate having this option

  2. Wow. Incredible difference. its two complete different light situations. Can we see the texture used?

  3. Light probe came from Paul Debevec's gallery: This is the Grace Cathedral version, where I modified it to be a lat-long map and turned into an EXR.

  4. Hey Mike,

    First of all congrats on your baby!! Must have been an overwhelming experience.

    I've been following the development you have done on cycles and seriously thank you so much for the contribution!!

    I would be interested in using full 32-bit range as well since i intend to use only cpu at the moment.
    I was wondering if you could implement some sort of switch and leave it to the user to decide if he wants complete HDR range or only LDR for textures?? I'm not much of a coder so i don't know if thats even possible. I guess you would know better.

    Anyway thanks once again for all the hard work!!

    1. afaik, HDRI's are 16-32bits with linear color space. And they are supported in cycles./make sure you are using linear instead of sRGB/

    2. vitos1k: any input texture format is allowed (as long as OpenImageIO can read it), but they are all converted to 8-bits-per-component RGBA format (32-bits total per texel) when uploaded to device memory. Unfortunately, that effectively means a clamp to LDR, and the sRGB vs linear operates on those clamped values.

      My test images in this post all used linear environment maps, the first with 8 bits per component (32 bits total), and the second with 32 bits per component (float components, 128 bits total).

  5. I always wondered why Cycles clamps the textures when HDRI lighting is so important.

  6. Thank you for this, I really could use the HDR environment. I always thought the sky looked burned in blender reflections but I never thought the HDRI was loaded as a simple 8-bit texture, what a waste.

    So how can we test this much needed tweak?

  7. Thanks for this, Mike. I recently wrote a post on this topic as well and got feedback from Dewald Swanepoel ( and has mentioned you.

    I hope you don't mind me linking your post to mine as well.

    Thanks, Mike.


    1. Thanks for the mention, and I don't mind the back-links at all. Good luck with your blog!