有沒有想過,游戲里的鏡子、傳送門、或者屏幕上播放的實時3D動畫是怎么實現的?
答案就是一項黑科技——渲染目標(Render Targets)。它允許我們不直接渲染到屏幕,而是“偷偷地”渲染到一張幕后的貼圖上,然后再把這張包含了另一個世界畫面的貼圖,應用到我們主場景的任意物體上。
🖼? 核心思想:先畫到“小畫板”上
這個概念聽起來很繞,我們用一個簡單的比喻來理解:
想象一下,你不是直接在房間的墻壁上作畫,而是:
- 先拿出一塊小畫板(這就是?
WebGLRenderTarget
)。 - 在這塊小畫板上,精心畫了一幅動態的畫(比如一個獨立旋轉的3D物體)。
- 最后,你把這塊已經畫好了動態內容的小畫板,像一張照片一樣,掛到墻上(應用為主場景里某個物體的貼圖)。
于是,你就實現了“畫中畫”的神奇效果!
? 無窮的可能性
掌握了這項技術,你就能解鎖無數種高級特效:
- 實時監控畫面?📹: 在一面墻上顯示另一個房間的實時3D影像。
- 神奇的傳送門?🌀: 門的那一頭是另一個動態的世界。
- 車內后視鏡?🚗: 鏡子里實時反射出身后的場景。
- 后期處理濾鏡?🎞?: 實現電影級別的調色、模糊、輝光等屏幕特效。
🛠? 盜夢空間:完整代碼
下面的代碼將完整演示這個“盜夢空間”的過程:我們將創建一個主場景,里面有一個立方體;同時,我們還會創建另一個獨立的“內部”場景,里面有一個旋轉的環面紐結。然后,我們會把“內部”場景實時渲染的結果,作為貼圖應用到主場景的立方體上。
代碼亮點:
- 創建了?
mainScene
?和?rtScene
?(Render Target Scene) 兩個獨立的場景。 - 在動畫循環中,通過?
renderer.setRenderTarget()
?來回切換渲染目標,實現“先畫到小畫板,再畫到主屏幕”的流程。
<!-- end list -->
HTML
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Three.js Render Targets</title><style>body { margin: 0; }canvas { display: block; }</style>
</head>
<body><script type="importmap">{"imports": {"three": "https://unpkg.com/three@0.165.0/build/three.module.js","three/addons/": "https://unpkg.com/three@0.165.0/examples/jsm/"}}</script><script type="module">import * as THREE from 'three';import { OrbitControls } from 'three/addons/controls/OrbitControls.js';// 1. 主場景設置const mainScene = new THREE.Scene();mainScene.background = new THREE.Color(0x333333);const mainCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);mainCamera.position.z = 5;const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);new OrbitControls(mainCamera, renderer.domElement);// 2. 創建一個 Render Target (我們的小畫板)const rt = new THREE.WebGLRenderTarget(512, 512);// 3. 創建 "內部" 場景 (畫板上的內容)const rtScene = new THREE.Scene();rtScene.background = new THREE.Color(0x87CEEB);const rtCamera = new THREE.PerspectiveCamera(75, 1, 0.1, 100); // 寬高比為1,匹配RT尺寸rtCamera.position.z = 3;// 為內部場景添加自己的燈光和物體rtScene.add(new THREE.AmbientLight(0xffffff, 0.5));rtScene.add(new THREE.DirectionalLight(0xffffff, 1));const rtKnot = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.3, 100, 16),new THREE.MeshStandardMaterial({ color: 0xff0088, roughness: 0.2 }));rtScene.add(rtKnot);// 4. 在主場景創建一個立方體,并使用Render Target的紋理const cubeMaterial = new THREE.MeshBasicMaterial({ map: rt.texture });const mainCube = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), cubeMaterial);mainScene.add(mainCube);// 5. 動畫循環function animate(time) {time *= 0.001;// 讓兩個場景的物體都動起來rtKnot.rotation.x = time;rtKnot.rotation.y = time;mainCube.rotation.x = -time * 0.2;mainCube.rotation.y = -time * 0.2;// 關鍵步驟:先渲染到 Render Targetrenderer.setRenderTarget(rt);renderer.render(rtScene, rtCamera);// 然后再渲染到主屏幕renderer.setRenderTarget(null);renderer.render(mainScene, mainCamera);requestAnimationFrame(animate);}animate();</script>
</body>
</html>
🎬 “盒中世界”誕生
打開頁面,你會看到一個正在旋轉的立方體。但神奇的是,立方體的表面并非純色或普通貼圖,而是一個實時播放的、完全不同的3D動畫(一個旋轉的彩色紐結)!
你正在從主世界,窺探一個被實時渲染出來的“盒中世界”。
渲染目標是Three.js中一個從“會用”到“精通”的分水嶺。它將渲染過程本身變成了創造工具,是實現高級視覺特效的基礎。掌握了它,你的創意將不再受限于單一的場景!
#ThreeJS #特效 #渲染 #WebGPU #Shader #游戲開發 #前端開發 #JavaScript #技術干貨