纹理
在矩阵表面贴上图像
- 纹理映射(texture mapping): 根据纹理图像,为光栅化后的每个片元填充适当的颜色
- 纹理(texture)/纹理图像(texture image)
- 纹素(texels,texture elements): 组成纹理图像的像素
- 纹理坐标(texture coordinates): 决定纹理图像哪个部分用于覆盖
表示图像上的坐标,同样进行了归一化处理,
为了和xy坐标区分,WebGL使用 st坐标系统 。
step1-提供纹理
准备好映射到几何图形上的纹理图像。
step2-配置映射
为几何图形配置纹理映射方式。
step3-加载纹理
加载纹理图像并对其进行一些配置。
step4-抽取纹素
在片元着色器中将相应纹素从纹理中抽取出来,赋值给片元。
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()
用于创建纹理对象。
纹理图像管理单元
一共有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); |
答: WebGL与一般格式图片的y轴方向 相反,需要将图片y轴反转才能进行正确映射,或者手动反转映射坐标。
- WebGL Y(t)轴方向: ↑
- 图片系统 Y轴方向: ↓
STEP2:激活纹理单元
1 | gl.activeTexture(gl.TEXTURE0); |
答: 是WebGL用于 同时使用多个纹理 的一种机制。
每个纹理单元有一个 单元编号 来管理一张纹理图像,
系统支持的纹理单元的个数取决于:
- 硬件
- 浏览器的WebGL实现 (通常是8个)
STEP3:绑定纹理对象
1 | gl.bindTexture(gl.TEXTURE_2D,texture); |
可以看到,纹理对象的绑定实际上有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); |
纹理数据格式的选择要根据 纹理图像的格式:
- PNG 通常使用 gl.RGBA
- JPG、BMP 通常使用 gl.RGB
- 灰度图 通常使用 gl.LUMINANCE/gl.LUMINANCE_ALPHA
表示物体表面亮度,通常使用物体表面红绿蓝分量值加权平均计算流明。
WebGL中,internalformat必须和format一样。
除了UNSIGNED_BYTE数据类型,其他格式基本都遵循16比特规则:
将RGB三分量压缩入16比特中。
设置不同格式目的是为了 压缩数据,减少加载时间 。
STEP6:将纹理单元传递给片元着色器
1 | uniform sampler2D u_Sampler; |
- uniform 表示纹理图像不会随片元变化
- sampler2D 是一种专用于纹理对象的数据类型
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:基本原理就和查字典一样,知道大体的模块,再知道具体坐标,就能提取到一切你需要查询的信息。
就必须要提到之前对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
本案例主要介绍如何同时处理多幅纹理。
1 |
|
1 | let VSHADER_SOURCE = |