Three.js 中的 Reflector 类

Posted 脉望

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 对象可以通过以下步骤使用:

  1. 创建一个 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);
  1. 将 Reflector 对象添加到场景中。
scene.add(reflector);
  1. 渲染场景,并将渲染结果显示到屏幕上。
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>

Three.js 中的 BoxBufferGeometry 与 BoxGeometry 有啥区别?

【中文标题】Three.js 中的 BoxBufferGeometry 与 BoxGeometry 有啥区别?【英文标题】:What is difference between BoxBufferGeometry vs BoxGeometry in Three.js?Three.js 中的 BoxBufferGeometry 与 BoxGeometry 有什么区别? 【发布时间】:2018-10-02 01:02:40 【问题描述】:

我正在学习 Three.js。我找不到关于 BoxBufferGeometry 与 BoxGeometry 之间区别的正确答案。帮帮我。

【问题讨论】:

【参考方案1】:

[Primitive]Geometry 类对所有 JS 几何类的操作友好,内存不友好。

这意味着定义此几何图形的每条数据都存储为某个类的实例 (Vector3,Vector2,Face3) 等。这些带有方便的方法,因此您可以点一个顶点使用其他向量,平移顶点,修改 uvs,修改法线等。但它在内存和性能方面有开销(创建所有这些实例,存储它们)。

[Primitive]BufferGeometry 类是性能友好的几何类,它们依赖于类型化数组以 WebGL 友好的格式存储数据。

这意味着顶点不是Vector3s 的数组,而是类型化数组:

Array[v0,v1... vN]
vs:
Float32Array[v0x,v0y,v0z,v1x,v1y,v1z... vNx,vNy,vNz] 

它们的存储效率更高,但更难操作。

如果你想修改一个顶点:

Geometry

//with Geometry you just get vertex 5 and have access to it's x...
//AND the methods of the class -> Vector3.add(Vector3)
myGeom.vertices[5].add(new THREE.Vector3(1,2,3))

BufferGeometry

//xyz are just numbers, so with a stride of 3
//we select x , and  then the next two for y and z
//we have to know that the 15th number in this array is x of vertex 5...
const stride = 3
const index = 5
let offset = index * stride
myGeom.attributes.position.array[offset++] += 1 
myGeom.attributes.position.array[offset++] += 2 
myGeom.attributes.position.array[offset  ] += 3 

但是

THREE.BufferAttribute 确实有几种方法可以从该数组中写入和读取内容。它仍然更加冗长:

//add x: 1 y: 2 z: 3 to 5th vertex

const index = 5
const attribute = myGeometry.attributes.position
const v3add = new THREE.Vector3(1,2,3)

attribute.setXYZ( 
  index, 
  attribute.getX(index) + v3add.x,
  attribute.getY(index) + v3add.y,
  attribute.getZ(index) + v3add.z
)

【讨论】:

虽然更容易阅读,但我倾向于说BufferGeometry 更改示例是不正确的。 1) 标准[Primitive]BufferGeometry 对象的顶点存储在myGeom.attributes.position 中。除非您为自定义着色器添加自定义属性,否则没有 vertices 属性。 2) 您仍然可以通过索引获取和设置值,只是有点冗长。 var pos = myGeom.attributes.position; pos.setXYZ(5, pos.getX(5) + 1, pos.getY(5) + 2, pos.getZ(5) + 3);. 这个答案有多个错误,@pailhead 更清楚。 请编辑一下,这主要是伪代码,add(1,2,3) 也没有意义。 这个想法是从概念上解释差异,而不是给出确切的名称和属性。这里的重要概念是数组本身,而不是它所在的位置,但我已经编辑了答案。【参考方案2】:

@Pailhead 的回答是正确的。如果您需要以某种方式修改盒子.. 通过移动它的顶点或更改 texcoords,操作 BoxGeometry 会更容易,但 BoxBufferGeometry 的渲染效率更高。您可以使用 .fromGeometry 和 .fromBufferGeometry 在类型之间进行转换。

【讨论】:

这确实应该是对 pailhead 答案的评论,而不是单独的答案。

以上是关于Three.js 中的 Reflector 类的主要内容,如果未能解决你的问题,请参考以下文章

Three.js 中的“THREE.OrbitControls”中的相机位置变化

three.js 中的光

Three.js 打造缤纷夏日3D梦中情岛 🌊

Three.js 中的 BoxBufferGeometry 与 BoxGeometry 有啥区别?

创建胶囊体和甜圈圈(three.js实战8)

为啥 Three.js 中的 ColladaLoader.js 无法加载我的文件?