学习: Threejs (9)& Threejs (10)

Threejs (9)

一、生成圆弧顶点

model.js

生成圆弧顶点数据

首先定义

javascript 复制代码
const R = 100; //圆弧半径
const N = 50; //分段数量
const sp = 2 * Math.PI / N;//两个相邻点间隔弧度

循环

javascript 复制代码
for (let i = 0; i < N ; i++) {
    const angle =  sp * i;
    // 以坐标原点为中心,在XOY平面上生成圆弧上的顶点数据
    const x = R * Math.cos(angle);
    const y = R * Math.sin(angle);
  }

声明数组,存放圆弧上的顶点数据

javascript 复制代码
const R = 100; //圆弧半径
const N = 50; //分段数量
// 批量生成圆弧上的顶点数据
const arr = [];
const sp = 2 * Math.PI / N;//两个相邻点间隔弧度
// const sp = 1 * Math.PI / N;//半圆弧
for (let i = 0; i < N; i++) {
    const angle =  sp * i;
    // 以坐标原点为中心,在XOY平面上生成圆弧上的顶点数据
    const x = R * Math.cos(angle);
    const y = R * Math.sin(angle);
    arr.push(x, y, 0);
}

线模型渲染圆弧线

javascript 复制代码
//类型数组创建顶点数据
const vertices = new Float32Array(arr);
// 创建属性缓冲区对象
const attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
// 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;

// 线材质
const material = new THREE.LineBasicMaterial({
    color: 0xffffff //线条颜色
});
// 创建线模型对象   构造函数:Line、LineLoop、LineSegments

const line = new THREE.Line(geometry, material); 

或者将i<N改成 i<N+1

javascript 复制代码
for (let i = 0; i < N + 1; i++) {
    const angle =  sp * i;
    // 以坐标原点为中心,在XOY平面上生成圆弧上的顶点数据
    const x = R * Math.cos(angle);
    const y = R * Math.sin(angle);
    arr.push(x, y, 0);
}

绘制半圆弧

圆弧设置圆心坐标

javascript 复制代码
const R = 100; //圆弧半径
const N = 50; //分段数量
const sp = 2 * Math.PI / N;//两个相邻点间隔弧度
// 设置圆心坐标
const cx = 200;
const cy = 100;
for (let i = 0; i < N+1; i++) {
    const angle = sp * i;//当前点弧度
    const x = cx + R * Math.cos(angle);
    const y = cy + R * Math.sin(angle);
    arr.push(x, y, 0);
}

二、几何体方法.setFromPoints()

三维向量Vector3表示顶点坐标

用三维向量Vector3表示顶点的x、y、z坐标,作为数组元素创建一组顶点坐标。

javascript 复制代码
const pointsArr = [
    // 三维向量Vector3表示的坐标值
    new THREE.Vector3(0,0,0),
    new THREE.Vector3(0,100,0),
    new THREE.Vector3(0,100,100),
    new THREE.Vector3(0,0,100),
];

几何体方法.setFromPoints()

javascript 复制代码
// 创建几何体对象
const geometry = new THREE.BufferGeometry();
// 把数组pointsArr里面的坐标数据提取出来,赋值给`geometry.attributes.position`属性
geometry.setFromPoints(pointsArr);
javascript 复制代码
console.log('几何体变化',geometry.attributes.position);

二维向量Vector2表示顶点坐标

javascript 复制代码
const pointsArr = [
    // 二维向量Vector2表示的坐标值
    new THREE.Vector2(0,0),
    new THREE.Vector2(100,0),
    new THREE.Vector2(100,100),
    new THREE.Vector2(0,100),
];

三、曲线Curve简介

曲线都有一个共同的父类Curve

椭圆EllipseCurve例子

javascript 复制代码
// 参数1和2表示椭圆中心坐标  参数3和4表示x和y方向半径
const arc = new THREE.EllipseCurve(0, 0, 120, 50);

曲线Curve方法.getPoints()

椭圆弧线EllipseCurve的父类是曲线Curve,自然会继承父类曲线.getPoints()方法,通过.getPoints()可以从曲线上获取顶点数据。

javascript 复制代码
//getPoints是基类Curve的方法,平面曲线会返回一个vector2对象作为元素组成的数组
const pointsArr = arc.getPoints(50); //分段数50,返回51个顶点
console.log('曲线上获取坐标',pointsArr);

.setFromPoints()提取曲线坐标数据

javascript 复制代码
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(pointsArr);
console.log('geometry.attributes',geometry.attributes);
javascript 复制代码
// 点材质
const material = new THREE.PointsMaterial({
    color: 0xffff00,
    size: 10.0 //点对象像素尺寸
}); 
// 点模型
const points = new THREE.Points(geometry, material);

getPoints()

setFromPoints()

通过.getSpacedPoints().getPoints()一样也可以从曲线Curve上返回一系列曲线上的顶点坐标。

通过.getSpacedPoints()是按照曲线长度等间距返回顶点数据,.getPoints()获取点的方式并不是按照曲线等间距的方式,而是会考虑曲线斜率变化,斜率变化快的位置返回的顶点更密集。

四、椭圆、圆

椭圆弧线EllipseCurve

参数1和2表示椭圆中心坐标 参数3和4表示x和y方向半径

javascript 复制代码
const arc = new THREE.EllipseCurve(0, 0, 50, 50);//x和y方向半径相同就是圆形效果

圆弧线ArcCurve

javascript 复制代码
// 圆弧,参数3表示半径
const arc = new THREE.ArcCurve(0, 0, 60);

曲线精度

弧线起始角度

javascript 复制代码
// 完整圆弧
const arc = new THREE.ArcCurve(0, 0, 100, 0, 2 * Math.PI);
javascript 复制代码
// 半圆弧
const arc = new THREE.ArcCurve(0, 0, 100, 0, Math.PI);
javascript 复制代码
// 四分之一圆弧
const arc = new THREE.ArcCurve(0, 0, 100, 0, Math.PI/2);

顺逆时针

javascript 复制代码
// 逆时针绘制圆弧,参数5默认false,就是逆时针
const arc = new THREE.ArcCurve(0, 0, 100, 0, Math.PI/2,false);
javascript 复制代码
// 顺时针绘制圆弧
const arc = new THREE.ArcCurve(0, 0, 100, 0, Math.PI/2,true);

五、样条曲线

javascript 复制代码
// 三维向量Vector3创建一组顶点坐标
const arr = [
    new THREE.Vector3(-50, 20, 90),
    new THREE.Vector3(-10, 40, 40),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(60, -60, 0),
    new THREE.Vector3(70, 0, 80)
]

三维样条曲线CatmullRomCurve3

javascript 复制代码
// 三维样条曲线
const curve = new THREE.CatmullRomCurve3(arr);
javascript 复制代码
const pointsArr = curve.getPoints(100); //曲线上获取点
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(pointsArr); //读取坐标数据赋值给几何体顶点

// 线材质
const material = new THREE.LineBasicMaterial({
    color: 0x00fffff
});
// 线模型
const line = new THREE.Line(geometry, material);
javascript 复制代码
const group = new THREE.Group();
group.add(line);
export default group;

点模型可视化曲线经过的点

javascript 复制代码
// 用点模型可视化样条曲线经过的顶点位置;
const geometry2 = new THREE.BufferGeometry();
geometry2.setFromPoints(arr);
const material2 = new THREE.PointsMaterial({
  color: 0xff00ff,
  size: 10,
});
//点模型对象
const points = new THREE.Points(geometry2, material2);

const group = new THREE.Group();

group.add(line, points);

2D样条曲线

javascript 复制代码
// 二维向量Vector2创建一组顶点坐标
const arr = [
    new THREE.Vector2(-100, 0),
    new THREE.Vector2(0, 30),
    new THREE.Vector2(100, 0),
];
// 二维样条曲线
const curve = new THREE.SplineCurve(arr);

六、贝塞尔曲线

二维二次贝塞尔曲线QuadraticBezierCurve

javascript 复制代码
const p1 = new THREE.Vector2(-80, 0);
const p2 = new THREE.Vector2(20, 100);
const p3 = new THREE.Vector2(80, 0);
// 二维二次贝赛尔曲线
const curve = new THREE.QuadraticBezierCurve(p1, p2, p3);

线模型line渲染贝塞尔曲线

javascript 复制代码
const pointsArr = curve.getPoints(100); //曲线上获取点
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(pointsArr); //读取坐标数据赋值给几何体顶点

// 线材质
const material = new THREE.LineBasicMaterial({
    color: 0x00fffff
});
// 线模型
const line = new THREE.Line(geometry, material);

const group = new THREE.Group();
group.add(line);

观察贝塞尔曲线规则

javascript 复制代码
// 可视化p1、p2、p3三个点的位置,并用直线相连接,便于观察贝塞尔曲线的绘制规律
const geometry2 = new THREE.BufferGeometry();
geometry2.setFromPoints([p1,p2,p3]);
const material2 = new THREE.PointsMaterial({
    color: 0xff00ff,
    size: 10,
});
//点模型对象
const points = new THREE.Points(geometry2, material2);
// 三个点构成的线条
const line2 = new THREE.Line(geometry2, new THREE.LineBasicMaterial());


const group = new THREE.Group();

group.add(line, points, line2);

三维二次贝赛尔曲线QuadraticBezierCurve3

javascript 复制代码
// p1、p2、p3表示三个点坐标
const p1 = new THREE.Vector3(-80, 0, 0);
const p2 = new THREE.Vector3(20, 100, 0);
const p3 = new THREE.Vector3(80, 0, 100);
// 三维二次贝赛尔曲线
const curve = new THREE.QuadraticBezierCurve3(p1, p2, p3);

二维三次贝塞尔曲线CubicBezierCurve

二维三次贝塞尔曲线CubicBezierCurve与二维二次贝赛尔曲线QuadraticBezierCurve区别就是多了一个控制点。

javascript 复制代码
// p1、p2、p3、p4表示4个点坐标
// p1、p4是曲线起始点,p2、p3是曲线的控制点
const p1 = new THREE.Vector2(-80, 0);
const p2 = new THREE.Vector2(-40, 50);
const p3 = new THREE.Vector2(50, 50);
const p4 = new THREE.Vector2(80, 0);

// 二维三次贝赛尔曲线
const curve = new THREE.CubicBezierCurve(p1, p2, p3, p4);

三维三次贝赛尔曲线CubicBezierCurve3

三维三次贝赛尔曲线CubicBezierCurve3与二维三次贝塞尔曲线CubicBezierCurve区别就是多了一个维度,参数是三维向量对象Vector3。

七、样条、贝塞尔曲线应用

曲线的起始点设置在XOZ平面上,y方向为曲线高度方向。

三维样条曲线CatmullRomCurve3实现飞线轨迹

javascript 复制代码
// p1、p3轨迹线起始点坐标
const p1 = new THREE.Vector3(-100, 0, -100);
const p3 = new THREE.Vector3(100, 0, 100);
// 计算p1和p3的中点坐标
const x2 = (p1.x + p3.x)/2;
const z2 = (p1.z + p3.z)/2;
const h = 50;
const p2 = new THREE.Vector3(x2, h, z2);

const arr = [p1, p2, p3];
// 三维样条曲线
const curve = new THREE.CatmullRomCurve3(arr);

三维二次贝赛尔曲线QuadraticBezierCurve3实现飞线轨迹

javascript 复制代码
// p1、p3轨迹线起始点坐标
const p1 = new THREE.Vector3(-100, 0, -100);
const p3 = new THREE.Vector3(100, 0, 100);
// 计算p1和p3的中点坐标
const x2 = (p1.x + p3.x)/2;
const z2 = (p1.z + p3.z)/2;
const h = 100;
const p2 = new THREE.Vector3(x2, h, z2);
// 三维二次贝赛尔曲线
const curve = new THREE.QuadraticBezierCurve3(p1, p2, p3);

八、组合曲线CurvePath拼接不同曲线

直线线段

3D直线线段LineCurve3,参数是表示x、y、z坐标的三维向量Vector3对象。

javascript 复制代码
new THREE.LineCurve3(new THREE.Vector3(), new THREE.Vector3());

2D直线线段LineCurve,参数是表示x、y坐标的二维向量Vector2对象。

javascript 复制代码
new THREE.LineCurve(new THREE.Vector2(), new THREE.Vector2());

整体思路CurvePath.curves

javascript 复制代码
const R = 80;//圆弧半径
const H = 200;//直线部分高度
// 直线1
const line1 = new THREE.LineCurve(new THREE.Vector2(R, H), new THREE.Vector2(R, 0));
// 圆弧
const arc = new THREE.ArcCurve(0, 0, R, 0, Math.PI, true);
// 直线2
const line2 = new THREE.LineCurve(new THREE.Vector2(-R, 0), new THREE.Vector2(-R, H));

// CurvePath创建一个组合曲线对象
const CurvePath = new THREE.CurvePath();
//line1, arc, line2拼接出来一个U型轮廓曲线,注意顺序
CurvePath.curves.push(line1, arc, line2);

组合曲线CurvePath取点

javascript 复制代码
//组合曲线上获取点
const pointsArr = CurvePath.getPoints(16); 
const geometry = new THREE.BufferGeometry();
geometry.setFromPoints(pointsArr); //读取坐标数据赋值给几何体顶点

点模型可视化组合曲线返回的顶点

javascript 复制代码
// 可视化curve.getPoints从曲线上获取的点坐标
const material2 = new THREE.PointsMaterial({
    color: 0xff00ff,
    size: 10,
});
//点模型对象
const points = new THREE.Points(geometry, material2);

// 可视化curve.getPoints从曲线上获取的点坐标
const material2 = new THREE.PointsMaterial({
    color: 0xff00ff,
    size: 10,
});
//点模型对象
const points = new THREE.Points(geometry, material2);

.getSpacedPoints()取点测试

直线部分会按照等间距方式返回顶点数据,需要把.getSpacedPoints()的精度参数提升,圆弧部分才会更加圆滑。

九、曲线路径管道TubeGeometry

样条曲线

javascript 复制代码
// 三维样条曲线
const path = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-50, 20, 90),
    new THREE.Vector3(-10, 40, 40),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(60, -60, 0),
    new THREE.Vector3(70, 0, 80)
]);

管道TubeGeometry

javascript 复制代码
// path:路径   40:沿着轨迹细分数  2:管道半径   25:管道截面圆细分数
const geometry = new THREE.TubeGeometry(path, 40, 2, 25);

观察管道内壁

javascript 复制代码
const material = new THREE.MeshLambertMaterial({
    side:THREE.DoubleSide,//双面显示看到管道内壁
});

组合曲线CurvePath生成管道

javascript 复制代码
import * as THREE from 'three';


// 创建多段线条的顶点数据
const p1 = new THREE.Vector3(0, 0,100)
const p2 = new THREE.Vector3(0, 0,30);
const p3 = new THREE.Vector3(0, 0,0);
const p4 = new THREE.Vector3(30, 0, 0);
const p5 = new THREE.Vector3(100, 0, 0);
// 1. 3D直线线段
const line1 = new THREE.LineCurve3(p1, p2);
// 2. 三维二次贝塞尔曲线
const curve = new THREE.QuadraticBezierCurve3(p2, p3, p4);
// 3. 3D直线线段
const line2 = new THREE.LineCurve3(p4, p5);

const CurvePath = new THREE.CurvePath(); 
// 三条线拼接为一条曲线
CurvePath.curves.push(line1, curve, line2); 

// CurvePath:路径   40:沿着轨迹细分数  2:管道半径   25:管道截面圆细分数
const geometry = new THREE.TubeGeometry(CurvePath, 50, 2, 25);
const material = new THREE.MeshLambertMaterial({
    color: 0x00ffff,
    side: THREE.DoubleSide, //双面显示看到管道内壁
});
const mesh = new THREE.Mesh(geometry, material);


export default mesh;

十、旋转成型LatheGeometry

旋转LatheGeometry

javascript 复制代码
// Vector2表示的三个点坐标,三个点构成的轮廓相当于两端直线相连接
const pointsArr = [
    new THREE.Vector2(50, 60),
    new THREE.Vector2(25, 0),
    new THREE.Vector2(50, -60)
];
// LatheGeometry:pointsArr轮廓绕y轴旋转生成几何体曲面
// pointsArr:旋转几何体的旋转轮廓形状
const geometry = new THREE.LatheGeometry(pointsArr);

旋转细分精度

javascript 复制代码
// 30:旋转圆周方向几何体细分精度
const geometry = new THREE.LatheGeometry(pointsArr, 30);

旋转角度

javascript 复制代码
// 0, Math.PI:旋转的开始角度和结束角度
const geometry = new THREE.LatheGeometry(pointsArr, 30,0, Math.PI);

曲线生成旋转轮廓

javascript 复制代码
// 通过三个点定义一个二维样条曲线
const curve = new THREE.SplineCurve([
    new THREE.Vector2(50, 60),
    new THREE.Vector2(25, 0),
    new THREE.Vector2(50, -60)
]);
javascript 复制代码
//曲线上获取点,作为旋转几何体的旋转轮廓
const pointsArr = curve.getPoints(50); 

十一、轮廓填充ShapeGeometry

javascript 复制代码
// 一组二维向量表示一个多边形轮廓坐标
const pointsArr = [
    new THREE.Vector2(-50, -50),
    new THREE.Vector2(-60, 0),
    new THREE.Vector2(0, 50),
    new THREE.Vector2(60, 0),
    new THREE.Vector2(50, -50),
]

多边形轮廓Shape

javascript 复制代码
// Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
const shape = new THREE.Shape(pointsArr);

轮廓填充几何体ShapeGeometry

javascript 复制代码
// 多边形shape轮廓作为ShapeGeometry参数,生成一个多边形平面几何体
const geometry = new THREE.ShapeGeometry(shape);

查看ShapeGeometry生成的三角形

javascript 复制代码
  wireframe:true

十二、拉伸ExtrudeGeometry

拉伸几何体ExtrudeGeometry和上节课讲到的轮廓填充几何体ShapeGeometry一样,都是基于一个基础的平面轮廓Shape进行变换,生成一个几何体。

定义一个Shape轮廓

javascript 复制代码
// Shape表示一个平面多边形轮廓
const shape = new THREE.Shape([
    // 按照特定顺序,依次书写多边形顶点坐标
    new THREE.Vector2(-50, -50), //多边形起点
    new THREE.Vector2(-50, 50),
    new THREE.Vector2(50, 50),
    new THREE.Vector2(50, -50),
]);

拉伸成型

javascript 复制代码
//拉伸造型
const geometry = new THREE.ExtrudeGeometry(
    shape, //二维轮廓
    {
        depth: 20, //拉伸长度
    }
);

拉伸倒角

倒圆角

javascript 复制代码
const geometry = new THREE.ExtrudeGeometry(
    shape,{
        depth: 20,
        bevelThickness: 5, //倒角尺寸:拉伸方向
        bevelSize: 5, //倒角尺寸:垂直拉伸方向
        bevelSegments: 20, //倒圆角:倒角细分精度,默认3
    }
);

倒直角

javascript 复制代码
const geometry = new THREE.ExtrudeGeometry(
    shape,{
        bevelSegments: 1, //倒直角
    }
);

拉伸取消默认倒角

javascript 复制代码
//拉伸造型
const geometry = new THREE.ExtrudeGeometry(
    shape, //二维轮廓
    {
        depth: 20, //拉伸长度
        bevelEnabled: false, //禁止倒角,默认true
    }
);

十三、扫描ExtrudeGeometry

扫描轮廓

javascript 复制代码
// 扫描轮廓:Shape表示一个平面多边形轮廓
const shape = new THREE.Shape([
    // 按照特定顺序,依次书写多边形顶点坐标
    new THREE.Vector2(0,0), //多边形起点
    new THREE.Vector2(0,10),
    new THREE.Vector2(10,10),
    new THREE.Vector2(10,0),
]);

扫描轨迹

javascript 复制代码
// 2.扫描轨迹:创建轮廓的扫描轨迹(3D样条曲线)
const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3( -10, -50, -50 ),
    new THREE.Vector3( 10, 0, 0 ),
    new THREE.Vector3( 8, 50, 50 ),
    new THREE.Vector3( -5, 0, 100)
]);

扫描造型

javascript 复制代码
//3.扫描造型:扫描默认没有倒角
const geometry = new THREE.ExtrudeGeometry(
    shape, //扫描轮廓
    {
        extrudePath:curve,//扫描轨迹
        steps:100//沿着路径细分精度,越大越光滑
    }
);

十四、多边形轮廓Shape简介

多边形轮廓Shape的父类Path

Shape的父类是Path,Path提供了直线、圆弧、贝塞尔、样条等绘制方法

.currentPoint属性

默认值Vector2(0,0)

javascript 复制代码
const shape = new THREE.Shape();
console.log('currentPoint',shape.currentPoint);

.moveTo()方法

javascript 复制代码
shape.moveTo(10, 0); //.currentPoint变为(10,0)
console.log("currentPoint后", shape.currentPoint);

除了.moveTo()方法,Path其他的直线、圆弧等方法也可能会改变.currentPoint属性

绘制直线.lineTo()

.lineTo()方法和.moveTo()方法,一样会改变.currentPoint属性

创建好的多边形轮廓Shape作为几何体的参数

javascript 复制代码
// ShapeGeometry填充Shape获得一个平面几何体
const geometry = new THREE.ShapeGeometry(shape);
javascript 复制代码
// ExtrudeGeometry拉伸Shape获得一个长方体几何体
const geometry = new THREE.ExtrudeGeometry(shape, {
    depth:20,//拉伸长度
    bevelEnabled:false,//禁止倒角
});

十五、多边形轮廓Shape(圆弧)

圆弧方法.arc()

圆弧.arc()参数的圆心坐标是相对当前.currentPoint而言,而不是坐标原点。

javascript 复制代码
// 下面代码绘制了一个矩形+扇形的轮廓,圆心在(100, 0),半径50。
const shape = new THREE.Shape();
shape.lineTo(100+50, 0); //.currentPoint变为(100+50,0)
// 圆弧.arc参数的圆心0,0坐标是相对当前.currentPoint而言,而不是坐标原点
shape.arc(-50,0,50,0,Math.PI/2); //.currentPoint变为圆弧线结束点坐标
console.log('currentPoint',shape.currentPoint);
// 绘制直线,直线起点:圆弧绘制结束的点  直线结束点:(0, 0)
shape.lineTo(0, 50);

直线和圆弧起点之间的缺口threejs内部会自动补上

javascript 复制代码
// 下面代码绘制了一个矩形+扇形的轮廓,圆心在(100, 0),半径50。
const shape = new THREE.Shape();
shape.lineTo(100, 0); //.currentPoint变为(100,0)
// 圆弧.arc参数的圆心0,0坐标是相对当前.currentPoint而言,而不是坐标原点
shape.arc(0,0,50,0,Math.PI/2); //.currentPoint变为圆弧线结束点坐标
console.log('currentPoint',shape.currentPoint);
// 绘制直线,直线起点:圆弧绘制结束的点  直线结束点:(0, 0)
shape.lineTo(0, 50);

Shape作为几何体参数

javascript 复制代码
// shape:填充轮廓  
const geometry = new THREE.ShapeGeometry(shape, 20);
javascript 复制代码
const geometry = new THREE.ExtrudeGeometry(shape, {
    depth:20,//拉伸长度
    bevelEnabled:false,//禁止倒角
    curveSegments:20,//shape曲线对应曲线细分数
});

绝对圆弧方法.absarc()

javascript 复制代码
// 下面代码绘制了一个矩形+扇形的轮廓,圆心在(100, 0),半径50。
const shape = new THREE.Shape();
shape.lineTo(100, 0); //.currentPoint变为(100,0)
// absarc圆心坐标不受到.currentPoint影响,以坐标原点作为参考
shape.absarc(100,0,50,0,Math.PI/2); //.currentPoint变为圆弧线结束点坐标
console.log('currentPoint',shape.currentPoint);
shape.lineTo(0, 50);

十六、多边形Shape(内孔.holes)

外轮廓

javascript 复制代码
const shape = new THREE.Shape();
// .lineTo(100, 0)绘制直线线段,线段起点:.currentPoint,线段结束点:(100,0)
shape.lineTo(100, 0);
shape.lineTo(100, 100);
shape.lineTo(0, 100);

.holes设置内孔的轮廓

javascript 复制代码
//Shape内孔轮廓
const path1 = new THREE.Path();// 圆孔1
path1.absarc(20, 20, 10);
const path2 = new THREE.Path();// 圆孔2
path2.absarc(80, 20, 10);
const path3 = new THREE.Path();// 方形孔
path3.moveTo(50, 50);
path3.lineTo(80, 50);
path3.lineTo(80, 80);
path3.lineTo(50, 80);
//三个内孔轮廓分别插入到holes属性中
shape.holes.push(path1, path2,path3);
javascript 复制代码
const geometry = new THREE.ShapeGeometry(shape);
javascript 复制代码
const geometry = new THREE.ExtrudeGeometry(shape, {
    depth:20,//拉伸长度
    bevelEnabled:false,//禁止倒角
    curveSegments:50,
});

十七、模型边界线EdgesGeometry

长方体边线

model.js

javascript 复制代码
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshLambertMaterial({
    color: 0x004444,
    transparent:true,
    opacity:0.5,
});
const mesh = new THREE.Mesh(geometry, material);


// 长方体作为EdgesGeometry参数创建一个新的几何体
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({
  color: 0x00ffff,
})
const line = new THREE.LineSegments(edges, edgesMaterial);
mesh.add(line);

圆柱边线

javascript 复制代码
const geometry = new THREE.CylinderGeometry(60, 60, 100, 30);
const edges = new THREE.EdgesGeometry(geometry);
javascript 复制代码
const edges = new THREE.EdgesGeometry(geometry,30);

外部gltf模型设置材质和边线

javascript 复制代码
loader.load("../建筑模型.gltf", function (gltf) {
    // 递归遍历设置每个模型的材质,同时设置每个模型的边线
    gltf.scene.traverse(function (obj) {
        if (obj.isMesh) {
            // 模型材质重新设置
            obj.material = new THREE.MeshLambertMaterial({
                color: 0x004444,
                transparent: true,
                opacity: 0.5,
            });
            // 模型边线设置
            const edges = new THREE.EdgesGeometry(obj.geometry);
            const edgesMaterial = new THREE.LineBasicMaterial({
                color: 0x00ffff,
            })
            const line = new THREE.LineSegments(edges, edgesMaterial);
            obj.add(line);
        }
    });
    model.add(gltf.scene);
})

十八、几何体顶点颜色数数据

几何体顶点颜色.attributes.color

javascript 复制代码
const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    50, 0, 0, //顶点2坐标
    0, 25, 0, //顶点3坐标
]);
// 顶点位置
geometry.attributes.position = new THREE.BufferAttribute(vertices, 3);

与几何体BufferGeometry顶点位置数据.attributes.position一一对应的顶点颜色数据.attributes.color

javascript 复制代码
//类型数组创建顶点颜色color数据
const colors = new Float32Array([
    1, 0, 0, //顶点1颜色
    0, 0, 1, //顶点2颜色
    0, 1, 0, //顶点3颜色
]);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB

需要设置材质属性vertexColors:true

javascript 复制代码
// 点渲染模式
const material = new THREE.PointsMaterial({
     vertexColors:true,//默认false,设置为true表示使用顶点颜色渲染
    size: 20.0, //点对象像素尺寸
});

颜色渐变(颜色插值)

自定几何体顶点颜色数据,然后用线模型Line渲染,可以看到直线的颜色是渐变的。

javascript 复制代码
// 线模型渲染几何体顶点颜色,可以看到直线颜色渐变效果
const material = new THREE.LineBasicMaterial({
    vertexColors:true,//默认false,设置为true表示使用顶点颜色渲染
});
const line = new THREE.Line(geometry, material);

网格模型颜色渐变

javascript 复制代码
// 定义好几何体顶点颜色,使用Mesh渲染,Mesh产生颜色渐变效果
const material = new THREE.MeshBasicMaterial({
    vertexColors:true,//默认false,设置为true表示使用顶点颜色渲染
    side: THREE.DoubleSide, //两面可见
});
const mesh = new THREE.Mesh(geometry, material); 

十九、一段曲线颜色渐变

样条曲线几何体

javascript 复制代码
const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
// 三维样条曲线
const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-50, 20, 90),
    new THREE.Vector3(-10, 40, 40),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(60, -60, 0),
    new THREE.Vector3(70, 0, 80)
]);
const pointsArr = curve.getSpacedPoints(100); //曲线取点      
geometry.setFromPoints(pointsArr); //pointsArr赋值给顶点位置属性 

设置几何体顶点颜色.attributes.color

javascript 复制代码
const pos = geometry.attributes.position;
const count = pos.count; //顶点数量
// 计算每个顶点的颜色值
const colorsArr = [];
for (let i = 0; i < count; i++) {
    const percent = i / count; //点索引值相对所有点数量的百分比
    //根据顶点位置顺序大小设置颜色渐变
    // 红色分量从0到1变化,蓝色分量从1到0变化
    colorsArr.push(percent, 0, 1 - percent); //蓝色到红色渐变色
}

//类型数组创建顶点颜色color数据
const colors = new Float32Array(colorsArr);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3);

线模型渲染渐变色曲线

javascript 复制代码
const material = new THREE.LineBasicMaterial({
    vertexColors: true, //使用顶点颜色渲染
});
const line = new THREE.Line(geometry, material);

二十、Color颜色渐变插值

javascript 复制代码
const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
// 三维样条曲线
const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-50, 20, 90),
    new THREE.Vector3(-10, 40, 40),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(60, -60, 0),
    new THREE.Vector3(70, 0, 80)
]);
const pointsArr = curve.getSpacedPoints(100); //曲线取点      
geometry.setFromPoints(pointsArr); //pointsArr赋值给顶点位置属性     

颜色对象Color颜色插值方法.lerpColors()

执行.lerpColors(Color1,Color2, percent)通过一个百分比参数percent,可以控制Color1和Color2两种颜色混合的百分比,Color1对应1-percent,Color2对应percent

javascript 复制代码
const c1 = new THREE.Color(0xff0000); //红色
const c2 = new THREE.Color(0x0000ff); //蓝色
const c = new THREE.Color();
javascript 复制代码
c.lerpColors(c1,c2, 0);
console.log('颜色插值结果',c);
javascript 复制代码
c.lerpColors(c1,c2, 0.5);
console.log('颜色插值结果',c);

颜色对象Color颜色插值方法.lerp()

javascript 复制代码
c1.lerp(c2, percent);

颜色克隆.clone()

javascript 复制代码
const c = c1.clone().lerp(c2, percent);//颜色插值计算

二十一、查看或设置gltf几何体顶点

获取gltf模型几何体顶点数据

javascript 复制代码
loader.load("../地形.glb", function (gltf) { //gltf加载成功后返回一个对象
    model.add(gltf.scene); //三维场景添加到model组对象中
    //mesh表示地形网格模型
    const mesh = gltf.scene.children[0];
    // 顶点数据
    const att = mesh.geometry.attributes;
    console.log('att', att);
    // 顶点位置数据
    const pos = mesh.geometry.attributes.position;
    console.log('pos', pos);
})

几何体顶点索引属性geometry.index

javascript 复制代码
console.log('index', mesh.geometry.index);

顶点数量BufferAttribute.count

javascript 复制代码
const count = pos.count; //几何体顶点数量
console.log('count', count);

.getX().getY().getZ()

javascript 复制代码
  // 获取几何体第一个顶点的x坐标
  const x = pos.getX(0);
  console.log("第一个顶点的x", x);

.setX().setY().setZ()

pos.setY(0,100)把索引为0,也就是第一个顶点的y坐标设置为100.

javascript 复制代码
pos.setX(0,100);

批量重置几何体顶点y坐标

重置前

javascript 复制代码
loader.load("../地形.glb", function (gltf) {
    model.add(gltf.scene);
    //mesh表示地形网格模型
    const mesh = gltf.scene.children[0];
    // 顶点位置数据
    const pos = mesh.geometry.attributes.position;
    const count = pos.count; //几何体顶点数量
    // 批量设置所有几何体顶点位置的y坐标
    for (let i = 0; i < count; i++) {
        const y = pos.getY(i);//获取第i+1个顶点y坐标
        pos.setY(i,y*2)//设置第i+1个顶点y坐标为自身2倍
    }
})

重置后

二十二、山脉地形高度可视化

山脉几何体y坐标范围

javascript 复制代码
// 1. 计算模型y坐标高度差
    const yArr = [];//顶点所有y坐标,也就是地形高度
    for (let i = 0; i < count; i++) {
        yArr.push(pos.getY(i));//获取顶点y坐标,也就是地形高度
    }
    yArr.sort();//数组元素排序,从小到大
    const miny = yArr[0];//y最小值
    const maxy = yArr[yArr.length - 1];//y最大值
    const height = maxy - miny; //山脉整体高度 

根据山脉顶点高度设置渐变颜色

javascript 复制代码
// 2. 计算每个顶点的颜色值
const colorsArr = [];
const c1 = new THREE.Color(0x0000ff);//山谷颜色
const c2 = new THREE.Color(0xff0000);//山顶颜色
for (let i = 0; i < count; i++) {
    //当前高度和整体高度比值
    const percent = (pos.getY(i) - miny) / height;
    const c = c1.clone().lerp(c2, percent);//颜色插值计算
    colorsArr.push(c.r, c.g, c.b); 
}
const colors = new Float32Array(colorsArr);
// 设置几何体attributes属性的颜色color属性
mesh.geometry.attributes.color = new THREE.BufferAttribute(colors, 3);

Mesh渲染山脉顶点颜色

javascript 复制代码
// 3. 设置材质,使用顶点颜色渲染
mesh.material = new THREE.MeshLambertMaterial({
    vertexColors:true,
});

三种颜色渐变

model.js

javascript 复制代码
// 引入Three.js
import * as THREE from 'three';
// 引入gltf模型加载库GLTFLoader.js
import {
    GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';


const loader = new GLTFLoader(); //创建一个GLTF加载器

const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景

loader.load("../地形.glb", function (gltf) { //gltf加载成功后返回一个对象

    model.add(gltf.scene); //三维场景添加到model组对象中
    //mesh表示地形网格模型
    const mesh = gltf.scene.children[0];
    const pos = mesh.geometry.attributes.position;
    const count = pos.count; //几何体顶点数量

    // 1. 计算模型y坐标高度差
    const yArr = []; //顶点所有y坐标,也就是地形高度
    for (let i = 0; i < count; i++) {
        yArr.push(pos.getY(i)); //获取顶点y坐标,也就是地形高度
    }
    yArr.sort();//数组元素排序,从小到大
    const miny = yArr[0];//y最小值
    const maxy = yArr[yArr.length - 1];//y最大值
    
    const height = maxy - miny; //山脉整体高度  

    // 2. 计算每个顶点的颜色值
    const colorsArr = [];
    const color1 = new THREE.Color(0x0000ff); //山谷颜色
    const color2 = new THREE.Color(0x00ff00); //山腰颜色
    const color3 = new THREE.Color(0xff0000); //山顶颜色
    for (let i = 0; i < count; i++) {
        //当前高度和整体高度比值
        const percent = (pos.getY(i) - miny) / height;
        // 颜色插值计算
        let c;
        if (percent <= 0.5) { //0.5作为颜色插值分界点
            // color1到color2之间插值
            c = color1.clone().lerp(color2, percent * 2)
        } else {
            // color2到color3之间插值
            c = color2.clone().lerp(color3, (percent - 0.5) * 2)
        }
        colorsArr.push(c.r, c.g, c.b);
    }
    const colors = new Float32Array(colorsArr);
    // 设置几何体attributes属性的颜色color属性
    mesh.geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
    // 3. 设置材质,使用顶点颜色渲染
    mesh.material = new THREE.MeshLambertMaterial({
        vertexColors: true,
    });
})


export default model;

Threejs (10)

一、正投影相机

渲染范围设置

javascript 复制代码
// 正投影相机
const width = window.innerWidth; //canvas画布宽度
const height = window.innerHeight; //canvas画布高度
const k = width / height; //canvas画布宽高比
const s = 600;//控制left, right, top, bottom范围大小
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);

相机位置和观察目标设置

javascript 复制代码
camera.position.set(1000, 2000, 1000);// 设置相机的 x、y、z 坐标值
camera.lookAt(0, 0, 0);//指向坐标原点

改变观察目标

正投影相机和透视投影相机区别

透视投影 相机模拟人在空中俯视地面的效果,如果使用正投影相机渲染效果就不太自然

透视投影

正投影

二、正投影相机-Canvas尺寸变化

WebGL渲染器更新Canvas画布尺寸

javascript 复制代码
// 画布跟随窗口变化
window.onresize = function () {
    renderer.setSize(window.innerWidth, window.innerHeight);

};

相机参数更新

当canvas画布尺寸发生变化的时候,需要更新透视投影相机PerspectiveCamera.aspect属性。

javascript 复制代码
// 画布跟随窗口变化
window.onresize = function () {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
};

正投影相机OrthographicCameraleftright属性受到canvas画布宽高比影响,所以需要随着canvas画布更新。

javascript 复制代码
// Canvas画布跟随窗口变化
window.onresize = function () {
    const width = window.innerWidth; //canvas画布宽度
    const height = window.innerHeight; //canvas画布高度
    // 1. WebGL渲染器渲染的Cnavas画布尺寸更新
    renderer.setSize(width, height);
    // 2.1.更新相机参数
    const k = width / height; //canvas画布宽高比
    camera.left = -s*k;
    camera.right = s*k;
    // 2.2.相机的left, right, top, bottom属性变化了,通知threejs系统
    camera.updateProjectionMatrix();
};

三、包围盒Box3

包围盒Box3

javascript 复制代码
const box3 = new THREE.Box3()
console.log('box3',box3);
box3.min = new THREE.Vector3(-10, -10,0);
box3.max = new THREE.Vector3(100, 20,50);

计算模型最小包围盒.expandByObject()

javascript 复制代码
const box3 = new THREE.Box3();
box3.expandByObject(mesh); // 计算模型包围盒
console.log("查看包围盒", box3);

包围盒尺寸.getSize()

javascript 复制代码
const scale = new THREE.Vector3();
// getSize()计算包围盒尺寸
// 获得包围盒长宽高尺寸,结果保存在参数三维向量对象scale中
box3.getSize(scale);
console.log("模型包围盒尺寸", scale);

包围盒几何中心.getCenter()

javascript 复制代码
// 计算包围盒中心坐标
const center = new THREE.Vector3()
box3.getCenter(center)
console.log('模型中心坐标', center);

四、地图案例(包围盒、正投影)

data.js

javascript 复制代码
export default [
  // 河南边界轮廓边界经纬度坐标
  [110.3906, 34.585],
  [110.8301, 34.6289],
  [111.1816, 34.8047],
  [111.5332, 34.8486],
  [111.7969, 35.0684],
  [112.0605, 35.0684],
  [112.0605, 35.2881],
  [112.7637, 35.2002],
  [113.1152, 35.332],
  [113.6426, 35.6836],
  [113.7305, 36.3428],
  [114.873, 36.123],
  [114.9609, 36.0791],
  [115.1367, 36.2109],
  [115.3125, 36.0791],
  [115.4883, 36.167],
  [115.3125, 35.8154],
  [116.1035, 36.0791],
  [115.4883, 35.7275],
  [115.2246, 35.4199],
  [115.0488, 35.376],
  [114.7852, 35.0684],
  [115.4004, 34.8486],
  [115.5762, 34.585],
  [116.1914, 34.585],
  [116.1914, 34.4092],
  [116.543, 34.2773],
  [116.6309, 33.9258],
  [116.1914, 33.7061],
  [116.0156, 33.9697],
  [115.6641, 34.0576],
  [115.5762, 33.9258],
  [115.5762, 33.6621],
  [115.4004, 33.5303],
  [115.3125, 33.1787],
  [114.873, 33.1348],
  [114.873, 33.0029],
  [115.1367, 32.8711],
  [115.2246, 32.6074],
  [115.5762, 32.4316],
  [115.8398, 32.5195],
  [115.9277, 31.7725],
  [115.4883, 31.6846],
  [115.4004, 31.4209],
  [115.2246, 31.4209],
  [115.1367, 31.5967],
  [114.7852, 31.4648],
  [114.6094, 31.5527],
  [114.5215, 31.7725],
  [114.1699, 31.8604],
  [113.9941, 31.7725],
  [113.8184, 31.8604],
  [113.7305, 32.4316],
  [113.4668, 32.2998],
  [113.2031, 32.4316],
  [112.3242, 32.3438],
  [111.5332, 32.6074],
  [111.0059, 33.2666],
  [111.0059, 33.5303],
  [110.6543, 33.8379],
  [110.6543, 34.1455],
  [110.4785, 34.2334],
  [110.3906, 34.585],
];

model.js

javascript 复制代码
const pointsArr = []; // 一组二维向量表示一个多边形轮廓坐标
data.forEach(function (e) {
  // data坐标数据转化为Vector2构成的顶点数组
  const v2 = new THREE.Vector2(e[0], e[1]);
  pointsArr.push(v2);
});
// Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
const shape = new THREE.Shape(pointsArr);
// 多边形shape轮廓作为ShapeGeometry参数,生成一个多边形平面几何体
const geometry = new THREE.ShapeGeometry(shape);

const material = new THREE.MeshLambertMaterial({
  color: 0xffffff,
  side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh


export default mesh;

选择相机

javascript 复制代码
// 正投影相机
const width = window.innerWidth; //canvas画布宽度
const height = window.innerHeight; //canvas画布高度
const k = width / height; //canvas画布宽高比
const s = 50; //控制left, right, top, bottom范围大小
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);
camera.position.set(300, 300, 300); 
camera.lookAt(0, 0, 0); //指向坐标原点

河南地图并没有居中显示,在canvas画布上显示的效果也比较小。

包围盒Box3计算模型的中心位置和尺寸

javascript 复制代码
// 包围盒计算模型对象的大小和位置
const box3 = new THREE.Box3();
box3.expandByObject(mesh); // 计算模型包围盒
const size = new THREE.Vector3();
box3.getSize(size); // 计算包围盒尺寸
// console.log('模型包围盒尺寸',size);
const center = new THREE.Vector3();
box3.getCenter(center); // 计算包围盒中心坐标
// console.log('模型中心坐标',center);

地图居中显示

设置相机.lookAt()参数指向包围盒几何中心即可。

javascript 复制代码
const x = 113.51,y = 33.88;
camera.lookAt(x, y, 0);

注意如果使用了OrbitControls,需要设置.target.lookAt()参数是相同坐标。

javascript 复制代码
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(x, y, 0); //与lookAt参数保持一致
controls.update();//update()函数内会执行camera.lookAt(controls.target)

地图基本填充cnavas画布

把正投影相机控制上下左右的变量s,设置为地图模型整体尺寸的一半左右即可。

javascript 复制代码
// const s = 50; //控制left, right, top, bottom范围大小
const s = 2.5;//根据包围盒大小调整s,包围盒y方向尺寸的一半左右
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);

地图平行于canvas画布

javascript 复制代码
camera.position.set(x, y, 300);

五、相机动画(.position和.lookAt())

相机运动动画

javascript 复制代码
// 渲染循环
function render() {
    camera.position.z -= 0.3;//相机直线运动动画
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

相机圆周运动

javascript 复制代码
// 渲染循环
let angle = 0; //用于圆周运动计算的角度值
const R = 100; //相机圆周运动的半径
function render() {
    angle += 0.01;
    // 相机y坐标不变,在XOZ平面上做圆周运动
    camera.position.x = R * Math.cos(angle);
    camera.position.z = R * Math.sin(angle);
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

执行lookAt()计算相机视线方向

改变.position属性后,如果不执行.lookAt()方法,相机的观察方向默认不变。

javascript 复制代码
// .position改变,重新执行lookAt(0,0,0)计算相机视线方向
    camera.lookAt(0,0,0);

六、不同方向的投影视图

javascript 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Three.js中文网:www.webgl3d.cn</title>
    <style>
        body {
            overflow: hidden;
            margin: 0px;
        }

        .bu {
            background: #393535;
            width: 60px;
            height: 60px;
            text-align: center;
            color: #ffffff;
            display: inline-block;
            background-color: rgb(100, 91, 91);
            border-radius: 30px;
        }

        .bu:hover {
            background-color: rgb(150, 141, 141);
        }

        .pos {
            position: absolute;
            left: 50%;
            margin-left: -65px;
            bottom: 100px;
        }
    </style>
</head>

<body>
    <div class="pos">
        <div id="x"
            class="bu">x</div>
        <div id="y"
            class="bu"
            style="margin-left: 10px;">y</div>
        <div id="z"
            class="bu"
            style="margin-left: 10px;">z</div>
    </div>
    <!-- type="importmap"功能:.html文件中也能和nodejs开发环境中一样方式,引入npm安装的js库 -->
    <script type="importmap">
        {
			"imports": {
				"three": "../../../three.js/build/three.module.js",
                "three/addons/": "../../../three.js/examples/jsm/"
			}
		}
	</script>
    <script src="./index.js"
        type="module">
        </script>
</body>

</html>

x轴方向观察

javascript 复制代码
document.getElementById("x").addEventListener("click", function () {
  camera.position.set(500, 0, 0); //x轴方向观察
  camera.lookAt(0, 0, 0); //重新计算相机视线方向
});

y轴方向观察

javascript 复制代码
// 通过UI按钮改变相机观察角度
document.getElementById("y").addEventListener("click", function () {
  camera.position.set(0, 500, 0); //y轴方向观察
  camera.lookAt(0, 0, 0); //重新计算相机视线方向
});

z轴方向观察

javascript 复制代码
// 通过UI按钮改变相机观察角度
document.getElementById('z').addEventListener('click', function () {
    camera.position.set(0, 0, 500); //z轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})

七、旋转渲染结果(.up相机上方向)

测试改变上方向.up

注意执行顺序问题

注意.up属性和.position属性一样,如果在.lookAt()执行之后改变,需要重新执行.lookAt()

八、管道漫游案例

管道模型

javascript 复制代码
// 三维样条曲线
const path = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-50, 20, 90),
    new THREE.Vector3(-10, 40, 40),
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(60, -60, 0),
    new THREE.Vector3(90, -40, 60),
    new THREE.Vector3(120, 30, 30),
]);
// 样条曲线path作为TubeGeometry参数生成管道
const geometry = new THREE.TubeGeometry(path, 200, 5, 30);
const texLoader = new THREE.TextureLoader(); 
//纹理贴图
const texture = texLoader.load('./diffuse.jpg');
//UV坐标U方向阵列模式
texture.wrapS = THREE.RepeatWrapping;
//纹理沿着管道方向阵列(UV坐标U方向)
texture.repeat.x = 10;
const material = new THREE.MeshLambertMaterial({
    map:texture,
    side: THREE.DoubleSide, //双面显示看到管道内壁
});
const mesh = new THREE.Mesh(geometry, material);

相机选择透视投影相机

获得运动轨迹上的顶点

model.js

javascript 复制代码
// 从曲线上等间距获取一定数量点坐标
const pointsArr = path.getSpacedPoints(500);
console.log("pointsArr:", pointsArr);
javascript 复制代码
export {mesh,pointsArr};

相机放在管道内轨迹线上

index.js

javascript 复制代码
import {mesh,pointsArr} from './model.js'; //模型对象
// 从曲线上等间距获取一定数量点坐标
const pointsArr = path.getSpacedPoints(500);
const i = 100;
// 相机位置:曲线上当前点pointsArr[i]
camera.position.copy(pointsArr[i]);
// 相机观察目标:当前点的下一个点pointsArr[i + 1]
camera.lookAt(pointsArr[i + 1]);

改变视场角度fov调节渲染效果

javascript 复制代码
const camera = new THREE.PerspectiveCamera(90, width / height, 1, 3000);

相机控件.target.lookAt()参数一致

javascript 复制代码
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.copy(pointsArr[i+1]);
controls.update();
javascript 复制代码
// 从曲线上等间距获取一定数量点坐标
const pointsArr = path.getSpacedPoints(500);
// 渲染循环
let i = 0; //在渲染循环中累加变化
function render() {
    if (i < pointsArr.length - 1) {
        // 相机位置设置在当前点位置
        camera.position.copy(pointsArr[i]);
        // 曲线上当前点pointsArr[i]和下一个点pointsArr[i+1]近似模拟当前点曲线切线
        // 设置相机观察点为当前点的下一个点,相机视线和当前点曲线切线重合
        camera.lookAt(pointsArr[i + 1]);
        i += 1; //调节速度
    } else {
        i = 0
    }
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

九、9.OrbitControls旋转缩放限制

禁止右键平移.enablePan属性

index.js

javascript 复制代码
controls.enablePan = false; //禁止右键拖拽

禁止缩放或旋转

javascript 复制代码
controls.enableZoom = false;//禁止缩放
javascript 复制代码
controls.enableRotate = false; //禁止旋转

OrbitControls.target属性

相机控件OrbitControls.target属性对应的就是相机的.lookAt()观察目标。

执行controls.update();,相机控件内部会执行camera.lookAt(controls.target)

javascript 复制代码
// controls.target默认值是坐标原点
controls.target.set(x, y, z);
//update()函数内会执行camera.lookAt(x, y, z)
controls.update(); 

透视投影相机缩放范围

.minDistance表示相机位置.position和相机目标观察点controls.target的最小距离。

javascript 复制代码
//相机位置与观察目标点最小值
controls.minDistance = 200;

.maxDistance表示相机位置.position和相机目标观察点controls.target的最大距离。

javascript 复制代码
//相机位置与观察目标点最大值
controls.maxDistance = 500;

正投影缩放范围

javascript 复制代码
// 缩放范围
controls.minZoom = 0.5;
controls.maxZoom = 2;

相机位置与目标观察点距离.getDistance()

javascript 复制代码
//相机位置与目标观察点距离
const dis = controls.getDistance();
console.log('dis',dis);

可视化设置相机缩放范围

javascript 复制代码
controls.addEventListener('change',function(){
    //相机位置与目标观察点距离
    const dis = controls.getDistance();
    console.log('dis',dis);
})

设置旋转范围

javascript 复制代码
// 上下旋转范围
controls.minPolarAngle = 0;//默认值0
controls.maxPolarAngle = Math.PI;//默认值Math.PI

.maxPolarAngle属性设置为90度,这样不能看到工厂模型底部

javascript 复制代码
controls.maxPolarAngle = Math.PI/2;

通过.minAzimuthAngle.maxAzimuthAngle属性控制左右的旋转范围。

javascript 复制代码
// 左右旋转范围
controls.minAzimuthAngle = -Math.PI/2;
controls.maxAzimuthAngle = Math.PI/2;

十、相机控件MapControls

引入相机控件MapControls

javascript 复制代码
// 引入相机控件`MapControls`
import { MapControls } from 'three/addons/controls/MapControls.js';

使用MapControls

javascript 复制代码
const controls = new MapControls(camera, renderer.domElement);

MapControls本质

javascript 复制代码
controls.addEventListener('change', function () {
    // 鼠标右键旋转时候,查看.position变化
    // 鼠标左键拖动的时候,查看.position、.target的位置会变化
    console.log('camera.position',camera.position);
    console.log('controls.target',controls.target);
});

缩放、旋转或平移禁止

整体思路是一样的,只是鼠标操作有差异。

javascript 复制代码
controls.enablePan = false; //禁止平移
javascript 复制代码
controls.enableZoom = false;//禁止缩放
javascript 复制代码
controls.enableRotate = false; //禁止旋转

透视投影相机缩放范围

javascript 复制代码
//相机位置与观察目标点最小值
controls.minDistance = 200;
//相机位置与观察目标点最大值
controls.maxDistance = 500;

设置旋转范围

javascript 复制代码
// 上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI/2;
javascript 复制代码
// 左右旋转范围
controls.minAzimuthAngle = -Math.PI/2;
controls.maxAzimuthAngle = Math.PI/2;
相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习