Three.js——二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字

个人简介

👀个人主页: 前端杂货铺
开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

内容参考链接
WebGL专栏WebGL 入门
Three.js(一)创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化
Three.js(二)scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机
Three.js(三)聚光灯、环境光、点光源、平行光、半球光
Three.js(四)基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质

文章目录

    • 前言
    • 一、二维平面
    • 二、二维圆
    • 三、自定义二维图形
    • 四、立方体
    • 五、球体
    • 六、圆柱体
    • 七、圆环
    • 八、扭结
    • 九、多面体
    • 十、文字
    • GUI 控制文件
    • 总结

前言

大家好,这里是前端杂货铺。

上篇文章我们学习了 基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质。接下来,我们继续我们 three.js 的学习!

在学习的过程中,如若需要深入了解或扩展某些知识,可以自行查阅 => three.js官方文档。


一、二维平面

PlaneBufferGeometry 一个用于生成平面几何体的类。相较于于 PlaneGeometry 更加轻量化。

new THREE.PlaneBufferGeometry(width : Float, height : Float, widthSegments : Integer, heightSegments : Integer)
参数名称描述
width平面沿着 X 轴的宽度。默认值是 1
height平面沿着 Y 轴的高度。默认值是 1
widthSegments(可选)平面的宽度分段数,默认值是 1
heightSegments(可选)平面的高度分段数,默认值是 1
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
    <script>
        // 创建场景
        const scene = new THREE.Scene();
        // 创建相机 视野角度FOV、长宽比、近截面、远截面
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
        // 设置相机位置
        camera.position.set(0, 0, 20);

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        // 设置渲染器尺寸
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 添加平面 平面沿着 X 轴的宽度 | 平面沿着 Y 轴的高度 | 平面的宽度分段数 | 平面的高度分段数
        const geometry = new THREE.PlaneBufferGeometry(10, 10, 2, 2);

        const lambert = new THREE.MeshLambertMaterial({
            color: 0xff0000
        });
        const basic = new THREE.MeshBasicMaterial({
            wireframe: true
        });

        const mesh = {
            pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
                lambert, basic
            ])
        };

        // 添加到场景
        scene.add(mesh.pointer);

        // 添加灯光
        const spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-10, 10, 90);
        scene.add(spotLight);

        initControls(geometry, camera, mesh, scene);

        const animation = () => {
            mesh.pointer.rotation.x += 0.01;
            mesh.pointer.rotation.y += 0.01;

            // 渲染
            renderer.render(scene, camera);
            requestAnimationFrame(animation);
        }

        animation();
    </script>
</body>

</html>

二维平面


二、二维圆

CircleGeometry 是欧式几何的一个简单形状,它由围绕着一个中心点的三角分段的数量所构造,由给定的半径来延展。 同时它也可以用于创建规则多边形,其分段数量取决于该规则多边形的边数。

new MeshDepthMaterial(parameters: Object);
参数名称描述
radius圆形的半径,默认值为1
segments分段(三角面)的数量,最小值为3,默认值为 32
thetaStart第一个分段的起始角度,默认为0
thetaLength圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 添加二维圆 半径 | 指定创建圆需要的面的数量(最少3个) | 开始画的角度,0-Math.PI * 2 | 角度,定义圆要画多大
    const geometry = new THREE.CircleGeometry(4, 10, 0, Math.PI * 2);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

二维圆


三、自定义二维图形

自定义二维图形需要使用到 Shape 对象 和 ShapeGeometry 对象。

Shpae 使用路径以及可选的孔洞来定义一个二维形状平面。 它可以和 ExtrudeGeometry、ShapeGeometry 一起使用,获取点,或者获取三角面。

new THREE.Shape( points : Array );
参数名称描述
points一个 Vector2 数组

ShapeGeometry 形状缓冲几何体,用于从一个或多个路径形状中创建一个单面多边形几何体。

new THREE.ShapeGeometry(shapes : Array, curveSegments : Integer)
参数名称描述
shapes一个单独的shape,或者一个包含形状的Array
curveSegments每一个形状的分段数,默认值为12
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    const shape = new THREE.Shape();

    // 将绘制点移动到某处
    shape.moveTo(0, 0);

    // 从起始位置开始绘制,绘制到xy处停止
    shape.lineTo(0, 3);
    shape.lineTo(2, 3);
    shape.lineTo(5, 0);
    shape.lineTo(0, 0);

    // 绘制圆
    // shape.arc(1, 1, Math.PI * 2, Math.PI * 2, 100);

    const geometry = new THREE.ShapeGeometry(shape);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

自定义二维图形


四、立方体

BoxGeometry 是四边形的原始几何类,它通常使用构造函数所提供的 “width”、“height”、“depth” 参数来创建立方体或者不规则四边形。

new THREE.BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
参数名称描述
widthX 轴上面的宽度,默认值为 1
heightY 轴上面的高度,默认值为 1
depthZ 轴上面的深度,默认值为 1
widthSegments(可选)宽度的分段数,默认值是 1
heightSegments(可选)高度的分段数,默认值是 1
depthSegments(可选)深度的分段数,默认值是 1
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 宽度 x轴方向 | 高度 y轴方向 | 深度 z轴方向 | x轴方向将面分成多少份 | y... | z...
    const geometry = new THREE.BoxGeometry(3, 3, 3, 1, 1, 1);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

立方体


五、球体

SphereGeometry 是 一个用于生成球体的类

new THREE.SphereGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)
参数名称描述
radius球体半径,默认为 1
widthSegments水平分段数(沿着经线分段),最小值为 3,默认值为 32
heightSegments垂直分段数(沿着纬线分段),最小值为 2,默认值为 16
phiStart指定水平(经线)起始角度,默认值为 0
phiLength指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2
thetaStart指定垂直(纬线)起始角度,默认值为0
thetaLength指定垂直(纬线)扫描角度大小,默认值为 Math.PI
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 半径 | x轴方向将面分成多少份 | y轴方向将面分成多少份 | 从x轴什么位置开始回值 | 绘制多少 | 从y轴什么位置开始回值 | 绘制多少
    const geometry = new THREE.SphereGeometry(2, 20, 20, Math.PI * 2, Math.PI * 2, Math.PI * 2, Math.PI * 2);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

球体


六、圆柱体

CylinderGeometry 是 一个用于生成圆柱几何体的类

new THREE.CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float);
参数名称描述
radiusTop圆柱的顶部半径,默认值是 1
radiusBottom圆柱的底部半径,默认值是 1
height圆柱的高度,默认值是 1
radialSegments圆柱侧面周围的分段数,默认为 32
heightSegments圆柱侧面沿着其高度的分段数,默认值为 1
openEnded一个 Boolean 值,指明该圆锥的底面是开放的还是封顶的。默认值为 false,即其底面默认是封顶的
thetaStart第一个分段的起始角度,默认为 0
thetaLength圆柱底面圆扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆柱
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 半径 | x轴方向将面分成多少份 | y轴方向将面分成多少份 | 从x轴什么位置开始回值 | 绘制多少 | 从y轴什么位置开始回值 | 绘制多少
    const geometry = new THREE.CylinderGeometry(2, 2, 2, 20, 4, false);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

圆柱体


七、圆环

TorusGeometry 是 一个用于生成圆环几何体的类

new THREE.TorusGeometry(radius : Float, tube : Float, radialSegments : Integer, tubularSegments : Integer, arc : Float);
参数名称描述
radius环面的半径,从环面的中心到管道横截面的中心。默认值是 1
tube管道的半径,默认值为 0.4
radialSegments管道横截面的分段数,默认值为 12
tubularSegments管道的分段数,默认值为 48
arc圆环的圆心角(单位是弧度),默认值为 Math.PI * 2
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 半径 | 管子的半径 | 沿圆环长度分为多少段 | 宽度分成多少段 | 是否形成一个完整的闭环 |
    const geometry = new THREE.TorusGeometry(2, 1, 10, 10, Math.PI * 2);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

圆环


八、扭结

TorusKnotGeometry 用于创建一个圆环扭结,其特殊形状由一对互质的整数,p和q所定义。如果p和q不互质,创建出来的几何体将是一个环面链接。

new THREE.TorusKnotGeometry(radius : Float, tube : Float, tubularSegments : Integer, radialSegments : Integer, p : Integer, q : Integer);
参数名称描述
radius圆环的半径,默认值为 1
tube管道的半径,默认值为 0.4
tubularSegments管道的分段数量,默认值为 64
radialSegments横截面分段数量,默认值为 8
p这个值决定了几何体将绕着其旋转对称轴旋转多少次,默认值是 2
q这个值决定了几何体将绕着其内部圆环旋转多少次,默认值是 3
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 半径 | 管子的半径 | 沿圆环长度分为多少段 | 宽度分成多少段 | 定义结的形状 | 定义结的形状 | 拉伸纽结 |
    const geometry = new THREE.TorusKnotGeometry(2, 1, 20, 6, 1, 3, 1);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

扭结


九、多面体

PolyhedronGeometry 多面体在三维空间中具有一些平面的立体图形。这个类将一个顶点数组投射到一个球面上,之后将它们细分为所需的细节级别。

new THREE.PolyhedronGeometry(vertices : Array, indices : Array, radius : Float, detail : Integer);

参数名称描述
vertices一个顶点Array(数组):[1,1,1, -1,-1,-1, … ]
indices一个构成面的索引Array(数组), [0,1,2, 2,3,0, … ]
radius最终形状的半径
detail将对这个几何体细分多少个级别。细节越多,形状就越平滑
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 构成多面体的顶点 | 创建出的面 | 指定多面的大小 | 处理多面体细节 |
    const geometry = new THREE.PolyhedronGeometry(vertices, indices, 4, 0);

    // 正四面体
    // const geometry = new THREE.TetrahedronGeometry(4, 0);

    // 正八面体
    // const geometry = new THREE.OctahedronGeometry(4, 0);

    // 正二十面体
    // const geometry = new THREE.IcosahedronGeometry(4, 0);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

多面体


十、文字

TextGeometry 是一个用于将文本生成为单一的几何体的类。 它是由一串给定的文本,以及由加载的font(字体)和该几何体 ExtrudeGeometry 父类中的设置所组成的参数来构造的。

// text 将要显示的文本
new THREE.TextGeometry(text : String, parameters : Object);
参数名称描述
fontTHREE.Font 的实例
size字体大小,默认值为100
depth挤出文本的厚度。默认值为 50
curveSegments(表示文本的)曲线上点的数量。默认值为 12
bevelEnabled是否开启斜角,默认为 false
bevelThickness文本上斜角的深度,默认值为 20
bevelSize斜角与原始文本轮廓之间的延伸距离。默认值为 8
bevelSegments斜角的分段数。默认值为 3
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <script src="../lib/three/dat.gui.js"></script>
    <script src="../controls/index.js"></script>
    <script src="../assets/font/font.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 文字
    const geometry = new THREE.TextGeometry("THREE", textOptions);

    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    const mesh = {
        pointer: new THREE.SceneUtils.createMultiMaterialObject(geometry, [
            lambert, basic
        ])
    };

    // 添加到场景
    scene.add(mesh.pointer);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    initControls(geometry, camera, mesh, scene);

    const animation = () => {
        mesh.pointer.rotation.x += 0.01;
        mesh.pointer.rotation.y += 0.01;

        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

文字


GUI 控制文件

老规矩,我们把本篇文章需要使用的 ./controls/index.js 补充完毕

const basicType = {
    // 颜色。默认为一个白色(0xffffff)的 Color 对象。
    color: {
        method: 'addColor',
        getValue: item => item.color.getStyle(),
        setValue: (item, value) => item.color.setStyle(value),
    },
    // 
    skyColor: {
        method: 'addColor',
        getValue: item => item.skyColor.getStyle(),
        setValue: (item, value) => item.skyColor.setStyle(value),
    },
    // 光照强度。默认值为 1
    intensity: {
        method: 'add',
        extends: [0, 2],
        getValue: item => item.intensity,
        setValue: (item, value) => item.intensity = +value,
    },
    // 光源照射的最大距离。默认值为 0(无限远)
    distance: {
        method: 'add',
        extends: [0, 1],
        getValue: item => item.distance,
        setValue: (item, value) => item.distance = +value,
    },
    // 光线照射范围的角度。默认值为 Math.PI/3
    angle: {
        method: 'add',
        extends: [0, Math.PI / 2],
        getValue: item => item.angle,
        setValue: (item, value) => item.angle = +value,
    },
    // 决定了光线强度递减的速度。
    exponent: {
        method: 'add',
        extends: [0, 20],
        getValue: item => item.exponent,
        setValue: (item, value) => item.exponent = +value,
    },
    // 亮度
    opacity: {
        extends: [0, 1],
        getValue: item => item.opacity,
        setValue: (item, value) => item.opacity = +value
    },
    // 透明度
    transparent: {
        getValue: item => item.transparent,
        setValue: (item, value) => item.transparent = value
    },
    // 线框
    wireframe: {
        getValue: item => item.wireframe,
        setValue: (item, value) => item.wireframe = value
    },
    // 显隐
    visible: {
        getValue: item => item.visible,
        setValue: (item, value) => item.visible = value
    },
    cameraNear: {
        extends: [0, 50],
        getValue: (item, camera) => camera.near,
        setValue: (item, value, camera) => camera.near = value
    },
    cameraFar: {
        extends: [50, 200],
        getValue: (item, camera) => camera.far,
        setValue: (item, value, camera) => camera.far = value
    },
    side: {
        extends: [
            ['font', 'back', 'double']
        ],
        getValue: (item, camera) => 'font',
        setValue: (item, value) => {
            switch (value) {
                case 'font':
                    item.side = THREE.FrontSide;
                    break;
                case 'back':
                    item.side = THREE.BackSide;
                    break;
                case 'double':
                    item.side = THREE.DoubleSide;
                    break;
            }
        }
    },
    // 材料的环境颜色
    ambient: {
        method: 'addColor',
        getValue: (item) => item.ambient.getHex(),
        setValue: (item, value) => item.ambient = new THREE.Color(value),
    },
    // 物体材料本身发出的颜色
    emissive: {
        method: 'addColor',
        getValue: (item) => item.emissive.getHex(),
        setValue: (item, value) => item.emissive = new THREE.Color(value),
    },
    // 设置高亮部分的颜色
    specular: {
        method: 'addColor',
        getValue: (item) => item.specular.getHex(),
        setValue: (item, value) => item.specular = new THREE.Color(value),
    },
    // 设置高亮部分的亮度
    shininess: {
        extends: [0, 100],
        getValue: (item) => item.shininess,
        setValue: (item, value) => item.shininess = value,
    },
    red: {
        extends: [0, 1],
        getValue: (item) => item.uniforms.r.value,
        setValue: (item, value) => item.uniforms.r.value = value,
    },
    alpha: {
        extends: [0, 1],
        getValue: (item) => item.uniforms.a.value,
        setValue: (item, value) => item.uniforms.a.value = value,
    },
    dashSize: {
        extends: [0, 5],
        getValue: (item) => item.dashSize,
        setValue: (item, value) => item.dashSize = +value,
    },
    width: getMeshValue([0, 20], 'width'),
    height: getMeshValue([0, 20], 'height'),
    widthSegments: getMeshValue([0, 20], 'widthSegments'),
    heightSegments: getMeshValue([0, 20], 'heightSegments'),
    radius: getMeshValue([1, 20], 'radius'),
    segments: getMeshValue([3, 80], 'segments'),
    thetaStart: getMeshValue([0, Math.PI * 2], 'thetaStart'),
    thetaLength: getMeshValue([0, Math.PI * 2], 'thetaLength'),
    depth: getMeshValue([0, 20], 'depth'),
    depthSegments: getMeshValue([0, 20], 'depthSegments'),
    phiStart: getMeshValue([0, Math.PI * 2], 'phiStart'),
    radiusTop: getMeshValue([-20, 20], 'radiusTop'),
    radiusBottom: getMeshValue([-20, 20], 'radiusBottom'),
    radialSegments: getMeshValue([1, 60], 'radialSegments'),
    openEnded: getMeshValue([], 'openEnded'),
    tube: getMeshValue([1, 6], 'tube'),
    tubularSegments: getMeshValue([0, Math.PI * 2], 'tubularSegments'),
    arc: getMeshValue([1, 20], 'arc'),
    p: getMeshValue([1, 10], 'p'),
    q: getMeshValue([1, 10], 'q'),
    detail: getMeshValue([0, 5], 'detail'),
    heightScale: getMeshValue([0, 5], 'heightScale'),
    size: getMeshValue([0, 10], 'size'),
    bevelThickness: getMeshValue([1, 30], 'bevelThickness'),
    bevelEnabled: getMeshValue([], 'bevelEnabled'),
    bevelSegments: getMeshValue([1, 30], 'bevelSegments'),
    curveSegments: getMeshValue([1, 30], 'curveSegments'),
    steps: getMeshValue([0, 10], 'steps'),
}

const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1];
const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1];

function createMaterial(geometry) {
    const lambert = new THREE.MeshLambertMaterial({
        color: 0xff0000
    });
    const basic = new THREE.MeshBasicMaterial({
        wireframe: true
    });

    return new THREE.SceneUtils.createMultiMaterialObject(geometry, [
        lambert, basic
    ]);
}

// 字体配置
const textOptions = {
    size: 1,
    height: 1,
    weight: 'normal',
    font: 'helvetiker',
    bevelThickness: 1,
    bevelEnabled: false,
    bevelSegments: 1,
    curveSegments: 1,
    steps: 1
}

const roundValue = {
    width: 1,
    height: 1,
    depth: 1,
    widthSegments: 1,
    heightSegments: 1,
    depthSegments: 1, // 不能为 undefined
    radialSegments: 1,
    tubularSegments: 1,
    detail: 1,
    size: 1,
    bevelSegments: 1,
    curveSegments: 1,
    steps: 1
}

const isPolyhedron = item => item.type === 'PolyhedronGeometry';

const isFont = item => item.type === 'TextGeometry';

function removeAndAdd(item, value, camera, mesh, scene, controls) {
    const {
        x,
        y,
        z
    } = mesh.pointer.rotation;

    scene.remove(mesh.pointer);
    const arg = [];
    for (const key in controls) {
        if (roundValue[key]) {
            // ~~位运算符,转为数字
            controls[key] = ~~controls[key];
        }
        arg.push(controls[key]);
    }

    // 多面体
    if (isPolyhedron(item)) {
        arg.unshift(vertices, indices);
    }

    if (isFont(item)) {
        mesh.pointer = createMaterial(new THREE[item.type]('THREE', Object.assign(textOptions, controls)));
    } else {
        mesh.pointer = createMaterial(new THREE[item.type](...arg));
    }

    mesh.pointer.rotation.set(x, y, z);

    scene.add(mesh.pointer);
}

function getMeshValue(extend, name) {
    return {
        extends: extend,
        getValue: (item, camera, mesh) => isFont(item) && textOptions[name] !== undefined ? textOptions[name] : mesh.children[0].geometry.parameters[name],
        setValue: (...arg) => removeAndAdd(...arg)
    }
}

const itemType = {
    SpotLight: ['color', 'intensity', 'distance', 'angle', 'exponent'], // 聚光灯
    AmbientLight: ['color'], // 环境光
    PointLight: ['color', 'intensity', 'distance'], // 点光源
    DirectionalLight: ['color', 'intensity'], // 平行光
    HemisphereLight: ['groundColor', 'intensity'], // 半球光
    MeshBasicMaterial: ['color', 'opacity', 'transparent', 'wireframe', 'visible'], // 基础网格材质
    MeshDepthMaterial: ['wireframe', 'cameraNear', 'cameraFar'], // 深度网格材质
    MeshNormalMaterial: ['opacity', 'transparent', 'wireframe', 'visible', 'side'],
    MeshLambertMaterial: ['opacity', 'transparent', 'wireframe', 'visible', 'side', 'ambient', 'emissive', 'color'], // 朗伯材质
    MeshPhongMaterial: ['opacity', 'transparent', 'wireframe', 'visible', 'side', 'ambient', 'emissive', 'color', 'specular', 'shininess'], // Phong材质
    ShaderMaterial: ['red', 'alpha'], // 着色器材质
    LineBasicMaterial: ['color'], // 直线
    LineDashedMaterial: ['dashSize', 'gapSize'], // 虚线
    PlaneBufferGeometry: ['width', 'height', 'widthSegments', 'heightSegments'], // 二维屏幕
    CircleGeometry: ['radius', 'segments', 'thetaStart', 'thetaLength'], // 二维圆
    BoxGeometry: ['width', 'height', 'depth', 'widthSegments', 'heightSegments', 'depthSegments'], // 立方体
    SphereGeometry:  ['radius', 'widthSegments', 'heightSegments', 'phiStart', 'phiLength', 'thetaStart', 'thetaLength'], // 球体
    CylinderGeometry: ['radiusTop', 'radiusBottom', 'height', 'radialSegments', 'heightSegments', 'openEnded'], // 圆柱体
    TorusGeometry: ['radius', 'tube', 'radialSegments', 'tubularSegments', 'arc'], // 圆环
    TorusKnotGeometry: ['radius', 'tube', 'radialSegments', 'tubularSegments', 'p', 'q', 'heightScale'], // 纽结
    PolyhedronGeometry: ['radius', 'detail'], // 多面缓冲几何体
    TetrahedronGeometry: ['radius', 'detail'], // 四面缓冲几何体
    OctahedronGeometry: ['radius', 'detail'], // 八面缓冲几何体
    IcosahedronGeometry: ['radius', 'detail'], // 二十面缓冲几何体
    TextGeometry: ['size', 'bevelThickness', 'bevelEnabled', 'bevelSegments', 'curveSegments', 'steps'],
}

function initControls(item, camera, mesh, scene) {
    console.log('item', item)
    const typeList = itemType[item.type];
    const controls = {};

    if (!typeList || !typeList.length) {
        return;
    }

    const gui = new dat.GUI();

    for (let i = 0; i < typeList.length; i++) {
        const child = basicType[typeList[i]];
        if (child) {
            controls[typeList[i]] = child.getValue(item, camera, mesh.pointer);

            const childExtends = child.extends || [];

            gui[child.method || 'add'](controls, typeList[i], ...childExtends).onChange((value) => {
                child.setValue(item, value, camera, mesh, scene, controls);
            })
        }
    }
}

总结

本篇文章我们讲解了几种常见几何体的基本使用,包括二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字。

更多内容扩展请大家自行查阅 => three.js官方文档,真心推荐读一读!!

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. Three.js 官方文档
  2. WebGL+Three.js 入门与实战【作者:慕课网_yancy】

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/649594.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

外汇天眼:野村证券和Laser Digital与GMO互联网集团合作发行日元和美元稳定币

野村控股和Laser Digital将与GMO互联网集团合作&#xff0c;在日本探索发行日元和美元稳定币。GMO互联网集团的美国子公司GMO-Z.com Trust Company, Inc. 在纽约州金融服务部的监管框架下&#xff0c;在以太坊、恒星币和Solana等主要区块链上发行稳定币。GMO-Z.com Trust Compa…

三坐标测量机在汽车零部件质量控制中的应用

高质量的零部件能够确保汽车的性能达到设计标准&#xff0c;包括动力性能、燃油效率、操控稳定性等&#xff0c;从而提供更好的驾驶体验&#xff0c;建立消费者对汽车品牌的信任&#xff1b;也推动了汽车行业的技术创新&#xff0c;制造商不断研发新材料、新工艺&#xff0c;以…

驾考学法减分拍照搜题模拟考试小程序开发

驾考学法减分拍照搜题模拟考试小程序开发 项目介绍 驾考搜题&#xff0c;一款专为驾考学员设计的智能搜题助手&#xff0c;集合了海量题库与实战模拟&#xff0c;助力学员快速掌握驾考知识&#xff0c;轻松应对各类考试。 主要功能特点 智能搜题 通过关键词、拍照搜索&#xf…

Python爬虫实战(实战篇)—16获取【百度热搜】数据—写入Ecel(附完整代码)

文章目录 专栏导读背景结果预览1、爬取页面分析2、通过返回数据发现适合利用lxmlxpath3、继续分析【小说榜、电影榜、电视剧榜、汽车榜、游戏榜】4、完整代码总结 专栏导读 &#x1f525;&#x1f525;本文已收录于《Python基础篇爬虫》 &#x1f251;&#x1f251;本专栏专门…

【数据结构】图解红黑树以及代码实现

目录 一、相关概念 性质 二、图解 1、插入操作 2、parent在左边情况1&#xff1a;cur为红色节点parent也是红色节点、uncle也为红色节点 3、parent在左边情况2&#xff1a;cur为红色节点parent也是红色节点、uncle为黑色或者是空&#xff0c;cur是parent的left 4、parent…

【清灰教程】联想拯救者Y7000p(2018款)拆机清灰教程+更换硅脂

清灰教程 本人电脑&#xff1a;联想拯救者Y7000p&#xff08;2018款&#xff09;第一步&#xff1a;购买清灰道具&#xff08;提前买好&#xff09;螺丝刀1.硅脂 这里随便买的 2.刮刀&#xff08;买硅脂送&#xff09;4.刷子&#xff08;清风扇灰&#xff09;5.撬后盖用&#x…

wordpress主题给网站增加一个版权声明区块代码分享

在数字化时代&#xff0c;网络上的信息传播变得越来越便捷&#xff0c;给人们生活和工作带来了极大的便利。然而&#xff0c;在这个过程中也产生了很多版权问题。为了更好地保护自己的版权&#xff0c;许多网站开始在其网页上添加版权声明。本文将探讨在网站上添加版权声明的重…

sklearn线性回归--岭回归

sklearn线性回归--岭回归 岭回归也是一种用于回归的线性模型&#xff0c;因此它的预测公式与普通最小二乘法相同。但在岭回归中&#xff0c;对系数&#xff08;w&#xff09;的选择不仅要在训练数据上得到好的预测结果&#xff0c;而且还要拟合附加约束&#xff0c;使系数尽量小…

rust语言初识

程序设计实践课上水一篇ing 来源&#xff1a;rust基础入门-1.初识rust-酷程网 (kucoding.com) rust作为一名新兴语言&#xff0c;与go又有些许不同&#xff0c;因为它的目标是对标系统级开发&#xff0c;也就是C、C这两位在编程界的位置。比如我们最常用的windows系统&#x…

韩语“再见” 怎么说,柯桥韩语培训

1.1 标准写法及读法 안녕 (annyeong) 音译&#xff1a; 安宁 罗马音&#xff1a; Annyeong 使用情境&#xff1a; 适用于朋友之间或非常熟悉的关系中&#xff0c;不分场合&#xff0c;可以用于打招呼或告别&#xff0c;表示“你好”或“再见”。 안녕히 가세요 (annyeonghi …

Elasticsearch之文本分析

文本分析基本概念 官网&#xff1a;Text analysis | Elasticsearch Guide [7.17] | Elastic 官网称为文本分析&#xff0c;这是对文本进行一直分析处理的方式&#xff0c;基本处理逻辑是为按照预先制定的分词规则&#xff0c;把原本的文档进行分割成多个小颗粒度的词项&#x…

只争朝夕,不负韶华!

学生成绩分析云平台是基于云计算技术和数据分析算法的在线平台&#xff0c;用于对学生的学业成绩进行全面的分析和评估。学生成绩分析云平台可以为学校、教育机构和教师提供全面的学生成绩管理和分析解决方案&#xff0c;帮助他们更好地了解学生的学业表现、优化教学策略&#…

HC32F103BCB使用SPI获取AS5040编码器数据

1.AS5040介绍 2.硬件电路 硬件上使用SSI通信方式连接。 3.配置硬件SPI 查看手册&#xff0c;AS5040时序 可以看到在空闲阶段不发生数据传输的时候时钟(CLK)和数据(DO)都保持高电位(tCLKFE阶段)&#xff0c;在第一个脉冲的下降沿触发编码器载入发送数据&#xff0c;然后每一个…

算法设计第七周(应用哈夫曼算法解决文件归并问题)

一、【实验目的】 &#xff08;1&#xff09;进一步理解贪心法的设计思想 &#xff08;2&#xff09;掌握哈夫曼算法的具体应用 &#xff08;3&#xff09;比较不同的文件归并策略&#xff0c;探讨最优算法。 二、【实验内容】 设S{f1,…,fn}是一组不同的长度的有序文件构…

【测评】OrangePi AIPro环境配置与基础应用

1.介绍 官网&#xff1a;http://www.orangepi.cn/ 社区&#xff1a;http://forum.orangepi.cn/ 昇腾社区&#xff1a;https://www.hiascend.com/ OrangePi AIPro 是一款基于昇腾AI技术的开发板&#xff0c;它采用华为昇腾910E AI芯片&#xff0c;集成4核64位CPU和AI处理器&am…

CRMEB多门店的门店后台首页路由

如何在输入 http://localhost:8080/、http://localhost:8080/store/、http://localhost:8080/custom-store/ 这三个中任意一个链接都能正确跳转到 http://localhost:8080/store/home/index 。要实这个要求&#xff0c;有两种方式&#xff1a; 重定向const router new VueRout…

恒创科技:Linux 服务器和 Windows 服务器哪个更好?

选择正确的服务器系统至关重要&#xff0c;目前广泛使用的选项是 Windows 服务器 和 Linux 服务器&#xff0c;它们各有优缺点。本文将比较 Linux 与 Windows 服务器&#xff0c;让我们来看看它们的主要区别&#xff0c;然后再决定哪种操作系统适合使用。 主要区别&#xff1a;…

几种流行的并行方法了解

几种流行的并行方法&#xff1a; 数据并行&#xff08;data parallel&#xff09;模型并行&#xff08;model parallel&#xff09; tensor并行pipeline并行sequence并行Zero Redundancy Data Parallelism&#xff08;ZeRO&#xff09; Data parallelism (DP) 经典的数据并行…

基本Java语法和语义 (Reading 2)

&#xff08;1&#xff09;Java和C在变量类型命名和使用 基本数据类型 对象类型与引用类型 特殊类型 关键字和修饰符 &#xff08;2&#xff09;快照图&#xff1a; IDE调试工具: 许多IDE&#xff08;如Eclipse、IntelliJ IDEA&#xff09;提供了调试功能&#xff0c;可以…

智慧水坝:科技变革的里程碑

在曾经的水利工程领域&#xff0c;水坝只是为了水资源的调配和控制&#xff0c;提供一定的安全储备。然而&#xff0c;随着现代科技的不断发展&#xff0c;传统的水坝已经不再是单一的水源控制工程&#xff0c;而是变成了一个充满智慧与创新的生态系统。智慧水坝的概念已经超越…