阅读视图

发现新文章,点击刷新页面。

贝塞尔曲线:让计算机画出丝滑曲线的魔法

想象一下,如果你让计算机画一条曲线,它可能会像个刚学画画的孩子,画出的线条要么僵硬得像铁丝,要么歪歪扭扭如同毛毛虫。但有了贝塞尔曲线,计算机突然就像掌握了绘画技巧的艺术家,能画出从字体轮廓到动画路径的各种丝滑线条。今天我们就来揭开这个让计算机变身为 "曲线大师" 的秘密。

从点到线:贝塞尔曲线的底层逻辑

贝塞尔曲线的核心原理其实很简单:用几个控制点 "拉扯" 出一条平滑曲线。就像你用手指捏住绳子的几个点,轻轻一拉就能得到自然的弧线。这背后藏着一种叫 "插值" 的数学思想 —— 通过已知的点,算出中间该有的样子。

最基础的是一次贝塞尔曲线,说穿了就是直线。取两个点,比如 (0,0) 和 (100,100),连接它们的线段就是一次贝塞尔曲线。这时候你可能会说:"这有什么了不起?" 别急,精彩的在后面。

当我们增加到三个点时,就得到了二次贝塞尔曲线。想象中间那个点是个 "磁铁",它会把直线段往自己这边吸,形成一条优美的抛物线。三个点分工明确:起点和终点固定曲线的两端,中间的控制点则决定了曲线的弯曲程度 —— 离直线越远,曲线弯得越厉害,就像有人在中间用力拽了一把。

让曲线更灵活:高阶贝塞尔曲线

三次贝塞尔曲线是应用最广泛的,它有四个控制点:起点、终点和两个中间控制点。这两个中间控制点就像两个方向舵,能让曲线做出更复杂的转弯。你可以把它想象成一条被两个人从不同方向拉扯的绳子,最终形成的形状取决于两人用力的方向和大小。

更高阶的贝塞尔曲线原理类似,只是增加了更多控制点。但有趣的是,在实际应用中,我们很少用到五阶以上的曲线。这就像做菜,加太多调料反而会破坏原本的味道,三个到四个控制点已经能满足绝大多数设计需求了。

数学背后的小秘密

贝塞尔曲线的数学表达其实是一系列多项式的组合,但我们可以用更形象的方式理解:曲线上每个点的位置,都是由所有控制点按一定比例 "混合" 而成的

以三次贝塞尔曲线为例,想象有一辆小车从起点开往终点,行驶过程中会受到两个中间控制点的 "引力" 影响。刚出发时,起点的引力最大,小车几乎直线冲向第一个控制点;随着前进,第一个控制点的引力逐渐减弱,第二个控制点的引力逐渐增强;快到终点时,终点的引力变成主导,小车会从第二个控制点的方向平滑地驶入终点。整个过程就像一场精心编排的舞蹈,每个控制点都在特定时刻发挥着恰到好处的作用。

这种 "混合" 比例遵循着类似二项式展开的规律,每个控制点的影响力随曲线位置呈现平滑的增减变化,这正是曲线能保持连续光滑的关键。

用代码画出贝塞尔曲线

让我们用 JavaScript 来实践一下,通过 Canvas 绘制一条三次贝塞尔曲线:

// 获取画布元素
const canvas = document.getElementById('bezierCanvas');
const ctx = canvas.getContext('2d');
// 设置画布尺寸
canvas.width = 600;
canvas.height = 400;
// 定义四个控制点
const startPoint = { x: 50, y: 200 };         // 起点
const controlPoint1 = { x: 200, y: 50 };      // 第一个控制点
const controlPoint2 = { x: 400, y: 350 };     // 第二个控制点
const endPoint = { x: 550, y: 200 };          // 终点
// 绘制辅助线和控制点(帮助理解)
ctx.strokeStyle = '#cccccc';
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
ctx.lineTo(controlPoint1.x, controlPoint1.y);
ctx.lineTo(controlPoint2.x, controlPoint2.y);
ctx.lineTo(endPoint.x, endPoint.y);
ctx.stroke();
// 绘制控制点标记
[startPoint, controlPoint1, controlPoint2, endPoint].forEach((point, index) => {
    ctx.fillStyle = index === 0 || index === 3 ? 'green' : 'red';
    ctx.beginPath();
    ctx.arc(point.x, point.y, 6, 0, Math.PI * 2);
    ctx.fill();
});
// 绘制贝塞尔曲线(这才是主角!)
ctx.strokeStyle = '#3366ff';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
// 核心API:绘制三次贝塞尔曲线
ctx.bezierCurveTo(
    controlPoint1.x, controlPoint1.y,
    controlPoint2.x, controlPoint2.y,
    endPoint.x, endPoint.y
);
ctx.stroke();

运行这段代码,你会看到一条蓝色的平滑曲线,旁边还有灰色的辅助线连接着四个控制点。绿色的是起点和终点,红色的是中间控制点。试着修改控制点的坐标值,你会发现曲线的形状会随之发生奇妙的变化 —— 这就是贝塞尔曲线的魅力所在。

动画中的贝塞尔魔法

在动画领域,贝塞尔曲线更是不可或缺的工具。当你看到一个物体先加速后减速的自然运动,或者一个元素平滑地转弯绕行时,很可能就是贝塞尔曲线在背后默默工作。

比如下面这个简单的动画示例,让一个小球沿着贝塞尔曲线运动:

const ball = document.getElementById('ball');
let time = 0;
function updateBallPosition() {
    // 计算当前时间在动画中的比例(0到1之间)
    time += 0.01;
    if (time > 1) time = 0;
    
    const t = time;
    // 三次贝塞尔曲线的位置计算公式(简化版)
    const cx = 3 * (1 - t) * (1 - t) * t * controlPoint1.x 
             + 3 * (1 - t) * t * t * controlPoint2.x 
             + t * t * t * endPoint.x 
             + (1 - t) * (1 - t) * (1 - t) * startPoint.x;
             
    const cy = 3 * (1 - t) * (1 - t) * t * controlPoint1.y 
             + 3 * (1 - t) * t * t * controlPoint2.y 
             + t * t * t * endPoint.y 
             + (1 - t) * (1 - t) * (1 - t) * startPoint.y;
             
    // 更新小球位置
    ball.style.left = `${cx}px`;
    ball.style.top = `${cy}px`;
    
    requestAnimationFrame(updateBallPosition);
}
// 开始动画
updateBallPosition();

这段代码通过不断计算小球在贝塞尔曲线上的位置,让它看起来像是沿着一条平滑的路径运动。你可以调整控制点的位置,让小球做出各种有趣的轨迹 —— 直线、弧线、S 形曲线,甚至是看似不可能的急转弯。

无处不在的贝塞尔曲线

贝塞尔曲线的应用远不止于此:从你手机上的图标设计到汽车的流线型车身,从字体的优美轮廓到地图上的路线规划,都能看到它的身影。每当你在屏幕上画出一条平滑的线条,或者看到一个自然流畅的动画时,不妨想一想:这背后是不是有贝塞尔曲线在施展魔法?

下次当你再看到那些令人赞叹的数字设计时,或许会对它们多一份理解和欣赏 —— 因为你知道,那些看似复杂的曲线背后,其实是几个控制点和一段精妙的数学逻辑共同谱写的优雅篇章。

Three.js 贴图:给 3D 世界穿上花衣裳

在 Three.js 的魔法世界里,我们搭建的 3D 模型就像一个个等待盛装出席舞会的 “裸模”。它们光秃秃地站在那里,虽然已经有了迷人的身材(几何形状)和挺拔的身姿(空间位置),但总觉得缺了点什么 —— 没错,就是那件能让它们惊艳全场的 “衣裳”,而这件衣裳,就是我们今天要讲的贴图

一、揭开贴图的神秘面纱

从底层原理来看,贴图本质上就是一张二维图像,它会被 “包裹” 在三维模型表面,给模型赋予丰富的视觉细节。这就好比你给一个素色的陶瓷花瓶贴上精美的贴纸,原本平淡无奇的花瓶瞬间变得五彩斑斓。在计算机的世界里,显卡就像是一位心灵手巧的裁缝,它会按照一定的规则,把二维图像准确地 “缝制” 到三维模型上。

1.1 贴图的基本类型

Three.js 支持多种类型的贴图,常见的有纹理贴图法线贴图高光贴图等。

  • 纹理贴图:这是最基础、最常用的贴图类型,就像给模型贴上一张高清照片。比如你想创建一个木纹桌面,只需要找一张逼真的木纹图片作为纹理贴图,贴到桌面模型上,瞬间就能让桌面看起来质感十足。
// 加载纹理贴图
const textureLoader = new THREE.TextureLoader();
const woodTexture = textureLoader.load('wood.jpg');
// 创建材质并应用纹理
const material = new THREE.MeshBasicMaterial({ map: woodTexture });
  • 法线贴图:它就像给模型添加了 “凹凸滤镜”,通过改变模型表面的光照计算方式,让模型看起来有凹凸不平的效果。想象一下,你在平面上贴了一张有岩石纹路的法线贴图,原本平滑的表面在光线照射下,就能呈现出岩石那种坑坑洼洼的立体感,而实际上模型的几何形状并没有改变。
const normalMapLoader = new THREE.TextureLoader();
const normalMap = normalMapLoader.load('rock_normal.jpg');
const bumpMaterial = new THREE.MeshPhongMaterial({ 
    map: woodTexture,
    normalMap: normalMap,
    normalScale: new THREE.Vector2(1, 1) 
});
  • 高光贴图:决定了模型表面哪些地方更亮、更反光,就像是给模型的 “皮肤” 调整光泽度。比如制作一个金属质感的物体,通过高光贴图可以让它看起来闪闪发亮,仿佛镀了一层金属膜。
const specularMapLoader = new THREE.TextureLoader();
const specularMap = specularMapLoader.load('metal_specular.jpg');
const shinyMaterial = new THREE.MeshPhongMaterial({ 
    map: metalTexture,
    specularMap: specularMap,
    specular: 0x111111 
});

二、贴图的 “穿衣” 过程

当我们把贴图加载好之后,该怎么让它准确地 “穿” 到模型身上呢?这就涉及到UV 映射。UV 映射可以理解为给三维模型制作的 “裁剪图”,它定义了二维贴图上的每个点对应到三维模型表面的位置。

想象你要把一张世界地图贴到一个地球仪上,如果随便乱贴,肯定会变得乱七八糟。UV 映射就是告诉你,地图上的某个角落应该贴在地球仪的北极,另一个地方应该贴在赤道附近。在 Three.js 中,很多基础的几何模型都已经内置了合理的 UV 映射,我们可以直接使用。但如果是自定义的复杂模型,可能就需要手动调整 UV 映射了。

// 创建一个立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 为立方体指定材质(包含纹理贴图)
const cubeMaterial = new THREE.MeshBasicMaterial({ map: someTexture });
const cube = new THREE.Mesh(geometry, cubeMaterial);

三、进阶技巧:让贴图更 “丝滑”

3.1 纹理重复与偏移

有时候,我们的贴图尺寸可能不够大,覆盖不了整个模型,或者想实现一些有趣的图案效果,这时候就可以用到纹理重复和偏移。

  • 纹理重复:就像用同一块瓷砖铺满整个地面,通过设置repeat属性,可以让贴图在模型表面重复显示。
woodTexture.wrapS = THREE.RepeatWrapping;
woodTexture.wrapT = THREE.RepeatWrapping;
woodTexture.repeat.set(2, 2); // 在UV两个方向上都重复2次
  • 纹理偏移:则是把贴图在模型表面 “挪动” 一下位置,通过offset属性来实现。比如你想让木纹图案从模型的某个角落开始显示,就可以调整偏移值。
woodTexture.offset.set(0.5, 0.5); // 让纹理在UV方向上都偏移0.5

3.2 纹理过滤

当模型离我们很远或者进行快速移动时,为了防止贴图出现模糊、锯齿等难看的效果,我们需要设置纹理过滤。纹理过滤就像是给贴图加上一个 “美颜滤镜”,让它在各种情况下都能保持良好的视觉效果。Three.js 提供了多种过滤方式,如THREE.NearestFilter(最近邻过滤,速度快但可能有锯齿)、THREE.LinearFilter(线性过滤,效果平滑但计算稍复杂)等。

const filteredTexture = new THREE.TextureLoader().load('image.jpg');
filteredTexture.minFilter = THREE.LinearFilter;
filteredTexture.magFilter = THREE.LinearFilter;

四、实战演练:打造一个奇幻小屋

现在,我们就用刚刚学到的知识,来搭建一个充满童话色彩的小屋。

  1. 加载屋顶的瓦片纹理贴图和墙壁的砖块纹理贴图。
const roofTexture = textureLoader.load('tiles.jpg');
const wallTexture = textureLoader.load('bricks.jpg');
  1. 创建屋顶和墙壁的几何模型,并分别赋予对应的材质。
// 屋顶
const roofGeometry = new THREE.ConeGeometry(5, 2, 32);
const roofMaterial = new THREE.MeshBasicMaterial({ map: roofTexture });
const roof = new THREE.Mesh(roofGeometry, roofMaterial);
// 墙壁
const wallGeometry = new THREE.BoxGeometry(4, 3, 4);
const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTexture });
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
  1. 将屋顶和墙壁组合在一起,调整位置和角度,一个可爱的小屋就诞生啦!

在 Three.js 的世界里,贴图就像是我们手中的调色盘和画笔,能让原本单调的 3D 模型变得栩栩如生。掌握了贴图的应用技巧,你就能成为这个魔法世界里的顶级设计师,创造出无数令人惊叹的作品。快去发挥你的创意,给你的 3D 模型们都穿上华丽的 “衣裳” 吧!

上述内容从多方面展示了 Three.js 贴图应用。若你对某个部分想深入了解,或有其他功能添加需求,欢迎随时告诉我。

GPU 编程:用 CUDA 和 OpenCL 解锁算力超能力

想象一下,你的电脑里住着一群 “小工人”。CPU 是那个聪明绝顶、做事井井有条的首席执行官,它每次专注处理一项重要任务,深思熟虑地做出决策。而 GPU 则是工厂里数以千计的流水线工人,虽然单个工人不算特别聪明,但胜在数量庞大,能够齐心协力、不知疲倦地并行处理海量相似任务。这就是为什么在处理图像渲染、科学计算这类需要大量重复计算的工作时,GPU 能展现出惊人的速度优势,而 GPU 编程,就是指挥这群 “小工人” 高效工作的艺术。

为什么选择 GPU 编程?

在传统的 CPU 编程世界里,当我们面对大规模数据的计算任务,比如处理一张高清图片的每个像素点,或者进行复杂的物理模拟计算时,CPU 这位 “首席执行官” 就会显得力不从心。它只能按部就班地一个一个处理任务,就像让一个人独自搬运一整座仓库的货物,效率十分低下。

而 GPU 编程就像是给你配备了一支庞大的搬运队。它利用 GPU 强大的并行计算能力,将大任务拆解成无数个小任务,让数以千计的 “小工人” 同时开工。原本需要 CPU 花费很长时间完成的工作,在 GPU 的加持下,可能瞬间就能得出结果。无论是游戏开发中的实时渲染,还是人工智能领域的深度学习训练,GPU 编程都已经成为了不可或缺的技术。

CUDA 和 OpenCL:指挥 GPU 的两大 “语言”

CUDA:NVIDIA 家的 “方言”

CUDA(Compute Unified Device Architecture)是 NVIDIA 推出的一种并行计算平台和编程模型。它就像是 NVIDIA GPU 专属的 “方言”,只有 NVIDIA 的 GPU 能听得懂。使用 CUDA 进行编程,就好比你掌握了一门特殊的沟通技巧,可以直接和 NVIDIA 的 GPU “小工人” 对话,让它们按照你的要求高效工作。

在 CUDA 的世界里,我们通过编写 “内核函数” 来定义 GPU 需要执行的任务。这些内核函数会被大量复制,分配到 GPU 的各个计算核心上并行执行。下面是一个用 JavaScript 风格模拟 CUDA 内核函数的简单示例,展示如何对一个数组的每个元素进行加 1 操作:

// 模拟CUDA内核函数
function cudaKernel(array) {
    const threadId = getThreadId(); // 假设存在获取线程ID的函数
    if (threadId < array.length) {
        array[threadId]++;
    }
}
// 模拟调用CUDA内核函数
function callCudaKernel() {
    const dataArray = [1, 2, 3, 4, 5];
    const numThreads = dataArray.length;
    for (let i = 0; i < numThreads; i++) {
        cudaKernel(dataArray);
    }
    console.log(dataArray);
}
callCudaKernel();

在实际的 CUDA 编程中,我们需要安装 NVIDIA 的 CUDA Toolkit,并使用 C 或 C++ 等语言进行编写,然后通过编译器将代码编译成 GPU 能够执行的指令。

OpenCL:跨平台的 “世界语”

与 CUDA 不同,OpenCL(Open Computing Language)是一种跨平台的并行编程框架,它就像是编程语言中的 “世界语”,无论是 NVIDIA 的 GPU、AMD 的 GPU,甚至是 CPU、FPGA 等其他计算设备,都能理解 OpenCL 的指令。这使得开发者可以编写一套代码,在不同的硬件设备上运行,大大提高了代码的通用性和可移植性。

OpenCL 的编程模型相对复杂一些,它将计算设备抽象为 “平台”“设备”“上下文”“命令队列” 等概念。开发者需要先获取设备信息,创建上下文和命令队列,然后将计算任务(内核函数)编译并提交到命令队列中执行。以下是一个用 JavaScript 风格模拟 OpenCL 编程流程的示例:

// 模拟获取设备信息
function getOpenCLDevices() {
    return [/* 假设返回设备列表 */];
}
// 模拟创建上下文
function createOpenCLContext(devices) {
    return { /* 假设返回上下文对象 */ };
}
// 模拟创建命令队列
function createCommandQueue(context, device) {
    return { /* 假设返回命令队列对象 */ };
}
// 模拟OpenCL内核函数
function openCLKernel(array) {
    const workItemId = getWorkItemId(); // 假设存在获取工作项ID的函数
    if (workItemId < array.length) {
        array[workItemId]++;
    }
}
// 模拟编译内核函数
function compileOpenCLKernel(context, kernelSource) {
    return { /* 假设返回编译后的内核对象 */ };
}
// 模拟调用OpenCL内核函数
function callOpenCLKernel() {
    const devices = getOpenCLDevices();
    const context = createOpenCLContext(devices);
    const device = devices[0];
    const commandQueue = createCommandQueue(context, device);
    const dataArray = [1, 2, 3, 4, 5];
    const kernelSource = `
        __kernel void openCLKernel(__global int* array) {
            int workItemId = get_global_id(0);
            if (workItemId < get_global_size(0)) {
                array[workItemId]++;
            }
        }
    `;
    const kernel = compileOpenCLKernel(context, kernelSource);
    // 这里省略数据传输等实际操作
    console.log(dataArray);
}
callOpenCLKernel();

在真实的 OpenCL 编程中,我们通常使用 C 语言风格的语法来编写内核函数,并通过特定的 API 与计算设备进行交互。

GPU 编程的挑战与乐趣

虽然 GPU 编程能带来巨大的性能提升,但它也充满了挑战。由于 GPU 的架构与 CPU 有很大不同,开发者需要深入理解并行计算的原理,合理地分配任务和管理内存。比如,在 CUDA 和 OpenCL 编程中,我们需要考虑线程的同步问题,避免出现数据竞争;还要优化内存访问模式,充分利用 GPU 的高速缓存,以提高计算效率。

然而,正是这些挑战,让 GPU 编程充满了乐趣。当你经过一番努力,成功地让 GPU 以惊人的速度完成复杂的计算任务时,那种成就感是难以言喻的。而且,随着技术的不断发展,GPU 编程的应用场景也在不断拓展,从科学研究到工业生产,从娱乐传媒到医疗健康,它正在改变着我们生活的方方面面。

如果你对计算机科学充满热情,想要探索更广阔的计算领域,那么 GPU 编程绝对是一个值得深入学习的方向。无论是选择 CUDA 还是 OpenCL,都像是掌握了一把打开新世界大门的钥匙,让你能够充分发挥 GPU 的强大算力,创造出令人惊叹的应用和成果。快来加入 GPU 编程的奇妙之旅吧!

上述文章涵盖了 GPU 编程的基础概念与实践模拟。若你觉得内容深度、示例复杂度等方面需调整,或是有其他修改需求,欢迎随时告诉我。

Three.js 补间动画与相机移动:让数字世界动起来的魔法

在 Three.js 的数字宇宙里,静态场景就像是沉默的雕塑,虽然精致却少了些生机。而补间动画和相机移动,就是赋予这个世界灵魂的神奇魔法,让场景中的元素 “活” 过来,带着观众穿梭于奇妙的虚拟空间。接下来,我们就化身 Three.js 的魔法师,学习如何施展这两大魔法。

一、补间动画:让元素翩翩起舞

补间动画,简单来说,就是告诉 Three.js 从 A 状态到 B 状态,如何优雅地 “过渡”。想象一下,你的模型是一位害羞的舞者,补间动画就是指挥它从舞台一侧缓缓走到另一侧,同时完成各种优美动作的 “舞蹈编排”。

1. 引入补间动画库

在正式开始编舞前,我们需要一个得力助手 ——gsap(GreenSock Animation Platform)。它就像是动画界的瑞士军刀,功能强大又灵活。通过npm install gsap将它引入项目,或者直接在 HTML 文件中添加。

2. 创建基础场景

首先,搭建一个 Three.js 的基础舞台。创建场景、相机和渲染器,就像搭建一个空的剧场,等待演员(模型)和观众(相机)入场:

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

3. 添加需要动画的元素

在剧场里放置一个 “演员”,比如一个正方体:

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

4. 编写补间动画

现在,让正方体这位 “舞者” 动起来!我们使用gsap来编写它的舞蹈动作:

gsap.to(cube.position, {
    x: 3, // 移动到x=3的位置
    duration: 2, // 动画持续2秒
    ease: "power2.out" // 动画的缓动效果,这里让结束时更平滑
});

上面的代码就像是给正方体下达了一个指令:在 2 秒内,以一种优雅的方式(power2.out缓动效果)移动到 x 轴为 3 的位置。你还可以组合更多属性,让动画更复杂,比如同时改变位置、旋转和缩放:

gsap.to(cube, {
    position: { x: 3, y: 2, z: 1 },
    rotation: { x: Math.PI / 2, y: Math.PI / 4 },
    scale: 2,
    duration: 3,
    ease: "elastic.out(1, 0.5)"
});

这里的正方体就像一个活泼的精灵,在 3 秒内完成移动、旋转和放大,最后还带着俏皮的弹性效果停下。

二、相机移动:带领观众探索世界

相机就是观众的眼睛,控制相机移动,就像是带着观众在虚拟世界中漫步、飞翔、穿梭。

1. 基础移动

最基础的相机移动,就是改变相机的位置属性position。比如,让相机沿着 z 轴向后退:

camera.position.z -= 1;

这就像观众往后撤了一步,看到的场景范围更大了。如果想让相机平滑移动,可以结合补间动画:

gsap.to(camera.position, {
    z: 10,
    duration: 2,
    ease: "linear"
});

这样,观众就像是坐着缓缓后退的观光车,慢慢欣赏场景的变化。

2. 轨道控制

为了让观众能更自由地探索场景,我们可以添加轨道控制器OrbitControls。它就像是一个智能导游,观众可以通过鼠标操作,围绕场景中的某个点旋转、缩放、平移相机。

首先,引入轨道控制器:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

然后,初始化控制器:

const controls = new OrbitControls(camera, renderer.domElement);

现在,观众就能通过鼠标操作,360 度无死角地欣赏场景啦!

3. 路径动画

更炫酷的是,我们可以为相机设计一条专属的 “旅行路线”,让观众沿着精心设计的路径游览场景。这里可以使用Path和Spline来定义路径。

先创建一条路径:

const path = new THREE.CatmullRomCurve3([
    new THREE.Vector3(-5, 0, 0),
    new THREE.Vector3(0, 5, 0),
    new THREE.Vector3(5, 0, 0)
]);

这条路径就像是为相机规划的一条山间小路,从左边出发,爬到山顶,再到右边。然后,使用补间动画让相机沿着路径移动:

const points = path.getSpacedPoints(100);
let i = 0;
const animateCamera = () => {
    requestAnimationFrame(animateCamera);
    if (i < points.length) {
        camera.position.copy(points[i]);
        i++;
    }
};
animateCamera();

这样,观众就像是坐上了一辆自动驾驶的小车,沿着这条美丽的路径,欣赏 Three.js 世界里的独特风景。

三、魔法升级:让动画与移动配合无间

补间动画和相机移动并不是孤立的,将它们巧妙结合,能创造出令人惊叹的视觉效果。比如,在场景中的物体开始表演精彩动画时,相机适时地拉近镜头,聚焦关键动作;当物体完成表演,相机缓缓拉远,展示整个场景的全貌。就像电影导演一样,通过镜头和画面的配合,讲述一个引人入胜的数字故事。

在 Three.js 的魔法世界里,补间动画和相机移动的可能性是无穷无尽的。希望通过这篇教程,你已经掌握了这两大魔法的基本咒语。快去发挥你的创意,打造属于自己的动态数字奇观吧!

上面的内容展示了 Three.js 补间动画与相机移动的核心技巧。你对文章的深度、案例还有其他想法,或者想补充其他功能,都能随时告诉我。

❌