WebGL API文档
文档会把常用的WebGL方法和属性一一列举,并介绍相关用法,大家在阅读别人代码遇到不懂的方法或属性时,可以通过浏览器页面,“Ctrl+F”快速搜索关键字,把本文档当做学习字典即可, 可以降低大家记忆压力。互联网的知识更新非常快不可能什么都记忆,往往很多知识都是在查看手册完成开发任务,而掌握了一项新的技能。
-
WebGL标准链接:
- WebGL 1.0标准
- WebGL 2.0草稿
获取WebGL上下文
所谓API就是一个应用程序编程接口,为了实现能够在Web上控制显卡硬件创建三维场景,对显卡的硬件细节进行了封装抽象,提供了一系列可以控制GPU渲染管线的函数, 这些函数的集合就是WebGL API。通过canvas画布对象的方法getContext()可以获得一个webgl的上下文,WebGL API的所有函数都是执行getContext()方法返回对象的方法, Javascript语言可以调用这些方法创建三维场景。
如果知识面不够宽,提到WebGL、WebGL API、OpenGL API、OpenGL ES、canvas元素、canvas标签、canvas方法和属性、GLSL语言、GLSL ES、Javascript、JavaScript API、ECMAScript等概念可能会有所混乱, 如果分不太清可以先学习具体的命令如何使用,随着时间的推移,和知识的逐步积累,自然会理解。三维模型功能的在线预览实现需要通过HTML中众多元素中的一个canvas元素,canvas元素可以实现2D和3D绘图。 通过DOM对象方法getElementById()获取canvas对象,然后通过canvas对象的方法getContext(),可以获取一个上下文, 也可以说返回一个可以在canvas画布上绘图的环境,也可以说返回一个具有各种方法和属性的对象,当getContext()方法的参数是2d时,返回的是一个2D绘图环境;当getContext()方法的参数是webgl时, 返回的是一个3D绘图环境,也就是返回一个具有系列绘图方法和属性的CanvasRenderingContext对象,该对象具有一系列方法和属性,可以和Javascript编程语言、GLSL ES着色器语言相互配合完成一个三维场景的构建。 Javascript语言可以调用CanvasRenderingContext对象的方法和属性,CanvasRenderingContext对象可以称为Javascript语言的外置对象,该对象的方法和属性就是WebGL API,自然也可以称为Javascript API。这些方法和属性的 制定参照的是OpenGL ES,不同版本的WebGL参照不同版本的OpenGL ES。
canvas元素
属性 | 意义 |
height | 画布高度 |
width | 画布宽度 |
background-color | 画布背景颜色 |
opacity | 画布透明度 |
//在body标签里面创建一个canvas元素,画布的宽度、长度均为200,效果如下
<canvas id="myCanvas" width="200" height="200" style="background: #00B7FF;opacity: 0.5"> </canvas>
//在script标签里面获取canvas元素。
var canvas= document.getElementById('myCanvas');
getContext()方法
使用canvas元素对象的getContext()方法,“webgl”作为参数获取canvas的webgl上下文
-
//书写格式
gl = canvas.getContext("webgl")
-
//有些教程可能大家会看到getContext()方法的参数是experimental-webgl,是因为WebGL是新生的事物,对于标准正在制定中的时候, 从浏览器获得WebGL上下文需要加前缀experimental标识
gl = canvas.getContext("experimental-webgl");
-
//使用Javascript语言的或“||”运算符,兼容不同浏览器对WebGL的不同支持状况
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
-
//使用Javascript语言的或“||”运算符,兼容不同浏览器对WebGL的不同支持状况
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
-
//现在多数浏览器支持WebGL 1.0标准,通过参数“webgl”可以使用WebGL 1.0标准编程环境,WebGL2.0浏览器的支持度还不够, 大家可以自行查阅标准制定、支持情况情况,以自己当前时间为准,不以贫道阐述时间(2017.01.22)为准,比如火狐最新版浏 览器支持通过参数“experimental-webgl2”获取支持WebGL2.0标准的编程环境
var gl = canvas.getContext(“experimental-webgl2”);
类型数组和顶点缓冲区配置
类型数据
在前端页面设计时,通常使用无类型数组构造函数Array,为了处理WebGL中大量的顶点数据在Javascript语言新标准ECMAScript 2015中引入类型数组,当然引入的类型数组构造函数也可以用解决来其它编程问题。
数据的用途不同,要求的精度和形式自然不同,比如顶点索引使用整数即可,根据顶点的数量可以选择Uint8、Uint16、Uint32中的哪一种整型数据,顶点的位置一般使用浮点数来表示,浮点数可以选择不同的精度表示。 关于Javascript语言类型数组的更多基础知识可以关注《类型化数组》,下面简单列举两个例子一个是为了创建顶点数据, 一个是为了创建顶点数据的索引。
- 构造函数Float32Array:返回的数组元素是32位浮点数,通常数组数据会通过Javascript程序传递给顶点着色器代码段中表示顶点坐标、颜色、纹理坐标的变量
var data = new Float32Array([1.0, 0.2, 1.3, -1.0, -2.3, -1.4]);
var data = new Uint8Array([1,3,2,4,6,4,2,4,1]);
getAttribLocation(program,attributeName)
执行该方法返回顶点着色器中顶点变量的索引地址
参数 | 值 |
program | 程序对象 |
attributeName | 顶点着色器程序中的顶点变量名 |
//获取顶点着色器的位置变量apos var aposLocation = gl.getAttribLocation(program,'apos');
顶点数据配置
//创建缓冲区对象 var buffer=gl.createBuffer(); //绑定缓冲区对象 gl.bindBuffer(gl.ARRAY_BUFFER,buffer); //顶点数组data数据传入缓冲区 gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); //缓冲区中的数据按照一定的规律传递给位置变量apos gl.vertexAttribPointer(aposLocation,3,gl.FLOAT,false,0,0); //允许数据传递 gl.enableVertexAttribArray(aposLocation);
顶点索引配置
//创建缓冲区对象 var indexesBuffer=gl.createBuffer(); //绑定缓冲区对象 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexesBuffer); //索引数组indexes数据传入缓冲区 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW);
createBuffer()和deleteBuffer()
createBuffer()方法会在GPU控制的显存上创建一个缓冲区用来存储顶点或顶面索引数据,通过deleteBuffer(buffer)方法的作用是删除某个缓冲区,参数buffer表示顶点或顶点索引缓冲区的名字,也就是执行createBuffer()方法返回的对象变量名。
bindBuffer(target,buffer)
target相同,也就是同类缓冲区在同一时刻只能绑定一个,只有处于绑定状态才能传入数据。
参数 | 值 |
target | gl.ARRAY_BUFFER:表示顶点缓冲区
gl.ELEMENT_ARRAY_BUFFER:表示顶点索引缓冲区 |
buffer | 顶点缓冲区变量名 |
bufferData(target,data,usage)
bufferData()方法的作用是把CPU控制的主存中类型数组数据传入GPU控制的显存顶点或顶点索引缓冲区中。
参数 | 值 |
target | 同bindBuffer()方法的target参数,保持一致 |
data | 类型数组变量名:表示要传入缓冲区中的数组数据 |
usage | 通过不同的值控制传入缓冲区数据的方式、GPU使用缓冲区调用数据方式
gl.STATIC_DRAW:静态绘制模式 gl.STREAM_DRAW:流绘制模式 gl.DYNAMIC_DRAW:动态绘制模式 |
vertexAttribPointer(location,size,type,normalized,stride,offset)
顶点索引缓冲区不需要该方法,该方法的作用是规定GPU从顶点缓冲去中读取数据的方式,很多时候为了提高顶点数据的传输读取效率,往往会把顶点位置、顶点颜色、顶点法向量、纹理坐标交叉定义在一个类型数组中, 一次性传入顶点缓冲区中,CPU和GPU不需要多次通信,只要执行一次databuffer()方法,这时候GPU为了使用顶点缓冲去区的不同用途数据,就要按照一定规律读取,所以类型数据中的数据会把同一个顶点的所有用途数据连续放在一起, 不同顶点的数据依次排列。
可以在同一个WebGL程序中定义多个该方法,每个方法的参数location分别只想一个不同的顶点变量,然后控制后面其它的参数保证取用不用的数据,最简单的思路就是每间隔几个数据,取用几个。
参数 | 值 |
location | 顶点变量的位置 |
size | size是整数1~4,表示每次取用几个数据,如果size是1,着色器中的顶点变量vec4后默认2、3分量是0,第4分量是1。 |
type | 顶点数据类型,定义该参数主要是为了控制数据的存取,所有的数据没有分界线,只能靠数据类型占用的位bit数来分界
不同值 对应的类型数组 gl.UNSIGNED_BYTE: Uint8Array gl.SHORT: Int16Array gl.UNSIGNED_SHORT: Uint16Array gl.INT: Int32Array gl.UNSIGNED_INT: Uint32Array gl.FLOAT: Float32Array |
normalized | 布尔值true或false,表示是否归一化到区间[0,1]或[-1,1] |
stride | 相邻两个顶点间隔的数据字节数,具体说就是一个顶点所有的顶点位置、顶点颜色、顶点法向量等顶点数据数量减去你要选择的那种顶点数据的数量, 顶点数量再乘以该类型数据一个元素占用的字节数,比如Float32Array创建的数据每个元素是4个字节。 |
offset | 每个顶点的位置、颜色、法向量等数据是一组,offset是字节数,规定了GPU从一组数据中第几个元素开始读取数据。 |
enableVertexAttribArray(location)
顶点缓冲区和GPU渲染管线之间存在一个硬件单元可以决定GPU是否能读取顶点缓冲区中的顶点数据,开启方法是enableVertexAttribArray(),能开启自然能够关闭,关闭的方法是disableVertexAttribArray(), 两个方法的参数都是顶点着色器程序中顶点变量的索引位置
编译着色器程序
//声明着色器初始化函数 function initShader(gl,vertexShaderSource,fragmentShaderSource){ //创建顶点着色器对象 var vertexShader = gl.createShader(gl.VERTEX_SHADER); //创建片元着色器对象 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); //引入顶点、片元着色器源代码 gl.shaderSource(vertexShader,vertexShaderSource); gl.shaderSource(fragmentShader,fragmentShaderSource); //编译顶点、片元着色器 gl.compileShader(vertexShader); gl.compileShader(fragmentShader); //创建程序对象program var program = gl.createProgram(); //附着顶点着色器和片元着色器到program gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader); //链接program gl.linkProgram(program); //使用program gl.useProgram(program); //返回程序program对象 return program; }
-
createShader(shaderType)
创建着色器对象,参数是着色器类型,标记一个着色器程序会被GPU渲染管线上哪一个着色器执行,参数shaderType是gl.VERTEX_SHADER表示该着色器程序编译后被顶点着色器执行,参数shaderType是gl.FRAGMENT_SHADER表示该着色器程序编译后被片元着色器执行。
//创建顶点着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
//创建片元着色器
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
-
shaderSource(shaderObject, shaderSource)
参数 值 shaderObject 着色器对象变量名 shaderSource 字符串格式着色器程序 把字符串形式的顶点着色器代码、片元着色器的代码分配给各自的着色器对象
//参数vertexShaderSource是GLSL ES编写的字符串形式顶点着色器代码
gl.shaderSource(vertexShader,vertexShaderSource);
//参数fragShaderSource是GLSL ES编写的字符串形式片元着色器代码
gl.shaderSource(fragmentShader,fragShaderSource);
-
compileShader(shaderSource)
compileShader()方法的作用是编译顶点着色器程序和片元着色器程序,通过参数shaderSource指定着色器程序源码源码,该参数的具体值是字符串格式着色器程序变量名。
gl.compileShader(vertexShader); gl.compileShader(fragmentShader);
-
createProgram()
创建程序对象,程序对象存在的意义是为了实现CPU和GPU的通信,控制GPU着色器的工作状态,切换不同的着色器程序。
var program = gl.createProgram();
-
attachShader(program,shaderObject)
绑定着色器对象到一个程序对象上,每个程序对象就关联了一组顶点着色器程序、片元着色器程序,第一个参数program表示目标程序对象,第二个参数shaderObject表示你要绑定的着色器对象。
gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader);
-
linkProgram(program)
在执行useprogram方法之前,要先连接程序对象program的顶点和片元着色器程序,检查着色程序的错误。 通过连接测试后,才能通过useprogram方法把着色器程序传递给GPU,否则报错。
- 检查顶点、片元着色器程序中同名varying变量是否一一对应
- 检查顶点着色器程序中是否给varying变量赋值顶点数据
- 硬件资源有限,要检测attribute、uniform、varying变量的数量是否超出限制范围
测试项
gl.linkProgram(program);
-
getProgramParameter(program, value)
通过该方法可以判断linkProgram()方法是否连接成功,program参数指定要连接的程序对象,参数value定义了该方法执行后,需要反馈的数据,
参数value的值及其含义
值 含义 gl.DELETE_STATUS 是否执行deleteProgram删除程序对象program,返回结果true或false gl.LINK_STATUS 程序对象program是否通过linkProgram()方法连接验证,返回结果true或false gl.VALIDATE_STATUS 程序对象program是否通过验证,返回结果true或false gl.ATTACHED_SHADERS 分配给程序的着色器对象数量 gl.ACTIVE_ATTRIBUTES attribute变量的数量 gl.ACTIVE_UNIFORMS uniform变量的数量 -
useProgram(program)
定义useProgram()方法调用程序对象program,执行WebGL绘制函数drawArrays()的时候,WebGL系统会把程序对象对应的顶点、片元着色器程序传递GPU渲染管线的顶点、片元着色器功能单元。 同一时刻GPU只能配置一组顶点、片元着色器程序,也就是说如果你定义了多个程序对象,分别关联了一组顶点、片元着色器程序,不会同时传递给GPU。 在代码中useProgram的特点是当再次调用方法useProgram,使用新的program程序对象作为新的参数,再次执行绘制函数的时候CPU会与GPU进行通信, 给GPU传入新程序对象program对应的顶点、片元着色器程序,这时候就实现了GPU着色器程序的切换,每次切换都会耗费一定的硬件资源,可以简单的类比CPU线程的切换。 一般复杂的场景都会编写多套着色器程序,放在文件中,供WebGL程序调用,比如有纹理贴图和没有纹理贴图的时候着色器程序就不同。
gl.useProgram(program);
-
deleteShader(shaderObject)
参数指定着色器对象变量名shaderObject,定义要删除的着色器对象,如果已经执行attachShader()方法把着色器对象绑在程序对象program上, 系统不会立即执行deleteShader()定义的删除操作,如果没有程序对象再使用本着色器对象,deleteShader()定义的删除操作就会执行,释放内存。
-
deleteProgram(program)
deleteProgram()方法的作用是删除程序对象,参数program是程序对象变量名,指定要删除的程序对象,如果已经使用方法useprogram()调用了该程序对象, 该方法在程序中不是立即执行,删除程序对象的原则是该程序对象program不再使用,所谓不再使用就是通过方法useprogram(program)调用新的程序对象,了解useProgram()用法可以知道, 执行useprogram()调用新的程序对象,原来的程序对象program不再使用。
delete删除对象
编写WebGL程序的时候删除WebGL中特定的对象和编写普通Javascript程序是删除对象释放内存的原则基本一致,当对象没有引用的时候,才会删除对象释放内存空间。
给uniform和attribute变量传入数据
顶点着色器中和片元着色器中会声明一系列的变量,attribute关键字声明的顶点类数据,uniform关键字声明的光线颜色方向、变换矩阵等统一类数据,除了不同的关键字定义个数据用途不同, 着色器语言中的数据类型也分很多类别,不同类型、不同用途的数据之间进行排列组合会创建出许多种变量,每种变量都要规定一个用于数据传输的WebGL API,如果数据量非常大,为了提高数据的传输读取效率, 就会在GPU可以控制的显存上创建一个缓冲区,一次性把CPU主存中的数据传入进去,比如最常见的顶点数据,复杂的几何模型往往MB或GB数量级,不可能GPU每读取一个顶点数据就与CPU通信一次,对于简单的变换矩阵、光线方向的统一类变量, 不需要创建缓冲区,执行一次相关的API就可以把数据传递给GPU着色器。关于大批量顶点数据的传输读取问题可以参考《类型数组和顶点缓冲区配置》,通过下面树状图列举的API传输数据不需要在显存上创建缓冲区。