three.js 附件介绍

416 阅读5分钟

three 附件文档总结之后,写出如下附件介绍,主要包括,功能函数类 DataUtils, 切割多边形三角剖分算法实现类 Earcut, 图片功能函数类 ImageUtils, 环境贴图类 PMREMGenerator, 形状实用的函数类 ShapeUtils

先介绍下 DataUtils 这个类有两个方法,并且这个类不是构造函数

  • toHalfFloat 将数据转换成半精度浮点值
  • fromHalfFloat 将半精度浮点值转换成单精度浮点值

先介绍下什么是单精度浮点数,什么时半精度浮点数

  • 单数度浮点数由 (S)符号位正负号 (EEEEEEEE)指数表示位整数部分 (FFFFFFFFFFFFFFF)浮点数的有效数字部分表示小数精度 组成共计32位 S | EEEEEEEE | FFFFFFFFFFFFFFF
  • 半精度浮点数由(S)符号位正负号 (EEEEE)指数位表示整数部分 (FFFFFFFFFF)浮点数的有效数字部分表示小数精度 组成共计16位 S | EEEEE | FFFFFFFFFF

代码示例

const DataUtils = THREE.DataUtils //获取指向
// 半精度转单精度
const halfFloatValue = 0x3c00;
const floatValue = DataUtils.fromHalfFloat(halfFloatValue);
console.log(floatValue); //

// 单精度转半精度
const value = 1.5;
const halfFloat = THREE.DataUtils.toHalfFloat(value);
console.log(halfFloat); // 16 位整数

Earcut 这个类只有一个方法

  • triangulate 多边形的顶点转换成索引位置

使用示例


    // 外部多边形顶点
    const vertices = [
    0, 0,   // 顶点 1
    4, 0,   // 顶点 2
    4, 4,   // 顶点 3
    0, 4,   // 顶点 4

    // 孔的顶点
    1, 1,   // 孔的顶点 1
    3, 1,   // 孔的顶点 2
    3, 3,   // 孔的顶点 3
    1, 3    // 孔的顶点 4
    ];

    // 孔的起始位置索引
    const holeIndices = [4]; // 孔的顶点从索引 4 开始

    // 使用 Earcut 进行三角剖分
    const indices = Earcut.triangulate(vertices, holeIndices, 2); // 2 表示二维顶点

ImageUtils 图片工具类 只有一个方法

  • getDataURL 返回图片对象的数据 URL 返回的是BASE64数据

基本使用示例

    THREE.ImageUtils.getDataURL(renderer.domElement)

PMREMGenerator 贴图环境纹理这个对象有六个方法

  • fromScene 使用 hdr 生成与场景图相对应的反射 通常与反光物体使用反射当前对应的场景
  • fromEquirectangular 使用 hdr 生成场景图
  • fromCubemap 使得物体可以物体根据帖图进行光照反射这里的反射与贴图颜色相关,但不会显示贴图
  • compileCubemapShader 预编译立方体帖图着色器 多为内部使用
  • compileEquirectangularShader 预编译柱状着色器 多为内部使用
  • dispose 销毁方法,防止内存泄露

代码示例


<template>
    <div id="parkingLot" ref="parkingLot">
    </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from 'three';
import { PMREMGenerator } from 'three/src/extras/PMREMGenerator.js';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import hdrImg from "@public/hdr/bgImage.hdr"
import pngImg from "@public/banner2.jpg"
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const parkingLot = ref();


onMounted(() => {
    const DOMEl = parkingLot.value;
    // 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
    const width = DOMEl.clientWidth;
    const height = DOMEl.clientHeight;


    const renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    DOMEl.appendChild( renderer.domElement );
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 2000 );
    camera.position.set( 0, 300, 800 );
    camera.lookAt( 0, 0, 0 );

    // 创建 PMREM 生成器
    const pmremGenerator = new PMREMGenerator(renderer);


    
    // 使用 RGBELoader 加载 HDR 环境图
    new RGBELoader().load(hdrImg, (hdrTexture) => {
        console.log(hdrTexture,"hdrTexture");

        // 从 HDR 图像生成 PMREM 贴图
        const pmremTexture = pmremGenerator.fromEquirectangular(hdrTexture).texture;

        scene.background = pmremTexture;   // 作为背景图像
        scene.environment = pmremTexture;  // 作为全局环境贴图
        
        scene.add(new THREE.Mesh(new THREE.SphereGeometry(10, 32, 32), new THREE.MeshBasicMaterial({ map: hdrTexture })));

        const pmremTextureImg = pmremGenerator.fromScene(scene, 0).texture;
        
        // 创建立方体
        const geometry = new THREE.BoxGeometry(100,100,100);
        // 使用 PMREM 纹理创建材质
        const material = new THREE.MeshStandardMaterial({
            envMap: pmremTextureImg,
            roughness: 0.1,
            metalness: 0.9
        });
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);


        // 假设你已经加载了六个图像作为立方体贴图的面
        const loader = new THREE.CubeTextureLoader();
        const cubemap = loader.load([
            pngImg, pngImg,
            pngImg, pngImg,
            pngImg, pngImg
        ]);

        // 从立方体贴图生成 PMREM 贴图
        const pmremTexture2 = pmremGenerator.fromCubemap(cubemap).texture;

        // 使用 PMREM 纹理创建材质
        const material2 = new THREE.MeshStandardMaterial({
            envMap: pmremTexture2,
            roughness: 0.1,// 光滑度,0 完全光滑  1 表面非常粗粗
            metalness: 0.9 // 反射属性是否类似于金属 0 非类似 1 完全
        });
        // 创建立方体
        const geometry2 = new THREE.BoxGeometry(256,256,256);

        const cube2 = new THREE.Mesh(geometry2, material2);
        cube2.position.z = 200
        scene.add(cube2);


        // 清理生成器以释放内存
        hdrTexture.dispose();
        pmremTexture.dispose();
        pmremGenerator.dispose();
        // 创建 OrbitControls
        const controls = new OrbitControls(camera, renderer.domElement);


        // 创建地板
        const floorGeometry = new THREE.PlaneGeometry(400, 400); // 宽度和高度为 10 的地板
        const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 }); // 灰色材质
        const floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.rotation.x = -Math.PI / 2; // 将平面旋转到水平位置
        scene.add(floor);

        
        // 创建光源
        const light = new THREE.DirectionalLight(0x0000ff, 10);
        light.position.set(10, 10, 10);
        light.castShadow = true;
        scene.add(light);


        // 渲染循环
        function animate() {
            requestAnimationFrame(animate);
            controls.update();  // 更新控制器
            renderer.render(scene, camera);
        }

        animate();

    });
});
</script>
<style lang="scss" scoped="scoped">

    #parkingLot {
        width: 940px;
        height: 940px;
        border: 1px solid #ccc;
        margin: 30px auto;
    }

</style>

ShapeUtils 这个类有三个方法

  • area 获取 2D 多边形区域
  • isClockWise 判断顶点的顺序
  • triangulateShape 这个用来获取顶端索引 与 Earcut.triangulate 中的 类似但是 Earcut.triangulate 适用于复杂的多边形

使用示例


<template>
    <div id="parkingLot" ref="parkingLot">
    </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from 'three';
const parkingLot = ref();

const renderer = new THREE.WebGLRenderer();

onMounted(() => {
    const DOMEl = parkingLot.value;
    // 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
    const width = DOMEl.clientWidth;
    const height = DOMEl.clientHeight;


    renderer.setSize( width, height );
    DOMEl.appendChild( renderer.domElement );
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 2000 );
    camera.position.set( 0, 300, 800 );
    camera.lookAt( 0, 0, 0 );

    
    // 创建多边形形状
    const shape = new THREE.Shape();
    shape.moveTo(0, 0);
    shape.lineTo(100, 0);
    shape.lineTo(100, 100);
    shape.lineTo(0, 100);
    shape.lineTo(0, 0);

    // 将形状转换为几何体
    const geometry = new THREE.ShapeGeometry(shape);

    // 创建材质
    const material = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });

    // 创建网格
    const mesh = new THREE.Mesh(geometry, material);

    // 将网格添加到场景中
    scene.add(mesh);

    const ShapeUtils = THREE.ShapeUtils;

        // 计算形状的面积
    const area = ShapeUtils.area(shape.getPoints());

    // 使用 ShapeUtils.isClockWise 判断顶点顺序
    const isClockWise = ShapeUtils.isClockWise(shape.getPoints());

    console.log('Area:', area, isClockWise);
    

    // 创建光源
    const light = new THREE.DirectionalLight(0x0000ff, 10);
    light.position.set(10, 10, 10);
    light.castShadow = true;
    scene.add(light);


    renderer.render(scene, camera);

});

onUnmounted(() => {
    if(renderer){
        renderer.dispose()
    }
})

</script>
<style lang="scss" scoped="scoped">

    #parkingLot {
        width: 940px;
        height: 940px;
        border: 1px solid #ccc;
        margin: 30px auto;
    }

</style>