初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体

一、简介

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网页游戏等。
WebGL 运行在电脑的 GPU 中,因此需要使用能在 GPU 上运行的代码,这样的代码需要提供成对的方法,每对方法中的一个叫顶点着色器(计算顶点的位置,即提供顶点在裁剪空间中的坐标值。)而另外一个叫做片元着色器(计算图元的颜色值,我们可以将片元着色器大致理解成网页中的像素。),并且使用 GLSL 语言。将顶点着色器和片元着色器连接起来的方法叫做着色程序。
使用 WebGL 只需要给它提供这两个东西。 因此我们通过提供两个着色器来做这两件事,一个顶点着色器提供裁剪空间坐标值,一个片元着色器提供颜色值。
WebGL 只能够绘制点、线段、三角形这三种基本图元。立方体、球体、柱体等规则形体,本质上是由一个一个的点组成,GPU 将这些点用三角形图元绘制成一个个的微小平面,这些平面之间互相连接,从而组成各种各样的立体模型。

二、几种 WebGL 开发的框架

1. Three.js
Three.js 运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,是 WebGL 的综合库,应用范围较广泛,缺点是没有比较全面详细的官方文档,入门比较不易。
2. Cesium.js
Cesium.js 是专用于 3D 地图开发的 WebGL 库,其拥有较为全面的 3D 地图开发 API,对于需要开发 3D 地图的开发者而言是一个不错的选择,但针对其他场景的应用开发覆盖的就不是很全面了。
3. Babylon.js
Babylon.js 是一款国外应用较广泛的 WebGL 库。

Three.js 作为一种应用较为广泛的WebGL 开发框架,那必然作为webgl入门的首要选择了。这一次我们要完成的作业是实现一个顶点不同颜色的旋转立方体。

三、基于Three.js绘制顶点不同颜色的旋转立方体

首先需要了解Three.js的工作机制,如下图所示
初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体_第1张图片

将所有物体添加到场景scene中,然后将需要渲染的数据传递给渲染器(renderer),渲染器负责将场景在 画布上绘制出来。
可以看出来,Three.js编程需要具备三个要素:场景Scene、相机Camera、渲染器Renderer。将三者结合起来才可以渲染出可见的内容。

1. 场景Scene

场景Scene是所有物体的容器,也就是我们所看到的三维世界。Scene包括网络模型和光照。
初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体_第2张图片

// 1、创建一个场景
scene = new THREE.Scene();
// 在场景中添加雾的效果,Fog参数分别代表‘雾的颜色’、‘开始雾化的视线距离’、刚好雾化至看不见的视线距离’
scene.fog = new THREE.Fog(0x000000, 0, 10000);
scene.background = new THREE.Color(0xffeeff);

1)创建模型
我们需要创建一个顶点不同颜色的立方体,Three.js提供了BoxGeometry,很方便的创建了立方体,但是没有办法设置顶点颜色。很遗憾,需要换一种方式实现了。Three.js还提供了缓冲几何体对象BufferGeometry,采用绘制三角面元的思想,通过类型数组设置几何体顶点位置与颜色,从而得到想要的立方体模型。

// 2、创建模型
geometry = new THREE.BufferGeometry(); //声明一个缓冲几何体对象

//类型数组创建顶点位置position数据
vertices = new Float32Array([
  0, 0, 0, //顶点1坐标
  50, 0, 0, //顶点2坐标
  50, 50, 0, //顶点3坐标
  50, 50, 0, //顶点3坐标
  0, 50, 0, //顶点4坐标
  0, 0, 0, //顶点1坐标

  50, 0, 0, //顶点2坐标
  50, 50, 0, //顶点3坐标
  50, 50, 50, //顶点8坐标
  50, 50, 50, //顶点8坐标
  50, 0, 50, //顶点7坐标
  50, 0, 0, //顶点2坐标

  0, 0, 0, //顶点1坐标
  0, 50, 0, //顶点4坐标
  0, 50, 50, //顶点5坐标
  0, 50, 50, //顶点5坐标
  0, 0, 50, //顶点6坐标
  0, 0, 0, //顶点1坐标

  0, 50, 50, //顶点5坐标
  0, 0, 50, //顶点6坐标
  50, 0, 50, //顶点7坐标
  50, 0, 50, //顶点7坐标
  50, 50, 50, //顶点8坐标
  0, 50, 50, //顶点5坐标

  50, 50, 0, //顶点3坐标
  0, 50, 0, //顶点4坐标
  0, 50, 50, //顶点5坐标
  0, 50, 50, //顶点5坐标
  50, 50, 50, //顶点8坐标
  50, 50, 0, //顶点3坐标

  50, 0, 0, //顶点2坐标
  0, 0, 0, //顶点1坐标
  0, 0, 50, //顶点6坐标
  0, 0, 50, //顶点6坐标
  50, 0, 50, //顶点7坐标
  50, 0, 0, //顶点2坐标
]);
// 创建属性缓冲区对象
attribute = new THREE.BufferAttribute(vertices, 3); //3个为一组,作为一个顶点的xyz坐标
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribute;
//类型数组创建顶点颜色color数据
colors = new Float32Array([
  1, 1, 1, //顶点1颜色
  1, 0, 1, //顶点2颜色
  1, 0, 0, //顶点3颜色
  1, 0, 0, //顶点3颜色
  1, 1, 0, //顶点4颜色
  1, 1, 1, //顶点1颜色

  1, 0, 1, //顶点2颜色
  1, 0, 0, //顶点3颜色
  0, 0, 0, //顶点8颜色
  0, 0, 0, //顶点8颜色
  0, 0, 1, //顶点7颜色
  1, 0, 1, //顶点2颜色

  1, 1, 1, //顶点1颜色
  1, 1, 0, //顶点4颜色
  0, 1, 0, //顶点5颜色
  0, 1, 0, //顶点5颜色
  0, 1, 1, //顶点5颜色
  1, 1, 1, //顶点1颜色

  0, 1, 0, //顶点5颜色
  0, 1, 1, //顶点5颜色
  0, 0, 1, //顶点7颜色
  0, 0, 1, //顶点7颜色
  0, 0, 0, //顶点8颜色
  0, 1, 0, //顶点5颜色

  1, 0, 0, //顶点3颜色
  1, 1, 0, //顶点4颜色
  0, 1, 0, //顶点5颜色
  0, 1, 0, //顶点5颜色
  0, 0, 0, //顶点8颜色
  1, 0, 0, //顶点3颜色

  1, 0, 1, //顶点2颜色
  1, 1, 1, //顶点1颜色
  0, 1, 1, //顶点5颜色
  0, 1, 1, //顶点5颜色
  0, 0, 1, //顶点7颜色
  1, 0, 1, //顶点2颜色
]);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB
//材质对象
material = new THREE.PointsMaterial({
  // 使用顶点颜色数据渲染模型,不需要再定义color属性
  // color: 0xff0000,
  side: THREE.DoubleSide, // 双面渲染,否则旋转到某个角度有些面就看不到了,默认单面渲染
  vertexColors: THREE.VertexColors, //以顶点颜色为准
  size: 2.0, //点对象像素尺寸
});
// points = new THREE.Points(geometry, material); //点模型对象
// scene.add(points);
console.log("geometry", geometry);
console.log("顶点位置数据", geometry.vertices);
// 网格模型对象Mesh,生成网格
mesh = new THREE.Mesh(geometry, material);
// 网格模型添加到场景中
scene.add(mesh);

2)设置光源
模型有了,接下来需要设置光源,光影效果是让画面效果更完美。这里使用点光源。

// 3、创建点光源
point = new THREE.PointLight(0xffffff);
point.position.set(100, 100, 100);
scene.add(point);

2. 相机Camera

从一个点找个角度来看三维世界,就像照相机的功能一样,这种这就是相机Camera的概念。Three中使用采用常见的右手坐标系定位。
相机Camera初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体_第3张图片

// 4、创建相机,设置相机位置和相机镜头的朝向
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
var k = width / height; //窗口宽高比
var s = 100; //三维场景显示范围控制系数,系数越大,显示的范围越大
// 创建相机对象
camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 800);
camera.position.set(100, 400, 100); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)

3. 渲染器Renderer

在场景中添加了物体、光源以及照相机,想要完整的将所有的东西渲染到屏幕上,就需要渲染器Renderer来完成了。
初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体_第4张图片

// 5、创建webgl渲染器实例
renderer = new THREE.WebGLRenderer(); // antialias 抗锯齿
renderer.setPixelRatio(window.devicePixelRatio);
// 设置渲染器画布的大小
renderer.setSize(window.innerWidth, window.innerHeight - 50);
// 把画布实例(canvas)放入容器中
document.body.appendChild(renderer.domElement);

与canvas相同,实现动画效果,可以采用 requestAnimationFrame 来一帧帧的渲染。

// 动画刷新
animation() {
  !geometry && this.initGeo();
  this.update();
  window.requestAnimationFrame(this.animation.bind(this));
}
// 旋转状态
update() {
  mesh.rotateX(Math.PI / 180);
  mesh.rotateY(Math.PI / 180);
  mesh.rotateZ(Math.PI / 180);
  renderer.render(scene, camera);
}

最终我们实现效果如下:
初探WebGL-基于Three.js绘制顶点不同颜色的旋转立方体_第5张图片

旋转起来(gif压缩损失了部分画质,清晰gif可查看:https://img.ljcdn.com/beike/ajax/m/1638254259866.gif)

代码:https://github.com/ElviraD/webGLDemo

四、小结

对于初学者而言,直接使用 WebGL 原生 API 进行 3D 网页的开发,显然是不合适的。这时候我们就可以借助像 Three.js 这样的 WebGL 封装库进行开发。相较之原生 API 的开发,这类第三方封装好的 WebGL 库大大降低了我们的开发成本,同时也能帮助我们开发出更加炫酷的页面效果。
Three.js 是为了简化 3D 渲染的开发框架,它提供了场景 Scene,里面可以包含各种可渲染的物体;Sence 的渲染出来需要指定一个相机;最后通过 Renderer 渲染出来,有动画效果可通过 requestAnimationFrame 来一帧帧的渲染。

五、参考资料

  1. https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-how-it-works.html
  2. https://juejin.cn/post/6994940475459731463
  3. https://discoverthreejs.com/book/first-steps/first-scene/
  4. http://www.webgl3d.cn/Three.js/
  5. https://juejin.cn/post/7033059180789366814

你可能感兴趣的:(web,javascript,webgl,three.js,前端)