webgl 系列 —— 初识 WebGL
初识 WebGL
什么是 WebGL
webgl 在支持 canvas 的浏览器中进行 2d 或 3d 渲染。
webgl 程序除了有 Html、javascript,还需要加入着色器语言(GLSL ES)。
WebGL 使得网页在支持 HTML <canvas>
标签的浏览器中,不需要使用任何插件,便可以使用基于 OpenGL ES 2.0 的 API 在 canvas 中进行 3D 渲染 —— MDN WebGL 教程
通过 caniuse 得知 webgl
(98.15%) 和 webgl 2.0
(94.12%) 的支持情况。请看下图:
Tip:个人计算机上,绘制三维最广泛使用的技术有 Direct3D 和 OpenGL,前者是微软的,后者是开源免费的。OpenGL 有个特殊版本 OpenGL ES 专门用于嵌入式计算机、手机,而 WebGL 就是从 OpenGL ES 派生出来的。下图是 OpenGL、OpenGL ES、WebGL 三者之间的关系。其中 webgl 2.0 基于 OpenGL ES 3.0 未画出来:
canvas
Canvas_API 提供了一个通过JavaScript 和 HTML的 <canvas>
元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
Canvas API 主要聚焦于 2D 图形。而同样使用<canvas>
元素的 WebGL API 则用于绘制硬件加速的 2D 和 3D 图形。
示例:
// canvas.html
<body>
<canvas id="canvas" width="300" height="300">
抱歉,您的浏览器不支持 canvas 元素
(这些内容将会在不支持<canvas>元素的浏览器或是禁用了 JavaScript 的浏览器内渲染并展现)
</canvas>
<script>
var canvas = document.getElementById('canvas');
// getContext - 方法返回canvas 的上下文,如果上下文没有定义则返回 null
var ctx = canvas.getContext('2d');
// 设置填充颜色
ctx.fillStyle = 'green';
// 绘制矩形
ctx.fillRect(10, 10, 100, 100);
</script>
</body>
效果如下:
Tip:不管绘制二维还是三维都是这三步:
- 获取 canvas
- 请求绘图上下文
- 调用绘图上下文中的绘图函数
第一个webgl示例
需求
:清空绘图区。也就是使用背景色清空 canvas 的绘图区
实现如下:
// webgl01.html
<body>
<canvas id="canvas" width="300" height="300">
抱歉,您的浏览器不支持 canvas 元素
(这些内容将会在不支持<canvas>元素的浏览器或是禁用了 JavaScript 的浏览器内渲染并展现)
</canvas>
<script>
var canvas = document.getElementById('canvas');
const gl = canvas.getContext("webgl");
// 使用完全不透明的蓝色清除所有图像
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 用上面指定的颜色清除缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
效果如下:
仍旧是3步:
- 获取 canvas
- 请求绘图上下文
- 调用绘图上下文中的绘图函数
在 canvas 绘制矩形之前需要指定颜色(ctx.fillStyle = 'green';
),在 webgl 中类似,清空绘图区之前也得指定背景色,一旦指定背景色,背景色就会在 webgl 系统中存留,将来还需要使用同样的颜色清空绘图区,,就不需要再次指定背景色。
clearColor 和 clear语法如下:
// WebGLRenderingContext.clearColor() 方法用于设置清空颜色缓冲时的颜色值。指定调用 clear() 方法时使用的颜色值
void gl.clearColor(red, green, blue, alpha)
// WebGLRenderingContext.clear() 方法使用预设值来清空缓冲。
void gl.clear(mask);
mask
gl.COLOR_BUFFER_BIT // 颜色缓冲区
gl.DEPTH_BUFFER_BIT // 深度缓冲区 - 三维世界中使用
gl.STENCIL_BUFFER_BIT // 模板缓冲区 - 很少使用
如果没有指定背景色,默认值如下:
- 颜色缓冲区 -
(0.0, 0.0, 0.0, 0.0)
- 深度缓冲区 - 1.0
绘制一个点
需求
需求:在 canvas 中心画一个 10px 红色的点。
效果如下:
思路
用 canvas 绘制一个矩形很简单,先指定颜色,在绘制矩形。就像这样:
// canvas绘制矩形
ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 100, 100);
但 webgl 需要使用着色器,着色器提供了灵活且强大的绘制二维或三维的方法,也更加复杂。
我们先看代码,有一个具体的感受后,在分析其中细节。
代码
共3个文件。重点关注 point01.js
即可。
- 新建入口文件 point01.html:
<!-- point01.html -->
<script src="./cuon-utils.js"></script>
<script src="./point01.js"></script>
<body onload="main()">
<canvas id="webgl" width="300" height="300"> 抱歉,您的浏览器不支持 canvas 元素</canvas>
</body>
Tip:以上这段代码在 chrome 中运行通过,浏览器会自动补全格式,例如把 script 标签放入 head 中。
- 新建 point01.js:
// point01.js
// 顶点着色器
const VSHADER_SOURCE = `
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 10.0;
}
`
// 片元着色器
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);
}
文档加载后运行 main() 方法,相对第一个 webgl 示例,这里增加了初始化着色器
。
Tip:现在只需要把初始化着色器的方法(initShaders() - 请看本篇 cuon-utils.js
章节)作为一个库中的辅助方法看待,后续文章将介绍其中原理。
- 新建 cuon-utils.js(内容见本篇
扩展
),主要提供初始化着色器的方法
代码解析
总体流程
文档加载后执行 main()
方法,有如下5个阶段:
- 获取canvas
- 取得 webgl 上下文
初始化着色器
- 清除绘图区
- 调用
drawArrays
绘图
下面我们主要讲一下第三步和最后一步。
齐次坐标
齐次坐标就是将一个原本是 n 维的向量用一个 n+1 维向量来表示。齐次坐标能提高处理三维数据的有效率,所以在三维系统中大量使用。齐次坐标(x, y, z, w)
等价于三维坐标 (x/w, y/w, z/w)
顶点着色器
顶点着色器
(Vertex Shader) - 用来描述顶点特征的程序。例如这里的位置和大小。顶点指二维(x, y)或三维(x, y, z)空间中的一个点,例如端点或交点。
内置变量:
gl_Position
- 用于描述顶点位置,必传,类型是vec4
(即4个float)gl_PointSize
- 用户描述顶点的尺寸(像素),如果不传,默认 1.0,类型是float
关于位置,我们只有 (x, y, z) 三个变量,但 vec4 是 4 个,所以需要使用内置函数 vec4() 帮忙创建 vec4 类型的变量。
代码中 vec4(0.0, 0.0, 0.0, 1.0)
,这里第四个分量是 1.0,使用的是齐次坐标。
Tip:先记着 (0.0, 0.0, 0.0) 就是绘图区的中心,本篇 坐标系统
中会详细讲解。
片元着色器
片元着色器
(Fragment Shader) - 进行逐片元处理过程如光照的程序。片元
是 webgl 的一个术语,暂时可以将其理解成像素
。
内置变量:
gl_FragColor
- 指定片元颜色(RGBA格式),类型是 vec4
初始化着色器
webgl 需要两种着色器:顶点着色器
(Vertex Shader)、片元着色器
(Fragment Shader)。
在三维场景中,仅仅用线条和颜色把图画出来不够,还需要考虑光照上去或者观察者的视角发生变化,对场景有什么影响。着色器可以灵活的完成这些工作。
初始化着色器之前,顶点着色器和片元着色器都是空白,把着色器程序作为字符串形式传给 initShaders(initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
)之后,webgl 系统中的着色器就建立好。
下图是执行 initShaders() 前后的情形:
Tip: 先执行顶点着色器,然后把 gl_Position
和 gl_PointSize
传给片元着色器。实际上片元着色器接收到的是经过栅格化处理后的片元(栅格化
在画三角形时在讲解)。
绘图
建立着色器之后,首先清空绘图区域,然后使用 gl.drawArrays() 进行绘制。
gl.drawArrays(mode, first, count) 执行顶点着色器,按照 mode 指定的参数绘制图形。first 指定从哪个点开始绘制,count 指绘制需要几个点。
Tip:mode 类型有:
- gl.POINTS: 绘制一系列点。
- gl.LINE_STRIP: 绘制一个线条。即,绘制一系列线段,上一点连接下一点。
- gl.LINE_LOOP: 绘制一个线圈。即,绘制一系列线段,上一点连接下一点,并且最后一点与第一个点相连。
- gl.LINES: 绘制一系列单独线段。每两个点作为端点,线段之间不连接。
- gl.TRIANGLE_STRIP:绘制一个三角带。
- gl.TRIANGLE_FAN:绘制一个三角扇。
- gl.TRIANGLES: 绘制一系列三角形。每三个点作为顶点。
例如我们这里是:gl.drawArrays(gl.POINTS, 0, 1)
,绘制图形(点),需要一个点,从第一个点开始绘制。后续画多个点时会对 first 和 count 有更清晰的理解。
代码注释
// point01.js
// 顶点着色器
const VSHADER_SOURCE = `
// 和 C 语言一样,必须包含一个 main() 函数,void 表示没有返回值
// 注:不能给 main() 指定参数
void main() {
// 顶点着色器内置变量: gl_Position 顶点位置、gl_PointSize 顶点尺寸
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 10.0;
}
`
// 片元着色器
const FSHADER_SOURCE = `
void main() {
// 片元着色器内置变量: gl_FragColor 指定片元颜色
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('初始化着色器失败');
return;
}
// 清空绘图区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制图形(点),需要一个点,从第一个点开始绘制
gl.drawArrays(gl.POINTS, 0, 1);
}
扩展
坐标系统
webgl 的坐标系(x, y, z)和 canvas 的坐标系(x, y)不同。
canvas 的原点(0, 0)在左上角。webgl 处理的是三维,所以使用三维坐标系统(笛卡尔坐标系),可用(x, y, z) 表示。也可认为是右手坐标系。请看下图:
webgl 坐标和 canvas 坐标对应关系如下(可对照上面中间那张图):
cuon-utils.js
// cuon-utils.js (c) 2012 kanda and matsuda
/**
* Create a program object and make current
* @param gl GL context
* @param vshader a vertex shader program (string)
* @param fshader a fragment shader program (string)
* @return true, if the program object was created and successfully made current
*/
function initShaders(gl, vshader, fshader) {
var program = createProgram(gl, vshader, fshader);
if (!program) {
console.log('Failed to create program');
return false;
}
gl.useProgram(program);
gl.program = program;
return true;
}
/**
* Create the linked program object
* @param gl GL context
* @param vshader a vertex shader program (string)
* @param fshader a fragment shader program (string)
* @return created program object, or null if the creation has failed
*/
function createProgram(gl, vshader, fshader) {
// Create shader object
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
if (!vertexShader || !fragmentShader) {
return null;
}
// Create a program object
var program = gl.createProgram();
if (!program) {
return null;
}
// Attach the shader objects
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// Link the program object
gl.linkProgram(program);
// Check the result of linking
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
var error = gl.getProgramInfoLog(program);
console.log('Failed to link program: ' + error);
gl.deleteProgram(program);
gl.deleteShader(fragmentShader);
gl.deleteShader(vertexShader);
return null;
}
return program;
}
/**
* Create a shader object
* @param gl GL context
* @param type the type of the shader object to be created
* @param source shader program (string)
* @return created shader object, or null if the creation has failed.
*/
function loadShader(gl, type, source) {
// Create shader object
var shader = gl.createShader(type);
if (shader == null) {
console.log('unable to create shader');
return null;
}
// Set the shader program
gl.shaderSource(shader, source);
// Compile the shader
gl.compileShader(shader);
// Check the result of compilation
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var error = gl.getShaderInfoLog(shader);
console.log('Failed to compile shader: ' + error);
gl.deleteShader(shader);
return null;
}
return shader;
}
webgl 系列 —— 初识 WebGL的更多相关文章
- WebGL入门教程(一)-初识webgl
一.WebGL和传统网页的区别: 普通网页组成成分:HTML.JavaScript: WebGL网页组成成分:HTML5.JavaScript和GLSL ES(着色器语言 OpenGL ES): 二. ...
- WebGL自学教程——WebGL演示样本:开始
最终开始WebGL样品演示,...... 开始 使用WebGL步骤,非常easy: 1. 获得WebGL的渲染环境(也叫渲染上下文). 2. 发挥你的想象力,利用<WebGL參考手冊>中的 ...
- WebGL自学教程——WebGL演示样例:開始
最终開始WebGL的演示样例了,...... 開始 使用WebGL的步骤,非常easy: 1. 获得WebGL的渲染环境(也叫渲染上下文). 2. 发挥你的想象力,利用<WebGL參考手冊> ...
- 初识WEBGL
WEBGL (全写Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一 ...
- WEBGL学习【一】初识WEBGL
<html lang="zh-CN"> <head> <title>NeHe's WebGL</title> <meta ch ...
- [WebGL] Setting Up WebGL
In this lesson we cover setting up WebGL for use, including creating a canvas, getting the WebGL ren ...
- 【WebGL】《WebGL编程指南》读书笔记——第6章
一.前言 最近重感冒发烧,妈蛋好难受,请假了3天,驾校也没去,简直僵硬!今天继续WebGL的学习. 二.正文 A. GLSL支持两种数据值类型: 整数型(int)与浮点型( ...
- 【WebGL】《WebGL编程指南》读书笔记——第5章
一.前言 终于到了第五章了,貌似开始越来越复杂了. 二.正文 Example1:使用一个缓冲区去赋值多个顶点数据(包含坐标及点大小) function initVerte ...
- 【WebGL】《WebGL编程指南》读书笔记——第3章
一.前言 根据前面一章的内容,继续第三章的学习. 二.正文 一起绘制三个点,这里要使用到缓存了 var n = initVertexBuffers(gl); //返回绘制点的个数 n ) ...
- 【WebGL】《WebGL编程指南》读书笔记——第2章
一.前言 最近看了<WebGL编程指南>这本书,发现还是很有意思的,故每章阅读后做个笔记. 二.正文 Example1:在canvas中绘制矩形 <!DOCTYPE html> ...
随机推荐
- 常用函数/异常处理/for循环本质
常用内置函数 1,map() - 映射 格式: map(函数,可遍历对象) 指将遍历的元素挨个取出来做函数的行参传参,得到的返回值全部放回map工厂中,map工厂可以被转换成列表查看到 每一个被函数处 ...
- tomcat下找不到运行的程序文件
1.重新部署tomcat(删掉原有的tomcat) 2.更改部署路径
- GOCVHelper图像处理算法库实例整编
GOCVHelper主要包含图像处理.图像增强和基础文件处理三个部分.由于前两个部分较具有通用性,而且我在不同项目中都进行了反复使用,为了进一步说明类库内容,这里反过来从项目角度出发,对现有的 ...
- [深度学习] ubuntu18.04配置深度学习环境笔记
最近装过很多ubuntu18.04系统的nvidia驱动,cuda10.2,cudnn7.6.5,发现每次都会出现一些小问题.总结了具体步骤,做个记录.主要分为三个步骤:驱动安装,cuda安装,cud ...
- CentOS7下搭建JumpServer
JumpServer 部署架构图 JumpServer 功能架构图 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统. JumpServer 使用 Python ...
- mac下 idea 注释快捷键冲突
你好,我是悦创. 博客首发:https://bornforthis.cn/posts/28.html 打开偏好设置,将冲突的快捷键关掉,就可以解决 idea 使用注释快捷键,会打开 help 页面的问 ...
- Git使用记录 - 持续更新
本地生成 sshkey 打开git命令工具 cd ~/.ssh ssh-keygen -t rsa -C "实际的eamil地址" ··· // 一路回车,出现以下则说明成功 Yo ...
- Hadoop生态元数据管理平台——Atlas2.3.0发布!
大家好,我是独孤风. 今天我们来聊一下另一个元数据管理平台Apache Atlas.Atlas其实有一些年头了,是在2015年的时候就开源. 相对于Datahub来说,Atlas显得有一些" ...
- DVWA靶场实战(七)——SQL Injection
DVWA靶场实战(七) 七.SQL Injection: 1.漏洞原理: SQL Inject中文叫做SQL注入,是发生在web端的安全漏洞,主要是实现非法操作,例如欺骗服务器执行非法查询,他的危害在 ...
- 如何通过Terraform Associate考试并获得证书
1 什么是Terraform? Terraform是一个IaC工具,IaC全称为Infrastructure as Code,基础设施即代码.它的理念是通过代码来管理基础设施,如服务器.数据库等,更多 ...