之前对着色器的初始化都是使用的initShaders()
这个方法,部分代码在文章最后,其内部分执行细节没有去清晰的了解过,下面做进一步的了解和分析,才能更深入的了解webgl
的原理
initShaders()
方法的主要作用是编译GLSL ES
代码,创建和初始化着色器,以便供WebgGL
使用,具体初始化分为以下七步;
gl.createShader()
gl.shaderSource()
gl.compileShader()
gl.createProgram()
gl.attachShader()
gl.linkProgram()
gl.useProgram()
着色器对象:着色器对象管理一个顶点着色器或者一个片元着色器
程序对象:程序对象是管理着着色器对象的容器
gl.createShader()
通过gl.createShader()
来创建着色器对象,gl.VERTEX_SHADER
或者gl.FRAGMENT
gl.createShader(type)
是WebGL
提供的一个方法,MDN
文档这样解释
WebGLRenderingContext.createShader()
用于创建一个WebGLShader
着色器对象,该对象可以使用WebGLRenderingContext.shaderSource()
和WebGLRenderingContext.compileShader()
方法配置着色器代码.
该方法接收一个参数type
type
参数为gl.VERTEX_SHADER
或gl.FRAGMENT_SHADER
两者中的一个
如果不再需要这个着色器可以使用gl.deleteShader()
将其删除,但是如果着色器对象还在使用,那么gl.deleteShader()
并不会立刻将其删除,而是要等到程序对象不再使用该着色器后,才将其删除
gl.shaderSource()
gl.shaderSource(shader, source)
方法,向着色器指定GLSL ES
源代码,在js
中源代码以字符串的形式存在;
gl.compileshader()
使用WebGLRenderingContext.compileShader()
用于编译一个GLSL
着色器,使其成为二进制数据,然后就可以被WebGLProgram
对象所使用
语法
void gl.compileShader(shader)
var shader = gl.createShader(gl.VERTEX_SHADER);// 创建着色器对象
gl.shaderSource(shader, shaderSource);// 指定着色器对象的代码
gl.compileShader(shader);// 编译着色器
当使用gl.compileShader()
方法时,如果着色器源代码中存在错误,那么就会出现编译错误,可以使用gl.getShaderParameter()
来检查着色器的状态
有以下参数
gl.DELETE_STATUS
:标示着色器是否被删除,删除(GL_TRUE
)未删除(GL_FALSE
).gl.COMPILE_STATUS
: 标示着色器是否编译成功,是(GL_TRUE
)不是(GL_FALSE
)gl.SHADER_TYPE
: 标示着色器类型,是顶点着色器(gl.VERTEX_SHADER
)还是片段着色器(gl.FRAGMENT_SHADER
)返回true
或者false
如果编译失败,返回false
,此时webgl
系统会把编译错误的具体内容写入着色器的 信息日志,可以通过 gl.getShaderInfoLog()
来获取报错信息
gl.createProgram()
程序对象包含顶点着色器和片元着色器,可以调用gl.createProfram()
来创建程序对象,
该方法没有参数,返回一个WebGLProgram
对象
一个
WebGLProgram
对象,由两个编译过后的WebGLShader
组成,顶点着色器和片元着色器,这些组合成一个可用的webgl
着色器程序
示例:
var program = gl.createProgram();
// 添加预先定义好的顶点着色器和片段着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {
var info = gl.getProgramInfoLog(program);
throw "Could not compile WebGL program. \n\n" + info;
}
类似的可以使用gl.deleteProgram()
方法来删除程序对象
一旦程序对象被创建之后,需要向程序附上两个着色器
gl.attachShader()
webgl
要运行起来必须要有两个着色器,顶点着色器和片元着色器,使用gl.attachShader()
方法,为程序对象分配这两个着色器
如刚才上面的那段代码,就是在程序对象被创建之后开始分配着色器对象
着色器在附给程序对象前,并不一定要为其指定代码或进行编译,也就是说把空的着色器附给程序对象也是可以的,也可以使用gl.detachShader()
来解除分配给程序对象的着色器
gl.linkProgram()
在为程序对象分配了两个着色器对象后,还需要将着色器连接起来,使用gl.linkProgram()
方法来执行
程序对象连接操作目的时保证以下几点:
varying
变量同名同类型,且一一对应varying
变量赋了值uniform
变量也是同类型的attribute
、uniform
、和varying
变量的个数没有超过着色器的上限如果程序已经连接成功了,那么就会得到一个二进制的可执行模块,供webgl
系统使用
需要注意的是,程序对象即使连接成功了,也有可能运行失败,比如没有为取样器分配纹理单元,这些错误是在运行阶段而不是连接阶段产生的,在运行阶段错误检查的开销很大,所以通常只在调式程序时这样做;
gl.useProgram()
最后通过gl.useProgram()
告知webgl
系统使用哪个对象
WebGLRenderingContext.useProgram()
方法将定义好的WebGLProgram
对象添加到当前的渲染状态中
这个方法使得webgl
具有了一个强大的特性,那就是在绘制前准备多个程序对象,然后在绘制的时候根据需要切换程序对象
// 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);
// 将此时的程序对象夫hi给
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) {
// 创建着色器对象
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
if (!vertexShader || !fragmentShader) {
return null;
}
// 创建程序对象
var program = gl.createProgram();
if (!program) {
return null;
}
// 为程序对象分配着色器对象
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 连接程序对象
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) {
// 创建着色器对象
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;
}
/**
* Initialize and get the rendering for WebGL
* @param canvas element
* @param opt_debug flag to initialize the context for debugging
* @return the rendering context for WebGL
*/
function getWebGLContext(canvas, opt_debug) {
// Get the rendering context for WebGL
var gl = WebGLUtils.setupWebGL(canvas);
if (!gl) return null;
// if opt_debug is explicitly false, create the context for debugging
if (arguments.length < 2 || opt_debug) {
gl = WebGLDebugUtils.makeDebugContext(gl);
}
return gl;
}