Three.js 赛博空间高拟真黑洞引力透镜(Gravitational Lensing):基于空间弯曲着色器
在《星际穿越》(Interstellar)等顶级科幻作品中,巨型黑洞(Black Hole)周围的时空弯曲与引力透镜效应(Gravitational Lensing)是最具宇宙宏大感与物理震撼力的视觉奇观:
- 黑洞中央是一片吞噬一切光线的绝对事件视界(Event Horizon / 纯黑核心);
- 紧随其后的是一道以数倍相对论速度剧烈旋转的高能发光吸积盘(Accretion Disk);
- 宇宙深处的背景星光在穿过黑洞强引力场时,被弯曲折射为壮丽的爱因斯坦环(Einstein Ring)与多重扭曲光弧。
传统的 3D 模型无法表现“背景光线被弯曲”的物理特性。
通过编写自定义的Three.js 引力透镜后处理着色器(Gravitational Lensing Post-processing Shader / GLSL),我们可以在浏览器端利用广义相对论光线偏折公式,以纯 60 FPS 满帧实时渲染出令人屏息的赛博宇宙黑洞奇观!
一、引力透镜光线弯曲与爱因斯坦环物理模型拓扑
graph LR BackgroundStars[背景赛博星空与星系纹理 (Background Texture)] --> PostShader[GPU 引力透镜片元着色器 (Screen-space Lens Shader)] subgraph 广义相对论光线偏折计算 (Relativistic Ray Deflection) PostShader --> CenterDist[计算当前屏幕像素 UV 到黑洞屏幕中心 (x0, y0) 的距离 r] CenterDist --> Schwarzschild[史瓦西半径判定: 若 r < r_event -> 输出绝对纯黑 (事件视界)] CenterDist --> LensFormula[偏折方程: UV_offset = normalize(UV - Center) * (G * M / r^2)] end LensFormula --> DistortionSample[利用偏折后的 UV' 重新采样背景星空纹理] DistortionSample --> AccretionDisk[叠加吸积盘发光贴图 + 多普勒红移调色] AccretionDisk --> Output[输出逼真的爱因斯坦环与黑洞引力透镜画面]二、引力透镜后处理着色器(GLSL 实现)
// shaders/cyberBlackHoleLensingShader.ts import * as THREE from 'three'; export const CyberBlackHoleLensingShader = { uniforms: { tDiffuse: { value: null }, // 场景与背景星空纹理 uBlackHoleScreenPos: { value: new THREE.Vector2(0.5, 0.5) }, // 黑洞屏幕归一化坐标 uEventHorizonRadius: { value: 0.08 }, // 事件视界纯黑半径 uLensingStrength: { value: 0.045 }, // 引力透镜弯曲强度 uTime: { value: 0 }, uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform sampler2D tDiffuse; uniform vec2 uBlackHoleScreenPos; uniform float uEventHorizonRadius; uniform float uLensingStrength; uniform float uTime; uniform vec2 uResolution; varying vec2 vUv; void main() { // 修正屏幕宽高比 (Aspect Ratio Correction) float aspect = uResolution.x / uResolution.y; vec2 aspectCorrectedUv = vec2(vUv.x * aspect, vUv.y); vec2 aspectCorrectedCenter = vec2(uBlackHoleScreenPos.x * aspect, uBlackHoleScreenPos.y); // 1. 计算当前像素到黑洞中心的二维欧氏距离 vec2 delta = aspectCorrectedUv - aspectCorrectedCenter; float dist = length(delta); // 2. 核心判定 A: 若处于事件视界 (Event Horizon) 内部,光线无法逃逸 -> 绝对纯黑 if (dist < uEventHorizonRadius) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); return; } // 3. 核心判定 B: 广义相对论引力透镜光线偏折计算 (爱因斯坦偏角) // 距离中心越近,偏折角越剧烈 float normalizedDist = dist - uEventHorizonRadius; float deflection = uLensingStrength / (normalizedDist + 0.01); // 计算弯曲后的新采样坐标 vec2 distortedUv = vUv - normalize(delta) * deflection; // 4. 采样偏折后的背景星空 vec4 sceneColor = texture2D(tDiffuse, distortedUv); // 5. 在事件视界边缘激发出高能光子环 (Photon Sphere Glow) float photonRing = 0.0; if (dist >= uEventHorizonRadius && dist <= uEventHorizonRadius * 1.35) { photonRing = pow(1.0 - (dist - uEventHorizonRadius) / (uEventHorizonRadius * 0.35), 3.0); } vec3 finalColor = sceneColor.rgb; // 叠加赛博青/亮金色光子环 finalColor += vec3(0.0, 0.95, 1.0) * photonRing * 1.8; gl_FragColor = vec4(finalColor, sceneColor.a); } `, };三、场景装配与吸积盘动态旋转装配
// scene/cyberBlackHoleStage.ts import * as THREE from 'three'; import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'; import { CyberBlackHoleLensingShader } from '../shaders/cyberBlackHoleLensingShader'; export function setupBlackHoleScene(container: HTMLElement) { const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 2, 10); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); container.appendChild(renderer.domElement); // 1. 创建发光旋转吸积盘网格 (Accretion Disk Mesh) const diskGeo = new THREE.RingGeometry(1.2, 3.8, 64); const diskMat = new THREE.MeshBasicMaterial({ color: 0x00f3ff, side: THREE.DoubleSide, transparent: true, opacity: 0.85, blending: THREE.AdditiveBlending, }); const accretionDisk = new THREE.Mesh(diskGeo, diskMat); accretionDisk.rotation.x = Math.PI * 0.4; scene.add(accretionDisk); // 2. 组装引力透镜后处理合成器 const composer = new EffectComposer(renderer); composer.addPass(new RenderPass(scene, camera)); const lensingPass = new ShaderPass(CyberBlackHoleLensingShader); composer.addPass(lensingPass); return { composer, update: (delta: number) => { // 旋转吸积盘 accretionDisk.rotation.z += delta * 0.6; lensingPass.uniforms.uTime.value += delta; composer.render(); }, }; }四、黑洞引力透镜三大极客优化法则
- 屏幕坐标系自适应映射(Screen-space Center Projection):若黑洞在三维世界中移动,将黑洞的世界坐标通过
worldPosition.project(camera)动态计算为屏幕归一化坐标uBlackHoleScreenPos,实现视角自由旋转时引力透镜的完美跟随; - 多普勒红移蓝移模拟(Doppler Beaming):在吸积盘一侧增强亮度偏向亮蓝(朝向观察者高速运动),另一侧降低亮度偏向暗红(远离观察者),还原真实的相对论物理效应;
- 单 Pass 极致高能性能:全套引力偏折方程仅需两次向量运算与单次纹理重采样,在手机端保持恒定60 FPS满帧渲染。
用严谨的广义相对论方程在三维 Web 空间雕刻时空弯曲的宇宙奇迹,赋予前端界面令人惊叹的硬核极客美学。