pmndrs / postprocessing

A post processing library for three.js.
zlib License
2.37k stars 214 forks source link

WebGL Errors on some mobile devices #280

Closed ranbuch closed 3 years ago

ranbuch commented 3 years ago

When using selectiveBloomEffect I'm getting many WebGL errors: image

To reproduce you can try to visit this bloom effect example. Problem is, it's not consistant over all devices.

I was able to reproduce the issue with an iPhone Xr (iOS 14 - Chrome and Safari) and Samsung Galaxy S10 with the latest stable Chrome (89) and Galaxy S9 Plus.

vanruesc commented 3 years ago

Hi,

it looks like the failing device doesn't support the OES_texture_float_linear extension. This means that linear filtering cannot be used for float type buffers on this device. There are two things you can do to address this:

  1. Disable postprocessing effects on devices that don't fully support float type textures (easy solution).
  2. Don't use high precision frame buffers (HalfFloatType) on these devices and bypass color space conversions. Set renderer.outputEncoding to LinearEncoding (default) and set the encoding of all textures to LinearEncoding.

The second option is a viable workaround for mobile devices as it avoids float type buffers without producing banding artifacts. However, calculations will be performed on sRGB colors which leads to slightly incorrect results. This approach works well with unlit scenes but requires some tweaking if dynamic lights are used.

To change the encoding of textures, use the following function:

/**
 * Sets the encoding for all color maps used by the given object.
 *
 * @param {Object3D} object - A 3D object.
 * @param {Number} encoding - The texture encoding.
 */

function setColorMapEncoding(object, encoding) {

    object.traverse((node) => {

        const m = node.material;

        if(m !== undefined && m !== null) {

            const maps = [
                m.map,
                m.emissiveMap,
                m.specularMap
            ];

            for(const map of maps) {

                if(map !== undefined && map !== null) {

                    map.encoding = encoding;

                }

            }

        }

    });

}

setColorMapEncoding(gltf.scene, LinearEncoding);

Here's an updated example: https://codesandbox.io/s/falling-platform-l5obq?file=/src/App.js

I've also discovered two issues with the current implementation of the SelectiveBloomEffect:

  1. The effect uses a ClearPass to clear the bloom texture, but this incorrectly uses the clear color of the renderer. It should instead set a black override clear color.
  2. The depth comparison uses a predefined epsilon value to decide if depth values are equal. This value is too small and should be configurable.

I've added workarounds for these bugs in the sandbox and I'll publish a patch release soon.

vanruesc commented 3 years ago

The bugs I mentioned above are now fixed in postprocessing@6.21.2.

I've also updated the sandbox: https://codesandbox.io/s/falling-platform-l5obq?file=/src/App.js

ranbuch commented 3 years ago

@vanruesc Disabling the effect by checking if the browser has support for OES_texture_float_linear extension does work.

I don't know why but replacing the encoding to LinearEncoding doesn't work in my case (though your example does work).

Are there anymore texture types that I should update except map, emissiveMap and specularMap?

I did changed the renderer.outputEncoding to LinearEncoding and updated to postprocessing v6.21.2.

I'm using renderer.gammaFactor = 2.2, could it be related?

vanruesc commented 3 years ago

I don't know why but replacing the encoding to LinearEncoding doesn't work in my case

Make sure that you're passing an actual Object3D instance into the function. GLTF wrapper objects cannot be traversed. Also don't start rendering before the encoding is set.

Are there anymore texture types that I should update

No, I think these are the only maps that may use sRGBEncoding, with map being the most important one.

I'm using renderer.gammaFactor = 2.2, could it be related?

No, renderer.gammaFactor is only used if renderer.outputEncoding is set to GammaEncoding.

ranbuch commented 3 years ago

Make sure that you're passing an actual Object3D instance into the function. GLTF wrapper objects cannot be traversed. Also don't start rendering before the encoding is set.

Done, Still doesn't work. I'm passing the same Object3D that I'm passing to the RenderPass and the SelectiveBloomEffect.

Must be something I miss.

Here is a link to my case.

vanruesc commented 3 years ago

I'm afraid I can't help unless you provide a reproduction of the issue. I don't know what you mean when you say "it doesn't work". This is how it looks on my end:

ring

You could try to inspect your model at runtime to check if the diffuse maps use the correct encoding. The encodings should read 3000.

ranbuch commented 3 years ago

The encodings should read 3000.

Yes, I'm getting 3000 for LinearEncoding.

I don't know what you mean when you say "it doesn't work".

It means that I'm still getting the same errors: image

vanruesc commented 3 years ago

Devices that don't support this extension don't support high precision frame buffers. So make sure not to initialize the composer with frameBufferType: HalfFloatType.

ranbuch commented 3 years ago

Got it, works great!

Many thanks @vanruesc