首页 > 其他分享 >webgl 系列 —— 初识 WebGL

webgl 系列 —— 初识 WebGL

时间:2023-02-27 10:15:18浏览次数:46  
标签:program canvas 0.0 webgl 着色器 初识 gl 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:不管绘制二维还是三维都是这三步:

  1. 获取 canvas
  2. 请求绘图上下文
  3. 调用绘图上下文中的绘图函数

第一个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步:

  1. 获取 canvas
  2. 请求绘图上下文
  3. 调用绘图上下文中的绘图函数

在 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 onl oad="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_Positiongl_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;
}

标签:program,canvas,0.0,webgl,着色器,初识,gl,WebGL
From: https://www.cnblogs.com/pengjiali/p/17156241.html

相关文章

  • 1.【go-kit教程】go-kit初识
    go-kit简介go-kit本身不是一个框架,而是一套微服务工具集,是框架的底层,可以用go-kit做适应自己平台的框架。它的设计目标是帮助开发者构建健壮、可维护、可测试的分布式......
  • 【学无止境】初识 ChatGPT
    最近ChatGPT很火,那么今天就来试一下。ChatGPI注册首先,用梯子“合理上网”。墙内ChatGPT不提供服务。其次,租一个可以接受国外短信的号码,常用的网站是这个:https://sms-......
  • jmeter运行环境及界面初识
    1、安装jdk2、下载jmeter并启动jmeter.bat3、工具本身日志,java代码日志  4、添加线程组:线程数、循环次数5、取样器:不同协议不同取样器http、soap、jdbc、websocket......
  • Pytest初识
    一、单元测试框架简介1.什么是单元测试单元测试是指在软件开发过程中,针对软件的最小单位(函数,方法)进行正确性的检查测试。2.常用单元测试框架2.1Java类别junit......
  • <<运维监控系统实战笔记>> 小记随笔 —— Prometheus 初识
    Prometheus简介Prometheusserver包含时序库、告警引擎、数据展示三大块,体系中最核心的组件Exporters采集数据的客户端,负载采集数据存在内存中,提供http接口,让......
  • JAVAWEB-NOTE01-初识mysql
    目录JAVAWEB介绍数据库相关概念数据库数据库管理系统SQL常见的关系型数据库管理系统MySQL数据库安装配置登录、退出卸载数据模型JAVAWEB介绍数据库相关概念数据库数......
  • asp.net core webapi初识
    1.建立webapi项目  2.把启用SSL取消勾选(取消https)  3.选择项目-右键-发布,进行发布信息设置,选择文件夹发布   4.打开IIS管理器,新建网站,物理路径选择刚刚......
  • 初识单片机
     单片机:英文全称Single-ChipMicrocomputer,表示是一个微型计算器,有时也称为MCU(MicrocontrollerUnit)。是一种把中央处理器CPU、存储器(RAM\ROM\FLASH等)、输入输......
  • MXNet学习——初识AlexNet
     相关理论&故事背景书上都有,不打算复述一遍,这里仅作代码记录&分享此处非直接可用代码,由于学习过程中多次使用相同函数,故而将一些常用函数整理成了工具包,​​MxNet学习——......
  • Diffusers库的初识及使用
    diffusers库的目标是:将扩散模型(diffusionmodels)集中到一个单一且长期维护的项目中以公众可访问的方式复现高影响力的机器学习系统,如DALLE、Imagen等让开发人员可以很......