image.png

Three.js 是一个 JavaScript 库,用于在 Web 浏览器中创建 3D Web 图形。在《随着元宇宙开启WEB3D之路》中介绍了一些知识。

什么是 WebGL?

WebGL 是一种 JavaScript API,它允许浏览器在不使用任何插件的情况下渲染 3D 图形。WebGL 允许在网页上的 HTML5 画布元素内进行 GPU 加速渲染。第一个稳定版 1.0 于 2011 年 3 月发布,现在 2.0 版于 2017 年 1 月发布。

目标

  • 开始使用 Three.js 库
  • 创建第一个场景
  • 在场景中放置一些东西
  • 让它动起来
  • 给场景一些光

安装

这是初学者指南,所以从最简单方式开始。只会使用两个文件 index.htmlmain.js。从 HTML 文件开始,制作一个基本的 HTML 页面并导入 Three.js 库。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta
            name="viewport"
            content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <title>Three.js 入门指南DEMO</title>
        <style>
            body {
                margin: 0;
            }
            canvas {
                width: 100%;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <!-- CDN Link to Three.js -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.js"></script>

        <script src="./main.js"></script>
    </body>
</html>

现在来看看 JavaScript 文件,每当使用 Three.js 时,需要设置的基础是场景、相机和渲染器。

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 });

从一个场景开始,稍后将添加它,但现在将创建一个不带参数的新场景元素,相机将使用 PerspectiveCamera 选项,有四个默认相机可供使用,但 PerspectiveCamera 是一个很好的起点,可以模仿人眼。需要给这个函数四个参数:

  • 视野:相机视角的宽度。
  • 纵横比:像电视屏幕一样,如宽度/高度。
  • 剪裁平面附近:比这更近的元素不会在屏幕上呈现。如果正在拍摄的电影,那么摄像机后面的任何东西都不会出现,为此,基本上使用 0
  • 远剪裁平面:比这更远的元素不会渲染,值太高可能会导致性能问题。

渲染器将采用选项对象,只会使用 {antialiasing: true} 以便在要创建的立方体上具有更平滑的边缘。但不是必须,但看起来会更好一些。接下来需要设置幕布的大小。

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

将渲染器的大小设置为整个窗口的大小,并告诉它将自己附加到 HTML 页面的正文中,效果如下:

image.png

接下来是创建立方体,需要一个场景、相机和渲染器来开始一样,需要三个核心元素来处理想要渲染的每个对象:

  • 几何:物体的形状,由顶点(点)和面(物体的平面)组成。Three.js 也给了很多预制的几何图形只需要给它一些参数它会处理那里的细节。
  • 材质:物体是由什么组成的,这将包括像颜色这样的东西,它将决定光如何与我们的物体交互。如果有一个鹅卵石和一个金属球轴承,它们会有相似的几何形状,但它们的材料是非常不同的,这将改变它在观众眼中的样子。
  • 网格:几何体和材质的组合。

现在来看下完整代码,如下:

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);

renderer.setClearColor("#222222");
document.body.appendChild(renderer.domElement);
camera.position.z = 5;

// resize 事件
window.addEventListener("resize", () => {
    let width = window.innerWidth;
    let height = window.innerHeight;
    renderer.setSize(width, height);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
});

// 立体方
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
    color: 0xff0051,
    flatShading: true,
    metalness: 0,
    roughness: 1,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// 维数据集
const geometry2 = new THREE.BoxGeometry(3, 3, 3);
const material2 = new THREE.MeshBasicMaterial({
    color: "#dadada",
    wireframe: true,
    transparent: true,
});
const wireframeCube = new THREE.Mesh(geometry2, material2);
scene.add(wireframeCube);

// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);

// 点光源
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(25, 50, 25);
scene.add(pointLight);

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.04;
    cube.rotation.y += 0.04;
    wireframeCube.rotation.x -= 0.01;
    wireframeCube.rotation.y -= 0.01;
    renderer.render(scene, camera);
}
animate();

BoxGeometry 方法将接受构造函数参数,分别是盒子的宽度、高度和深度。

对于材质,将使用不受光源影响的最基本的提供材质,并为其提供颜色选项,color: 0xff0051。3D 渲染的真正威力来自于它如何处理光源以创建逼真的场景。材料有多反射、阴影、颜色如何出现等等,都靠光,Three.js 中最基本的光照形式和新的 AmbientLight 开始。

const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);

创建一个光源,告诉它颜色,以及应该有多强烈。环境光无所不在,同样适用于所有事物。它不能投射阴影,因为它没有方向。它只会改变我们颜色的显示方式。

接下来还要创建一个点光源,可以把它想象成灯泡,来自此的光将从原点均匀地向各个方向传播。

const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(25, 50, 25);
scene.add(pointLight);

最后一块拼图是改变立方体材质,选择了不与光相互作用的最基本材质,但现在可以使用光源,将之前的材质代码改为:

const material = new THREE.MeshStandardMaterial({
    color: 0xff0051,
    flatShading: true,
    metalness: 0,
    roughness: 1,
});

现在来看一个完整的效果,如下:

start3.gif