如何在Three.js中创建高性能高亮发光二十面体
问题描述
我想要在Three.js中创建高亮发光的二十面体几何体(Icosahedron Geometry)。目前的实现方式是将使用MeshStandardMaterial(透明度0.5)的二十面体加入Group,再通过三层循环添加27个PointLight来实现发光效果——视觉效果符合预期,但运行帧率极低(<1fps)。我希望移除这些灯光,寻求一种高性能的替代方案来实现类似效果(要求类似无限宝石的发光,且更亮、尺寸更大)。
原实现代码
import { AdditiveBlending, FrontSide, MeshStandardMaterial } from "three"; import { ShaderMaterial } from "three"; import { Group } from "three"; import { AmbientLight } from "three"; import { Color } from "three"; import { PointLight } from "three"; import { IcosahedronGeometry } from "three"; import { Mesh, PerspectiveCamera, Scene, WebGLRenderer } from "three"; const container = document.body; const scene = new Scene(); const fov = 35; const aspect = container.clientWidth / container.clientHeight; const near = 0.1; const far = 10000; const camera = new PerspectiveCamera(fov, aspect, near, far); camera.position.set(0, 0, 100); const renderer = new WebGLRenderer(); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); const tp = performance.now(); const stoneConfigs = { red: { geometry: new IcosahedronGeometry(10, 0), material: new MeshStandardMaterial({ color: new Color("red"), opacity: 0.5, }), position: [-24, 0, 0], color1: new Color("red"), update: function () { // this.position.z -= 0.1; this.rotation.y = (performance.now() - tp) * 0.0001; }, }, blue: { geometry: new IcosahedronGeometry(10, 0), material: new MeshStandardMaterial({ color: 0x0000ff, opacity: 0.5, }), position: [24, 0, 0], color1: 0x0000ff, update: function () { // this.position.z -= 0.1; this.rotation.y = (performance.now() - tp) * 0.0001; }, }, yellow: { geometry: new IcosahedronGeometry(10, 0), material: new MeshStandardMaterial({ color: 0xffff00, opacity: 0.5, }), position: [0, 0, 0], color1: 0xffdd00, update: function () { // this.position.z -= 0.1; this.rotation.y = (performance.now() - tp) * 0.1; }, }, }; let stones = []; appendStones(); function appendStones() { for (let stoneName in stoneConfigs) { const stoneConfig = stoneConfigs[stoneName]; const stoneGroup = new Group(); const stone = new Mesh(stoneConfig.geometry, stoneConfig.material); stoneGroup.add(stone); for (let xPos = -1; xPos < 2; xPos++) { for (let yPos = -1; yPos < 2; yPos++) { for (let zPos = -1; zPos < 2; zPos++) { const light = new PointLight(stoneConfig.color1, 6, 10); light.position.set(xPos * -10, yPos * -10, zPos * -10); stoneGroup.add(light); } } } stoneGroup.position.set(...stoneConfig.position); stones.push({ stoneConfig, stone: stoneGroup }); scene.add(stoneGroup); } } requestAnimationFrame(function render() { renderer.render(scene, camera); stones.forEach(({ stoneConfig, stone }) => { stoneConfig.update.bind(stone)(); }); requestAnimationFrame(render); }); addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.render(scene, camera); }); container.append(renderer.domElement);
高性能替代方案
核心思路是用材质和后期处理替代实时光源,实时光源(尤其是批量点光源)会大幅增加Draw Call和光照计算开销,而材质/后期方案是GPU友好的高效实现方式。
方案1:多层Mesh+ additive混合(快速实现基础发光)
给每个二十面体添加两层Mesh,分别负责实体质感和发光光晕:
- 内层:保留原半透明
MeshStandardMaterial的二十面体,维持实体形态 - 外层:尺寸更大的二十面体,使用
MeshBasicMaterial开启AdditiveBlending,模拟柔和光晕
修改appendStones函数示例:
function appendStones() { for (let stoneName in stoneConfigs) { const stoneConfig = stoneConfigs[stoneName]; const stoneGroup = new Group(); // 内层实体 const stone = new Mesh(stoneConfig.geometry, stoneConfig.material); stoneGroup.add(stone); // 外层发光光晕 const glowGeometry = new IcosahedronGeometry(12, 0); // 比内层大2单位 const glowMaterial = new MeshBasicMaterial({ color: stoneConfig.color1, transparent: true, opacity: 0.3, blending: AdditiveBlending, side: FrontSide }); const glowMesh = new Mesh(glowGeometry, glowMaterial); stoneGroup.add(glowMesh); stoneGroup.position.set(...stoneConfig.position); stones.push({ stoneConfig, stone: stoneGroup }); scene.add(stoneGroup); } }
方案2:自定义ShaderMaterial(精细控制发光效果)
用自定义Shader实现基于顶点距离的渐变发光,能模拟无限宝石那种"能量汇聚"的动态效果,比多层Mesh更灵活。
示例ShaderMaterial:
const glowShader = { uniforms: { color: { value: new Color("red") }, innerRadius: { value: 10 }, outerRadius: { value: 15 }, intensity: { value: 2 } }, vertexShader: ` varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform vec3 color; uniform float innerRadius; uniform float outerRadius; uniform float intensity; varying vec3 vPosition; void main() { float dist = length(vPosition); // 内半径内全亮,外半径外透明,中间平滑过渡 float alpha = smoothstep(outerRadius, innerRadius, dist) * intensity; gl_FragColor = vec4(color, alpha); } `, transparent: true, blending: AdditiveBlending, side: FrontSide }; // 使用时替换外层光晕材质 const glowMaterial = new ShaderMaterial(glowShader);
方案3:UnrealBloomPass后期处理(真实光晕扩散)
如果需要更自然的光晕扩散效果,推荐使用Three.js官方的UnrealBloomPass后期处理,只需标记自发光物体,就能实现全局发光效果,性能远优于实时光源。
实现步骤:
- 引入后期处理相关模块
- 创建效果合成器,添加渲染通道和Bloom通道
- 给实体材质添加自发光属性控制发光强度
代码示例:
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'; // 初始化后期合成器 const composer = new EffectComposer(renderer); const renderPass = new RenderPass(scene, camera); composer.addPass(renderPass); // 配置Bloom参数:强度、半径、阈值 const bloomPass = new UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85 ); composer.addPass(bloomPass); // 修改实体材质为带自发光的版本 const stoneMaterial = new MeshStandardMaterial({ color: new Color("red"), opacity: 0.5, transparent: true, emissive: new Color("red"), emissiveIntensity: 3 // 数值越高,发光越明显 }); // 渲染循环替换为合成器渲染 requestAnimationFrame(function render() { composer.render(); stones.forEach(({ stoneConfig, stone }) => { stoneConfig.update.bind(stone)(); }); requestAnimationFrame(render); });
额外性能优化点
- 彻底移除所有
PointLight,避免光照计算开销 - 保持二十面体的细分参数为0(
IcosahedronGeometry(10, 0)),减少顶点数量 - 若对画质要求不高,可关闭
WebGLRenderer的antialias,或降低pixelRatio - 静态几何体默认使用
BufferGeometry(Three.js r155+已默认支持),提升渲染效率
内容的提问来源于stack exchange,提问作者Hit and Run
相关产品推荐
相关产品推荐

