Three.js 中的 Reflector 类
说明
Reflector 是 three.js 中的一个类,用于创建反射效果的对象。它继承自 Mesh,可以添加到场景中作为一个可渲染的物体。
Reflector 的构造函数如下:
constructor(geometry?: BufferGeometry, options?: ReflectorOptions)
其中,geometry 参数是可选的,用于指定 Reflector 的几何形状。options 参数也是可选的,是一个配置选项对象,包含以下属性:
color
: 反射面的颜色,可以是一个 CSS 颜色字符串或是一个 three.js 的 Color 对象,默认值是 0x7F7F7F。textureWidth
: 反射纹理的宽度,单位是像素,默认值是 512。textureHeight
: 反射纹理的高度,单位是像素,默认值是 512。clipBias
: 剪裁偏移值,用于控制剪裁平面的位置,可以用于解决渲染的反射对象和原始对象之间的闪烁问题,默认值是 0。shader
: 用于渲染反射效果的着色器程序,可以是一个 three.js 的 ShaderMaterial 对象,默认值是 undefined,表示使用内置的着色器。encoding
: 反射纹理的编码格式,默认值是 LinearEncoding。multisample
: 反射纹理的多重采样级别,用于抗锯齿,默认值是 0,表示不使用多重采样。
Reflector 对象有以下方法:
getRenderTarget()
: 获取渲染到的反射纹理对象,可以用于后续的处理。dispose()
: 释放 Reflector 对象的资源,包括纹理和几何形状。
Reflector 对象可以通过以下步骤使用:
- 创建一个 Reflector 对象,可以指定几何形状和配置选项。
import { Reflector } from 'three';
const geometry = new PlaneBufferGeometry(10, 10);
const options = {
color: 0x7F7F7F,
textureWidth: 512,
textureHeight: 512,
clipBias: 0,
shader: undefined,
encoding: LinearEncoding,
multisample: 0
};
const reflector = new Reflector(geometry, options);
- 将 Reflector 对象添加到场景中。
scene.add(reflector);
- 渲染场景,并将渲染结果显示到屏幕上。
renderer.render(scene, camera);
Reflector 对象会根据设置的几何形状和配置选项,在指定位置渲染反射效果,可以通过调整配置选项来控制反射效果的外观和行为。当不再需要使用 Reflector 对象时,可以调用 dispose()
方法来释放资源。
示例:
效果:
代码:
<template>
<div ref="containerRef" class="container"></div>
</template>
<script setup>
import * as THREE from 'three'
import { useRoute } from 'vue-router'
import { onMounted, ref, onUnmounted } from 'vue'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { Reflector } from 'three/examples/jsm/objects/Reflector.js'
// 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'
const route = useRoute()
document.title = route.meta.title
const containerRef = ref(null)
let camera, scene, renderer
let cameraControls
let sphereGroup, smallSphere
let groundMirror, verticalMirror
THREE.ColorManagement.enabled = true
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
// scene
scene = new THREE.Scene()
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500)
camera.position.set(0, 75, 160)
cameraControls = new OrbitControls(camera, renderer.domElement)
cameraControls.target.set(0, 40, 0)
cameraControls.maxDistance = 400
cameraControls.minDistance = 10
cameraControls.update()
const planeGeo = new THREE.PlaneGeometry(100.1, 100.1)
// reflectors/mirrors
let geometry, material
geometry = new THREE.CircleGeometry(40, 64)
groundMirror = new Reflector(geometry, {
clipBias: 0.003,
textureWidth: window.innerWidth * window.devicePixelRatio,
textureHeight: window.innerHeight * window.devicePixelRatio,
color: '#b5b5b5'
})
groundMirror.position.y = 0.5
groundMirror.rotateX(- Math.PI / 2)
scene.add(groundMirror)
geometry = new THREE.PlaneGeometry(100, 100)
verticalMirror = new Reflector(geometry, {
clipBias: 0.003,
textureWidth: window.innerWidth * window.devicePixelRatio,
textureHeight: window.innerHeight * window.devicePixelRatio,
color: '#c1cbcb'
})
verticalMirror.position.y = 50
verticalMirror.position.z = - 50
scene.add(verticalMirror)
sphereGroup = new THREE.Object3D()
scene.add(sphereGroup)
geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos(Math.PI / 180 * 30), 0.1, 24, 1)
material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: '#8d8d8d' })
const sphereCap = new THREE.Mesh(geometry, material)
sphereCap.position.y = - 15 * Math.sin(Math.PI / 180 * 30) - 0.05
sphereCap.rotateX(- Math.PI)
geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120)
const halfSphere = new THREE.Mesh(geometry, material)
halfSphere.add(sphereCap)
halfSphere.rotateX(- Math.PI / 180 * 135)
halfSphere.rotateZ(- Math.PI / 180 * 20)
halfSphere.position.y = 7.5 + 15 * Math.sin(Math.PI / 180 * 30)
sphereGroup.add(halfSphere)
geometry = new THREE.IcosahedronGeometry(5, 0)
material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: '#7b7b7b', flatShading: true })
smallSphere = new THREE.Mesh(geometry, material)
scene.add(smallSphere)
// walls
const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff }))
planeTop.position.y = 100
planeTop.rotateX(Math.PI / 2)
scene.add(planeTop)
const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff }))
planeBottom.rotateX(- Math.PI / 2)
scene.add(planeBottom)
const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xbbbbfe }))
planeFront.position.z = 50
planeFront.position.y = 50
planeFront.rotateY(Math.PI)
scene.add(planeFront)
const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 }))
planeRight.position.x = 50
planeRight.position.y = 50
planeRight.rotateY(- Math.PI / 2)
scene.add(planeRight)
const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 }))
planeLeft.position.x = - 50
planeLeft.position.y = 50
planeLeft.rotateY(Math.PI / 2)
scene.add(planeLeft)
// lights
const mainLight = new THREE.PointLight('#e7e7e7', 1.5, 250)
mainLight.position.y = 60
scene.add(mainLight)
const greenLight = new THREE.PointLight('#00ff00', 0.25, 1000)
greenLight.position.set(550, 50, 0)
scene.add(greenLight)
const redLight = new THREE.PointLight('#ff0000', 0.25, 1000)
redLight.position.set(- 550, 50, 0)
scene.add(redLight)
const blueLight = new THREE.PointLight('#bbbbfe', 0.25, 1000)
blueLight.position.set(0, 50, 550)
scene.add(blueLight)
// 后期模糊处理
/* const composer = new EffectComposer(renderer)
const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.5, 0.4, 0.85)
composer.addPass(bloomPass) */
function animate() {
requestAnimationFrame(animate)
const timer = Date.now() * 0.01
sphereGroup.rotation.y -= 0.002
smallSphere.position.set(
Math.cos(timer * 0.1) * 30,
Math.abs(Math.cos(timer * 0.2)) * 20 + 5,
Math.sin(timer * 0.1) * 30
)
smallSphere.rotation.y = (Math.PI / 2) - timer * 0.1
smallSphere.rotation.z = timer * 0.8
renderer.render(scene, camera)
// composer.render()
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
groundMirror.getRenderTarget().setSize(
window.innerWidth * window.devicePixelRatio,
window.innerHeight * window.devicePixelRatio
)
verticalMirror.getRenderTarget().setSize(
window.innerWidth * window.devicePixelRatio,
window.innerHeight * window.devicePixelRatio
)
}
onMounted(() => {
containerRef.value.appendChild(renderer.domElement)
animate()
window.addEventListener('resize', onWindowResize)
})
onUnmounted(() => {
window.removeEventListener('resize', onWindowResize)
})
</script>