Three.js——创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化

2024-04-02 14:36

本文主要是介绍Three.js——创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

个人简介

👀个人主页: 前端杂货铺
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🍉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官方文档,真心推荐读一读!!

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. Three.js 官方文档
  2. WebGL+Three.js 入门与实战【作者:慕课网_yancy】

在这里插入图片描述


这篇关于Three.js——创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/870239

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

Java中Runnable和Callable的区别和联系及使用场景

《Java中Runnable和Callable的区别和联系及使用场景》Java多线程有两个重要的接口,Runnable和Callable,分别提供一个run方法和call方法,二者是有较大差异的,本文... 目录一、Runnable使用场景二、Callable的使用场景三、关于Future和FutureTa

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组

Javascript访问Promise对象返回值的操作方法

《Javascript访问Promise对象返回值的操作方法》这篇文章介绍了如何在JavaScript中使用Promise对象来处理异步操作,通过使用fetch()方法和Promise对象,我们可以从... 目录在Javascript中,什么是Promise1- then() 链式操作2- 在之后的代码中使

MyBatis的配置对象Configuration作用及说明

《MyBatis的配置对象Configuration作用及说明》MyBatis的Configuration对象是MyBatis的核心配置对象,它包含了MyBatis运行时所需的几乎所有配置信息,这个对... 目录MyBATis配置对象Configuration作用Configuration 对象的主要作用C