[CG] WebGL Shader中的数据和简单的工作流
1 Shader中的数据
1.1 获取数据的方式
1.1.1 属性(attribute)和缓冲(buffer)
缓冲是发送到GPU的一些二进制数据序列,通常情况下包括位置、法向量、纹理坐标、顶点颜色值等。一下是常用的API(gl代表WebGLRenderingContext)
gl.createBuffer()
,创建并初始化一个用于储存顶点数据或着色数据的WebGLBuffer
对象gl.bindBuffer(target, buffer)
,target是绑定点,可以理解为将缓冲绑定为当前缓冲,之后的操作就可以在这个缓冲上进行,当要对别的缓冲进行设置操作时,要绑定切换成别的缓冲。gl.bufferData(target, ArrayBufferView srcData, usage, srcOffset, length)
,创建并初始化WebGLBuffer
对象的数据存储区,可以理解为将数据复制进缓冲中。target是绑定点,srcData是将要被复制到数据存储区的数据,usage是存储区使用的方式,例如是否被经常使用?修改?srcOffset初始元素索引的偏移量。gl.deleteBuffer(buffer)
,用于删除指定的缓冲,若已经删除,则不做操作。gl.isBuffer(buffer)
,检查缓冲区是否有效。gl.getAttribLocation(program, name)
,返回给定的WebGLProgram
对象中某属性在program
中的位置,相当于是索引。
创建缓存,绑定缓存,发送数据,查询属性的索引,一般在初始化部分完成。
接下来就是,告诉gl
,我们想从缓冲中提供数据,从哪个缓冲呢?所以要bind一个我们需要缓冲到当前缓冲以便操作,然后将属性和缓冲链接起来,并设定读取、解析的方式。
gl.enableVertexAttribArray(index)
,在使用属性之前要使用此方法进行激活,没有激活的属性不会被使用。这里index
是一个GLuint
类型的索引值,这个值在初始化的时候一般会获取,这个索引是针对当前使用的program
来说的,因为这个索引就是类似于0,1...这样的数字,不是唯一的,所以是针对当前的program
来说的。gl.vertexAttribPointer( index, numComponents, typeOfData, normalizeFlag, strideToNextPieceOfData, offsetIntoBuffer)
;
将属性与当前gl.ARRAY_BUFFER
绑定的缓冲区绑定,并指定如何从缓冲区里读取数据,index
是属性的索引,numComponents
为1,2,3,4中的一个,指定一个顶点数据由几个component
组成,typeOfData
是每个数据(component
)的类型,normalizeFlag
数据要不要归一化,strideToNextPieceOfData
,指明顶点属性是不是紧密连续的,中间有没有空隙,一般为0,offset
初始的偏移量,一般为0
属性来指明如何从缓冲中读取数据并提供给vertex shader
,每次vertex shader
会按照规则读取。
1.1.2 全局变量(Uniforms)
全局变量在shader program
运行前赋值,在运行过程中全局有效,在一次绘制过程中,传递给shader
的值都是一样的。例如Varyings
的值是在fragment shader
里插值得到的,所以可能每个像素的值都不同。全局变量的设置方式是,先找到全局变量的地址,然后给其设置值
gl.getUniformLocation(program, name)
,从program
中找到全局变量的位置gl.uniform[1234][fi][v]()(index, value)
,设定全局变量的值
全局变量有许多类型,每个类型都对应不同的设置方法。
1.1.3 纹理(Textures)
纹理是一个数据序列,可以在program运行中随意读取其中的数据。 大多数情况存放的是图像数据,但是纹理仅仅是数据序列, 可以随意存放除了颜色数据以外的其它数据。
在着色器中获取纹理信息,可以先创建一个sampler2D类型全局变量,然后用GLSL方法texture2D 从纹理中提取信息。在渲染的时候webGL要求纹理必须绑定到一个纹理单元上
uniform sampler2D u_texture
texture2D(u_texture, texcoord)
gl.getUniformLocation(someProgram, "u_texture")
gl.activeTexture(gl.TEXTURE0 + unit)
,激活指定的纹理gl.bindTexture(target, texture)
,将纹理绑定到绑定点gl.uniform1i(someSamplerLoc, unit)
,设定全局变量的值
1.1.4 变量(Varyings)
vertex shader
给fragment shader
传值的方式,依照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。在两个shader
中,名字要定义成一样。
1.2 vertex shader
- 属性
- 全局
- 纹理
将顶点转换到裁剪空间的坐标中去,每个顶点会调用一次,设置一个特殊的变量gl_Position
,裁剪空间中的坐标值,GPU接收该值,并保存
1.3 fragment shader
- 全局
- 纹理
- 可变量
基于vertex shader
的结果,绘制像素点,每个像素都会调用设置gl_FragColor
,定义像素颜色
2 简单的工作流
- 初始化阶段
- 获取
html
的canvas
元素 - 获取
webgl
上下文 - 获取
vertex shader
和fragment shader
的字符串 - 创建和编译
shader
- 链接两个
shader
,成为一个program
- 获取属性的地址
- 创建缓冲
- 绑定新建的缓冲到当前缓冲
- 给缓冲发送数据
- 获取
- 渲染阶段
- 从裁剪空间到像素空间
- 清空
Canvas
- 设置我们要使用的
program
程序 - 打开属性
- 绑定缓冲到当前缓冲
- 将属性绑定到当前缓冲,并设置读取的方式
- 画图