个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js🍖数据结构与算法体系教程🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
内容 | 参考链接 |
---|---|
WebGL专栏 | WebGL 入门 |
文章目录
- 前言
- 一、创建场景
- 二、渲染三维对象
- 三、旋转动画
- 四、添加灯光
- 五、添加阴影
- 六、添加雾化
- 总结
前言
大家好,这里是前端杂货铺。
原 three.js 专栏
本专栏将以更清晰专业的方式记录并讲解 three.js 相关的基础知识。
在学习的过程中,如若需要深入了解或扩展某些知识,可以自行查阅 => three.js官方文档
一、创建场景
在渲染三维对象之前我们需要以下几个对象:场景(scene)、相机(camera)和渲染器(renderer),这样我们就能透过摄像机渲染出场景。
场景 用于存放我们需要的物体(比如桌子放在房间里,那么此时房间就是场景,桌子就是物体)。
相机 用于拍摄我们场景中的物体,它包括 PerspectiveCamera(透视摄像机:用来模拟人眼所看到的景象,近大远小)和 OrthographicCamera(正交摄像机:渲染的图片中物体的大小都保持不变)。
渲染器 用于把场景和场景里面的物体渲染到我们的屏幕上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../lib/three/three.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机 参数:视野角度、长宽比、近截面、远截面
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机位置
camera.position.set(0, 0, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的 canvas 添加到 body 中
document.body.appendChild(renderer.domElement);
</script>
</body>
</html>
此时的 renderer 是一个庞大的对象,renderer.domElement
其实就是一个 canvas,它此刻代表着我们屏幕上的黑色场景。
二、渲染三维对象
我们可以在场景中渲染的三维对象有很多种,比如:立方体、球体、椎体、圆柱体…
接下来,我们在刚刚创建好的场景中添加一个立方体。其实也很简单,我们只需要创建 立方缓冲几何体 实例,然后定义一个材质,最后通过 mesh 网格 构建出来几何体添加到场景中即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../lib/three/three.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机 视野角度、长宽比、近截面、远截面
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机位置
camera.position.set(0, 0, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的 canvas 添加到 body 中
document.body.appendChild(renderer.domElement);
// 添加立方体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
// 创建立方体材质
const cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000, // 立方体颜色
wireframe: true // 是否将几何体渲染为线框
});
// 基于以三角形为 polygon mesh(多边形网格)的物体的类 => 此时就是立方体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 添加到场景
scene.add(cube);
// 渲染
renderer.render(scene, camera);
</script>
</body>
</html>
若把 wireframe
置为 false,则渲染出的为几何体
三、旋转动画
接下来,我们来让刚刚被渲染出来的三维对象旋转起来,看一下它的总体形态。
在此,我们使用 window.requestAnimationFrame()
,它会告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
requestAnimatingFrame 相较于 setTimeout 的优点:为了提高性能和电池寿命,在大多数浏览器里,当 requestAnimationFrame() 运行在后台标签页或者隐藏的
<iframe>
里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。(总而言之,requestAnimatingFrame 有更高的效率!)
requestAnimationFrame 通常每秒执行 60 次,相当于每 0.01667s 执行一次,即 60帧。
下面代码中的 rotation 用于控制立方体的绕轴旋转,通过 requestAnimationFrame
设置每单位时间立方体绕 x 轴旋转 0.01,绕 y 轴也旋转 0.01。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../lib/three/three.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机 视野角度、长宽比、近截面、远截面
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机位置
camera.position.set(0, 0, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
console.log(renderer);
// 将渲染器的 canvas 添加到 body 中
document.body.appendChild(renderer.domElement);
// 添加立方体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
// 创建立方体材质
const cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 添加到场景
scene.add(cube);
const animation = () => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
animation();
</script>
</body>
</html>
立方体旋转动画
四、添加灯光
接下来,我们在场景中添加一个聚光灯光照 SpotLight(光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大)。从而模拟出有光照打在物体上的立体效果。
也很简单,我们只需要创建聚光灯实例,给它传递一个白色,设置合适的位置添加到场景中即可。
需要注意的是:添加灯光时的材质需要改为 MeshLambertMaterial
,因为 MeshBasicMaterial
不支持添加材质。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../lib/three/three.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机 视野角度FOV、长宽比、近截面、远截面
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机位置
camera.position.set(0, 0, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加立方体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
// 创建立方体材质
const cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000,
wireframe: false
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 添加到场景
scene.add(cube);
// 添加灯光
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-10, 10, 10);
scene.add(spotLight);
const animation = () => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
animation();
</script>
</body>
</html>
添加灯光
五、添加阴影
接下来,我们创建一个平面,模拟出实体旋转时映射到平面上的阴影效果。
平面的创建和实体差不多,都是先创建实例和材质,之后通过 mesh 创建出平面,最后调整到合适的位置添加到场景中即可。
此场景中,我们还创建了个球体,总体创建方式都一样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../lib/three/three.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机 视野角度FOV、长宽比、近截面、远截面
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// 设置相机位置
camera.position.set(0, 0, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 添加立方体
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
// 创建立方体材质
const cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000,
wireframe: false
});
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 添加到场景
scene.add(cube);
// 添加球体
const sphereGeometry = new THREE.SphereGeometry(1, 10, 10);
// 创建球体材质
const sphereMatrial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMatrial);
sphere.position.x = 3;
sphere.position.y = 1;
// 添加到场景
scene.add(sphere);
// 添加平面,用来接收阴影
const planeGeometry = new THREE.PlaneGeometry(20, 30);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0x999999
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotateZ(20);
plane.position.z = -10;
plane.position.x = 3;
scene.add(plane);
// 添加灯光
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-10, 10, 90);
scene.add(spotLight);
// 产生阴影
cube.castShadow = true;
sphere.castShadow = true;
// 接收阴影
plane.receiveShadow = true;
sphere.receiveShadow = true;
// 设置灯光开启阴影
spotLight.castShadow = true;
renderer.shadowMapEnabled = true;
const animation = () => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
animation();
</script>
</body>
</html>
添加物体阴影
六、添加雾化
添加雾化很简单,我们只需要在场景中添加一个 线性雾 实例即可。
// 雾化
scene.fog = new THREE.Fog(0xffffff, 1, 50);
添加雾化
总结
本篇文章我们熟悉了如何创建场景、怎么渲染出三维对象、如何进行灯光、阴影、雾化的添加等。知识点的总体难度较低,这也是 three.js 的强大之处,它暴露出了很多 API 供我们使用,极大的方便了我们对于实体的创建及操作。
更多内容扩展请大家自行查阅 => three.js官方文档,真心推荐读一读!!
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- Three.js 官方文档
- WebGL+Three.js 入门与实战【作者:慕课网_yancy】