Hi community!
Picture:
Example: JSFiddle
It’s all started with this topic and its reference picture: Three.js light and shadow
I had no idea about how to make that fog/nebula in the central part of the picture, but I had a thought about how to create those zones of variable intensity of light on these kind of steps, usign Selective Bloom technique. And here is my attempt.
There are two geometries in the scene. Merged spheres and merged capsules (see CapsuleGeom()
function).
And the main uniform globalBloom
, that I added to materials by patching them with .onBeforeCompile
That uniform indicates what state of the scene you will render: 0 - render all the scene as is, 1 - leave the parts, that have to be glowing, with their colors, and the other parts/objects must be black (including background).
uniforms.globalBloom.value = 1; // we'll render the scene with colours for glowing
renderer.setClearColor(0x000000); // background is black
bloomComposer.render(); // render for glowing
uniforms.globalBloom.value = 0; // we'll render the scene as is
renderer.setClearColor(0x220511); // any colour you like for the background :)
finalComposer.render(); // render as is + additive render of the scene with glowing parts
The patch for materials of non-bloomed objects is very simple:
onBeforeCompile: shader => {
shader.uniforms.globalBloom = uniforms.globalBloom;
shader.fragmentShader = `
uniform float globalBloom;
${shader.fragmentShader}
`.replace(
`#include <dithering_fragment>`,
`#include <dithering_fragment>
if (globalBloom > 0.5) {
gl_FragColor.rgb = vec3(0);
}
`
);
We add it to the very end of fragment shader here, so it’s guaranteed that we’ll get the pure black color when we render the scene for bloom.
For bloomed objects we could leave materials as is, but in this case we would get them just evenly glowing, which is not our goal.
So, we do this:
onBeforeCompile: shader => {
shader.uniforms.globalBloom = uniforms.globalBloom;
shader.uniforms.time = uniforms.time;
shader.vertexShader = `
varying vec3 vPos;
${shader.vertexShader}
`.replace(
`#include <fog_vertex>`,
`#include <fog_vertex>
vPos = position; // pass position in local coords
`
);
//console.log(shader.vertexShader);
shader.fragmentShader = `
uniform float globalBloom;
uniform float time;
varying vec3 vPos;
${simplex_noise}
${shader.fragmentShader}
`.replace(
`#include <color_fragment>`,
`#include <color_fragment>
vec2 lUv = vPos.xz / 10.; // compute uv from the local position
lUv *= 3.;
float fade = clamp(simplex_noise(vec3(lUv, time)) * 0.5 + 0.5, 0., 1.);
fade = pow(smoothstep(0., 0.875, fade), 12.); // "shaping" the intensity [0..1]
if (globalBloom > 0.5){
diffuseColor.rgb *= fade * 0.95 + 0.05; // for bloomComposer
} else {
diffuseColor.rgb += fade; // for finalComposer
}
`
);
The main idea is to have different parts of a geometry with different intensity of color, when we render the scene for glowing. In the above block of code, the value of fade
is the thing that controls the intensity (in my case it’s based on the simplex noise, but it may be based on a texture or something else).
PS Criticism and advice are welcomed
PPS The original function for capsules is here https://codepen.io/prisoner849/pen/qBaNKNM