Skip to content

BabylonJS - 反射探针

ReflectionProbe 是 Babylon.js 中一个强大的工具,用于捕捉场景中特定点周围的环境并生成一个立方体贴图纹理。然后,这个立方体贴图可以应用于材质(如 StandardMaterial 或 PBRMaterial)作为反射纹理,在镜子、抛光金属或水面等物体上创建逼真的反射。

可以把反射探针想象成一个位于特定点的小型摄像机,捕捉 360 度视图(上、下、左、右、前、后)。这个捕获的视图被存储为 CubeTexture。当应用于材质时,物体看起来会反射探针捕捉的环境。

通过提供名称、生成的立方体贴图面的尺寸(2 的幂次方,例如 512)以及场景来创建 ReflectionProbe。

import * as BABYLON from '@babylonjs/core';
// Assuming 'scene' is defined
const probeResolution = 512; // Quality of the reflection texture (e.g., 256, 512, 1024)
const reflectionProbe = new BABYLON.ReflectionProbe("myProbe", probeResolution, scene);
// By default, the probe captures everything. You often want to specify WHICH meshes
// should appear in the reflection. Add meshes to the probe's render list:
// reflectionProbe.renderList.push(meshToIncludeInReflection1);
// reflectionProbe.renderList.push(meshToIncludeInReflection2);

探针的位置决定了反射捕捉的视点。默认情况下,它位于 (0,0,0),但你可以将其附加到网格或手动设置其位置:reflectionProbe.position = new BABYLON.Vector3(x, y, z);。

要使用探针使对象具有反射性,请将探针生成的立方体纹理(probe.cubeTexture)分配给对象材质的 reflectionTexture 属性。

// Assuming 'reflectiveMesh' and its 'material' (Standard or PBR) are defined
// Assign the probe's cube map to the material's reflection texture slot
material.reflectionTexture = reflectionProbe.cubeTexture;
// Optional: Control reflection intensity and appearance
// For StandardMaterial:
// material.reflectionFresnelParameters = new BABYLON.FresnelParameters();
// material.reflectionFresnelParameters.bias = 0.1; // Controls falloff
// For PBRMaterial (reflection is controlled by metallic/roughness/reflectivity):
// material.metallic = 1.0;
// material.roughness = 0.1;
// or
// material.reflectivityColor = new BABYLON.Color3(1, 1, 1); // Full reflection
// material.microSurface = 0.1; // Smooth surface

probe.renderList 至关重要。它是一个数组,包含应渲染到反射探针立方体贴图中的网格。如果列表为空(或为 null,这是默认值),探针会尝试渲染场景中的 所有 网格,这可能效率低下,并可能包含不需要的对象(例如反射对象本身,导致无限反射)。最佳实践是明确只将必要的周围网格添加到 renderList 中。

Babylon.js 还具有 MirrorTexture,它创建平面反射(类似于平面镜)。ReflectionProbe 创建立方体贴图反射,适用于反射整个周围环境的曲面或复杂表面。

此演示显示一个中央反射的绳结,反射周围的几个球体和一个带纹理的地面平面。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Babylon.js - Reflection Probe Demo</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<style>
html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
#renderCanvas { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.1, 1.0);
const camera = new BABYLON.ArcRotateCamera("camera1", -Math.PI / 2, Math.PI / 2.5, 15, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
camera.wheelPrecision = 50;
camera.minZ = 0.1;
const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.5, 1, 0.2), scene);
light.intensity = 0.9;
// Reflective Object
const knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", { radius: 1, tube: 0.4, radialSegments: 128, tubularSegments: 64, p: 2, q: 3 }, scene);
const knotMaterial = new BABYLON.PBRMaterial("knotMat", scene); // Using PBR for better reflections
knotMaterial.metallic = 1.0; // Make it fully metallic
knotMaterial.roughness = 0.1; // Make it quite smooth/shiny
knot.material = knotMaterial;
// Objects to be reflected
const yellowSphere = BABYLON.MeshBuilder.CreateSphere("yellowSphere", { diameter: 1.5 }, scene);
yellowSphere.position = new BABYLON.Vector3(3, 0, 0);
const yellowMat = new BABYLON.StandardMaterial("yellowMat", scene);
yellowMat.diffuseColor = BABYLON.Color3.Yellow();
yellowSphere.material = yellowMat;
const blueSphere = BABYLON.MeshBuilder.CreateSphere("blueSphere", { diameter: 1.5 }, scene);
blueSphere.position = new BABYLON.Vector3(-3, 0, 0);
const blueMat = new BABYLON.StandardMaterial("blueMat", scene);
blueMat.diffuseColor = BABYLON.Color3.Blue();
blueSphere.material = blueMat;
const greenSphere = BABYLON.MeshBuilder.CreateSphere("greenSphere", { diameter: 1.5 }, scene);
greenSphere.position = new BABYLON.Vector3(0, 0, 4);
const greenMat = new BABYLON.StandardMaterial("greenMat", scene);
greenMat.diffuseColor = BABYLON.Color3.Green();
greenSphere.material = greenMat;
// Ground
const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 20, height: 20 }, scene);
const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
try {
// Use a texture for the ground (ensure 'textures/ground.jpg' exists)
const groundTexture = new BABYLON.Texture("textures/ground.jpg", scene);
groundTexture.uScale = 5;
groundTexture.vScale = 5;
groundMaterial.diffuseTexture = groundTexture;
} catch (err) {
console.warn("Ground texture not found. Using color.");
groundMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.4, 0.3);
}
groundMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Dull specular
ground.material = groundMaterial;
ground.position.y = -2;
// --- Reflection Probe Setup ---
const reflectionProbe = new BABYLON.ReflectionProbe("mainProbe", 512, scene);
reflectionProbe.position = knot.position; // Center the probe on the knot
// Add ONLY the surrounding objects to the probe's render list
reflectionProbe.renderList.push(yellowSphere);
reflectionProbe.renderList.push(blueSphere);
reflectionProbe.renderList.push(greenSphere);
reflectionProbe.renderList.push(ground);
// DO NOT add the 'knot' itself to avoid self-reflection issues
// Apply the probe to the knot's material
knotMaterial.reflectionTexture = reflectionProbe.cubeTexture;
// For PBR, setting metallic/roughness is usually enough.
// For StandardMaterial, you'd add Fresnel parameters here.
// ------------------------------
// Animate spheres for visual interest
let angle = 0;
scene.onBeforeRenderObservable.add(() => {
angle += 0.01;
yellowSphere.position.y = Math.sin(angle * 2) * 0.5;
blueSphere.position.y = Math.cos(angle * 3) * 0.5;
greenSphere.position.y = Math.sin(angle * 1.5 + Math.PI / 4) * 0.5;
});
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>

此演示显示一个中央闪亮的金属绳结。周围有黄色、蓝色和绿色球体略微上下浮动,下方是一个带纹理的地面平面。由于反射探针位于绳结中心,中央绳结的表面逼真地反射了这些周围的球体和地面。当你旋转摄像机时,反射会根据观察角度动态变化。请注意,绳结本身不会出现在其反射中,因为它被排除在探针的 renderList 之外。

为了显示地面纹理,你需要在 HTML 文件相对位置的 textures 文件夹中放置一个名为 ground.jpg 的图像文件。

反射探针会增加渲染开销,因为它需要渲染场景(或 renderList 中指定的场景部分)六次(立方体贴图的每个面一次)。应战略性地在关键的反射对象上使用它们,而不是过度使用。降低探针的分辨率(probeResolution)可以提高性能,但会牺牲反射的清晰度。对于静态场景,反射可能只需渲染一次。对于对象或反射表面移动的动态场景,探针可能需要定期更新。