着色器和着色器程序对象,初始化着色器说明
- 1.initShaders() 函数创建步骤
- 2.相关API介绍
-
- 2.1创建着色器对象 gl.createShader()
- 2.2指定着色器代码 gl.shaderSource()
- 2.3编译着色器 gl.compileShader()
- 2.4获取着色器信息 gl.getShaderParameter()
- 2.5获取写入着色器日志 gl.getShaderInfoLog()
- 2.6创建程序对象 gl.createProgram()
- 2.7删除程序对象 gl.deleteProgram()
- 2.8为程序对象分配着色器对象 gl.attachShader()
- 2.9取消程序对象分配的着色器对象 gl.detachShader()
- 2.10连接程序对象 gl.linkProgram()
- 2.11检查着色器是否连接成功 gl.getProgramParameter()
- 2.12获取程序对象的日志 gl.getProgramInfoLog()
- 2.13使用程序对象 gl.useProgram()
- 3.initShaders代码
- 4 后续
1.initShaders() 函数创建步骤
从第一个demo开始一直使用了一个着色器初始化函数initShaders(),但是一直没有详细说明,接下来我们了解一下它是怎么把字符串形式的 GLSL ES 代码编译成可在GPU上运行的着色器程序,该函数大致可分为以下七个步骤:
- 创建着色器对象–gl.createShader()
- 向着色器对象中填充着色器程序源代码–gl.createSource()
- 编译着色器–gl.compileShader()
- 创建程序对象–gl.createProgram()
- 为程序对象分配着色器–gl.attachShader()
- 连接程序对象–gl.linkProgram()
- 使用程序对象–gl.useProgram()
在以上七个步骤中使用到了两种对象:着色器对象 和 程序对象
它们之间的关系是:程序对象 是 着色器对象 的容器,而着色器对象又管理者两个着色器,一个是顶点着色器,一个是片元着色器
2.相关API介绍
2.1创建着色器对象 gl.createShader()
在WebGL中所有的着色器对象必须通过调用 gl.createShader() 来创建
调用示例:gl.createShader(type)
--------------------------------------------------------------------------
函数功能:创建由type指定的着色器对象
--------------------------------------------------------------------------
参数
type 指定创建着色器的类型,可以是以下中的一个
gl.VERTEX_SHADER 表示顶点着色器
gl.FRAGMENT_SHADER 表示片元着色器
--------------------------------------------------------------------------
返回值 非null 新创建的着色器
null 创建失败
--------------------------------------------------------------------------
错误 INVALID_ENUM type不是上述值之一
gl.createShader()根据传入的type创建顶点着色器或者片元着色器,如果不在需要这个着色器了,可以 像这样 gl.deleteShader(shader)
删除它,
注:删除着色器的时候,如果着色器对象还在使用(使用gl.attachShader()函数使这个着色器附件在了程序对象上),那么调用删除函数后,不会立即删除着色器,要等到不在使用该着色器时才会删除掉
2.2指定着色器代码 gl.shaderSource()
在javaScript程序中,源代码是以字符串的形式存储的,可以通过gl.shaderSouce()函数向着色器指定源码
调用示例:gl.shaderSource(shader,source)
--------------------------------------------------------------------------
函数功能:将source指定的字符串形式的代码传入shader指定的着色器,如果已经向shader
中传入过代码了,旧代码会被新代码替换掉
--------------------------------------------------------------------------
参数
shader 指定需要传入代码的着色器对象
source 指定字符串形式的代码
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 无
2.3编译着色器 gl.compileShader()
GLSL ES 语言与C或C++相似,需要编译成二进制的可执行文件才能使用,对于我们创建的着色器,在向着色器对象传入源代码之后,同样需要进行编译才能使用,WebGL 提供了gl.compileShader()函数,用来编译着色器
注:如果调用gl.shaderSource() 函数时,用新代码替换了旧代码,已编译好的可执行文件不会被自动替换掉,需要手动重新编译,即再次调用gl.compileShader()函数
调用示例:gl.compileShader(shader)
--------------------------------------------------------------------------
函数功能:编译shader指定的着色器中的源代码
--------------------------------------------------------------------------
参数 shader 指定要编译的着色器
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 无
2.4获取着色器信息 gl.getShaderParameter()
调用示例:gl.getShaderParameter(shader, pname)
--------------------------------------------------------------------------
函数功能: 获取shader指定的着色器中,pname指定的参数信息
--------------------------------------------------------------------------
参数
shader 指定待获取参数的着色器
pname 指定待获取参数的类型,可以是
gl.SHADER_TYPE、gl.DELETE_STATUS或者
gl.COMPILE_STATUS
--------------------------------------------------------------------------
返回值 根据pname不同返回不同的值
gl.SHADER_TYPE 返回是顶点着色器(gl.VERTEX_SHADER)
还是片元着色器(gl.FRAMENT_SHADER)
gl.DELETE_STATUS 返回着色器是否被删除成功(true或false)
gl.COMPILE_STATUS 返回着色器是否被编译成功(true或false)
--------------------------------------------------------------------------
错误 INVALID_ENUM pname值无效
2.5获取写入着色器日志 gl.getShaderInfoLog()
调用示例:gl.getShaderInfoLog(shader)
--------------------------------------------------------------------------
函数功能: 获取shader指定的着色器的信息日志
--------------------------------------------------------------------------
参数 shader 指定待获取参数的着色器
--------------------------------------------------------------------------
返回值 non-null 包含日志信息的字符串
null 没有编译错误
--------------------------------------------------------------------------
错误 无
通常 gl.getShaderParameter() 函数和 gl.getShaderInfoLog() 函数是配合使用的,一般是将gl.getShaderParameter()函数的参数pname指定为 gl.COMPILE_STATUS 就可以检查着色器是否编译成功,如果编译失败通过 gl.getShaderInfoLog() 函数获取错误信息
示例如下:
...
//编译顶点、片元着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
//检查顶点着色器是否编译成功
var vertexShaderStatus = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS);
if (!vertexShaderStatus) {
var error = gl.getShaderInfoLog(vertexShader);
console.log('编译顶点着色器失败: ' + error);
gl.deleteShader(vertexShader);
return null;
}
//检查片元着色器是否编译成功
var fragmentShaderStatus = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
if (!fragmentShaderStatus) {
var error = gl.getShaderInfoLog(fragmentShader);
console.log('编译片元着色器失败: ' + error);
gl.deleteShader(fragmentShader);
return null;
}
...
2.6创建程序对象 gl.createProgram()
调用示例:gl.createProgram()
--------------------------------------------------------------------------
函数功能:创建程序对象
--------------------------------------------------------------------------
参数 无
--------------------------------------------------------------------------
返回值 non-null 新创建的程序对象
null 创建失败
--------------------------------------------------------------------------
错误 无
2.7删除程序对象 gl.deleteProgram()
调用示例:gl.deleteProgram(program)
--------------------------------------------------------------------------
函数功能:创建程序对象
--------------------------------------------------------------------------
参数 program 要删除的程序对象
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 无
2.8为程序对象分配着色器对象 gl.attachShader()
WebGL程序要运行起来,必须要有两个着色器,一个顶点着色器,一个片元着色器,就需要通过gl.attachShader() 函数为程序对象分配这两个着色器
调用示例:gl.attachShader(program, shader)
--------------------------------------------------------------------------
函数功能:将shader指定的着色器对象分配给program指定的程序对象
--------------------------------------------------------------------------
参数 program 指定程序对象
shader 指定着色器对象
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 INVALID_OPERATION shader已经被分配给program
2.9取消程序对象分配的着色器对象 gl.detachShader()
调用示例:gl.detachShader(program, shader)
--------------------------------------------------------------------------
函数功能:取消shader指定的着色器对象对program指定的程序对象的分配
--------------------------------------------------------------------------
参数 program 指定程序对象
shader 指定着色器对象
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 INVALID_OPERATION shader没有被分配给program
2.10连接程序对象 gl.linkProgram()
为程序对象分配了两个着色器对象后,还需要将它们与程序对象连接起来,使用 gl.linkProgram() 函数完成这一操作
调用示例:gl.linkProgram(program)
--------------------------------------------------------------------------
函数功能:连接program指定的程序对象中的着色器
--------------------------------------------------------------------------
参数 program 指定程序对象
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 无
程序对象进行着色器连接操作,目的是检查以下四项:
- 顶点着色器和片元着色器中的varying变量同名同类型,且一一对应
- 顶点着色器中的每个varying变量都已经赋值
- 顶点着色器和片元着色器中的同名uniform变量也是同类型,无需一一对应
- 着色器中的attribute变量、uniform变量、varying变量个数有没有超过着色器的上限
2.11检查着色器是否连接成功 gl.getProgramParameter()
调用示例:gl.getProgramParameter(program, pname)
--------------------------------------------------------------------------
函数功能: 获取program指定的程序对象中,pname指定的参数信息
--------------------------------------------------------------------------
参数
program 指定待获取参数的程序对象
pname 指定待获取参数的类型,可以是
gl.DELETE_STATUS、gl.LINK_STATUS、
gl.VALIDATE_STATUS、gl.ATTACHED_SHADERS、
gl.ACTIVE_ATTRIBUTES、gl.ACTIVE_UNIFORM
--------------------------------------------------------------------------
返回值 根据pname不同返回不同的值
gl.DELETE_STATUS 程序是否被删除(true或false)
gl.LINK_STATUS 程序是否已经成功连接(true或false)
gl.VALIDATE_STATUS 程序是否已经通过验证(true或false)
gl.ATTACHED_SHADERS 已被分配给程序的着色器数量
gl.ACTIVE_ATTRIBUTES 顶点着色器中attribute变量的数量
gl.ACTIVE_UNIFORM 程序中uniform变量的数量
--------------------------------------------------------------------------
错误 INVALID_ENUM pname值无效
如果程序已经连接成功,我们就得到一个二进制的可执行模块供WebGL调用,如果连接失败可以通过 gl.getProgramInfoLog() 函数获取连接出错的信息
2.12获取程序对象的日志 gl.getProgramInfoLog()
调用示例:gl.getProgramInfoLog(program)
--------------------------------------------------------------------------
函数功能: 获取program指定的着色器的信息日志
--------------------------------------------------------------------------
参数 program 指定待获取参数的程序对象
--------------------------------------------------------------------------
返回值 包含日志信息的字符串
--------------------------------------------------------------------------
错误 无
...
//链接program
gl.linkProgram(program);
//检查程序对象是否连接成功
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;
}
...
2.13使用程序对象 gl.useProgram()
最后需要调用 gl.useProgram() 函数告知WebGL系统绘制时使用哪个程序对象,有了这个函数,我们就可以在绘制前准备多个程序对象,在绘制的时候根据需要切换程序对象
调用示例:gl.useProgram(program)
--------------------------------------------------------------------------
函数功能: 告知WebGL系统绘制时使用program指定的程序对象
--------------------------------------------------------------------------
参数 program 指定待使用的程序对象
--------------------------------------------------------------------------
返回值 无
--------------------------------------------------------------------------
错误 无
3.initShaders代码
//初始化着色器函数
function initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE) {
//创建顶点着色器对象
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, VSHADER_SOURCE);
//创建片元着色器对象
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, FSHADER_SOURCE);
if (!vertexShader || !fragmentShader) {
return null;
}
//创建程序对象program
var program = gl.createProgram();
if (!gl.createProgram()) {
return null;
}
//分配顶点着色器和片元着色器到program
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
//链接program
gl.linkProgram(program);
//检查程序对象是否连接成功
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;
}
//使用program
gl.useProgram(program);
gl.program = program
//返回程序program对象
return program;
}
function loadShader(gl, type, source) {
// 创建顶点着色器对象
var shader = gl.createShader(type);
if (shader == null) {
console.log('创建着色器失败');
return null;
}
// 引入着色器源代码
gl.shaderSource(shader, source);
// 编译着色器
gl.compileShader(shader);
// 检查顶是否编译成功
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var error = gl.getShaderInfoLog(shader);
console.log('编译着色器失败: ' + error);
gl.deleteShader(shader);
return null;
}
return shader;
}
4 后续
使用修改后的initShaders函数,可以在浏览器中调试着色器代码,如果出错,会在控制台上输出出错代码的行数,如下图,它会告诉你错误出现在第4行,在uniform11附件,这时你差不多能知道是你将uniform写成uniform11了
暂无评论内容