共计 4472 个字符,预计需要花费 12 分钟才能阅读完成。
作者:Aral Roca
翻译:疯狂的技术宅
原文:https://aralroca.com/blog/fir…
未经容许严禁转载
在本文中,咱们将理解什么是 WebGL,以及如何通过与 GPU 进行对话来绘制“三角形”。只管有更好的办法来实现本文中的例子,例如用具备 2d 上下文的 canvas 甚至能够用 CSS,但咱们要从 WebGL 开始。就像“hello world”一样,理解它是如何工作的。
什么是 WebGL?
WebGL 的字面定义是 Web Graphics Library(Web 图形库)。但这并不意味着咱们能够通过 3D 库提供的一个好用的 API 去指挥电脑:“在这里放个茶杯,在这里放摄像机,在这里画一个字符等”。
它属于低级 API,能够将 vertices(顶点)转换为pixels(像素)。你能够把 WebGL 了解为栅格化引擎。它基于 OpenGL ES 3.0 图形 API。
网上现有的 3d 库(例如 THREE.js 或 Babylon.js)应用图中最上面的 WebGL。他们须要一种可能与 GPU 通信来告知绘制内容的办法。
当然也能够通过 THREE.js 的 THREE.Triangle
间接解决。然而本文的目标是理解其在外部的工作形式,即这些 3d 库时如何通过 WebGL 与 GPU 通信的。咱们要在没有任何 3D 库的帮忙下渲染出一个三角形。
创立 WebGL 画布
在绘制三角形之前,须要先定义 WebGL 渲染的三角形的区域。
咱们将应用 HTML5 的元素 canvas,将上下文设置为 webgl2
。
import {useRef, useEffect} from 'preact/hooks'
export default function Triangle() {const canvas = useRef()
useEffect(() => {const bgColor = [0.47, 0.7, 0.78, 1] // r,g,b,a as 0-1
const gl = canvas.current.getContext('webgl2') // WebGL 2.0
gl.clearColor(bgColor) // 设置 canvas 的背景色彩
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT) // clear buffers
// @todo: 渲染三角形...
}, [])
return <canvas style={{width: '100vw', height: '100vh'}} ref={canvas} />
}
clearColor
办法用 RGBA(取值范畴:0 到 1)设置画布的背景色。
clear
办法用来把缓冲区初始化为预设值。其对应的常数值将取决于你的 GPU。
创立 canvas 后,就能够用 WebGL 渲染三角形了。
顶点坐标
首先要晓得咱们所应用的这些向量的取值范畴是 -1 至 1。
画布的坐标范畴:
- (0,0):核心
- (1,1):右上
- (1,-1):右下
- (-1,1):左上方
- (-1,-1):左下
要绘制的三角形的三个顶点为 (-1,-1),(0,1) 和(1,-1)。把三角的顶点坐标存储到数组中::
const coordinates = [-1, -1, 0, 1, 1, -1]
GLSL 和着色器
着色器是计算机图形学中的一种程序,能够高度灵便地计算渲染成果。这些着色器用相似于 C 或 C++ 的 OpenGL ES 着色语言(GLSL ES)编写,并在 GPU 上编码并运行。
每个 WebGL 程序都由两个着色器函数组成;顶点着色器(vertex shader)和 片段着色器(fragment shader)。简直所有的 WebGL API 都以不同的形式运行这两个函数。
顶点着色器
顶点着色器的工作是计算顶点的地位。有了这个后果(gl_Position)GPU 就在视口上定位了点、线和三角形。所以要先创立这个顶点着色器:
const vertexShader = `#version 300 es
precision mediump float;
in vec2 position;
void main () {gl_Position = vec4(position.x, position.y, 0.0, 1.0); // x,y,z,w
}
`
能够将其作为模板字符串保留在咱们的 JavaScript 代码中。
第一行(#version 300 es
)阐明了正在应用的 GLSL 版本。
第二行(precision mediump float;
)确定 GPU 用于计算浮点数的精度。可用的选项有 highp
,mediump
和 lowp
),然而某些零碎不反对 highp
。
在第三行(in vec2 position;
)为 GPU 定义了两个二维 (X,Y) 的输出变量。三角形的每个向量都是二维的。
初始化后在程序启动时调用 main
函数(和 C/C++ 语言一样)。GPU 将通过将以后顶点的地位保留到 gl_Position
来运行其内容(gl_Position = vec4(position.x, position.y, 0.0, 1.0);
)。第一个和第二个参数是 vec2
的地位 x
和 y
。第三个参数是 z
轴,在本例中为 0.0
,因为咱们是在 2D 而非 3D 中创立几何体。最初一个参数是 w
,默认状况下应将其设置为 1.0
。
GLSL 辨认并在外部应用 gl_Position
的值。
创立着色器后,应答其进行编译:
const vs = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vs, vertexShader)
gl.compileShader(vs)
// Catch some possible errors on vertex shader
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {console.error(gl.getShaderInfoLog(vs))
}
片段着色器
“片段着色器”在“顶点着色器”之后执行。着色器的工作是计算与每个地位对应的像素点的色彩。
咱们用雷同的色彩填充这个三角形:
const fragmentShader = `#version 300 es
precision mediump float;
out vec4 color;
void main () {color = vec4(0.7, 0.89, 0.98, 1.0); // r,g,b,a
}
`
const fs = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fs, fragmentShader)
gl.compileShader(fs)
// 在片段着色器上捕捉一些可能呈现的谬误
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {console.error(gl.getShaderInfoLog(fs))
}
只管在这里返回的 vect4
是指每个像素的色彩,但其语法与上一个十分类似。因为要用 rgba(179, 229, 252, 1)
填充三角形,所以要把每个 RGB 数字除以 255。
从着色器创立程序
编译好着色器后,须要创立要运行 GPU 的程序,同时增加两个着色器。
const program = gl.createProgram()
gl.attachShader(program, vs) // 附加顶点着色器
gl.attachShader(program, fs) // 附加片段着色器
gl.linkProgram(program) // 将两个着色器链接在一起
gl.useProgram(program) // 应用创立的程序
// 捕捉在程序中可能呈现的谬误
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {console.error(gl.getProgramInfoLog(program))
}
创立缓冲区
咱们将应用缓冲区把内存调配给 GPU,并把内存绑定到用于 CPU-GPU 通信的通道。用此通道将三角形坐标发送到 GPU。
// 为 gpu 分配内存
const buffer = gl.createBuffer()
// 将此内存绑定到通道
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
// 应用此通道将数据发送到 GPU(三角形坐标)gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array(coordinates),
// 在例子中是一个动态三角形,// 所以最好阐明咱们要如何应用数据,// 以便 WebGL 能够优化某些内容。gl.STATIC_DRAW
)
// 发送数据后开释内存,防止内存透露问题
gl.bindBuffer(gl.ARRAY_BUFFER, null)
把数据从 CPU 链接到 GPU
在咱们的顶点着色器中定义了一个名为 position
的输出变量。然而尚未指定此变量应采纳咱们要通过缓冲区的值。必须通过以下形式表明它:
const position = gl.getAttribLocation(program, 'position')
gl.enableVertexAttribArray(position)
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.vertexAttribPointer(
position, // 顶点属性的地位
2, // 维度 - 2D
gl.FLOAT, // 要发送到 GPU 的数据类型
gl.FALSE, // 是否应将数据标准化
0, // 步长
0 // 偏移量
)
绘制三角形
一旦为三角形创立了带有着色器的程序,并创立了将数据从 CPU 发送到 GPU 的链接缓冲区之后,最初就能够通知 GPU 渲染三角形了。
gl.drawArrays(
gl.TRIANGLES, // 根本类型
0, // 向量点数组中的起始索引
3 // 要渲染的顶点数
)
这个办法用来从数组数据渲染图元。图元能够是点、线或三角形。在这里指定 gl.TRIANGLES
。
论断
用 WebGL 只能绘制三角形、直线或点,因为它只能栅格化,所以只能进行向量能够执行的操作。这意味着 WebGL 从概念上讲是简略的,而过程却相当简单,而且依据你要开发的内容会变得越来越简单。光栅化 2D 三角形并不像在 3D 游戏中应用纹理、变动,变换那样。
本文首发微信公众号:前端先锋
欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章
欢送持续浏览本专栏其它高赞文章:
- 深刻了解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你进步开发效率的古代 CSS 框架
- 疾速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 对于 Git 的 20 个面试题
- 深刻解析 Node.js 的 console.log
- Node.js 到底是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩大插件
- Node.js 多线程齐全指南
- 把 HTML 转成 PDF 的 4 个计划及实现
- 更多文章 …