This is a common thing in sketchfab and i believe i have seen it in threejs examples as well but can’t remember where. Is there something in threejs that would allow me to do this with a regular environment map? I’m referring to the background only, i would still like objects to have reflections.
I don’t think it “just works” yet but there are a couple PRs that would enable it (#18241 and #20463). Alternatively you could generate a blurry background texture separately from the environment map and use them together. I believe you can do that on the fly with PREMGenerator, though you may have to use fromScene
on a scene with the background texture to apply your own blur.
oh wow, thanks! can’t wait for those to come in! i tried fromscene, but it complains about the blurradius being too high due to max_samples set to 20, which seems to produce a very tame blur. i tried to fork it with 1000 samples to get close to what i see on sketchfab. it’s almost there, but i see a weird “pinch”, you see it when you move the camera around, is that normal? https://codesandbox.io/s/color-grading-forked-wtoe7?file=/src/App.js
i tried to fork it with 1000 samples to get close to what i see on sketchfab. it’s almost there, but i see a weird “pinch”, you see it when you move the camera around, is that normal?
I don’t use PMREMGenerator a lot so I’m less familiar with the details of it’s implementation. It’s possible it’s a bug but at the same time it sounds like it may not have been designed to handle so many samples.
That sample either crashes CodeSandbox, or the preview crashes with Chrome sad face, on my macbook. I’m guessing it has something to do with the 1000 samples.
What’s the best way to blur a scene background these days?
I gave it a try with the blurTexture
function from
and you’re right, higher amounts of passes don’t really do much. I updated the blurTexture
to do a specified number of passes:
/** Adapted from https://discourse.threejs.org/t/39433 */
function blurTexture(renderer: WebGLRenderer, texture: Texture, blurAmount = 1) {
console.log('blur amount', blurAmount)
const width = texture.image.width
const height = texture.image.height
const cameraRTT = new OrthographicCamera(-1, 1, 1, -1, 0, 1)
const sceneRTT = new ThreeScene()
// render targets
const renderTarget1 = new WebGLRenderTarget(width, height)
const renderTarget2 = new WebGLRenderTarget(width, height)
// shader materials
const hBlurMaterial = new ShaderMaterial({
vertexShader: HorizontalBlurShader.vertexShader,
fragmentShader: HorizontalBlurShader.fragmentShader,
uniforms: UniformsUtils.clone(HorizontalBlurShader.uniforms),
})
hBlurMaterial.uniforms.tDiffuse.value = texture
hBlurMaterial.uniforms.h.value = 1 / width
const vBlurMaterial = new ShaderMaterial({
vertexShader: VerticalBlurShader.vertexShader,
fragmentShader: VerticalBlurShader.fragmentShader,
uniforms: UniformsUtils.clone(VerticalBlurShader.uniforms),
})
vBlurMaterial.uniforms.tDiffuse.value = renderTarget1.texture
vBlurMaterial.uniforms.v.value = 1 / height
// fullscreen quad
const planeGeometry = new PlaneGeometry(2, 2)
const fullScreenQuad = new Mesh(planeGeometry, hBlurMaterial)
sceneRTT.add(fullScreenQuad)
let lastTexture = texture
// passes
while (blurAmount--) {
// horizontal pass
fullScreenQuad.material = hBlurMaterial
hBlurMaterial.uniforms.tDiffuse.value = lastTexture
renderer.setRenderTarget(renderTarget1)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget1.texture
// vertical pass
fullScreenQuad.material = vBlurMaterial
vBlurMaterial.uniforms.tDiffuse.value = lastTexture
renderer.setRenderTarget(renderTarget2)
renderer.render(sceneRTT, cameraRTT)
renderer.setRenderTarget(null)
lastTexture = renderTarget2.texture
}
//
return renderTarget2.texture
}
I think there’s a better way to do this…
That’s nice for values closer to 1.0, but it has unwanted pixelation for values like 0.3.
The TriangleBlurShader is the nicest so far:
but now the problem is there’s a seam on one side of the background:
Note how it is not too blurry, but also not pixelated. Here’s the same scene with same background, but using backgroundBlurriness
0.1:
It is not as smooth.
Now I need to study backgroundBlurriness
to see how to make it seamless.
Three.js WebGLRenderer
’s WebGLBackground
is using the backgroundCube
shader, and it uses a mipmap-based “blur” effect from here:
This is probably cheap, but the result is not very good, and it is not customizeable.
I’m not yet sure how to avoid the seam problem with the custom blur.
I was able to do this by applying the roughness
logic to the output. Rather than sampling with reflection, i sample where the cubemap would normally be sampled. roughness of 0 makes it a clear cubemap, 1 makes it very blurry.
#define PI 3.14159265359
highp float rand( const in vec2 uv ) {
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
return fract(sin(sn) * c);
}
vec3 dithering( vec3 color ) {
//Calculate grid position
float grid_position = rand( gl_FragCoord.xy );
//Shift the individual colors differently, thus making it even harder to see the dithering pattern
vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );
//modify shift acording to grid position.
dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );
//shift the color by dither_shift
return color + dither_shift_RGB;
}
uniform float envMapIntensity;
uniform float roughness;
#include <cube_uv_reflection_fragment>
varying vec3 vWorldView;
uniform sampler2D envMap;
void main(){
vec3 world = normalize(vWorldView);
world.y *=-1.;
vec3 queryReflectVec = world;
vec4 envMapColor = textureCubeUV( envMap, queryReflectVec, roughness ) * envMapIntensity;
gl_FragColor = vec4(envMapColor.xyz,1.);
gl_FragColor.xyz = pow(gl_FragColor.xyz, vec3(.6));
gl_FragColor.rgb = dithering( gl_FragColor.rgb );
}
@dubois got a working sample?
@dubois So textureCubeUV
is specifically for sampling across the seams, if I understand correctly? And then you’re saying you apply dither so that the mipmap-based roughness is not as pixelated?
If I understand what you’re suggesting, I should replace this line in TriangleBlurShader
:
with the textureCubeUV
and use the shader with a cube mesh?