头像

three.js简单场景代码解析

2026-03-18

Written by: Jack-S-H

使用three实现一个简单的场景

js

2026年

3月

目录

  1. 前言
  2. 搭建基础环境
  3. 准备画布
  4. 增加几何体
  5. 可选优化
  6. 完整代码

前言

Three.js是一个基于 WebGL 的 3D 库,让开发者轻松在浏览器中创建三维内容。本文将展示 Three.js 的核心组件(场景、相机、渲染器、几何体、材质、网格),并实现一个非常简单的场景,包含一个彩色的平面几何体。

搭建基础环境

  1. 创建HTML文件

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <canvas id="c"></canvas>
</body>
</html>

这里包含了一个<canvas></canvas>元素,之后会用到

  1. 引入 Three.js

有多种方法,这里使用 CDN,还可以使用npm安装。

<script type="importmap">
{
	"imports":
    {
"three":"https://cdn.jsdelivr.net/npm/three@v0.181.0/build/three.module.js",
"three/addons/":"https://cdn.jsdelivr.net/npm/three@v0.181.0/examples/jsm/"
    }
}
</script>

three/addons/是three的扩展包,可以提供额外的材质、几何体、后期处理工具等

  1. 设置css属性

设置CSS宽高(比如 width: 100%; height: 100vh;)并隐藏滚动条,这样可以让画面占满视口。

html,
body {
    margin: 0;
    overflow: hidden;
    height: 100%;
}

准备画布

  1. 导入three包

import * as THREE from 'three';
  1. 设置宽高参数

后面设置大小时需要,提前定义

为了保证渲染与显示一致,这里的宽高从canvas元素获取

const width = canvas.clientWidth;
const height = canvas.clientHeight;
  1. 场景(Scene)

场景是所有物体的容器。

const scene = new THREE.Scene();
  1. 相机(Camera)

const fov = 75;
const aspect = width / height;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

fov:(视线角度)

aspect(渲染的宽高比)

near / far(渲染的远近范围)

  1. 设置相机位置

position决定了看物体的视角,接收的3个参数分别为z轴(上下),Y轴(远近),X轴(左右)

camera.position.set(0, -5, 30);

决定图像大小的3要素:几何体大小设置、像素设置、相机远近

  1. 绑定canvas元素

这里使用元素id绑定

const canvas = document.querySelector('#c');

还有一种添加canvas的方法,不在html文件中设定元素,使用js创建一个canvas画布,然后系统会自动插入

document.body.appendChild( renderer.domElement )

注:系统自动插入元素会有不确定性,所以推荐手动创建并绑定元素。

  1. 渲染器(Renderer)

three有webglwebgpu两种renderer

const renderer = new THREE.WebGLRenderer({ antialias: true, canvas});

antialias:抗锯齿

element:添加canvas画布

  1. 调用 setSize 设置渲染尺寸

目的:确保渲染尺寸和显示尺寸一致。

renderer.setSize(width,height)

为了图像清晰,要将几何体大小和渲染的像素大小保持一致

一般不使用canvas.height=heightcanvas.width=width

  1. 启动动画

setAnimationLoop不断调用渲染函数。

function animate() {
    renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
  1. 第一次运行

你已经建立了一个场景,你现在可以启动本地服务器,然后在浏览器中访问index.html,你将会看到一个全黑的画布画面

three只需要scenecamerarenderer即可运行!

设置几何体

接下来,我们将在场景中添加一个几何体。

  1. 几何体(Geometry)

不同的图形需要不同的参数,PlaneGeometry是一个平面几何体,接收4个参数。

width:宽度

height:高度

widthSegments:横向顶点数

heightSegments:纵向顶点数

顶点数会影响变形时的画面

const g_width = 30;
const g_height = 30;
const widthSegments = 128;
const heightSegments = 128;

const geometry = new THREE.PlaneGeometry(
    g_width, g_height,
    widthSegments, heightSegments);
  1. 材质(Material)

使用 MeshBasicMaterial,设置颜色 0x8b91b8。

const material = new THREE.MeshBasicMaterial({ color: 0x8b91b8, side: THREE.DoubleSide });

可选设置side: THREE.DoubleSide 使平面双面可见(否则旋转后背面不可见)。

  1. 网格(Mesh)

使用mesh将图形和材质结合,生成最终的图像

需要两个参数:geometrymaterial

const Plane = new THREE.Mesh(geometry, material);
  1. 添加到场景中

scene.add(Plane);
  1. 光源(Light)

增加PointLight光源,参数为颜色和强度(亮度),可以使用position.set设置位置,可以产生阴影,增加立体性,还可以通过增加多种光源改善画面

const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(50, 50, 100);
scene.add(pointLight);
  1. 旋转几何体

这边给一点角度可以使几何体看的更清除一点

geometry.rotateX(-Math.PI / 6);

可选优化

  1. 窗口自适应

现在画布大小是固定的,也就是说在调整浏览器窗口时,里面显示的物体会被拉伸。

我们可以通过监听 resize 事件,实时更新渲染器尺寸和相机宽高比,防止画面被拉伸.

function onWindowResize(){
    //获取最新的宽高
    const width = window.innerWidth;
    const height = window.innerHeight;
	//设置渲染大小和相机宽高比
    renderer.setSize(width,height);
    camera.aspect = width / height;
    //让相机生效
    camera.updateProjectionMatrix();
}
//监听浏览器的 resize 事件
window.addEventListener('resize', onWindowResize);

perspective 相机需要更新宽高比,因为相机的视野是固定的,如果不改,画面会被压扁

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Hallo Three.js</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        html,
        body {
            margin: 0;
            /* 一直占满屏幕,没有滚动条 */
            overflow: hidden;
            height: 100%;
        }
        #c {
            /* 如果父容器没有占满屏幕
            就需要增加绝对定位absolute position */
            width: 100%;
            height: 100%;
            display: block;
        }
    </style>
    <script type="importmap">
    {
    "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@v0.181.0/build/three.module.js",
        "three/addons/": "https://cdn.jsdelivr.net/npm/three@v0.181.0/examples/jsm/"

    }
    }
    </script>
</head>
<body>
    <script type="module" src="/main.js"></script>
    <canvas id="c">
    </canvas>
</body>
</html>
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const canvas = document.querySelector('#c');
const width = canvas.clientWidth;
const height = canvas.clientHeight;

const scene = new THREE.Scene();

const fov = 75;
const aspect = width / height; 
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

camera.position.set(0, -5, 30);

const renderer = new THREE.WebGLRenderer({ antialias: true, canvas});

renderer.setSize(width,height)

function onWindowResize(){
    const width = window.innerWidth;
    const height = window.innerHeight;

    renderer.setSize(width,height);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();

}
window.addEventListener('resize', onWindowResize);

const g_width = 30;
const g_height = 30;
const widthSegments = 128;
const heightSegments = 128;
const geometry = new THREE.PlaneGeometry(
    g_width, g_height,
    widthSegments, heightSegments);

const material = new THREE.MeshBasicMaterial({ color: 0x8b91b8, side: THREE.DoubleSide });
const Plane = new THREE.Mesh(geometry, material);

geometry.rotateX(-Math.PI / 6);

scene.add(Plane);

function animate() {
    renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);