前言
HTML的<canvas>元素为JavaScript提供了动态创建图形的API。这些图形需要使用特定上下文绘制,主要有两种:
第一种是Canvas,一般只能做2d操作,用canvas.getContext(“2d”)来建立一个CanvasRenderingContext2D二维渲染上下文。
第二种是WebGL,通过canvas.getContext(‘webgl’)就能获取WebGL的3d上下文,进行3D的渲染。
1. WebGL简介
1.1 WebGL概述
WebGL,是一项用来在网页上绘制和渲染复杂三维图形,并允许用户与之进行交互的技术。WebGL通过JavaScript操作OpenGL接口的标准,把三维空间图像显示在二维的屏幕上。
1.2 WebGL程序的结构
相对于传统网页,支持WebGL的浏览器底层接入了OpenGL/OpenGL ES标准,WebGL通过实现标准支持着色器语言编程语言GLSL ES,在我们实际开发过程中,GLSL ES通常是以字符串的形式存在JavaScript中,我们可以通过JavaScript修改GLSL ES字符串来改变着色器程序。
1.3 WebGL和OpenGL
OpenGL是一种用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口,是在个人计算机上使用最广泛的两种三维图形渲染技术之一,另一种是Direct3D。在某种意义上,WebGL就是“Web版的OpenGL”。
OpenGL ES在添加新特性的同时从OpenGL中移除了许多陈旧无用的就特性,使得在保持轻量级的同时,仍然具有足够的能力来渲染出精美的三维图形。
WebGL的技术规范继承自OpenGL ES,从2.0版本开始,OpenGL支持可编程着色器方法,这个支持可以让我们通过着色器语言编写着色器程序。
2. 着色器语言
着色器是WebGL依赖的实现图像渲染的一种绘图机制。WebGL在GPU中运行,因此需要使用能够在GPU上运行的代码,这样的代码需要提供成对的方法,他们分别是顶点着色器和片元着色器,可以简单理解为一个定位置一个添颜色。
2.1 顶点着色器
顶点着色器的作用是计算顶点的位置。根据计算出的一系列顶点位置,WebGL可以对点, 线和三角形在内的一些图元进行光栅化处理。
//顶点着色器 const VERTEX_SHADER_SOURCE = ` // 所有着色器都有一个main方法 void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0);//设置坐标 gl_PointSize = 10.0;//设置尺寸 } `
其中,gl_Position和gl_PointSize是着色器的内置变量,分别代表顶点的位置和大小,因此这段代码的作用是设置顶点的位置和大小。
在着色器内,一般命名以gl_开头的变量是着色器的内置变量。
2.2 片元着色器
片段着色器的作用是计算出当前绘制图元中每个像素的颜色值,逐片元控制片元的颜色和纹理等渲染。
//片元着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);//设置颜色 } `
内置变量gl_FragColor来确定顶点像素颜色,vec4是一个四维向量,用来表示一个 RGBA 颜色值,它与 CSS 的颜色区别是,CSS 的 RGB 值是 0 到 255,Alpha 值是 0 到 1,但是在着色器里面,RGBA 的值都是从 0 到 1。
3. webGL绘制一个点
webgl会有大量的重复性前置工作,也就是创建着色器 -> 传入着色器代码 -> 编译着色器 -> 创建着色器程序 -> 绑定、连接、启用着色器 -> 进行绘制。
创建着色器的代码一般封装好了直接调用就行,如下所示:
//gl代表渲染上下文,VERTEX_SHADER_SOURCE代表顶点着色器代码,FRAGMENT_SHADER_SOURCE代表片元着色器代码 function initShader (gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) { // 创建着色器 const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); // 指定着色器对象的代码 gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE); gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE); //编译着色器 gl.compileShader(vertexShader); gl.compileShader(fragmentShader); //创建程序对象 const program = gl.createProgram(); // 为程序对象分配顶点着色器和片元着色器 gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // 连接program指定的程序对象中的着色器 gl.linkProgram(program); //告知WebGL系统绘制时使用program指定的程序对象 gl.useProgram(program); return program; }
绘制点的完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./initShader.js"></script> </head> <body> <canvas id="canvas" width="400" height="400"> 不支持canvas </canvas> </body> <script> //获取<canvas>元素 const ctx = document.getElementById('canvas') //获取WebGL绘图上下文 const gl = ctx.getContext('webgl') //顶点着色器 const VERTEX_SHADER_SOURCE = ` void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 10.0; } ` //片元着色器 const FRAGMENT_SHADER_SOURCE = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } ` //创建着色器 const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) //执行绘制 gl.drawArrays(gl.POINTS, 0, 1) </script> </html>
效果
整个绘制流程如下所示:
程序的执行流程如下:
首先运行JavaScript程序,调用WebGL相关方法,然后顶点着色器和片元着色器就会执行,在颜色缓冲区内进行绘制,并且清空绘图区,最后颜色缓冲区的内容会自动在浏览器的canvas上显示出来。
4. webGL渲染过程
webGL渲染管线,其实就是图形的渲染过程。
渲染管线主要包括两个功能:一是将物体 3D 坐标转变为屏幕空间 2D 坐标,二是为屏幕每个像素点进行着色,渲染管线的一般流程如下图所示,分别是:顶点处理、裁剪和图元组装、光栅化、片元处理。
1.顶点处理
顶点处理是针对存储于顶点缓冲区的各个输入顶点进行操作,此阶段为可编程状态。主要的操作是对顶点进行坐标转换,把对象由其所定义的坐标系下的表示,转化为照相机下的坐标系。
2.裁剪和图元组装
在顶点处理完之后,就需要将顶点组合成一个个单元,这个单元就是我们的图元。这一步就是组装,将顶点组合成一个个单元,如点,线,三角形。
裁剪则是指将视口以外的片元进行裁剪。
3.光栅化
完成图元组装和裁剪后,下一步是光栅化。
光栅化是将几何图元转化为图像的过程。光栅化主要目的是为了将图元组装和裁剪之后的图元数据转化生成帧缓存中的像素。但是光栅化处理完之后,我们并没有直接得到像素,而是得到了片元。片元是一个像素大小的基本单位,但是它并非像素,而是像素的前身。片元相比于像素,除了 RGBA 之外,还会包含如深度值,法线,纹理坐标等信息。
4.片元处理
片元处理,主要是通过片元着色器,计算片元的最终颜色和深度。
光栅化后,程序调用片元着色器,假定光栅化后有10个片元,那么片元着色器将执行10次,每次调用处理一个片元,片元着色器计算该片元的颜色并写入颜色缓冲区,当最后一个片元被处理完成,浏览器就会显示出最终的结果。