零基础使用 WebGL — 快速入门

WebGL 作为一个非常底层的 API,学习和使用起来非常困难,因为 WebGL 需要大量的背景知识。网上教程一般都是介绍API就开始渲染,介绍不多,容易让人迷惑,也容易被劝退。即使你学会了如何使用API​​,也只是了解了表面知识,却不了解其背后的原理。很容易忘记。

《零基础玩转WebGL》系列教程将从最基础的知识开始,逐步讲解WebGL的使用和WebGL背后的原理,以及必备的数学知识。学习后不仅可以开发WebGL应用,还可以轻松阅读Three.js等渲染引擎的源码,开发自己的3D渲染引擎库。

这是“零基础玩 WebGL”的第一篇文章。请查看系列文章目录: 从零基础开始使用 WebGL — 目录

什么是 WebGL?

WebGL(Web Graphics Library)是一种通过 HTML5 画布元素暴露的 Web 标准 JavaScript API,无需使用插件即可在浏览器中渲染高性能交互式 3D 和 2D 图形。它目前由非营利组织 Khronos Group 设计和维护。

WebGL的使用方式和canvas 2d类似,都是通过 获取上下文 方法来获取渲染上下文,如下所示。

 常量画布 = 文档。创建元素(“画布”)  
  
 常量 gl = (  
 帆布。 getContext('webgl2') ||  
 帆布。 getContext('webgl') ||  
 帆布。 getContext('实验-webgl')  
 )  
 复制代码

上面的代码是根据 webgl2 , 网页浏览器 , 实验-webgl 为了获取 WebGL 渲染上下文。 webgl2 是最新版本,几乎与 WebGL1 完全兼容。 实验-webgl 曾经与旧版浏览器兼容,例如 IE 11。

兼容性

image.png

大多数浏览器都支持 WebGL1。支持WebGL2的现代浏览器也有很多,但是Apple还不支持WebGL2,所以在编写WebGL程序时,需要降级到WebGL1。

image.png

OpenGL

在深入 WebGL 之前,我们还需要了解 OpenGL,因为 WebGL 是基于 OpenGL 的。 OpenGL (Open Graphics Library) 是一种跨语言、跨平台的应用程序编程接口,用于渲染 2D 和 3D 矢量图形,常用于 CAD、虚拟现实、科学可视化程序和视频游戏开发。 OpenGL通常由显卡厂商根据规范实现。

OpenGL的前身是SGI的IRIS GL API,在当时被认为是最先进的技术,成为事实上的行业标准,后来被SGI转化为开放标准的OpenGL。 1992 年,SGI 创建了 OpenGL 架构审查委员会,并于 2006 年将 OpenGL API 标准的控制权移交给了 Khronos Group。

OpenGL 是跨平台的。在移动设备上,使用 OpenGL ES(用于嵌入式系统的 OpenGL),它是 OpenGL 的子集。下图显示了 OpenGL 和 OpenGL ES 的时间线。

image.png

WebGL 基于 OpenGL,Op​​enGL 的一个子集。 WebGL1 基于 OpenGL ES 2.0。 WebGL2 基于 OpenGL ES 3.0。

图形处理器

WebGL 高性能的原因在于它使用了 GPU。 GPU和CPU针对两种不同的应用场景。你可以把 CPU 想象成一个无所不知的教授,而 GPU 是一群只会做一些简单计算的小学生,所以对于大量的简单计算,GPU 的执行速度要比 CPU 大得多。

image.png

上图是显卡3090的配置参数,我们可以看到它拥有10000多核,24G显存。支持 3D API、DirectX 12 Ultimate 和 OpenGL 4.6(DirectX 是微软的图形 API)。

坐标系

WebGL的坐标系和canvas 2d的坐标系不一样。因为 WebGL 是 OpenGL 的子集,所以 WebGL 坐标系与 OpenGL 坐标系具有相同的值。

画布 2d 中的坐标系如下所示。

 常量画布 = 文档。创建元素(“画布”)  
 常量 ctx = 画布。获取上下文('2d')  
 复制代码

canvas 2d的坐标原点在左上角,X轴和Y轴的正值分别向右和向下。

WebGL的坐标系与OpenGL相同,更符合我们的常识。

原点在中间,右边是X轴正方向,上​​边是Y轴正方向,就像在数学中一样。

需要注意的是,WebGL中坐标值的范围是 -1 到达 1 , canvas 2d 基于画布的宽度和高度。如果画布宽度是 500 , 然后在 WebGL 1 相当于 500 , 0.5 相当于 250 ,这样做的好处是我们不需要关心画布的宽高,不管画布多大,用于渲染图形 -1 到达 1 .

当然,WebGL 中也有 Z 轴。 Z轴有两种形式,一种正值朝外,一种正值朝内。

Z 轴正向朝外时,我们称其为右手坐标系,Z 正轴朝内时,我们称其为左手坐标系。你可以像下图那样伸出双手做手势,你就知道为什么叫左手坐标系和右手坐标系了。

那么 WebGL 是左手坐标系还是右手坐标系?答案是 两者都不 .但在实际开发中使用 右手坐标系 当然,并不是右手坐标系比左手坐标系好,而是右手坐标系是OpenGL的约定。例如,微软的 DirectX 使用左手坐标系。

为什么说WebGL既不是左手坐标系也不是右手坐标系,原因会在后续文章中解释,现在只需要知道WebGL使用右手坐标系,也就是Z 轴的正值朝外。

三角形

WebGL 是一个相对低级的图形 API。与 canvas 2d 不同,WebGL 只能用它来渲染点、线和三角形。那些复杂的 3D 模型实际上是由三角形组成的。

image.png

例如,上面的汽车模型实际上是由 267,300 个三角形组成的。

点击此链接查看模型详情 sketchfab.com/3d-models/t… .

有同学可能会问,为什么是三角形,而不是5边、6边的形状?

因为三角形有很多优点,比如三角形必须在一个平面内,所以任何多边形都可以用三角形组成等值线。

渲染一个三角形

有了这么多的背景知识,让我们实际使用 WebGL 来渲染最简单的三角形之一。

 常量画布 = 文档。创建元素(“画布”)  
 帆布。宽度 = 画布。高度 = 300  
 文档。身体。附加(画布)  
 常量 gl = 画布。获取上下文('webgl')  
  
 冰川视口(0、0、gl.canvas.width、gl.canvas.height)  
 // 设置 webgl 视口并将 -1 映射到 1 到画布上的坐标  
  
 const vertexShader = gl. createShader(gl.VERTEX_SHADER) // 创建一个顶点着色器  
 冰川shaderSource(vertexShader, ` attribute vec4 a_position; void main() { gl_Position = a_position; // 设置顶点位置} `) // 编写顶点着色器代码  
 冰川compileShader(vertexShader) // 编译着色器  
  
 常量片段着色器 = gl。 createShader(gl. FRAGMENT_SHADER) // 创建片段着色器  
 冰川shaderSource(fragmentShader, `precision mediump float; uniform vec4 u_color; void main() { gl_FragColor = u_color; // 设置片段颜色} `) // 编写片段着色器代码  
 冰川compileShader(fragmentShader) // 编译着色器  
  
 常量程序 = gl。 createProgram() // 创建一个程序  
 冰川attachShader(program, vertexShader) // 添加顶点着色器  
 冰川attachShader(program, fragmentShader) // 添加片段着色器  
 冰川linkProgram(program) // 连接程序中的着色器  
  
 冰川useProgram(program) // 告诉 webgl 使用这个程序进行渲染  
  
 常量颜色位置 = gl。 getUniformLocation(program, 'u_color') // 获取 u_color 变量的位置  
 冰川uniform4f(colorLocation, 0.93, 0, 0.56, 1) // 设置它的值  
  
 常量位置位置 = gl。 getAttribLocation(程序,'a_position')  
 // 获取 a_position 位置  
 常量位置缓冲区 = gl。创建缓冲区()  
 // 创建一个顶点缓冲对象,返回它的ID,用来放三角形顶点数据,  
 冰川bindBuffer(gl.ARRAY_BUFFER, positionBuffer)  
 // 将此顶点缓冲区对象绑定到 gl.ARRAY_BUFFER  
 // 后续对 gl.ARRAY_BUFFER 的操作会映射到这个缓冲区  
 冰川bufferData(gl. ARRAY_BUFFER, new Float32Array([  
 0, 0.5,  
 0.5, 0,  
 - 0.5, - 0.5  
 ]), // 三角形的三个顶点  
 // 因为数据会发给GPU,为了节省数据解析,这里使用Float32Array直接传输数据  
 冰川STATIC_DRAW //表示缓冲区内容不经常变化  
 )  
 // 将顶点数据添加到新创建的缓冲区对象  
  
 冰川enableVertexAttribArray(positionLocation);  
 // 打开属性变量,以便顶点着色器可以访问缓冲区数据  
 冰川vertexAttribPointer( // 告诉 OpenGL 如何从 Buffer 中获取数据  
 positionLocation, // 顶点属性的索引  
 2, // 组件的数量,必须是 1、2、3 或 4。我们只提供了 x 和 y  
 冰川FLOAT, // 每个元素的数据类型  
 false, // 是否归一化到特定范围,对 FLOAT 类型数据无效  
 0, // stride数组中一行的长度,0表示数据紧密无间隙,让OpenGL决定具体的stride  
 0 // offset 以字节为单位的偏移量,它必须是类型长度(以字节为单位)的倍数。  
 )  
  
 冰川clearColor( 0, 1, 1, 1) // 清除颜色缓冲区时设置颜色值  
 冰川clear(gl. COLOR_BUFFER_BIT) // 清除颜色缓冲区,即清除画布  
  
 冰川drawArrays( // 从数组中绘制图元  
 冰川TRIANGLES, // 渲染三角形  
 0, // 从数组中的哪个点开始渲染  
 3 // 用多少个点,三角形的三个顶点  
 )  
 复制代码

渲染结果如下所示。

可以发现WebGL的代码非常复杂繁琐,一个很简单的三角形就需要写这么多代码。

上面的示例代码中有详细的注释,但是相信大家看完之后还是充满了问号。下面我们来看看WebGL渲染的全过程。一般WebGL程序都是JS提供数据(运行在CPU中),然后将数据发送到显存,交给GPU渲染。我们可以用 着色器 控制 GPU 渲染管道的某些阶段。

 // 中央处理器  
 const vertexShader = `shader source code` // 顶点着色器代码  
 const fragmentShader = `shader source code` // 片段着色器代码  
 const points = [{ x: 1, y: 1, z: 1 } /* ... */] // 准备数据  
 冰川draw(points, vertexShader, fragmentShader) // 将数据和着色器发送到 GPU  
  
 // 图形处理器  
 常量位置 = 数据。 map(point => vertexShader(point)) // 运行顶点着色器  
 const frags = Rasterization(positions) // 光栅化  
 常量颜色 = 碎片。 map( frag => fragmentShader(frag)) // 运行片段着色器  
 Display(colors) // 渲染到屏幕  
 复制代码

上面的伪代码简单的展示了WebGL程序的执行流程。 OpenGL中的着色器是用GLSL编写的,WebGL中也使用了GLSL着色器语言。它的语法有点类似于 C 语言。我们可以通过顶点着色器和片段着色器来控制 GPU 渲染的某些方面。

WebGL 中的两个着色器是顶点着色器和片段(也称为“片段”)着色器。顶点着色器用于处理图形的每个点,也就是上例中三角形的三个顶点。处理后,将进行光栅化。您可以将光栅化理解为将图形转换为像素。我们的显示屏是由像素组成的。要显示图形,我们需要计算图形中的每个像素。 Fragment shaders首先可以理解为像素着色器,即在光栅化中取每个像素,为每个像素计算一个颜色。整个过程如下图所示。

image.png

上图中的顶点数据发送到GPU后,顶点着色器计算每个点的位置,光栅化计算图形的每个像素,片段着色器计算每个像素的颜色,然后就可以渲染为显示器。 . (上图中的geometry shader可以忽略,WebGL中没有这个shader)这里简单介绍Shader,不懂shader也没关系。下一篇文章将更详细地解释。

事实上,WebGL 是一个非常大的状态机,它提供的方法就是改变 WebGL 的某种状态。我们需要在 CPU 中使用 JS 来设置 WebGL 的状态,准备好数据和着色器程序,然后发送到 GPU 执行。

上面的代码可以分为以下几个步骤。

  1. 由于WebGL的坐标是-1到1,首先我们使用viewport来设置viewport的大小信息。
  2. 创建顶点和片段着色器(下一篇关于着色器案例的文章),然后创建一个程序来连接顶点和片段着色器。
  3. 然后在着色器中获取变量,并设置如何将值传递给着色器。三角形由3个顶点组成,所以准备了3个点的坐标。
  4. 设置清屏颜色和清屏,类似于坐标,在WebGL中 颜色是 0 到 1 , 而不是 0 到 255。
  5. 将数据发送到 GPU 以渲染三角形

例子

上面这个简单的三角形一点也不酷。事实上,WebGL 可以做出很酷的效果。这里有一些很好的例子。如果你有兴趣,你可以看看。

三JS

image.png

threejs.org/

WebGL 示例

image.png

webglsamples.org/

谷歌实验

image.png

Experiments.withgoogle.com/

成人游泳

image.png

www.adultswim.com/etcetera/

埃文·华莱士

image.png

madebyevan.com/

总结

这篇文章讲解了什么是WebGL,了解了WebGL的大致轮廓,完成了一个最简单的WebGL应用。下一篇文章将详细解释非常重要的 WebGL 着色器。

如果觉得文章不错,欢迎 喜欢专注于 支持,我会尽快更新系列教程的下一篇。

零基础玩WebGL系列文章目录,请查看: 从零基础开始使用 WebGL — 目录

版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议。转载请附上原文出处链接和本声明。

这篇文章的链接: https://homecpp.art/5823/10339/1029

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/38874/52522312

posted @ 2022-09-23 12:54  哈哈哈来了啊啊啊  阅读(816)  评论(0编辑  收藏  举报