Many (IBL) CubeMaps / Bindless Textures

Started by
6 comments, last by kalle_h 8 years, 7 months ago

So I want Image Based Lighting. Reflections / GI is baked into cubemap textures (preferably with variable sizes between 32^2 x6 .. 256^2 x6) - with MipMaps btw.

That works, but the real challenge is to apply *the right* cubemap for any given pixel. Because there might be 10, maybe even 100 cubemaps loaded. A compute-shader can be used to sort out which probes are used where. But how to read these cubemaps? I can't bind 100+ textures "the old way".

I could make an texture-array with many layers, but...

- I can't put cubeMaps into an array (or sample them like you would sample/filter in a (mipmapped!) cubeMap), do I?

- Is the size gonna be a problem (its DDS compressed, yet I think the overall size of all probes together would consume 50 .. 75 MB)

Now I've read things about "Bindless Textures", though I haven't been able to succesfully implement it yet (weird errors, yet the nVidia demo's work, card supports GL 4.5). A few questions about that. If I understand it right, I'll:

- make my cubemaps as usual. MipMapped, eventually with different resolutions

- I put all their addressess into an array of some sort, and push that into my "lighting shader"

- In the (compute!) shader, I get the right texture via the address, and then just read

Would this be the solution?

One thing I don't quite get, is making textures "resident". I suppose any texture we're about to use, should be resident. But can I just make all my cubemaps resident just once, and leave it that way? Or do I have to "un- and re-resident" them each cycle? Also, is there a limit or penalty when making (too) many textures resident? As said, all probes together consume quite some megabytes.

Advertisement


I could make an texture-array with many layers, but...

- I can't put cubeMaps into an array (or sample them like you would sample/filter in a (mipmapped!) cubeMap), do I?


https://www.opengl.org/registry/specs/ARB/texture_cube_map_array.txt

Ok, didn't know about that, thanks!

Can't read that fast. but using "ARB_texture_cube_map_array" requires all cubemaps to use the same size right (or use multiple arrays eventually). It's not a deal-breaker, but... being able to use a bigger cubemap at some special spots with very reflective surfaces would be nice.

Other than that, what benefits (or disadvantages) does the "Bindless Textures" approach bring compared to an array of cubemaps? In my context:

- 50..75 MB of cubemap data

- Just one pass (so I won't be toggling textures between render-calls)

- Compute shader is doing a "Tiled" approach, thus sorting out for each tile which probes may affect it, then apply the probes per pixel.

I implemented exactly this Some time ago :)

I think there arent any benefits besides the dynamic memory usage. But if you really just have 75 MB of cubemaps, you probably wont have to bother.

Do you have enough cubemaps so that you have a Performance gain with compute shaders and tiled lighting? I didnt at all, sadly.

Additionally you could use two Arrays, one small Resolution and one higher. Switch on probes Resolution property in the shader.

>> Do you have enough cubemaps so that you have a Performance gain with compute shaders and tiled lighting?

Not sure what you mean with this...

The reason why I'm using a Tiled approach for IBL, is that I honestly don't know how else to do it. If I have a certain pixel, I need to know which 1 (or 2!) probes affect it. Looping through all 100 (the actual number might be a bit lower or higher) is not an option obviously. And since I'm doing tiled lighting already anyway, the same shader can just as well collect a list of probes per tile.

A tile has 32x32 or 64x64 pixels in my case, so that's at least 1024 pixels/tasks running in parallel, per tile. Each pixel will test 1 probe. If the sphere (with variable radius) intersects the tile frustum, it gets added to a list.

After that, each pixel loops through the "small", filtered list to do a final test and sampling. Does it work good? No idea, first I''ll need an array of CubeMaps to begin with, which is why I asked here :)

>> 2 arrays

I think I'll take that route. Bindless Textures is still fresh, new, (buggy?) and probably not supported by a whole lot of cards anyway. And if there are no clear advantages for this particular scenario... Easy does it!

Simple way to do this even with older hardware is to accumulate all cube maps using proxy geometry(sphere/cube/quad) and rendering to rgba16f target. You output color * weight to rgb and weight to alpha channel. Just render cubemaps as one by one and finally you use fullscreenpass to renormalize reflection buffer by dividing by alpha.

That doesn't sound crazy at all. Let's see if I get it straight:

It works a bit the same as ("old") Deferred Lighting, where light volumes (spheres/cones/...) were rendered into the scene, only affecting geometry it intersects. I'll use a "roughness" G-Buffer produced earlier to "blur" properly (taking multiple samples and/or picking lower mipmaps from the probe).

In the alpha channel we could store the weight (based on distance between pixel & probe centre and its radius; "attenuation"). It may happen 2 or even more probes overlap the same pixels, so we use additive blending to sum up both colors & weights. Finally we grab the produced "Reflection/GI texture", normalize it, and add it to the rest of the scene. Since I'm also using Screen Reflections (I can involve that here as well. Pixels that can make good use of realtime reflections, should use no or a lower weight for pre-baked reflections.

You know what, that sounds a whole lot easier than the tiled approach I had in mind. Only downside is that I'll have to resample the G-Buffer for each probe. Then again there won't be that many (overlapping) usually. And I guess its still a good idea to use a cubeMap array (or bindless textures) so we don't have to render probes one-by-one, switching cubeMap textures in between. But then I could first render the low quality (small-res cubemaps) array, then a high-quality array for example.

That doesn't sound crazy at all. Let's see if I get it straight:

It works a bit the same as ("old") Deferred Lighting, where light volumes (spheres/cones/...) were rendered into the scene, only affecting geometry it intersects. I'll use a "roughness" G-Buffer produced earlier to "blur" properly (taking multiple samples and/or picking lower mipmaps from the probe).

In the alpha channel we could store the weight (based on distance between pixel & probe centre and its radius; "attenuation"). It may happen 2 or even more probes overlap the same pixels, so we use additive blending to sum up both colors & weights. Finally we grab the produced "Reflection/GI texture", normalize it, and add it to the rest of the scene. Since I'm also using Screen Reflections (I can involve that here as well. Pixels that can make good use of realtime reflections, should use no or a lower weight for pre-baked reflections.

You know what, that sounds a whole lot easier than the tiled approach I had in mind. Only downside is that I'll have to resample the G-Buffer for each probe. Then again there won't be that many (overlapping) usually. And I guess its still a good idea to use a cubeMap array (or bindless textures) so we don't have to render probes one-by-one, switching cubeMap textures in between. But then I could first render the low quality (small-res cubemaps) array, then a high-quality array for example.

You did understand it correctly. If your gbuffer packs normals + roughness to same texture you could defer specular color sampling and fresnell calculation after renormalization pass. All standard deferred shading/prelight pass optimizations should work for this too.

This topic is closed to new replies.

Advertisement