文章目录
1. 3D技术是什么
3D,是three dimensional的缩写,其实就是三维,三个维度。我们的真实世界是三维的(忽略时间),那我们在计算机里,也想能创造类似现实的三维空间,这就促成了计算机三维技术的出现。
2. SGI(硅谷图形公司)与OpenGL
上世纪九十年代初,SGI(硅谷图形公司)崭露头角,专攻3D图形领域,下图是他们当年的logo,代表着当时其先进而卓越的3D技术。
而这个领域在当年就像现在的虚拟现实技术一样,各个大厂都争先空后的研发,Sun、惠普、IBM都在搞自己的技术和标准,以寻求成为行业领导者。
越来越多的竞争,让SGI市场份额锐减,公司做了一个大胆的决定,将自己公司的3D技术标准IRIS GL
转变为开放标准,OpenGL
从此诞生。
从企业标准到开放标准的转变,并不是那么容易,一些API的功能,需要交由硬件制造商或操作系统开发商来做,这就导致了IRIS GL
和OpenGL
并不兼容,为了维护客户,SGI必须两套标准一起维护。
问题并没有改变SGI开放标准的决心,1992年,SGI领导创建架构评审委员会(OpenGL ARB),制订和规范OpenGL标准。1995年,微软发布Direct3D
,成为新的行业搅局者,1997年,SGI与微软携手合作,旨在统一OpenGL
和Direct3D
标准,一年后,3C巨头惠普也加入进来,但最后无疾而终。
时间最终磨平了一切,OpenGL
标准在多轮竞争整最后存活了下来,但SGI公司,却逐渐没落了,2006年3月,SGI公司宣布破产,同年7月,OpenGL架构评审委员会决定讲OpenGL API标准的控制权交给Khronos Group(科纳斯组织),从此,3D标准领域的巨头组织扬帆起航。
OpenGL官网地址:https://www.opengl.org/
3. Khronos Group(科纳斯组织)
国内介绍Khronos这个组织的资料不多,这是一个由行业内巨头公司共同组建的免版税开放标准维护组织,旨在指定和规范3D图形化、虚拟现实、并行计算、机器学习等领域的开放标准。组织成员都是行业巨头,见下图。
经过多年的发展,组织的开放标准已经不再只是OpenGL
,分类见下图。
其实,只要你玩过3D游戏,看过3D动画,都绕不开这些标准。
4. WebGL
WebGL官网地址:https://www.khronos.org/webgl/
当3D模型技术盛行于客户端开发时,新的尝试开始了,有没有可能在浏览器中使用OpenGL技术,使我们最常用的浏览器支持三维模型的展示,甚至3D游戏的运行。
该标准的API基于OpenGL ES
,在前端领域,通过HTML5的Canvas元素承载,暴露符合ECMAScript标准(JavaScript语言的标准)的API。结果就是,我们可以在浏览器里,用js程序载入或创建3D空间,这个场景会创建到一个Canvas元素上运行,大大增加了浏览器程序的能力。
WebGL API接口参考:https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API
终于到了浏览器,有MDN的文档可以查了,年轻的前端工程师默默撸起了袖子,准备大干一场。但可能马上就会让你失望,『傻孩子们,快跑啊』,这并不是前端领域!
WebGL只是提供了OpenGL的浏览器接口,但核心技术的实现还是依托于OpenGL。看一段MDN上简单的WebGL程序:
MDN上WebGL的Demo源码:https://github.com/mdn/webgl-examples
const canvas = document.querySelector("#glcanvas")
// 初始化WebGL上下文
const gl = canvas.getContext("webgl")
// 创建着色器的方法
function createShader (gl, sourceCode, type) {
var shader = gl.createShader( type );
gl.shaderSource( shader, sourceCode );
gl.compileShader( shader );
if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
var info = gl.getShaderInfoLog( shader );
throw "Could not compile WebGL program. \n\n" + info;
}
return shader;
}
var vertexShaderSource =
"attribute vec4 position;\n"+
"void main() {\n"+
" gl_Position = position;\n"+
"}\n";
//从上面例子使用 createShader 函数。
var vertexShader = createShader(gl, vertexShaderSource, gl.VERTEX_SHADER)
var fragmentShaderSource =
"void main() {\n"+
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"+
"}\n";
//从上面例子使用 createShader 函数。
var fragmentShader = createShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER)
前面几行还好理解,获取canvas元素,拿到gl上下文。后面的功能是创建着色器,这是WebGL的核心功能,图像都是用着色器渲染出来的。注意看vertexShaderSource
和fragmentShaderSource
两个变量,这分别是定点着色器和片段着色器的实际代码,可以看到是一段程序,但是以字符串的形式定义,这明显不是js的语法,这其实是GLSL(OpenGL ES 着色语言),已经不是前端领域了。
GLSL官方文档:https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language
为了证明这一点,我翻阅了three.js
的源码,可以看到在three.js
里也定义了很多着色器,都是GLSL语法。
three.js源码中的着色器部分: https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk
所以,想要直接用WebGL开发3D应用是很难的,不是主流前端工程师做的任务,大部分项目还是基于three.js
这种库去做的,除非你是在做跟three.js
同级别的库,那就要去好好学学GLSL、OpenGL和计算机图形学的相关东西了。
5. three.js
Github地址:https://github.com/mrdoob/three.js
three.js目前在github的star数有81k,已经超过了angular,比它star多的前端包没几个了,大多是框架级别的库。作者是Mr.doob,一个比较传奇的人物,设计师下场写代码。
ubuntu关于Mr.doob的专访(2011年):https://ubuntu.com/blog/interview-with-mr-doob
three.js的功劳在于,它把WebGL通过JS进行了系统性的封装,提供了很多类与方法,可以直接使用,常用的类有Scene
(场景)、Camera
(相机)、Mesh
(网格体)、Light
(灯光)等等。前端工程师不需要陷入WebGL本身的细节当中,而专注于3D应用的功能实现,这大大提高了工作效率、降低了工作难度。
看一个最简单的例子
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// 几何体
const geometry = new THREE.BoxGeometry();
// 材质
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
// 网格体
const cube = new THREE.Mesh( geometry, material );
// 加入到模型
scene.add( cube );
// 相机定位
camera.position.z = 5;
这段代码就包括了创建3D空间的大部分要素,这里我们不再需要关系WebGL的底层细节,只要理解场景、相机、渲染器、几何体、材质等等的这些概念,就可以完成代码的编写。使3D技术真正进入了『浏览器时代』!
three.js更具体细节,之后写文章具体讨论。
6. react-three-fiber
如果当你能熟练使用three.js之后,就会面临一个新问题,如何让three.js与前端开发框架结合,当然这并不是必须的,你用任何前端框架,都可以使用three.js直接进行开发。
问题的关键依然是开发效率和可维护性的问题,three.js虽然封装了WebGL的细节,但暴露的API依然是比较基础的,真正做一个比较大型的3D应用,这也是远远不够的,还需要很多生态库去配合,才可以完成日常的大部分需求。
React,毋庸置疑,是目前最先进的前端框架之一,优势在于开发交互和逻辑都较为复杂的前端应用,3D应用本身就具有这种属性,比较适合应用React来开发,但three.js和React之前并没有任何联系,这就给开发带来了割裂行,你会发现在开发过程中无法用到React Hook这些非常好用的功能。
react-three-fiber的出现,就很好的解决了这个问题。如果将一个已经可以熟练使用three.js和React的前端开发者比作武林高手『金毛狮王谢逊』,那react-three-fiber就是谢逊手里的『屠龙宝刀』,它是前端3D开发领域的一把利器,有了它,你就可以在前端3D领域『大杀四方』了。
官网的酷炫Demo:https://docs.pmnd.rs/react-three-fiber/getting-started/examples
react-three-fiber的主要能力,是充分的利用了React框架的能力,将three.js的功能封装成符合JSX语法的React组件或一些React Hook,很大限度的又放大了three.js的能力。
看一下官网的例子,可以看到,代码又『升维了』,甚至看不到three.js的原生方法了,这里充分利用了hook和jsx,React会自动监听各个属性的变化,不需要我们在手动调用three.js的方法执行。
import { createRoot } from 'react-dom/client'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
function Box(props) {
// This reference will give us direct access to the mesh
const mesh = useRef()
// Set up state for the hovered and active state
const [hovered, setHover] = useState(false)
const [active, setActive] = useState(false)
// Subscribe this component to the render-loop, rotate the mesh every frame
useFrame((state, delta) => (mesh.current.rotation.x += 0.01))
// Return view, these are regular three.js elements expressed in JSX
return (
<mesh
{...props}
ref={mesh}
scale={active ? 1.5 : 1}
onClick={(event) => setActive(!active)}
onPointerOver={(event) => setHover(true)}
onPointerOut={(event) => setHover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
createRoot(document.getElementById('root')).render(
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>,
)
7. pmndrs
Github主页:https://opencollective.com/pmndrs
最后,我们介绍下pmndrs这个开源组织,他是react-three-fiber项目的所有者,但它并非只有react-three-fiber,在React和three.js领域,这个组织做了大量工作,基于react-three-fiber,已经做成了一个生态,用于开发炫酷的3D应用。
开发文档首页:https://docs.pmnd.rs/
除了主库@react-three/fiber以外,下边介绍几个常用的库,一些库我也正在学习中,这里是个挺大的世界,这些库的用法需要花时间需学习和探索。
@react-three/drei
这是@react-three/fiber
的辅助类库,里边封装了three.js核心以外的大量工具,包括控制器、加载器、一些帮助方法等等。
react-spring
这是一个react的弹簧动画库,可以用它来控制变量生成各种各样的动画。
@react-three/cannon
物理碰撞检测引擎,让你可以实现类似游戏一样的墙体或者障碍物效果。
8. 结语
到这里,我们基本厘清了前端3D技术的发展脉络,这一块技术其实涉及的知识比较复杂,往底层探究,可能要接触到计算机图形学、GPU性能等一系列基础问题,往顶层探索,可能要涉及3D建模的知识,甚至3DMAX这些软件的使用。很多知识体系其实已经超出了传统前端的范畴。不过,你不卷别人,就是被人卷,多了解点也没坏处。
本篇作为一个开篇,只是介绍了一些脉络,之后有时间的话,希望自己也能把这块好好整理下,做个记录,毕竟目前自己的工作涉及这块。
好了,这篇就写到这里了,该去写bug了,干巴爹!