<canvas class="webgl"></canvas>

<script type="x-shader/x-vertex" id="vertexshaderCandle">
    void main() {
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
</script>
<script type="x-shader/x-fragment" id="fragmentshaderCandle">
    uniform float time; 
    uniform float colorSpeed;
    uniform float delay; 
    uniform vec3 baseColor;
    varying vec2 vUv;
    
    void main() {
        float animatedTime = time - delay;
        animatedTime = mod(animatedTime, colorSpeed);
        float mixFactor = animatedTime / colorSpeed;
        vec3 finalColor = mix(baseColor, vec3(0.702,0.71,0.459), mixFactor);
        gl_FragColor = vec4(finalColor, 1.0);
    }
</script>
<!-- ========================== -->
<script type="x-shader/x-vertex" id="vertexshaderBubble">
    varying vec2 vUv;
    uniform float time;

    void main() {
        vUv = uv;
        vec3 newPosition = position;
        newPosition.x += sin(time * 2.0) * 0.0075;
        newPosition.y += cos(time * 2.0) * 0.0075;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    }
</script>
<script type="x-shader/x-fragment" id="fragmentshaderBubble">
    varying vec2 vUv;

    void main() {
        gl_FragColor = vec4(0.765,0.965,0.733, 1.0);
    }
</script>
<!-- ========================== -->  
<script type="x-shader/x-vertex" id="vertexshaderDoor">
    uniform float time;
    varying vec2 vUv;
    
    void main() {
        vUv = uv;
        vec2 center = vec2(0.0, 0.0);
        float distance = length(uv - center);
        vec3 newPosition = position + vec3(0.0, 0.0, 0.0);

        gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    }
</script>

<script type="x-shader/x-fragment" id="fragmentshaderDoor">
    uniform float time;
    varying vec2 vUv;
    
    void main() {
        vec3 waveColor = vec3(0.702,0.71,0.459);
    
        vec2 center = vec2(0.5, 0.5);
        float distance = length(vUv - center);
        float waveIntensity = abs(sin(distance * 100.0 + time));
    
        vec3 finalColor = mix(vec3(0.545,0.553,0.306), waveColor, waveIntensity);
    
        gl_FragColor = vec4(finalColor, 1.0);
    }
</script>


// Original code: https://codepen.io/aderaaij/details/BapYONL
<script type="x-shader/x-vertex" id="vertexshader">
    uniform float uPixelRatio;
    uniform float uSize;
    uniform float uTime;
    attribute float aScale;
    void main() {
        vec4 modelPosition = modelMatrix * vec4(position, 1.0);
        modelPosition.y += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2;
        modelPosition.z += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2;
        modelPosition.x += cos(uTime + modelPosition.x * 100.0) * aScale * 0.2;
        vec4 viewPosition = viewMatrix * modelPosition;
        vec4 projectionPostion = projectionMatrix * viewPosition;
        
        gl_Position = projectionPostion;
        gl_PointSize = uSize * aScale * uPixelRatio;
        gl_PointSize += (1.0 / - viewPosition.z);
    }
</script>

<script type="x-shader/x-fragment" id="fragmentshader">
    void main() {
        float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
        float strength = 0.05 / distanceToCenter - 0.1;
        gl_FragColor = vec4(1.0, 1.0, 1.0, strength);
    }
</script>



<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/postprocessing/EffectComposer.js'></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/postprocessing/RenderPass.js'></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/postprocessing/ShaderPass.js'></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/shaders/CopyShader.js'></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/shaders/LuminosityHighPassShader.js'></script>
<script src='https://unpkg.com/three@0.126.0/examples/js/postprocessing/UnrealBloomPass.js'></script>

<script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://unpkg.com/three@0.126.0/examples/js/controls/OrbitControls.js"></script>
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
    outline: none;
}

body{
    overflow: hidden;
    background-color: #0d0d0d;
}

.webgl{
    position: fixed;
    top: 0;
    left: 0;
}
View Compiled

const canvas = document.querySelector('.webgl')
const scene = new THREE.Scene()
const textureLoader = new THREE.TextureLoader()

let composer

const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

const params = {
    exposure: 1,
    bloomStrength: .4,
    bloomThreshold: .1,
    bloomRadius: 1
}

// Base camera
const camera = new THREE.PerspectiveCamera(10, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 18
camera.position.y = 8
camera.position.z = 20
scene.add(camera)

// Controls
const controls = new THREE.OrbitControls(camera, canvas)
controls.enableDamping = true
controls.enableZoom = true
controls.enablePan = true
controls.minDistance = 15
controls.maxDistance = 30
controls.minPolarAngle = Math.PI / 5
controls.maxPolarAngle = Math.PI / 2

// Renderer
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true,
    alpha: true,
})

renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.outputEncoding = THREE.sRGBEncoding

// Materials
const candleMaterial = new THREE.ShaderMaterial({
    uniforms: {
        time: { value: 1.0 }, 
        delay: { value: 1.0 }, 
        colorSpeed: { value: 5.0 }, 
        baseColor: { value: new THREE.Color(0xFFD7BC) }
    },
    vertexShader: document.getElementById( 'vertexshaderCandle' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshaderCandle' ).textContent,
})

const bubbleMaterial = new THREE.ShaderMaterial({
    uniforms: {
        time: { value: 0.0 },
    },
    vertexShader: document.getElementById( 'vertexshaderBubble' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshaderBubble' ).textContent,
})

const doorMaterial = new THREE.ShaderMaterial({
    uniforms: {
        time: { value: 10.0 },
        resolution: { value: new THREE.Vector2() },
      },
    vertexShader: document.getElementById( 'vertexshaderDoor' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshaderDoor' ).textContent,
})


//  Original code: https://codepen.io/aderaaij/details/BapYONL
const firefliesGeometry = new THREE.BufferGeometry()
const firefliesCount = 30
const positionArray = new Float32Array(firefliesCount * 3)
const scaleArray = new Float32Array(firefliesCount)

for (let i = 0; i < firefliesCount; i++) {
    new THREE.Vector3(
        (Math.random() - 0.5) * 2,
        (Math.random() * 2) * 1,
        (Math.random() - 0.5) * 4
    ).toArray(positionArray, i * 3)

    scaleArray[i] = Math.random()
    scaleArray[i] = Math.random()
}

firefliesGeometry.setAttribute("position", new THREE.BufferAttribute(positionArray, 3))
firefliesGeometry.setAttribute("aScale", new THREE.BufferAttribute(scaleArray, 1))

const firefliesMaterial = new THREE.ShaderMaterial({
    uniforms: {
        uTime: { value: 0 },
        uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
        uSize: { value: 15 }
    },
    vertexShader: document.getElementById( 'vertexshader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
    transparent: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false
})
const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial)
scene.add(fireflies)


const bakedTexture = textureLoader.load('https://rawcdn.githack.com/ricardoolivaalonso/ThreeJS-Room08/7937528ab05555575b588691c884195b73459f89/dist/baked.jpg')
bakedTexture.flipY = false
bakedTexture.encoding = THREE.sRGBEncoding

const bakedMaterial = new THREE.MeshBasicMaterial({
    map: bakedTexture,
    side: THREE.DoubleSide,
})

const candle = [ 'Sphere001', 'Sphere002', 'Sphere003']
const bubbles = ['Vela_Flama', 'Vela_Flama002', 'Vela_Flama003', 'Vela_Flama004', 'Vela_Flama005', 'Vela_Flama006', 'Vela_Flama007', 'Vela_Flama008', 'Vela_Flama009', 'Vela_Flama010', 'Vela_Flama018', 'Vela_Flama019', 'Vela_Flama020', 'Vela_Flama021', 'Vela_Flama022', 'Vela_Flama023', 'Vela_Flama024']


//Loader
const loader = new THREE.GLTFLoader()

loader.load( 'https://rawcdn.githack.com/ricardoolivaalonso/ThreeJS-Room08/d54201d862fc2225142bdfec341a93a726a11a74/dist/model.glb',
(gltf) => {
    const model = gltf.scene
        model.traverse( child => {
            child.material = bakedMaterial
          if (candle.includes(child.name)) child.material = candleMaterial 
            if (bubbles.includes(child.name)) child.material = bubbleMaterial
            if (child.name === 'Foto') child.material = doorMaterial
        })
        scene.add(model)
    },
    ( xhr ) => console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' )
)

const renderScene = new THREE.RenderPass( scene, camera )

const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( sizes.width, sizes.height ), 1, 1, 1 )

bloomPass.threshold = params.bloomThreshold
bloomPass.strength = params.bloomStrength
bloomPass.radius = params.bloomRadius

composer = new THREE.EffectComposer( renderer )
composer.addPass( renderScene )
composer.addPass( bloomPass )

window.addEventListener('resize', () =>
{
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  composer.setSize(sizes.width, sizes.height)
    composer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

const clock = new THREE.Clock()

// Animation
const tick = () => {
   const elapsedTime = clock.getElapsedTime()
    candleMaterial.uniforms.time.value += 0.075
    doorMaterial.uniforms.time.value += 0.1
    bubbleMaterial.uniforms.time.value += 0.035
    firefliesMaterial.uniforms.uTime.value = elapsedTime

    controls.update()
    renderer.render(scene, camera)
    //composer.render()
    window.requestAnimationFrame(tick)
}

tick()

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.