Frontend Development 24 min read

Creating a Glowing Portal Effect in Three.js with Post‑Processing

This article demonstrates how to use Three.js post‑processing techniques, including EffectComposer, RenderPass, BloomPass, and custom shaders, to build an animated 3D scene featuring a glowing portal and Rick and Morty model, covering shader setup, layer management, animation, and UI controls for fine‑tuning.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Creating a Glowing Portal Effect in Three.js with Post‑Processing

This tutorial explains the fundamentals of post‑processing in Three.js and walks through building a creative 3D scene that combines a glowing portal effect with a Rick and Morty model.

Post‑Processing Basics

Post‑processing is performed after the main scene render by using an EffectComposer that chains multiple Pass objects. Each pass can apply effects such as bloom, film grain, or custom shader operations.

Core Setup

First, import the required modules:

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass.js';
import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';

Initialize the renderer with renderer.autoClear = false so that post‑processing can render on top of the scene.

Creating the Portal Geometry

A PlaneBufferGeometry is used as the base mesh, and a ShaderMaterial supplies the portal visual:

const portalGeometry = new THREE.PlaneBufferGeometry(8, 8, 1, 1);
const portalMaterial = new THREE.ShaderMaterial({
  uniforms: { /* uniforms defined below */ },
  fragmentShader: portalFragmentShader,
  vertexShader: portalVertexShader
});
const portal = new THREE.Mesh(portalGeometry, portalMaterial);
scene.add(portal);
portal.layers.set(1); // portal on its own layer

Shader Uniforms

The portal shader receives several textures and color vectors to generate animated noise patterns. Important uniforms include time , perlinnoise , sparknoise , waterturbulence , noiseTex , and five color vectors ( color0 … color5 ), as well as resolution for screen size.

const portalMaterial = new THREE.ShaderMaterial({
  uniforms: {
    time: { type: 'f', value: 0.0 },
    perlinnoise: { type: 't', value: textureLoader.load('/images/perlinnoise.png') },
    sparknoise: { type: 't', value: textureLoader.load('/images/sparknoise.png') },
    waterturbulence: { type: 't', value: textureLoader.load('/images/waterturbulence.png') },
    noiseTex: { type: 't', value: textureLoader.load('/images/noise.png') },
    color0: { value: new THREE.Vector3(...options.color0) },
    /* color1 … color5 omitted for brevity */
    resolution: { value: new THREE.Vector2(sizes.width, sizes.height) }
  },
  fragmentShader: portalFragmentShader,
  vertexShader: portalVertexShader
});

Animating the Portal

The updateShaderMaterial function updates the time uniform each frame, while the render loop scales the portal using a cosine function to create a pulsating effect.

function updateShaderMaterial(deltaTime) {
  portalMaterial.uniforms.time.value = deltaTime / 5000;
  // update color uniforms if needed
}

function tick(deltaTime) {
  updateShaderMaterial(deltaTime);
  renderer.clear();
  camera.layers.set(1);
  bloomComposer.render();
  renderer.clearDepth();
  camera.layers.set(0);
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

Adding Bloom to the Portal

Only the portal should receive bloom, so a separate EffectComposer with a RenderPass and an UnrealBloomPass is created. The bloom composer’s renderToScreen flag is set to true .

const renderScene = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
const bloomComposer = new EffectComposer(renderer);
bloomComposer.renderToScreen = true;
bloomComposer.addPass(renderScene);
bloomComposer.addPass(bloomPass);

Model Loading and Layer Separation

The Rick and Morty GLB model is loaded with DRACOLoader and GLTFLoader . The model is placed on layer 0 so it does not receive the bloom effect.

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('/models/rickAndMorty.glb', (gltf) => {
  gltf.scene.scale.set(0.02, 0.02, 0.02);
  gltf.scene.position.x = -0.5;
  gltf.scene.rotation.y = Math.PI;
  scene.add(gltf.scene);
  gltf.scene.layers.set(0);
});

Interactive UI with dat.GUI

A dat.GUI panel lets users tweak bloom strength, radius, and the six portal colors in real time.

const gui = new dat.GUI();
const bloomFolder = gui.addFolder('bloom');
bloomFolder.add(options, 'bloomStrength', 0.0, 5.0).listen();
bloomFolder.add(options, 'bloomRadius', 0.1, 2.0).listen();
const colorsFolder = gui.addFolder('Colors');
colorsFolder.addColor(options, 'color0');
colorsFolder.addColor(options, 'color1');
/* … color5 omitted */

Final Touches

A star‑field background image and a simple logo are added via CSS to complete the visual theme. The article ends with an appendix that lists many other built‑in Three.js post‑processing passes (RenderPass, ShaderPass, GlitchPass, OutlinePass, etc.) and their typical parameters.

Conclusion

By combining basic post‑processing pipelines, custom shaders, layer‑based rendering, and UI controls, developers can create sophisticated 3D effects such as a glowing portal that integrates seamlessly with animated models, all within a standard Three.js front‑end project.

JavaScriptThree.jsWebGLShaderPost-processing3D animation
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.