-
将顶点的其他(非坐标)数据——如颜色等——传入顶点着色器。
-
发生在顶点着色器和片元着色器之间的从图形到片元的转化,又称为图元光栅化 (rasterzation process)。
-
将图像(或称纹理)映射到图形或三维对象的表面上。
创建多个缓冲区将非坐标数据传入顶点着色器
// MultiAttributeSize.js
// 顶点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute float a_PointSize;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = a_PointSize;\n' +
'}\n'
// 片元着色器
var FSHADER_SOURCE =
'void main(){\n' + ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n' + '}\n'
// 主函数
function main() {
// 获取canvas元素
let canvas = document.getElementById('webgl')
// 获取上下文
let gl = getWebGLContext(canvas)
if (!gl) {
console.log('Failed to get rendering context for WebGL')
return
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders')
return
}
// 设置顶点信息
let n = initVertexBuffers(gl)
if (n < 0) {
console.log('Failed to set the position of the vertices')
return
}
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空绘图区
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制
gl.drawArrays(gl.POINTS, 0, n)
}
function initVertexBuffers(gl) {
// 数据准备
let vertices = new Float32Array([
0.0,
0.5,
-0.5,
-0.5,
0.5,
-0.5, // 点的位置
])
let n = 3 // 点的数量
let sizes = new Float32Array([
10.0,
20.0,
30.0, // 点的尺寸
])
// 创建缓冲区
let vertexBuffer = gl.createBuffer()
let sizeBuffer = gl.createBuffer()
if (!vertexBuffer || !sizeBuffer) {
console.log('Failed to create the buffer object')
return -1
}
// 将顶点坐标写入缓冲区对象并开启
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) // 绑定缓冲区
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW) // 向缓冲区存入数据
// 将缓冲区分配给attribute变量
let a_Position = gl.getAttribLocation(gl.program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return -1
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
// 开启attribute变量(开启缓冲区)
gl.enableVertexAttribArray(a_Position)
// 将顶点尺寸写入缓冲区对象并开启
gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer)
gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW)
let a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
if (a_PointSize < 0) {
console.log('Failed to get the storage location of a_PointSize')
return -1
}
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_PointSize)
return n
}
代码中都是以往应用的功能,此处我们在原绘制多个点的示例基础上,做了如下改变:
- 新增了attribute变量a_PointSize,该变量为Float类型,负责接收JavaScript程序传入的顶点尺寸数据并被赋值给gl_PointSize变量,
- 在initVertexBuffers()函数中也增加了a_PointSize相关内容。
当执行 gl.drawArrays() 函数时,存储在缓冲区对象中的数据将按照其在缓冲区中的顺序依次传给对应的 attribute 变量
一个缓冲区对象传输多类型信息——步进和偏移参数与交错组织
相关内容:通过gl.vertexAttribPointer()函数的stride(步进)参数和offset(偏移)参数,在一个缓冲区对象中存储并读取多类型数据,示例中一个缓冲区对象包含有顶点位置和大小两类数据。
相关函数:gl.vertexAttribPointer(), TypedArray.BYTES_PER_ELEMENT
使用多个缓冲区对象向着色器传递多种数据,比较适合数据量不大的情况。当程序中的复杂三维图形具有成千上万个顶点时,维护所有的顶点数据是很困难的。然而,WebGL允许我们把顶点的坐标和尺寸数据打包到同一个缓冲区对象中(交错组织interleaving),并通过步进和偏移有差别地访问缓冲区对象中不同种类的数据,即使用gl.vertexAttribPointer()函数的第5个参数stride和第6个参数offset。
// MultiAttributeSize_Interleaved.js
// 顶点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute float a_PointSize;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = a_PointSize;\n' +
'}\n'
// 片元着色器
var FSHADER_SOURCE =
'void main(){\n' + ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n' + '}\n'
// 主函数
function main() {
// 获取canvas元素
let canvas = document.getElementById('webgl')
// 获取上下文
let gl = getWebGLContext(canvas)
if (!gl) {
console.log('Failed to get rendering context for WebGL')
return
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders')
return
}
// 设置顶点信息
let n = initVertexBuffers(gl)
if (n < 0) {
console.log('Failed to set the position of the vertices')
return
}
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空绘图区
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制
gl.drawArrays(gl.POINTS, 0, n)
}
function initVertexBuffers(gl) {
// 数据准备
let verticesSizes = new Float32Array([
// 顶点坐标和点的尺寸
0.0, 0.5, 10.0, // 第一个点
-0.5, -0.5, 20.0, // 第二个点
0.5, -0.5, 30.0, // 第三个点
])
let n = 3 // 点的数量
// 创建缓冲区
let vertexSizeBuffer = gl.createBuffer()
if (!vertexSizeBuffer) {
console.log('Failed to create the buffer object')
return -1
}
// 绑定缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertexSizeBuffer)
// 向缓冲区存入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesSizes, gl.STATIC_DRAW)
// 获取单个数据的大小
let FSIZE = verticesSizes.BYTES_PER_ELEMENT
// 获取a_Position的存储位置
let a_Position = gl.getAttribLocation(gl.program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return -1
}
// 将缓冲区分配给a_Position
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0)
// 开启attribute变量(开启缓冲区)
gl.enableVertexAttribArray(a_Position)
// 获取a_PointSize的存储位置
let a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
if (a_PointSize < 0) {
console.log('Failed to get the storage location of a_PointSize')
return -1
}
// 将缓冲区分配给a_PointSize
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2)
// 开启attribute变量(开启缓冲区)
gl.enableVertexAttribArray(a_PointSize)
return n
}
注意第五个参数是 字节 数,所以需要 verticesSizes.BYTES_PRE_ELEMENT 获取单个元素的字节长度
- 每个attribute变量都要先获取位置,再分配缓冲区,最后开启变量,两个变量分配缓冲区的方式不同。
// 将缓冲区分配给a_Position
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 3, 0)
// 将缓冲区分配给a_PointSize
gl.vertexAttribPointer(a_PointSize, 1, gl.FLOAT, false, FSIZE * 3, FSIZE * 2)
改变颜色(初识varying变量)——为顶点单独设置颜色
相关内容:通过varying变量从顶点着色器向片元着色器传值
相关函数:varying小结:1. varying变量的作用:从顶点着色器向片元着色器传输数据;2. varying变量可以赋值的数据类型:float及相关数据类型;3. varying变量的行为(如何传值):末尾有图展示——在WebGL中,如果顶点着色器与片元着色器中有类型和命名都相同的varying变量,那么顶点着色器赋给该变量的值会自动传入片元着色器。
// MultiAttributeColor.js
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n'
// 片元着色器
var FSHADER_SOURCE =
'precision mediump float;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_FragColor = v_Color;\n' +
'}\n'
// 主程序
function main() {
// 获取canvas元素
let canvas = document.getElementById('webgl')
// 获取上下文
let gl = getWebGLContext(canvas)
if (!gl) {
console.log('Failed to get rendering context for WebGL')
return
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders')
return
}
// 获取顶点位置
let n = initVertexBuffers(gl)
if (n < 0) {
console.log('Failed to set the position of the vertices')
return
}
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空绘图区
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制
gl.drawArrays(gl.POINTS, 0, n)
}
// 顶点位置相关函数
function initVertexBuffers(gl) {
// 数据准备
let verticesColors = new Float32Array([
// 顶点坐标和颜色
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,
])
let n = 3
// 创建缓冲区对象
let vertexColorBuffer = gl.createBuffer()
if (!vertexColorBuffer) {
console.log('Failed to create the buffer object')
return -1
}
// 向缓冲区存储数据
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW)
let FSIZE = verticesColors.BYTES_PER_ELEMENT
// 获取a_Position的存储位置,分配缓冲区并开启
let a_Position = gl.getAttribLocation(gl.program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return -1
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
gl.enableVertexAttribArray(a_Position) // Enable buffer assignment
// 获取a_Color的存储位置,分配缓冲区并开启
let a_Color = gl.getAttribLocation(gl.program, 'a_Color')
if (a_Color < 0) {
console.log('Failed to get the storage location of a_Color')
return -1
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2)
gl.enableVertexAttribArray(a_Color) // 开启缓冲区分配
return n
}
着色器部分:
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n'
// 片元着色器
var FSHADER_SOURCE =
'precision mediump float;\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_FragColor = v_Color;\n' +
'}\n'
顶点着色器中声明了attribute变量a_Color用以接收颜色数据,同时在两个着色器中都声明了varying变量v_Color变量,该变量将值从顶点着色器传递到片元着色器,最终赋值给gl_FragColor(在WebGL中,如果顶点着色器与片元着色器中又类型和命名都相同的varying变量,那么顶点着色器赋给该变量的值会自动传入片元着色器)。
varying变量只能是float(以及相关的vec2,vec3,vec4,mat2,mat3,mat4)类型。
片元着色器在声明新的变量的时候,需要在前面加上精度限定语句,此处为
precision mediump float
,这一语法在后面GLSL ES语言的介绍中会进行说明。
ColoredTriangle.js与几何图形装配、光栅化、执行片元着色器(varying变量的varying过程-内插)
相关内容:着色器工作细节:顶点着色器-几何图像装配-光栅化-片元着色器;varying变量的特性—传值和内插
相关函数:varying变量;片元着色器内置对象gl_FragCoord小结:
1. 顶点着色器在提供顶点坐标之后,会进入几何图像装配流程,根据gl.drawArrays()中的参数选择配装方式;
2. 配装之后,光栅化将配装后的图像转化为片元,每个片元中保存有窗口坐标系统坐标等信息(目前已知);
3. 片元着色器接收光栅化后的图像,为每个片元确定颜色,绘制到颜色缓冲区中;
4. 通过gl_FragCoord,我们可以获得片元在canvas坐标系统(窗口坐标系统)下的坐标,据此对gl_FragColor进行操作,可以根据坐标控制片元的颜色
// 绘制
gl.drawArrays(gl.TRIANGLES, 0, n)
将上一个例子中的 函数参数改一下就绘制了一个彩色的三角形
为了理解这种效果的产生方式,我们需要厘清顶点着色器和片元着色器之间的数据传输细节。
几何形状的装配和光栅化:
一个普通的绘制红色三角形的代码:
// HelloTriangle.js
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
'}\n'
// 片元着色器
var FSHADER_SOURCE =
'void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 主函数
function main() {
// 获取canvas元素
let canvas = document.getElementById('webgl')
// 获取WebGL上下文
let gl = getWebGLContext(canvas)
if (!gl) {
console.log('Failed to get the rendering context for WebGL')
return
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders')
return
}
// 设置顶点位置
let n = initVertexBuffers(gl)
if (n < 0) {
console.log('Failed to set the positions of the vertices')
return
}
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制
gl.drawArrays(gl.TRIANGLES, 0, n)
}
function initVertexBuffers(gl) {
let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])
let n = 3 // 点的个数
// 创建缓冲区对象
let vertexBuffer = gl.createBuffer()
if (!vertexBuffer) {
console.log('Failed to create the buffer object')
return -1
}
// 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
// 向缓冲区对象中写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
// 将缓冲区对象分配给a_Position变量
let a_Position = gl.getAttribLocation(gl.program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return -1
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
// 连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position)
return n
}
程序向 gl_Position 给出了三个顶点的坐标,谁来确定这三个点就是三角形的三个顶点?最终,为了填充三角形内部,谁来确定哪些像素需要被着色?谁来负责调用片元着色器,片元着色器又是怎样处理每个片元的?
如上图所示,顶点着色器和片元着色器之间有以下两个步骤:
- 图元装配过程:将孤立的顶点坐标装配成几何图形,几何图形的类别由
gl.drawArrays()
第一个参数决定。 - 光栅化过程:将装配好的几何图形转化为片元。(于是就可以进行逐片元操作了)
在上述代码示例中,顶点着色器会被执行三次,之后进行装配和光栅化,最后执行片元着色器,流程如下:
- 执行顶点着色器,缓冲区对象中第一个最表(0.0,0.5)传递给 attribute 变量 a_Position (自动补全为(0.0,0.5,0.0,1.0)),最后赋值给 gl_Position ,他就进入了图形装配区域并暂时储存在那里
- 执行顶点着色器,传入第二个坐标(-0.5,-0.5,0.0,1.0)
- 执行顶点着色器,传入第三个坐标(0.5,-0.5,0.0,1.0)
- 开始装配图形。使用传入的点的坐标,根据 gl.drawArrays() 第一个参数决定如何装配
- 将图形转化为片元,即光栅化(rasterization),光栅化之后,我们就得到了组成这个三角形的所有片元,片元的数目实际上是三角形最终在屏幕上所覆盖的像素数。
调用片元着色器:
光栅化之后,就可以逐片元调用片元着色器。
假如图形只有如下图所示实心正方形所示的10个片元,那么片元着色器会调用10次,每调用一次处理一个片元。对于每个片元(本身带有坐标信息),片元着色器计算出该片元的颜色,写入颜色缓冲区。
全部处理完毕后,浏览器会显示最终的结果。
对片元着色器内部操作:
片元本身带有坐标信息,调用片元着色器时,这些坐标信息也随着片元传了过去,我们可以通过片元着色器内置变量来访问该坐标信息:
varing 变量的作用和内插过程
内插之后,每个片元都根据三个顶点的v_Color变量得到了自己的v_Color变量,该变量和坐标信息(可能还有其他信息)一起保存在片元中。进入片元着色器后,新声明的varying变量v_Color接收原varying变量v_Color,然后赋值给gl_FragColor,最后会知道颜色缓冲区中。
所谓的内插过程(interpolation process)如下所示:
在矩形表面贴上图像
相关内容:WebGL中纹理映射的流程
相关函数:gl.createTexture(), gl.deleteTexture(), gl.pixelStorei(), gl.activeTexture(), gl.bindTexture(), gl.texParameteri(), gl.texImage2D(), gl.uniform1i(), texture2D()小结:1.纹理映射主体流程:着色器设置-顶点坐标和对应纹理坐标设置-加载图像,加载完成后配置纹理;2. 纹理流程细节:创建纹理对象-获取变量存储地址-加载图像,加载完成后:纹理对象预处理(Y轴反转)-开启纹理单元-绑定并开启纹理对象-配置纹理参数-配置纹理图像-根据纹理单元传输纹理;3. 纹理相关扩展:非2的n次幂图像的呈现问题。
除了通过给定数值为片元着色,在三维图形学中,有一项重要的技术可以帮助我们使用现有的图像为图形着色,被称为纹理映射(texture mapping)。
纹理映射的作用,就是根据纹理图像,为之前光栅化后的每个片元涂上合适的颜色。通俗说法,就是将一张图像(就像一张贴纸)映射(贴)到一个几何图形的表面上去。该图片又可以称为纹理图像(texture image)或纹理(texture),组成纹理图像的像素又被称为纹素(texels,texture elements),每一个纹素的颜色都是用RGB(jpg)或RGBA格式(png)编码。
在 WebGL中,要进行纹理映射,需要四步:
- 准备好映射到几何图形上的纹理图像
- 为几何图形配置纹理映射方式
- 加载纹理图像,对其进行一些配置,以在 WebGL 中使用它
- 在片元着色器中将相应的纹素从纹理中抽取出来,并将纹素的颜色赋给片元
确定“几何图形的某个片元”的颜色如何取决于“纹理图像中哪个(或哪几个)像素”的问题(即前者到后者的映射)。此处利用图形的顶点坐标来确定屏幕上哪部分被纹理图像覆盖,使用纹理坐标(texture coordinates)确定纹理图像的哪部分将覆盖到几何图形上。
纹理坐标:
纹理坐标就是纹理图像上的坐标,通过纹理坐标可以在纹理图像上获取纹理颜色。WebGL 系统中的纹理坐标系统是二维的。为了与 xy坐标分开,使用 st 坐标系统
因为坐标值与图像自身的尺寸无关,不管是 128 * 128 还是 256 * 256 ,其右上角的纹理坐标始终是 (1.0,1.0)
将纹理图像粘贴到几何图形上
示例程序:
- 首先在顶点着色器中为每个顶点指定纹理坐标,
- 然后在片元着色器中根据每个片元的纹理坐标从纹理图像中抽取纹素颜色
// 顶点着色器
let VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec2 a_TexCoord;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n' +
' gl_Position = a_Position;\n' +
' v_TexCoord = a_TexCoord;\n' +
'}\n'
// 片元着色器
let FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform sampler2D u_Sampler;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n' +
' gl_FragColor = texture2D(u_Sampler,v_TexCoord);\n' +
'}\n'
// 主函数
function main() {
// 获取canvas元素
let canvas = document.getElementById('webgl')
// 获取上下文
let gl = getWebGLContext(canvas)
if (!gl) {
console.log('Failed to get the rendering context for WebGL')
return
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders')
return
}
// 设置顶点坐标
let n = initVertexBuffers(gl)
if (n < 0) {
console.log('Failed to set the positions of the vertices')
return
}
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 配置纹理信息
if (!initTextures(gl, n)) {
console.log('Failed to configure the texture')
return
}
}
// 顶点坐标
function initVertexBuffers(gl) {
// 数据准备
let 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,
])
let n = 4
// 创建缓冲区
let vertexTexCoordBuffer = gl.createBuffer()
if (!vertexTexCoordBuffer) {
console.log('Failed to create vertexTexCoordBuffer')
return -1
}
// 绑定缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer)
// 缓冲区存储数据
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW)
// 顶点坐标相关配置
let FSIZE = verticesTexCoords.BYTES_PER_ELEMENT
let a_Position = gl.getAttribLocation(gl.program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return -1
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0)
gl.enableVertexAttribArray(a_Position)
// 纹理坐标相关配置
let a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord')
if (a_TexCoord < 0) {
console.log('Failed to get the storage location of a_TexCoord')
return -1
}
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2)
gl.enableVertexAttribArray(a_TexCoord)
// 一切顺利,返回顶点数量
return n
}
// 初始化纹理
function initTextures(gl, n) {
// 创建纹理对象
let texture = gl.createTexture()
if (!texture) {
console.log('Failed to create texture')
return
}
// 获取u_Sampler(纹理图像)的存储位置
let u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler')
if (!u_Sampler) {
console.log('Failed to get the storage loaction of u_Sampler')
return
}
// 创建一个image对象
let image = new Image()
// 注册图像加载事件的响应函数
image.onload = function () {
loadTexture(gl, n, texture, u_Sampler, image)
}
// 浏览器加载图像
image.src = './funaimu.jpg'
return true
}
function loadTexture(gl, n, texture, u_Sampler, image) {
// 对纹理图像进行Y轴反转
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1)
// 开启0号纹理单元
gl.activeTexture(gl.TEXTURE0)
// 向target绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture)
// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// 配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image)
// 将0号纹理传递给着色器
gl.uniform1i(u_Sampler, 0)
// 绘制矩形
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n)
}
可能的问题:
- 1,浏览器访问不了本地文件,这个需要设置一下
- 图片的尺寸不是 2 的倍数,代码报错:WebGL warning: drawArraysInstanced: TEXTURE_2D at unit 0 is incomplete: Non-power-of-two textures must have a wrap mode of CLAMP_TO_EDGE.
问题二会导致图片无法绘制出来,可以通过增加两行代码解决:
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
使 webgl设置图形纹理的时候,在设置水平和垂直如何填充的时候,设置成水平和垂直拉伸
纹理相关操作流程可分为以下五个部分:
- 顶点着色器中接收顶点的纹理坐标,光栅化后传递给片元着色器(GL SL ES)
- 片元着色器根据片元的纹理坐标,从纹理图像中抽取出纹素颜色,赋给当前片元(GL SL ES)
- 设置顶点的纹理坐标(initVertexBuffers())
- 准备待加载的纹理图像,令浏览器读取它(initTextures())
- 监听纹理图像的加载事件,一旦加载完成,就在 WebGL 系统中使用纹理(loadTexture())
参考:【《WebGL编程指南》读书笔记-颜色与纹理】_webgl warning: texsubimage: texture has not been i-CSDN博客