使用 Three.js 创建圣诞树场景

今天带大家一起实现一个圣诞树,先看下效果

一、导入模块并初始化 Three.js 场景

1. 创建场景

创建场景的目的是构建 3D 空间的容器,用于承载各种 3D 对象和光源。

javascript 复制代码
const scene = new THREE.Scene();

2. 创建相机

创建相机用于定义观察视角,我们使用透视相机(PerspectiveCamera),它能模拟人眼观察场景的效果。

  • 参数含义:
    • 75:视角大小,决定相机的视野范围。
    • window.innerWidth / window.innerHeight:宽高比,保证画面比例正常。
    • 0.11000:近裁剪面和远裁剪面,控制相机可见范围。
javascript 复制代码
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

3. 创建渲染器

渲染器用于将 3D 场景绘制到屏幕上。我们选择 WebGLRenderer 并启用抗锯齿功能以优化图形效果。

javascript 复制代码
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);

4. 将渲染器插入到 HTML 页面

将渲染器生成的 <canvas> 元素添加到页面的 #app 容器中,以便显示 3D 场景。

javascript 复制代码
document.getElementById("app").appendChild(renderer.domElement);

完整代码

javascript 复制代码
import * as THREE from "three";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("app").appendChild(renderer.domElement);

二、添加交互控件

为场景添加 OrbitControls 控件,方便用户通过鼠标交互操作场景。

1. 引入 OrbitControls

我们需要从 Three.js 的扩展模块中导入 OrbitControls。

javascript 复制代码
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

2. 初始化控件

OrbitControls 用于添加鼠标交互功能,让用户可以旋转、缩放和拖动场景。

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

3. 启用惯性效果

为了让交互更加自然,我们启用惯性功能,并设置阻尼系数。

javascript 复制代码
controls.enableDamping = true; // 开启惯性
controls.dampingFactor = 0.05; // 设置惯性阻尼系数

4. 设置初始相机位置

为便于观察场景,设置相机位置为 (0, 5, 10),稍微抬高视角。

javascript 复制代码
camera.position.set(0, 5, 10);
controls.update();

三、创建圣诞树

在这一部分,我们将详细讲解如何创建圣诞树,包括树干、树叶以及装饰的实现过程。

1. 创建树干

  1. 几何体创建
    • 使用 THREE.CylinderGeometry 创建一个圆柱体,表示树干的形状。
    • 圆柱体的底部半径为 0.5,顶部半径为 0.3,高度为 2.5,分段为 32
  2. 材质设置
    • 使用 THREE.MeshPhongMaterial,设置颜色为 0x8b4513(棕色)以模拟木材质感。
    • 增加光泽效果,设置 shininess20
  3. 位置调整
    • 设置 position.y = 1.25,将树干移动到地面以上。
javascript 复制代码
const trunkGeometry = new THREE.CylinderGeometry(0.3, 0.5, 2.5, 32);
const trunkMaterial = new THREE.MeshPhongMaterial({
  color: 0x8b4513,
  shininess: 20,
});
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.y = 1.25;
treeGroup.add(trunk);

2. 创建树叶

在圣诞树中,树叶部分是通过多个圆锥体逐层堆叠实现的。这种设计方法既简单又直观,每一层的大小逐渐减小,高度逐渐增高,从而形成自然的树形结构。

实现思路
  1. 层级控制
    • 设置 levels = 5,生成 5 层树叶,每层逐渐变小且向上移动。
  2. 几何体创建
    • 使用 THREE.ConeGeometry 创建圆锥体,控制半径逐层递减。
  3. 材质设置
    • 使用 THREE.MeshPhongMaterial 设置绿色(0x228b22),并增加光泽。
  4. 位置调整
    • 每层的高度间隔为 1.1,确保层次分明且紧凑。
javascript 复制代码
const levels = 5;
for (let i = 0; i < levels; i++) {
  const radius = 3 - i * 0.5;
  const height = 1.2;
  const coneGeometry = new THREE.ConeGeometry(radius, height, 32);
  const coneMaterial = new THREE.MeshPhongMaterial({
    color: 0x228b22,
    shininess: 30,
  });
  const cone = new THREE.Mesh(coneGeometry, coneMaterial);
  cone.position.y = 2.5 + i * 1.1;
  treeGroup.add(cone);
}

3. 添加装饰

为圣诞树添加装饰,比如彩球:

  1. 随机生成装饰球
    • 使用 Math.random() 控制彩球的大小、颜色和位置。
    • 设置彩球的数量为 35,大小范围为 0.08 ~ 0.2
  2. 材质优化
    • 彩球以随机颜色为主,设置 30% 的概率为金色(0xffd700)。
  3. 位置控制
    • 使用极坐标 Math.cos(angle)Math.sin(angle) 计算 xz 坐标。
    • 高度范围限定为 2.5 ~ 6.5
javascript 复制代码
const decorationCount = 35;
for (let i = 0; i < decorationCount; i++) {
  const size = 0.08 + Math.random() * 0.12;
  const ballGeometry = new THREE.SphereGeometry(size, 32, 32);
  const ballMaterial = new THREE.MeshPhongMaterial({
    color: Math.random() < 0.7 ? Math.random() * 0xffffff : 0xffd700,
    shininess: 100,
  });
  const ball = new THREE.Mesh(ballGeometry, ballMaterial);

  const angle = Math.random() * Math.PI * 2;
  const radius = Math.random() * 2.2;
  const height = Math.random() * 4 + 2.5;

  ball.position.x = Math.cos(angle) * radius;
  ball.position.y = height;
  ball.position.z = Math.sin(angle) * radius;

  treeGroup.add(ball);
}

4. 添加彩带

通过曲线模拟彩带,为圣诞树增加装饰。

  1. 曲线生成

    • 使用 THREE.CatmullRomCurve3 创建一条随机曲线。
    • 曲线由三个随机控制点构成,起始点在树干顶部附近。
  2. 管道几何体

    • 使用 THREE.TubeGeometry 基于曲线生成管道形状,模拟彩带。
  3. 材质设置

    • 彩带材质为随机红色系(0xff0000),光泽度高。
javascript 复制代码
const ribbonCount = 15;
for (let i = 0; i < ribbonCount; i++) {
  const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(0, 2.5 + Math.random() * 3, 0),
    new THREE.Vector3(
      Math.random() * 2 - 1,
      2.5 + Math.random() * 3,
      Math.random() * 2 - 1
    ),
    new THREE.Vector3(
      Math.random() * 3 - 1.5,
      2.5 + Math.random() * 3,
      Math.random() * 3 - 1.5
    ),
  ]);

  const ribbonGeometry = new THREE.TubeGeometry(curve, 20, 0.02, 8, false);
  const ribbonMaterial = new THREE.MeshPhongMaterial({
    color: Math.random() * 0xff0000,
    shininess: 80,
  });
  const ribbon = new THREE.Mesh(ribbonGeometry, ribbonMaterial);
  treeGroup.add(ribbon);
}

5. 添加礼物盒

在树底部放置礼物盒,提升整体氛围。

1. 创建礼物盒的容器:THREE.Group
javascript 复制代码
`const giftGroup = new THREE.Group();`;
  • 每个礼物盒由一个 THREE.Group 组成,用来包含盒子和丝带。
  • 使用 THREE.Group 可以方便地对礼物盒整体设置位置和旋转角度。

2. 随机生成盒子的尺寸
javascript 复制代码
const size = 0.4 + Math.random() * 0.3; // 宽度和深度:0.4 到 0.7
const height = 0.4 + Math.random() * 0.3; // 高度:0.4 到 0.7
  • 每个礼物盒的尺寸在一定范围内随机变化:
    • size 控制盒子的宽度和深度。
    • height 控制盒子的高度。
  • 这样可以避免所有盒子大小一致带来的单调感。

3. 创建盒子的几何形状和材质
javascript 复制代码
const boxGeometry = new THREE.BoxGeometry(size, height, size);
const boxMaterial = new THREE.MeshPhongMaterial({
  color: Math.random() * 0xffffff, // 随机颜色
  shininess: 50, // 适中的光泽度
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
  • 使用 THREE.BoxGeometry 定义盒子的几何形状。
  • 使用 THREE.MeshPhongMaterial 定义盒子的材质:
    • 颜色是随机生成的 RGB 值。
    • 设置光泽度为 50,使盒子表面具有柔和的反光效果。

4. 生成丝带
水平丝带
javascript 复制代码
const ribbonWidth = size * 0.1; // 丝带宽度是盒子宽度的 10%
const ribbonGeometry = new THREE.BoxGeometry(size, ribbonWidth, ribbonWidth);
const ribbonMaterial = new THREE.MeshPhongMaterial({
  color: 0xff0000, // 红色丝带
  shininess: 80, // 更高的光泽度
});
const ribbonH = new THREE.Mesh(ribbonGeometry, ribbonMaterial);
  • 水平丝带覆盖在盒子的顶部。
  • 宽度和盒子一样,但厚度很薄,确保比例适当。
垂直丝带
javascript 复制代码
const ribbonV = new THREE.Mesh(
  new THREE.BoxGeometry(ribbonWidth, ribbonWidth, size),
  ribbonMaterial
);
  • 垂直丝带沿着盒子的深度方向包裹盒子。
  • 使用与水平丝带相同的材质,保持统一的红色和光泽效果。
将丝带与盒子组合
javascript 复制代码
giftGroup.add(box, ribbonH, ribbonV);
  • 使用 giftGroup.add 方法将盒子和两条丝带加入到礼物盒组中,形成完整的礼物盒。

5. 设置礼物盒的位置和旋转
javascript 复制代码
const angle = Math.random() * Math.PI * 2; // 随机角度
const radius = 1.5 + Math.random() * 1; // 距离中心点的半径
giftGroup.position.x = Math.cos(angle) * radius; // 计算 x 坐标
giftGroup.position.y = height / 2; // y 坐标为盒子高度的一半
giftGroup.position.z = Math.sin(angle) * radius; // 计算 z 坐标
giftGroup.rotation.y = Math.random() * Math.PI * 2; // 随机旋转角度
  • 位置设置:
    • 使用三角函数 Math.cosMath.sin 将礼物盒沿树底圆周随机分布。
    • radius 确定盒子到树中心的距离,使礼物盒分布在树的底部区域。
    • height / 2 确保盒子底部与地面接触。
  • 旋转设置:
    • 每个礼物盒的朝向通过 rotation.y 随机化,增加随机性。
6. 将礼物盒添加到圣诞树组
javascript 复制代码
treeGroup.add(giftGroup);
  • 使用 treeGroup.add 将每个生成的礼物盒组添加到圣诞树的主组中。
  • 这样礼物盒会随着圣诞树一起移动或旋转。

6. 添加星星

为圣诞树顶部添加金色星星,提升节日气氛。

javascript 复制代码
const starGeometry = new THREE.OctahedronGeometry(0.3, 0);
const starMaterial = new THREE.MeshPhongMaterial({
  color: 0xffd700,
  shininess: 100,
});
const star = new THREE.Mesh(starGeometry, starMaterial);
star.position.y = 5.5;
treeGroup.add(star);

四、添加动画

我们将实现圣诞树顶部星星的旋转动画,并更新渲染器。

javascript 复制代码
function animate() {
  requestAnimationFrame(animate);
  if (christmasTree.children[christmasTree.children.length - 1]) {
    christmasTree.children[
      christmasTree.children.length - 1
    ].rotation.y += 0.01;
  }
  controls.update();
  renderer.render(scene, camera);
}
animate();

完整的圣诞树场景即告完成!

代码

github

https://github.com/calmound/threejs-demo/tree/main/shengdanshu

gitee

https://gitee.com/calmound/threejs-demo/tree/main/shengdanshu

相关推荐
周伯通*1 分钟前
策略模式以及优化
java·前端·策略模式
艾斯特_15 分钟前
前端代码装饰器的介绍及应用
前端·javascript
Sokachlh19 分钟前
【elementplus】中文模式
前端·javascript
轻口味19 分钟前
【每日学点鸿蒙知识】hap安装报错、APP转移账号、import本地文件、远程包构建问题、访问前端页面方法
前端·华为·harmonyos
lvbu_2024war0120 分钟前
MATLAB语言的网络编程
开发语言·后端·golang
m0_7482453426 分钟前
BY组态-低代码web可视化组件
前端·低代码
single59432 分钟前
【c++笔试强训】(第四十五篇)
java·开发语言·数据结构·c++·算法
游客52034 分钟前
自动化办公-合并多个excel
开发语言·python·自动化·excel
Cshaosun44 分钟前
js版本之ES6特性简述【Proxy、Reflect、Iterator、Generator】(五)
开发语言·javascript·es6
web182854825121 小时前
ctfshow-web 151-170-文件上传
前端·状态模式