🕹️ [WebGL编程指南] 5. 颜色与纹理
gl.vertextAttribPointer 的步进和偏移参数
WeblGL允许把顶点坐标和尺寸打包到同一个缓冲区对象中。如将“逐顶点”数据(坐标和尺寸)交叉存储在一个数组中。
javascriptCopy
const verticesSizes = new Float32Array([
// 按顺序分别代表
// x y 顶点大小
0.0, 0.5, 10.0,
-0,5, -0.5, 20.0,
0.5, -0.5, 30.0
])然后使用gl.vertextAttribPointer 的第五个参数stride 和第6个offset参数,有差别的从缓冲区获取特定数据。
javascriptCopy
// ...
const verticesSizes = new Float32Array([
// 按顺序分别代表
// x y 顶点大小
0.0, 0.5, 10.0,
-0,5, -0.5, 20.0,
0.5, -0.5, 30.0
])
const vertexSizeBuffer = gl.createBuffer()
gl.binBuffer(gl.ARRAY_BUFFER, vertexSizeBuffer)
gl.bufferData(gl.ARRAY_BUFFER, verticesSizes, gl.STATIC_DRAW)
const FSIZE = verticesSizes.BYTES_PER_ELEMENT;
const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
// ...
gl.vertextAttribPointer(a_Pointer, 2, gl.FLOAT, false, FSIZE * 3, 0)
// 开启分配
gl.enableVertexAttribArray(a_Pointer)
// ...
const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
// ...
gl.vertextAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2)
// 开启分配
gl.enableVertexAttribArray(a_PointSize)
传递颜色
javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_Color;\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" + // 设置坐标
" gl_PointSize = 10.0;\n" + // 设置大小
" v_Color = a_Color;\n" + // 将数据传递给片元着色器
"}\n";
// 片元着色器
const FSHADER_SOURCE =
"varying vec4 v_color;\n" +
"void main() {\n" +
" gl_FragColor = v_color;\n" + // 设置颜色
"}\n";
// ...
const verticesSizes = new Float32Array([
// 按顺序分别代表
// x y r g b
0.0, 0.5, 1.0, 0.0, 0.0,
-0,5, -0.5, 0.0, 1.0, 0.0,
0.5, -0.5, 0.0, 0.0, 1.0
])
// ...
const a_Color = gl.getAttribLocation(gl.program, 'a_Color')
// ...
gl.vertextAttribPointer(a_Color, 1, gl.FLOAT, false, FSIZE * 5, FSIZE * 2)
// 开启分配
gl.enableVertexAttribArray(a_Color)
几何形状的装配和光栅化
顶点着色器和片元着色器之间,存在两个步骤。
gl_Position实际上是几何图形装配阶段的输入数据。几何图形装配过程被又称为图元装配过程,因为装配出来的基本图形(点、线、面)又被称为图元。
当gl.drawArrays()的参数n为3时,顶点着色器将执行三次。整个绘制过程可以分成几步:
第一步:执行顶点着色器,将缓冲区的第一个坐标传递给a_Position 变量。一旦一个顶点坐标被赋值给gl_Position,它就进入了图形装配区,并暂时储存在那。
第二步:再次执行顶点着色器,将第二个坐标储存在装配区
第三部:最后执行顶点着色器,将第三个坐标储存在装配区
第四步:开始装配图形。使用传入的点坐标,根据gl.drawArrays的第一个参数(gl.TRIAGLES)来决定如何装配。比如用三个顶点装配出一个三角形
第五步:显示在屏幕上的三角形是由片元(像素)组成的,还需要将图形转换成片元,这个过程被称为光栅化

在矩形表面贴图像
纹理映射。是将一张图像映射到一个几何图形的表面上。这张图片又可以称为纹理图像或者纹理。纹理映射的作用,是根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色。组成纹理图像的像素被称为纹素。每个纹素的颜色使用RGB 或 RGBA编码。



javascriptCopy
// 顶点着色器
const VSHADER_SOURCE =
"attribute vec4 a_Position;\n" +
"attribute vec4 a_TexCoord;\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" + // 设置坐标
" v_TextCoord = a_TextCoord;\n" + // 纹理坐标
"}\n";
// 片元着色器
const FSHADER_SOURCE =
"uniform sampler2D u_Sampler;\n" +
"varying vec2 v_TextCoord;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(u_Sampler, v_TextCoord);\n" + // 设置纹素
"}\n";
/* ---- 初始化着色器 ---- */
const verticesTexCoords = new Float32Array([
// 顶点坐标,纹理坐标
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0
0.5, -0.5, 1.0, 0.0
])
const n = 4;
const vertextTexCoordBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertextTexCoordBuffer)
gl.bindData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW)
const FSIZE = vertextTexCoordBuffer.BYTES_PER_ELEMENT;
// ...
const a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord')
gl.vertextAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE*4, FSIZE*2)
gl.enableVertextAttribArray(a_TexCoord)
// ...
/* ---- 初始化纹理 ---- */
const texture = gl.createTexture(); // 创建纹理对象
// ...
const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler')
const image = new Image()
image.onload = () => {
// 对纹理图像进行y轴反转
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)
// 开启0号纹理
gl.activeTexture(gl.TEXTURE0)
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture)
// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILLTER, gl.LINEAR)
// 配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image)
// 将纹理发送给着色器
gl.uniformi(u_Sampler, 0)
// ...
// 绘制举矩阵形状
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n)
}
image.src = '...'





使用多幅纹理
javascriptCopy
// 片元着色器
const FSHADER_SOURCE =
"uniform sampler2D u_Sampler0;\n" +
"uniform sampler2D u_Sampler1;\n" +
"varying vec2 v_TextCoord;\n" +
"void main() {\n" +
" vec4 color0 = texture2D(u_Sampler0, v_TextCoord);\n" + // 1号纹理
" vec4 color1 = texture2D(u_Sampler1, v_TextCoord);\n" + // 2号纹理
" gl_FragColor = color0 * color1;\n" + // 设置纹素
"}\n";
// ...
// 导入两个纹理图像,在两个纹理图像加载后,调用gl.drawArray()