three CSS2DRenderer CSS 2D渲染器

158 阅读4分钟

CSS2DRenderer 是 Three.js 中一个用于将 2D HTML 元素渲染到 3D 场景中的工具。它允许你将普通的 HTML 元素(如 <div> <span>)与 Three.js 中的 3D 对象结合,使得这些元素能够随着 3D 对象的变化(如旋转、缩放、平移)而进行相应的调整。

CSS2DRenderer 有三个方法

// CSS2DRenderer()
// 使用示例
<template>
    <div id="parkingLot" ref="parkingLot">
    </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
const parkingLot = ref();
onMounted(async () => {
    const DOMEl = parkingLot.value;
    // 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
    const width = DOMEl.clientWidth;
    const height = DOMEl.clientHeight;
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    renderer.setPixelRatio( window.devicePixelRatio );
    DOMEl.appendChild( renderer.domElement );
    const scene = new THREE.Scene();
    renderer.setClearColor(0xaaaaaa);
    const camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );
    camera.position.set( 10, 5, 20 );
    camera.lookAt( 0, 0, 0 );
    // 创建一个时钟对象,用于计时和获取场景中的时间
    const clock = new THREE.Clock();
    // 创建纹理加载器,用于加载纹理贴图
    const textureLoader = new THREE.TextureLoader();
    // 声明月球和标签渲染器对象
    let moon, labelRenderer;
    // 地球和月球的半径
    const EARTH_RADIUS = 1;
    const MOON_RADIUS = 0.27;
    // 启用所有图层
    camera.layers.enableAll();
    // 创建一个定向光源,用于照亮场景
    const dirLight = new THREE.DirectionalLight( 0xffffff, 3 );
    dirLight.position.set( 0, 0, 1 ); // 设置光源的位置
    dirLight.layers.enableAll(); // 启用所有图层
    scene.add( dirLight ); // 将光源添加到场景中
    // 创建一个坐标轴辅助对象,用于显示场景的坐标系
    const axesHelper = new THREE.AxesHelper( 5 );
    axesHelper.layers.enableAll(); // 启用所有图层
    scene.add( axesHelper ); // 将坐标轴添加到场景中
    // 创建地球的几何体和材质
    const earthGeometry = new THREE.SphereGeometry( EARTH_RADIUS, 16, 16 );
    const earthMaterial = new THREE.MeshPhongMaterial( {
        specular: 0x333333, // 高光颜色
        shininess: 5, // 高光强度
        map: textureLoader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/textures/planets/earth_atmos_2048.jpg?raw=true' ), // 地球的纹理
        specularMap: textureLoader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/textures/planets/earth_specular_2048.jpg?raw=true' ), // 地球的高光纹理
        normalMap: textureLoader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/textures/planets/earth_normal_2048.jpg?raw=true' ), // 地球的法线纹理
        normalScale: new THREE.Vector2( 0.85, 0.85 ) // 法线缩放
    } );
    earthMaterial.map.colorSpace = THREE.SRGBColorSpace; // 设置颜色空间为 sRGB
    const earth = new THREE.Mesh( earthGeometry, earthMaterial ); // 创建地球网格
    scene.add( earth ); // 将地球添加到场景中
    // 创建月球的几何体和材质
    const moonGeometry = new THREE.SphereGeometry( MOON_RADIUS, 16, 16 );
    const moonMaterial = new THREE.MeshPhongMaterial( {
        shininess: 5, // 高光强度
        map: textureLoader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/textures/planets/moon_1024.jpg?raw=true' ) // 月球的纹理
    } );
    moonMaterial.map.colorSpace = THREE.SRGBColorSpace; // 设置颜色空间为 sRGB
    moon = new THREE.Mesh( moonGeometry, moonMaterial ); // 创建月球网格
    scene.add( moon ); // 将月球添加到场景中
    // 启用地球和月球的所有图层
    earth.layers.enableAll();
    moon.layers.enableAll();
    // 创建地球标签,并设置标签的内容和样式
    const earthDiv = document.createElement( 'div' );
    earthDiv.className = 'label';
    earthDiv.textContent = 'Earth';
    earthDiv.style.backgroundColor = 'transparent';
    // 创建 CSS2DObject 来显示地球的标签
    const earthLabel = new CSS2DObject( earthDiv );
    earthLabel.position.set( 1.5 * EARTH_RADIUS, 0, 0 ); // 设置标签的位置
    earthLabel.center.set( 0, 1 ); // 设置标签的中心点
    earth.add( earthLabel ); // 将标签添加到地球上
    earthLabel.layers.set( 0 ); // 设置标签的图层为 0
    // 创建地球质量标签
    const earthMassDiv = document.createElement( 'div' );
    earthMassDiv.className = 'label';
    earthMassDiv.textContent = '5.97237e24 kg';
    earthMassDiv.style.backgroundColor = 'transparent';
    // 创建 CSS2DObject 来显示地球质量的标签
    const earthMassLabel = new CSS2DObject( earthMassDiv );
    earthMassLabel.position.set( 1.5 * EARTH_RADIUS, 0, 0 ); // 设置标签的位置
    earthMassLabel.center.set( 0, 0 ); // 设置标签的中心点
    earth.add( earthMassLabel ); // 将标签添加到地球上
    earthMassLabel.layers.set( 1 ); // 设置标签的图层为 1
    // 创建月球标签,并设置标签的内容和样式
    const moonDiv = document.createElement( 'div' );
    moonDiv.className = 'label';
    moonDiv.textContent = 'Moon';
    moonDiv.style.backgroundColor = 'transparent';
    // 创建 CSS2DObject 来显示月球的标签
    const moonLabel = new CSS2DObject( moonDiv );
    moonLabel.position.set( 1.5 * MOON_RADIUS, 0, 0 ); // 设置标签的位置
    moonLabel.center.set( 0, 1 ); // 设置标签的中心点
    moon.add( moonLabel ); // 将标签添加到月球上
    moonLabel.layers.set( 0 ); // 设置标签的图层为 0
    // 创建月球质量标签
    const moonMassDiv = document.createElement( 'div' );
    moonMassDiv.className = 'label';
    moonMassDiv.textContent = '7.342e22 kg';
    moonMassDiv.style.backgroundColor = 'transparent'; // 设置背景色为红色
    // 创建 CSS2DObject 来显示月球质量的标签
    const moonMassLabel = new CSS2DObject( moonMassDiv );
    moonMassLabel.position.set( 1.5 * MOON_RADIUS, 0, 0 ); // 设置标签的位置
    moonMassLabel.center.set( 0, 0 ); // 设置标签的中心点
    moon.add( moonMassLabel ); // 将标签添加到月球上
    moonMassLabel.layers.set( 1 ); // 设置标签的图层为 1
    // 创建 CSS2DRenderer,用于渲染 2D 标签
    labelRenderer = new CSS2DRenderer();
    labelRenderer.setSize( width, height ); // 设置渲染器的尺寸
    labelRenderer.domElement.style.position = 'absolute';
    labelRenderer.domElement.style.top = '100px';
    labelRenderer.domElement.style.left = '50%';
    labelRenderer.domElement.style.transform = 'translateX(-50%)';
    document.body.appendChild( labelRenderer.domElement ); // 将渲染器添加到文档中
    // 创建 OrbitControls 实例,用于控制相机的视角
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.target.set(0, 0, 0); // 设置相机的目标为场景的原点(0, 0, 0)
    // 动画函数
    function animate() {
        requestAnimationFrame(animate); // 请求下一帧动画
        controls.update(); // 更新 OrbitControls,使其与用户的操作保持同步
        renderer.render(scene, camera); // 渲染场景
        const elapsed = clock.getElapsedTime(); // 获取已过去的时间
        moon.position.set( Math.sin( elapsed ) * 5, 0, Math.cos( elapsed ) * 5 ); // 设置月球的运动轨迹
        labelRenderer.render( scene, camera ); // 渲染 2D 标签
    }
    animate(); // 启动动画
});
</script>
<style lang="scss" scoped="scoped">
    #parkingLot {
        width: 940px;
        height: 940px;
        border: 1px solid #ccc;
        margin: 30px auto;
        color: #fff;
    }
</style>

方法

  • getSize () : Object 返回一个包含有渲染器宽和高的对象。
  • render ( scene : Scene, camera : Camera ) : undefined 使用camera渲染scene。
  • setSize (width : Number, height : Number) : undefined 将渲染器的尺寸调整为(width, height).