每日进步:一篇文章带你走进3D的世界

每日进步:一篇文章带你走进3D的世界

背景

是一篇关于前端开发中的3D技术及其应用的博客文章。在这篇文章中,我们将探索令人兴奋的前端3D世界,揭示如何在网页中创造出引人注目的三维效果。

首先,我们会介绍前端开发中3D技术的基本原理和概念,包括CSS 3D转换、CSS 3D变换和WebGL等技术。我们将解释如何使用这些技术来创建立体感知、逼真的视觉效果,并讨论它们对于增强用户体验的重要性。

随后,我们将深入了解前端3D技术的实际应用。从旋转、缩放和移动元素到创建复杂的3D场景,我们将探索如何使用CSS和JavaScript等工具和库来实现各种前端3D效果。我们还将介绍一些优秀的前端3D案例,让你能够欣赏到前端3D技术的魅力和创意。

此外,我们还会提供一些学习前端3D技术的资源和建议。无论你是一个新手还是有一定经验的开发者,这篇文章都会为你提供一些实践建议和学习路径,帮助你开始在前端开发中探索3D的世界。

通过本文,你将能够了解前端开发中的3D技术,并掌握一些实用的技巧和工具,以打造令人难忘的网页体验。无论你是一个对前端开发感兴趣的初学者,还是一个想要提升自己技能的专业开发者,这篇文章都将带你进入前端3D的精彩世界。

让我们一起探索前端3D的奇妙之处吧!

简介

某一天在网上冲浪的是否发现某东上面的一款商品的图片介绍竟然可以拖动,在一番了解过后下,知道了这个效果是通过前端的3D引擎来实现的【上面的背景是chatgpt编的】

boss上也有类似效果

前端web 3d引擎框架

目前主流的3d引擎框架:WebGL、ThreeJS、BabylonJS、SceneJS和LayaboxJS

1,WebGL

WebGL(全写Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等
浏览器支持:

2,BabylonJS(开源;JavaScript、TypeScript)

(一)特点

强大,美观,简单和开放的3D渲染体验。

(二)适用范围

Babylon.js是一款WebGL开发框架。 适合做中大型项目,尤其是多种媒体混杂的或者是游戏项目VR体验项目

(三)支持格式

glTF,OBJ,STL,.babylon (常用格式)。

(四)优缺点

1、 优点
功能较为全面,功能比较丰富、灵活、模型显示不失真。有微软背景,有不少的demo,有较详细的api文档,有供测试的平台,有提供3dsmax转换模型的插件,比three.js成熟;几经更新与完善之后,Babylon.js已更新至1.12版本,相比之前的版本,除了不断的Bug修复之外,Babylon.js还新增了.许多非常牛叉的新特性,并对已有功能进行了完善。更方便快捷地完成光线、轮船纹理、海浪等的3D建模,从而带来最佳的呈现效果。
2、缺点
学习难度大、周期长,需要进行大量深入的学习与研究。另外,在模型文件较大或较多时,浏览器打开时会等待较长时间;
中文资料很少,没有系统的中文教程,如从入门到精通都可以给你讲一遍系统的教程,论坛也会被墙,相关的qq群较少,群里的人也不多,所以能真正指导我们的大牛也比较少。国内资料基本没有。同Three.js类似,Firefox浏览器在本地同时打开多个较大模型时,会提示浏览器性能不足问题。另外,模型在100M以上时可能存在无法加载的现象。.babylon模型预览,可以通过官方提供的沙盒地址,通过拖放.babylon文件进行查看。经过测试,发现个别.babylon模型仍难以打开。
⦁ 浏览器支持
对目前主流的IE11以上、Chrome和FireFox都支持。
⦁ 速度
同Three.js类似,加载大模型时速度较慢。需提供正在载入信息提示,尽可能提高用户体验。不过Babylon为了尽可能的提高加载速度,提供了工具可以将.babylon文件转换成几个文件,缓式加载以提高显示速度。
⦁ 运行环境
运行Web端应用,需先在本地搭建应用服务器环境。例如:Apache、Tomcat、JBoss等。
主站:http://www.babyon.js.com/

3,ThreeJS(开源,JavaScript)

(一)特点

Three.js 是一款 webGL 开源框架,易用、简单、直观的方式封装了 3D 图形编程中常用的对象。在开发中使用了很多图形引擎的高级技巧,提高了性能。内置了很多常用对象和极易上手的工具,功能强大。

适用范围

可以做中小型的重表现的Web项目。Three.js以简单、直观的方式封装了3D图形编程中常用的对象。更方便快捷地完成光线、轮船纹理、海浪等的3D建模,从而带来最佳的呈现效果。

(三)支持格式

stl,obj+mtl+png,FBX,gltf格式(主要格式)

(四) 优缺点

1、优点
国内用的比较多,所以中文的资料也会比较多,有比较系统的中文教程如从入门到精通。用的人比较多,所以相关的qq群较多,群里的人也较多,接触到的大牛应该也会比较多。在WebGL的引入之前已经创建了three.js独特方便的模块化渲染接口,并在不用WebGL的情况下允许它使用SVG和HTML5画布元素。近些年,浏览器的功能越来越强大,渐渐得成为了复杂应用和图形的平台。同时,现有大多数浏览器实现了对 WebGL 的支持,但要直接使用 WebGL 相关接口进行开发,则需要学习复杂的着色器语言,且开发周期长,不利于项目的快速开发。
面对这种情况,Three.js 应运而生,它不但对 WebGL 进行了封装,将复杂的接口简单化,而且基于面向对象思维,将数据结构对象化,非常方便我们开发。Three.js 的发展十分迅速,然而配套的学习材料却比较匮乏,于是便有了当前的这个课程。
2、缺点
没有提供一些基础建模软件的插件,比如3dsmax的模型导出插件,虽然说提供一些读3ds格式,fbx格式的场景。要配合更多扩展库完成,因为你可能会需要联网通信功能的封装、声音普通控制甚至高级频谱控制、输入设备信息的处理等诸多渲染以外的功能。国内学习资料多,但加载速度慢、缺少碰撞检测等功能

4,LayaboxJS(layaair底层开发Ar,JS,TS)

(一)特点

LayaAir引擎的产品性能已达到原生APP的水准,甚至可以直接用于开发APP、HTML5、Flash的多端版本产品。裸跑性能堪比APP,支持2D,3D,VR开发。

(二)适用范围

大型游戏开发项目与游戏上市企业,广告,营销,教育,应用开发等领域。

(三)格式支持

Spline。

(四)优缺点

1、优点
裸跑性能堪比APP 多版本发布、知名CP首选引擎
极致性能:LayaAir优先使用webgl渲染,如果webgl不可用,自动无缝转为canvas渲染,引擎设计过程中处处以性能为优先原则,LayaAir是为裸跑而设计的HTML5引擎。
轻量易用:LayaAir API设计上追求精简,简单易用,上手容易,引擎本身非常注意自身大小,是目前同等功能最小的HTML5引擎。
支持多语言开发:LayaAir同时支持ActionScript3、TypeScript、JavaScript三种语言开发HTML使用任意一种自己喜欢的语言开发即可。
功能齐全:同时支持2D,3D,VR、时间轴动画,缓动、UI系统、粒子动画、骨骼动画、物理系统等。提供可视化辅助开发及工具流。LayaAirIDE提供代码开发工具及可视化编辑器,清晰的工作流,让美术,策划,程序紧密配合,提高开发效率。
开源免费:引擎全部开源并托管到github,并且全部免费使用,包括商用。
2、缺点
有些功能与问题,官方文档没提到。
底层技术
关于as/js/ts语言的选择,演讲者表示,LayaAir现在对as的支持最好;不过as毕竟是停止更新的语言,LayaAir以后对它的支持也会慢慢淡化,直至脱钩。在js和ts之间,演讲者建议选择ts,因为ts的类型健壮,而且ts编译成js的过程效率很高。
相关链接:https://ldc2.layabox.com/

5,SceneJS

(一)特点

它是针对计算机辅助设计的要求。 开源的JavaScript3D引擎,特别适合需要高精度细节的模型需求,比如工程学和医学上常用的高精度模型。

(二)适用范围

它是针对计算机辅助设计的要求。 开源的JavaScript3D引擎,特别适合需要高精度细节的模型需求,比如工程学和医学上常用的高精度模型。

(三)优缺点

1、优点
专门用于快速绘制大量单独连接的对象,而没有像阴影、反射等游戏引擎效果。SceneJS的API和JSON相似,它学习起来很简单。
2、缺点
相关社群几乎没有,中国很少人用。缺少碰撞检测等功能。加载大模型时速度较慢。需提供正在载入信息提示,尽可能提高用户体验。(可用工具可以将.babylon文件转换成几个文件,缓式加载以提高显示速度。)Firefox浏览器在本地同时打开多个较大模型时,会提示浏览器性能不足问题。另外,模型在100M以上时可能存在无法加载的现象。
相关链接:https://daybrush.com/scenejs/features.html#transition

ThreeJS实例

游戏

3D模型展示

数据可视化

web VR

3D引擎的基本知识

场景

一个容器,容纳着除渲染器以外的三维世界里的一切。场景的元素采用右手笛卡尔坐标系,x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外

摄像机

就像人的眼睛,在一个空间里可以看向任意方向,可以通过参数调节可视角度和可视距离。

左侧图片 右侧图片

几何体

3D世界里的所有物体都是 点组成面,面组成几何体。相信大家对以下标准的几何体比较熟悉


面是由点构成的,面又可以组成各式各样的几何体。以球体举例,球体面上的点越多,球就越圆。但点越多,运算量也会越大…

灯光

3d引擎在没有手动创建光的情况下会默认有个环境光,不然你什么都看不到。常见的灯光有以下几种类型

1
2
AmbientLight(环境光,没有方向全局打亮,不会产生明暗)
DirectionLight(平行光,参考日光来理解)

1
PointLight(点光源,参考灯泡来理解)

1
SpotLight(聚光灯,参考舞台聚光灯)

贴图

想象一下你手里有一个立方体,你用一张A4纸包裹上立方体的所有面,并在上面画画。你画的内容就是
贴图。

材质

1
2
3
4
5
6
7
8
延续贴图里的想象,你用白卡纸画画,还是用油纸画画,呈现出来的质感是不同的对不对,这就是
材质!下面五个球的颜色都是一样的,而材质从左至右分别是

MeshBasicMaterial(基础材质,不受光照影响)
MeshStandardMaterial(PBR标准材质)
MeshPhongMaterial(高光材质,适用于陶瓷,烤漆类质感)
MeshToonMaterial(卡通材质,俗称三渲二)三渲二:先通过3D技术建模,然后将3D模型渲染成2D的色块效果。
MeshStandardMaterial(PBR标准材质模拟金属反射)


总结

  1. 场景(Scene):是物体、光源等元素的容器,可以配合 chrome 插件使用,抛出 window.scene即可实时调整 obj 的信息和材质信息。
  2. 相机(Camera):场景中的相机,代替人眼去观察,场景中只能添加一个,一般常用的是透视相机(PerspectiveCamera)
  3. 物体对象(Mesh):包括二维物体(点、线、面)、三维物体,模型等等
  4. 光源(Light):场景中的光照,如果不添加光照场景将会是一片漆黑,包括全局光、平行光、点光源等
  5. 渲染器(Renderer):场景的渲染方式,如webGL\canvas2D\Css3D。
  6. 控制器(Control): 可通过键盘、鼠标控制相机的移动

实战

了解了基础知识后,使用ThreeJS就比较容易上手了。可以说在3dmax等软件中调出来的90%的效果,用threejs都能找到对应的配置参数。

引入 threejs

由于我这里使用的是 vue3 + vite + ts 创建的demo,所以我们引入@types/three

1
npm install --save-dev @types/three

应用threejs

在我们要用得项目的页面中引入three

1
import * as THREE from "three";
搭建基础场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<template>
<div id='container' style="width:100vw;height: 100vh;"></div>
</template>

<script setup lang="ts">
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { ref, onMounted } from 'vue'
var scene, camera, renderer;

function init() {
// 创建场景
scene = new THREE.Scene();
// 创建透视相机 不用正投影相机 fov视野角度,aspect摄像长宽比,near摄像机视近端面,far远端面
camera = new THREE.PerspectiveCamera(90, document.body.clientWidth / document.body.clientHeight, 0.1, 100);
// 相机篇拍照位置
camera.position.set(0, 0, 3);
// 渲染器
renderer = new THREE.WebGLRenderer();
renderer.setSize(document.body.clientWidth, document.body.clientHeight);
document.getElementById("container").appendChild(renderer.domElement);

var controls = new OrbitControls(camera, renderer.domElement);

//下方添加模型
// const geometry = new THREE.BoxGeometry( 1, 1, 1 );
// const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
// const cube = new THREE.Mesh( geometry, material );
// scene.add(cube);

loop();
}

function loop() {
requestAnimationFrame(loop);
renderer.render(scene, camera);
}

onMounted(() => {
init()
})
</script>

<style>
* {
margin: 0;
padding: 0;
}
</style>

运行起来就会是这样子【一片黑】

现在我们可以先添加一个标准几何体来试试看,比如我们添加一个立方体来试试看【把//下方添加模型注释解开】

1
2
3
4
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

requestAnimationFrame
可能有细心的同学发现了,requestAnimationFrame 这个函数到底是干嘛的
我们先了解一下什么时什么是重绘和回流?

重绘:
重绘: 当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,而不会影响布局的操作,比如 background-color,我们将这样的操作称为重绘

回流:
回流:当渲染树中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新构建的操作,会影响到布局的操作,这样的操作我们称为回流。
常见引起回流属性和方法:
任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发回流。

(1)添加或者删除可见的 DOM 元素;
(2)元素尺寸改变——边距、填充、边框、宽度和高度
(3)内容变化,比如用户在 input 框中输入文字
(4)浏览器窗口尺寸改变——resize事件发生时
(5)计算 offsetWidth 和 offsetHeight 属性
(6)设置 style 属性的值
(7)当你修改网页的默认字体时。

requestAnimationFrame的作用类似 setTimeout、setInterval ,那为啥这里不用呢
requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:

1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。

模型自动旋转
有了以上的对 requestAnimationFrame 函数的了解,如果我们在方法里面加入下列方法,那么模型就会自动旋转起来

1
2
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;

模型设计

要想在移动端网页里流畅运行,最好模型不能超过10万面,而我自己又缺少模型设计相关的能力,那问题来了,好看的模型怎么来呢?

sketchfab:https://sketchfab.com/search?q=plane&type=models

sketchfab 上模型有收费的有免费的,收费的要购买的话需要刀,没有境外支付方式的话最好不要尝试,别问我为什么,我自己亲生尝试过,纯属折磨人,免费的也有许多,但质量就比较收费的要差些许。上面的模型基本上可以满足大部分人对基础模型的需求,但如果要对模型进行对应自定义修改,就需要自己到软件上修改了

因为上家公司是做机票相关的,所以下个飞机的模型来作为demo
制作模型的软件很多,例如3DMax和Blender之类,但是3DMax虽然功能很齐全,但是没有系统学习的还是太难用了,我这里用的Blender来修改模型的

本来想做成这样的☝️,但是改着改着,就变成这样了👇

左侧图片 右侧图片

(不要过多在意外表,都是飞机拉横幅。。。)
梳理好模型结构后,我们就要准备模型文件了

3d模型的文件格式有很多,但threejs里常用的基本是

  1. OBJ格式

    老牌通用3d模型文件,不包含贴图,材质,动画等信息。

  2. GLTF格式(图形语言传输格式)

    由OpenGL官方维护团队推出的现代3d模型通用格式,可以包含几何体、材质、动画及场景、摄影机等信息,并且文件量还小。有3D模型界的JPEG之称。

【需要注意的是,sketchfab 下载模型的格式是根据用户上传的模型格式来的,原模型用的是OBJ格式,这里我们需要使用GLTF格式,可以通过建模软件或ThreeJs提供的editor来转换并导出】

Blender导出:

editor导出: 我导出一控制台报错,不知道为啥

通过GLTFLoader,我们可以加载一个.gltf格式的3d模型文件。需要注意的是,这些Loader都以插件的形式存在,需要引入相应的XXXLoader才能使用
three下载的时候就自己有XXXLoader,但是需要我们引入

然后我们只要在 setModel里加载对应模型就可以了
需要注意的是可能会出现3D模型太小,或者模型为黑色在背景中看不到的情况,可添加辅助线定位,并添加环境光源或者纹理

1
2
var axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);

也可以在初始化的时候把背景颜色设置为透明

1
2
3
4
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const loader = new GLTFLoader();
// console.log(loader)
loader.load(
'./model/plane.gltf',
function ( gltf ) {
scene.add( gltf.scene );
// var axesHelper = new THREE.AxesHelper(100); // 如果是黑色背景可能看不见模型,加条辅助线就可以了
// scene.add(axesHelper);
},
function ( xhr ) {
//侦听模型加载进度
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
function ( error ) {
//加载出错时的回调
console.log( 'An error happened' );
}
);

加载模型后的效果:

动画效果

看看上面的gif效果我们发现,本来我们的飞机后面的横幅是有动画的,但是在这里没有效果
我们现在全局定义动画存储数组,时钟对象

1
2
var mixers = [] // 定义动画存储数组
var clock = new THREE.Clock(); // 定义Three时钟对象

然后在loader里设置需要展示的动画效果,gltf.scene.children里面会获取模型的第一层场景集合,我们找到并把动画加到视图里面去

1
2
3
4
// 调用动画
var mixer = new THREE.AnimationMixer( gltf.scene.children[0] );
mixer.clipAction( gltf.animations[ 0 ] ).setDuration( 1 ).play();
mixers.push( mixer );

然后到渲染模型的时候把存储的动画全部重复播放即可

1
2
3
4
var delta = clock.getDelta();
for ( var i = 0; i < mixers.length; i ++ ) { // 重复播放动画
mixers[ i ].update( delta );
}

设置动画的效果大致如下

贴图和材质

现在我们来给几何体添加贴图,贴图怎么做是设计师的专业。这里不过多的说,我们只需要知道,这些贴图如何使用即可。我随便在文件夹中搞两张图片作为贴图。

其实真是情况下有许多种贴图,但奈何不会设计,就简单讲讲贴图的种类,实际使用还是大同小异

普通贴图
material.map,替代颜色
法线贴图
material.normalMap,让细节程度较低的表面生成高细节程度的精确光照方向和反射效果
环境光遮蔽贴图
material.aoMap,用来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果
环境反射贴图
material.envMap,用于模拟材质反射周围环境的效果

加载贴图

默认贴图加载不了
模型设计和导出的时候有默认贴图,导出到threeJS就全变成黑色了

我们可以需要在load的时候加载默认贴图样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//新建纹理
var texture = new THREE.TextureLoader().load();
var tmaterial = new THREE.MeshStandardMaterial({ map: texture });

load:
gltf.scene.children[0].material = tmaterial
gltf.scene.children[0].material.needsUpdate = true;
gltf.scene.traverse( function ( child ) {
//放射光颜色与放射光贴图 不设置可能导致黑模
if ( child.isMesh ) {
// console.log(child)
child.material.emissive = child.material.color;
child.material.emissiveMap = child.material.map ;

}

} );

载入默认贴图后

载入自定义贴图

gltf.scene.traverse(func())
gltf.scene.traverse 接收一个回调函数,它会便利当前模型所有的场景集合
打印一下 console.log(child)

上图中的 Mesh 就是我们需要贴图的 物体对象 ,对象内的name字段可以让我们知道到底去对哪个物体对象进行自定义贴图等一系列操作

1
2
3
4
5
6
7
8
9
10
if (child.name === '球体_2') {
child.material.map = allTexture["only_one"].texture;
child.material.normalMap = allTexture["only_one"].texture;
child.material.aoMap = allTexture["only_one"].texture;
} else if (child.name === 'mesh_0') {
console.log(child)
child.material.map = allTexture["logo"].texture;
child.material.normalMap = allTexture["logo"].texture;
child.material.aoMap = allTexture["logo"].texture;
}

保存刷新界面

结语

这篇文章其实也是在上家公司做技术分享的时候写的,虽然已经从上家离职,但是公司的栽培我还是挺感恩的。
以上只是对threejs一个非常粗浅的使用,threejs能实现的效果远远不止于此,在3DMax等主流3D建模软件上90以上的功能都能在threejs上得以实现,总的来说功能还是挺强大的

# 推荐文章
  1.卡片翻动效果
  2.每日进步:一篇文章带你走进3D的世界
  3.每日进步:动画的暂停与恢复
  4.每日进步:大整数相加
  5.响应式原理:Vue 3 的 nextTick ?

:D 舔狗日记获取中...