[SOLVED] A question about the addreflection?

Hello, @Mr_F @dave

First, I am very inspired by reading the source code and learn a lot, thanks very much for this brilliant engine…


There are basically three implementations for the addreflection when using prefilteredCubeMap(128 64 32 16 8 4 i.e. allMips == true) in the scene. If i have messed up something, please tell me :smile:

1 Lod

#extension GL_EXT_shader_texture_lod : enable

uniform samplerCube texture_prefilteredCubeMap128;
uniform float material_reflectivity;

void addReflection() {
}

2 NoLod

uniform samplerCube texture_prefilteredCubeMap128;
uniform samplerCube texture_prefilteredCubeMap64;
uniform samplerCube texture_prefilteredCubeMap32;
uniform samplerCube texture_prefilteredCubeMap16;
uniform samplerCube texture_prefilteredCubeMap8;
uniform samplerCube texture_prefilteredCubeMap4;

void addReflection() {
.....
}

3 dpAltas


In standard-material’s updateShader(), (suppose allmips === true) , device.extTextureLod, device.samplerCount < 16 decides to use which one, that’s what I am confused about:

On PC: sampleCount equals to 16, playcanvas use the noLod solution.
On iPhone: sampleCount < 16 and extTextureLod(true), playcanvas use Lod Solution.
On older android phones: sampleCount < 16 and extTextureLod(false), playcanvas use dpAltas…

The Lod solution should be the fastest from my understanding, and the noLod solution is slowest. Why playcanvas choose the noLod way rather than Lod solution on PC ?
And may I ask the pros and cons of dpAltas, which is pretty new to me

Thanks very much :slight_smile:

1 Like

Hi! Sorry for the delay.

Lod solution is obviously the cleanest, but the problem with it is that it simply maps glossiness to mip levels disregarding the way mip-mapping works normally. That means surfaces with sharp reflections can produce some aliasing. The slow solution is far from elegant, but it gives both glossiness interpolation and standard mip-mapping, so the results are better anti-aliased.

I tried augmenting the Lod solution, adding a mip-mapping factor to it (using screen-space derivatives), but it looked meh… because prefiltered BRDF mips are quite different from bilinearly downscaled ones. Basically the result looked like the surface suddenly becomes rougher with distance, instead of simply being downscaled.

sampleCount is just a kinda lame way to separate low-end devices and high-end devices. I’m not actually aware if this condition works for anything except iPhones, and it was designed mostly for them. Lod solution was way faster on iPhone and we decided to sacrifice some anti-aliasing for performance there. PC GPUs are generally faster, the perf difference is less dramatic, so we’re still using the slow solution there, unless something more elegant and with comparable quality is found.

dpAtlas is a solution for many Android devices, yeah. We needed something really fast there as well, but many devices don’t support the textureLod extension. After some experimentation I made the dpAtlas, which is basically a dual paraboloid reflection, with differently preconvolved mips packed in a texture atlas. I’m not aware of anyone else doing such packing anywhere, but the idea was to waste less texture space. Code only does 2 lookups of a single 2D texture and interpolates. Today I’d replace paraboloids with octahedron maps though.

2 Likes

Wow! thanks for the complete and great answer !!! l have learned much here :grinning:

Interestingly, yesterday I tried to simplify the NoLod solution by only uniform the texture_prefilteredCubeMap128(mipmapping with the self- send levels, not use system’s mipmapping) and did something like:

    cubes[0] = textureCube(prefilter128Sampler, fixedReflDir, 0.0);
    cubes[1] = textureCube(prefilter128Sampler, fixedReflDir, 1.0);
    cubes[2] = textureCube(prefilter128Sampler, fixedReflDir, 2.0);
    cubes[3] = textureCube(prefilter128Sampler, fixedReflDir, 3.0);
    cubes[4] = textureCube(prefilter128Sampler, fixedReflDir, 4.0);
    cubes[5] = textureCube(prefilter128Sampler, fixedReflDir, 5.0);

I thought when I textureCube with an integral bias (meaning that I only need the levels I send to the gpu, hope gpu don’t do any linear interpolation), i.e. I thought textureCube(prefilter128Sampler, fixedRefDir, 2.0) is equivalent to textureCube(prefilter32Sampler, fixedRefDir).

But the result looked really terrible…

So i guess textureCube with an integral bias dont return the level texture I send, or maybe the gpu still will do some interpolation to cause some seam or other artificial looks…

Bias in texture lookups isn’t really useful, because it’s hardly predictable. First, GPU calculates the mip level as usual, then bias is added on top. Because you don’t know the first value exactly, you can get anything. For example, take a sphere mesh - triangles on edges will be thin and squashed, therefore GPU will normally use a very low resolution mip on them (much lower than on front-facing triangles), and then your bias will make them even more lowres… so it’s messy.