在矩阵表面贴上图像
几个名词
- 纹理映射(texture mapping): 根据纹理图像,为光栅化后的每个片元填充适当的颜色
- 纹理(texture)/纹理图像(texture image)
- 纹素(texels,texture elements): 组成纹理图像的像素
- 纹理坐标(texture coordinates): 决定纹理图像哪个部分用于覆盖
纹理坐标
表示图像上的坐标,同样进行了归一化处理,
为了和xy坐标区分,WebGL使用 st坐标系统 。

纹理映射步骤

准备好映射到几何图形上的纹理图像。
为几何图形配置纹理映射方式。
加载纹理图像并对其进行一些配置。
在片元着色器中将相应纹素从纹理中抽取出来,赋值给片元。
TexturedQuad
TexturedQuad

1 |
|
1 |
|
步骤解析
设置纹理坐标:initVertexBuffers()
将顶点坐标分配给a_Position并开启:
1 | let a_Position = gl.getAttribLocation(gl.program, 'a_Position'); |
将纹理坐标分配给a_TexCoord并开启:
1 | let a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord'); |
配置和加载纹理:initTexture()
gl.createTexture()
用于创建纹理对象。

纹理图像管理单元
一共有8个管理纹理图像的纹理单元:gl.TEXTURE[0-7] ,
每一个都与 gl.TEXTURE_2D 相关,
gl.TEXTURE_2D是 绑定纹理时的纹理目标。

使用 gl.deleteTexture() 删除纹理对象:

异步加载纹理图像
处于安全考虑,WebGL不运行跨域请求纹理图像。
OpenGL程序 与 WebGL网页 读取纹理图片的区别:
OpenGL
直接从存储纹理图像的磁盘上读取纹理图像。
WebGL
一般情况下需要向浏览器请求图像资源。

为WebGL配置纹理:loadTexture()
``
1 | function loadTexture(gl, n, texture, u_Sampler, image) { |
纹理配置详细步骤
STEP1:图像Y轴反转
1 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1); |
为什么要进行Y轴反转?
答: WebGL与一般格式图片的y轴方向 相反,需要将图片y轴反转才能进行正确映射,或者手动反转映射坐标。
- WebGL Y(t)轴方向: ↑
- 图片系统 Y轴方向: ↓


STEP2:激活纹理单元
1 | gl.activeTexture(gl.TEXTURE0); |
纹理单元(texture unit)是用来干嘛的?
答: 是WebGL用于 同时使用多个纹理 的一种机制。
每个纹理单元有一个 单元编号 来管理一张纹理图像,
系统支持的纹理单元的个数取决于:
- 硬件
- 浏览器的WebGL实现 (通常是8个)


STEP3:绑定纹理对象
1 | gl.bindTexture(gl.TEXTURE_2D,texture); |
WebGL的两种纹理类型


可以看到,纹理对象的绑定实际上有2步:
- 纹理对象的开启
- 纹理单元 与 纹理对象 的绑定
必须将纹理对象绑定到纹理单元上,通过操作纹理单元来操作纹理对象。

STEP4:配置纹理对象的参数
1 | gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR); |
配置涉及的内容都包括哪些?
- 纹素获取方式
- 纹理填充方式

纹理参数可以指定4个值:
纹理获取方式 | 纹理填充方式 | |||||
---|---|---|---|---|---|---|
方法名 | 纹理参数 | 描述 | 默认值 | 图解 | 参数值 | 参数描述 |
放大方法 | gl.TEXTURE_MAG_FILTER | 小纹理绘制大范围, 填充空隙 | gl.LINEAR | ![]() | gl.NEAREST | 以中心像素为锚点, 按照点位与中心点的曼哈顿距离大小映射 |
gl.LINEAR | 距像素中心最近的四个像素颜色值加权平分求出的,质量更好,开销更大 | |||||
缩小方法 | gl.TEXTURE_MIN_FILTER | 大纹理绘制小范围, 剔除像素 | gl.NEAREST_MIPMAP_LINEAR | ![]() | gl.NEAREST | - |
gl.LINEAR | - | |||||
水平填充 | gl.TEXTURE_WRAP_S | 左右侧区域的填充 | gl.REPEAT | ![]() | gl.REPEAT | 平铺式的重复纹理 |
gl.MIRRORED_REPEAT | 镜像对称式的重复纹理 | |||||
gl.CLAMP_TO_EDGE | 使用纹理图像边缘值 | |||||
垂直填充 | gl.TEXTURE_WRAP_T | 上下区域的填充 | gl.REPEAT | - | ||
gl.MIRRORED_REPEAT | - | |||||
gl.CLAMP_TO_EDGE | - |
实际上是原始纹理图像的一系列不同分辨率的版本。
即直角距离,棋盘距离,(x1,y1)与(x2,y2)的曼哈顿距离为|x1-x2|+|y1-y2|。

STEP5:将纹理图像分配给纹理对象
1 | gl.texImage2D(gl.TEXTURE_2D,0,gl.RGB,gl.RGB,gl.UNSIGNED_BYTE,image); |


纹理数据格式(internalformat)
纹理数据格式的选择要根据 纹理图像的格式:
- PNG 通常使用 gl.RGBA
- JPG、BMP 通常使用 gl.RGB
- 灰度图 通常使用 gl.LUMINANCE/gl.LUMINANCE_ALPHA

表示物体表面亮度,通常使用物体表面红绿蓝分量值加权平均计算流明。
WebGL中,internalformat必须和format一样。
纹理数据类型(type)

除了UNSIGNED_BYTE数据类型,其他格式基本都遵循16比特规则:
将RGB三分量压缩入16比特中。
设置不同格式目的是为了 压缩数据,减少加载时间 。
STEP6:将纹理单元传递给片元着色器
sampler2D
1 | uniform sampler2D u_Sampler; |
- uniform 表示纹理图像不会随片元变化
- sampler2D 是一种专用于纹理对象的数据类型

uniform1i
1 | gl.uniform1i(u_Sampler,0); // 0 表示 gl.TEXTURE0 |
对于sampler类型,必须将其指定的 纹理单元编号(texture unit number) 传给着色器。

STEP7:从顶点着色器向片元着色器传输纹理坐标
1 | let VSHADER_SOURCE = |
Vertex_Shader获取到纹理坐标a_TexCoord后,
通过v_TexCoord传递到Fragment_Shader。
虽然只明确传入了绘制范围的顶点纹理坐标,
但是片元的纹理坐标会在光栅化的过程中内插出来。
根据计算出的片元纹理坐标,从纹理图像上抽取出纹素的颜色,然后涂抹到片元上。
STEP8:在片元着色器中获取纹理像素颜色
1 | gl_FragColor = texture2D( u_Sampler, v_TexCoord); |
texture2D是GLSL ES的内置函数,
它只需要知道两个参数: 纹理单元编号 和 纹理坐标,就能够取得纹理上的像素颜色。
**OS:**基本原理就和查字典一样,知道大体的模块,再知道具体坐标,就能提取到一切你需要查询的信息。

texture2D的返回值
就必须要提到之前对WebGL纹理进行配置的 texParameteri 和 texImage2D。
texParameteri 的 纹理参数(pname) 直接决定WebGL求取片元纹理坐标内插值的方式。
texImage2D 的 纹理数据格式(internalformat) 直接决定 texture2D返回值类型。

小实验
TexturedQuad_Repeat
1 | let verticesTexCoords = new Float32Array([ |

之所以会重复,是因为 gl.TEXTURE_WRAP_S 和 gl.TEXTURE_WRAP_T 都设置为了 gl.REPEAT。

TexturedQuad_Mirror
1 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); |

MultiTexture
本案例主要介绍如何同时处理多幅纹理。
MultiTexture Code

1 |
|
1 | let VSHADER_SOURCE = |
GLSL ES中的vec4矢量的分量乘法
