So basically Im running into an issue where Im getting an inferior result using
Is this the expected outcome with some textures or is there some way to improve the
EffectComposer's
quality?
PS. The texture used is a
Scene.background
CubeTexture with
.encoding = THREE.sRGBEncoding
PS. You will have to apply a tiny change from this PR in order to be able to create a
CubeTexture
out of 6 compressed images.
github.com/mrdoob/three.js
DolphinIQ:
Forgive my ignorance, but I feel like there is no way this behavior is intentional. The colorspace conversions are giving me a headache
I’ve run into this problem before when dealing with post-processing, and I confess, even after several days of studying color spaces and the tone mapping code in three.js, the only way I’ve been able to solve it is by guesswork.
I do think that there are some bugs in the way tone mapping is handled in the post-processing code, or if not, then the workflow needs to be improved and have much better documentation because it’s very confusing at the moment.
In the end, I solved this by disabling all color correction in three.js and adding my own final pass which does tone mapping/brightness/contrast.
EDIT:
looks like I should also add gamma correction (or rather, sRGB transform) into this, after tone mapping and before brightness/contrast.
* Combined post-processing pass
* ACESFilmic Tone mapping
* Brightness
* Contrast
const CombinedShader = {
uniforms: {
tDiffuse: { value: null },
toneMappingExposure: { value: 1.0 },
brightness: { value: 0 },
contrast: { value: 0 },
vertexShader:
/* glsl */`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
fragmentShader:
/* glsl */`
#define saturate(a) clamp( a, 0.0, 1.0 )
uniform sampler2D tDiffuse;
uniform float toneMappingExposure;
uniform float brightness;
uniform float contrast;
varying vec2 vUv;
vec3 ACESFilmicToneMapping( vec3 color ) {
color *= toneMappingExposure;
return saturate( ( color * ( 2.51 * color + 0.03 ) ) / ( color * ( 2.43 * color + 0.59 ) + 0.14 ) );
void main() {
gl_FragColor = texture2D( tDiffuse, vUv );
gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb );
gl_FragColor.rgb += brightness;
if (contrast > 0.0) {
gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) / (1.0 - contrast) + 0.5;
} else {
gl_FragColor.rgb = (gl_FragColor.rgb - 0.5) * (1.0 + contrast) + 0.5;
export default CombinedShader;
I intend to add color correction to this as well, I’ve held off on that as I’m not sure whether it does before or after tonemapping.
Some more considerations for post processing:
some passes need to be in linear space (before tone-mapping) and some need to be done after. Some can be done before or after but you’ll get different results (e.g. vignette).
Outline effect looks like it has tone mapping built in (probably incorrect?)
Before tone mappings: bloom, lensflare etc
Then, in order: 1. tonemapping 2. gamma correction
After tone mapping: brightness, contrast, FXAA, SMAA, color correction
Order not important (or, choose what looks best to you): DOF, vignette, film grain etc.
I’m not sure where where exposure comes into this - either before all passes, or just before tone mapping?
Thoughts:
We should remove all tone mapping except for ACESFilmic/NoToneMapping. ACES Filmic is the industry standard and as far as I know the only kind of tone mapping Unreal and Unity support. I have so far not seen any arguments for having other kinds of tone mapping.
It would be great if a final tone mapping pass was automatic when using the EffectComposer. Even better, combine color correction/tonemapping/brightness/contrast as the final pass (again, that’s what Unreal does I think)
(
see below
).
have post processing built into the core so it’s a first class three.js citizen - see Takahirox’s PR here:
github.com/mrdoob/three.js
Some more considerations for post processing:
some passes need to be in linear space (before tone-mapping) and some need to be done after. Some can be done before or after but you’ll get different results (e.g. vignette).
Outline effect looks like it has tone mapping built in (probably incorrect?)
AdaptiveToneMappingPass
and
ToneMapShader
have filmic tonemapping built in. We should change that to ACESFilmic.
Before tone mappings: bloom, lensflare etc
Then, in order: 1. tonemapping 2. gamma correction
After tone mapping: brightness, contrast, FXAA, SMAA, color correction
Order not important (or, choose what looks best to you): DOF, vignette, film grain etc.
I’m not sure where where exposure comes into this - either before all passes, or just before tone mapping?
Wow, thanks
@looeee
! This is some great, useful info I haven’t seen anywhere else
looeee:
It would be great if a final tone mapping pass was automatic when using the EffectComposer. Even better, combine color correction/tonemapping/brightness/contrast as the final pass (again, that’s what Unreal does I think).
That would be the dream scenario
but removing built in color corrections inside some passes would be a great start. Its simple to just add
toneMapping
,
gamma
etc at the end of the composer chain, but what complicates things is selfish passes with corrections built in, that break the chain.
Would Takahirox’s PR fix that issue?
Also Im not sure I got an answer for this, but why would
renderPass
and
renderPass + copyPass
give different colorspace renders? (or different results at all?) This actually seems to happen with 1 or more passes added after
renderPass
, regardless of the pass type (but a basic
copyPass
shows it clearly).
And why does it suddenly stop respecting the
renderer.outputEncoding
?
That happens because
WebGLRenderer.outputEncoding
is only respected when rendering to screen (or default framebuffer). When you rendering to a render target, the encoding from it’s texture property is evaluated. The internal render targets of
EffectComposer
use the default setting
LinearEncoding
(which makes sense since a render pass should always be in linear color space for further processing).
Mugen87:
In any event, I don’t think I would add tonemapping or gamma correction automatically at the end of the pass chain in
EffectComposer
Hmm, yeah on further consideration this suggestion is contradictory with the need to do some passes before tone mapping and some after.
However, as far as I’m aware, you will always (at least on LDR screens) need to do tone mapping/gamma correction at some point in the post processing chain so it would make sense to have a combined pass that does these and clearly demonstrate in docs or examples how and where to use it.
Well then, what would be the reason for the ugly effect on the second image? Because color-wise it seems like it is converted well, it’s just that those pixely (almost 8-bit looking?) artifacts are showing, almost as if precision was lost?
Im not very knowledgeable in the internals of
THREE.WebGLRenderer
. Is there a difference between it’s gamma correction and post processing’s shader pass? If not, then I must be missing some additional conversion pass in my composer chain.
So in other words: how can I achieve the first image output using the
EffectComposer
? Im particularly looking for doing selective bloom, but I might be adding other effects later on as well.
@DolphinIQ
unfortunately I can’t get your example to run.
THREE.WebGLRenderer: WEBGL_compressed_texture_astc extension not supported.
THREE.WebGLRenderer: WEBGL_compressed_texture_etc1 extension not supported.
THREE.WebGLRenderer: WEBGL_compressed_texture_pvrtc extension not supported.
THREE.WebGLRenderer: WEBKIT_WEBGL_compressed_texture_pvrtc extension not supported.
Loading Complete!
CompressedTexture {uuid: "8E71EAFF-6413-44CB-980E-645891D3A45B", name: "", image: {…}, mipmaps: Array(1), mapping: 300, …}
three.module.js:20960 THREE.WebGLState: TypeError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': No function was found that matched the signature provided.
at Object.texImage2D (three.module.js:20956)
at setTextureCube (three.module.js:21576)
at WebGLTextures.safeSetTextureCube (three.module.js:22344)
at SingleUniform.setValueT6 [as setValue] (three.module.js:17002)
at Function.WebGLUniforms.upload (three.module.js:17421)
at setProgram (three.module.js:25213)
at WebGLRenderer.renderBufferDirect (three.module.js:23935)
at renderObject (three.module.js:24687)
at renderObjects (three.module.js:24657)
at WebGLRenderer.render (three.module.js:24436)
It seems the issue can be fixed if the internal render targets of EffectComposer
are created differently. If you enhance the following section:
github.com
var parameters = {
minFilter: LinearFilter,
magFilter: LinearFilter,
format: RGBAFormat,
stencilBuffer: false
by this type: FloatType
, the result is as expected. The default type
is UnsignedByteType
by the way.
I’m not yet sure why this removes the artifacts but this might be related to a precision issue.
So it was related to precision after all
It reminded me a little of those old space games arts
Thanks a lot guys! This was a really instructive thread
Managed to get selective bloom working for my game!
image 1010×658 132 KB
Intrstingly enough, the precision still wasn’t as good as when using the renderer’s default encoding. Perhaps that one uses 64-bit floats? Anyway, I lowered the renderer.gammaFactor
to 1.7 to get the best of both worlds and all looks well now
What is the status of this issue? Has a bug been reported on GitHub for a proper fix?
We’re having a similar issue since upgrading to v112 where gamma correction is doesn’t seem to be applied when using Effect Composer but otherwise is.
Adding type: FloatType
in EffectComposer
(and optionally to all the various passes as well) or even replacing the default type in Texture
didn’t fix it.
neptilo:
Adding type: FloatType
in EffectComposer
(and optionally to all the various passes as well) or even replacing the default type in Texture
didn’t fix it.
Can you please demonstrate this with a live example or some code? There might be an issue in your app elsewhere.
I made a jsfiddle to exhibit the problem: https://jsfiddle.net/neptilo/femq6j93/4/
There, I copied the EffectComposer
and added type: FloatType
.
I added gamma-related parameters to the renderer.
The passes added to the effect composer are simply a render pass and a CopyShader
pass.
When useComposer
is false, gamma correction is applied. When true, it isn’t.