学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体

发布于:2025-03-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

👨‍⚕️ 主页: gis分享者
👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
👨‍⚕️ 收录于专栏:threejs gis工程师



一、🍀前言

本文详细介绍如何基于threejs在三维场景中构建THREE.ParametricGeometry参数化函数生成几何体,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️THREE.ParametricGeometry

THREE.ParametricGeometry生成由参数表示其表面的几何体。
代码示例:

const geometry = new THREE.ParametricGeometry( THREE.ParametricGeometries.klein, 25, 25 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const klein = new THREE.Mesh( geometry, material );
scene.add( klein );

创建一个波浪曲面:

import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';

// 定义参数化函数(波浪)
function wave(u, v, target) {
  const x = u * 20 - 10; // u: 0→1 → x: -10→10
  const y = v * 20 - 10; // v: 0→1 → y: -10→10
  const z = Math.sin(x * 0.5) * Math.cos(y * 0.5); // 计算波浪高度
  target.set(x, y, z);
}

// 创建几何体(50x50 分段)
const geometry = new ParametricGeometry(wave, 50, 50);

// 创建材质和网格
const material = new THREE.MeshPhongMaterial({ color: 0x0099ff, wireframe: true });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

创建一个圆环面:

function torus(u, v, target) {
  const R = 4; // 主半径
  const r = 1; // 管道半径
  const theta = u * Math.PI * 2; // u 对应环绕角度
  const phi = v * Math.PI * 2;   // v 对应管道角度

  const x = (R + r * Math.cos(phi)) * Math.cos(theta);
  const y = (R + r * Math.cos(phi)) * Math.sin(theta);
  const z = r * Math.sin(phi);
  target.set(x, y, z);
}

const geometry = new ParametricGeometry(torus, 64, 32);

1.1.1 ☘️构造函数

ParametricGeometry(func : Function, slices : Integer, stacks : Integer)
func — 一个函数,它接受一个介于 0 和 1 之间的 u 和 v 值,并修改第三个 Vector3 参数。默认是生成曲面的函数。

slices — 用于参数函数的切片数。默认值为 8。

stacks — 用于参数函数的堆栈数。默认值为 8。

1.1.2 ☘️属性

共有属性请参见其基类BufferGeometry

.parameters : Object
一个包含着构造函数中每个参数的对象。在对象实例化之后,对该属性的任何修改都不会改变这个几何体。

1.1.3 ☘️方法

共有方法请参见其基类BufferGeometry

二、🍀构建THREE.ParametricGeometry参数化函数生成几何体

1. ☘️实现思路

  • 1、初始化renderer渲染器。
  • 2、初始化Scene三维场景scene。
  • 3、初始化camera相机,定义相机位置 camera.position.set,设置相机方向camera.lookAt。
  • 4、创建THREE.SpotLight聚光灯光源spotLight,设置聚光灯光源位置和投影,scene场景加入spotLight。
  • 5、加载几何模型:定义klein方法,用于创建克莱因瓶函数。定义radialWave方法,用于创建纵向波函数。创建createMesh方法,使用THREE.MeshPhongMaterial高光材质,创建THREE.ParametricGeometry几何体。传入radialWave函数和其他参数调用createMesh方法创建参数化函数生成几何体网格对象mesh,scene场景添加mesh。定义render方法,实现mesh旋转动画。具体代码参考下面代码样例。
  • 6、加入stats监控器,监控帧数信息。

2. ☘️代码样例

<!DOCTYPE html>

<html>

<head>
    <title>学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<!-- Js 代码块 -->
<script type="text/javascript">

    // 初始化
    function init() {

        var stats = initStats();

        // 创建三维场景
        var scene = new THREE.Scene();

        // 创建相机
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // 创建渲染器并设置颜色、大小和投影
        var webGLRenderer = new THREE.WebGLRenderer();
        webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        webGLRenderer.setSize(window.innerWidth, window.innerHeight);
        webGLRenderer.shadowMapEnabled = true;

        // 设置相机位置和方向
        camera.position.x = -30;
        camera.position.y = 50;
        camera.position.z = 50;
        camera.lookAt(new THREE.Vector3(10, -20, 0));

        var spotLight = new THREE.DirectionalLight();
        spotLight.position = new THREE.Vector3(-20, 250, -50);
        spotLight.target.position.x = 30;
        spotLight.target.position.y = -40;
        spotLight.target.position.z = -20;
        spotLight.intensity = 0.3;

        scene.add(spotLight);

        // 渲染器绑定html页面元素
        document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

        var step = 0;
		// 克莱因瓶
        klein = function (u, v) {
            u *= Math.PI;
            v *= 2 * Math.PI;

            u = u * 2;
            var x, y, z;
            if (u < Math.PI) {
                x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(u) * Math.cos(v);
                z = -8 * Math.sin(u) - 2 * (1 - Math.cos(u) / 2) * Math.sin(u) * Math.cos(v);
            } else {
                x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(v + Math.PI);
                z = -8 * Math.sin(u);
            }

            y = -2 * (1 - Math.cos(u) / 2) * Math.sin(v);

            return new THREE.Vector3(x, y, z);
        };
		
		// 径向波
        radialWave = function (u, v) {
            var r = 50;

            var x = Math.sin(u) * r;
            var z = Math.sin(v / 2) * 2 * r;
            var y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 2.8;

            return new THREE.Vector3(x, y, z);
        };

        var mesh = createMesh(new THREE.ParametricGeometry(radialWave, 120, 120, false));
        scene.add(mesh);

        render();


        function createMesh(geom) {
            geom.applyMatrix(new THREE.Matrix4().makeTranslation(-25, 0, -25));
            var meshMaterial = new THREE.MeshPhongMaterial({
                specular: 0xaaaafff,
                color: 0x3399ff,
                shininess: 40,
                metal: true
            });
            meshMaterial.side = THREE.DoubleSide;
            // 创建波浪网格对象
            var plane = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial]);

            return plane;
        }

        function render() {
            stats.update();
            // 网格对象旋转动画
            mesh.rotation.y = step += 0.01;
            mesh.rotation.x = step;
            requestAnimationFrame(render);
            webGLRenderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();
            stats.setMode(0);

            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init;
</script>
</body>
</html>

效果如下:
径向波:
在这里插入图片描述
克莱因瓶:
在这里插入图片描述