概念
纹理映射能为图像添加上皮肤,将一张图片或者图像映射到几何图形上,其作用是根据纹理图像,为之前光栅化的每个片元涂上颜色 (组成纹理图像的像素又被称为纹素)
纹理坐标系(左) 与 图片坐标系(右)
在纹理坐标系中,向上为y轴的正方向,图片坐标系则相反
纹理坐标系是理想状态,实际渲染纹理时 webgl 使用的图片坐标系。因此在向缓冲区对象中传递几何图形的点位和纹理图像的点位时,应该使用图片坐标系作为参考。
取样器类型
取样器只能是 uniform 变量,唯一能给取样器变量赋值的是纹理单元编号,必须使用 gl.uniform1i()赋值
- sampler2D - 2维平面取样器
- samplerCube - 3维取样器
/* fragmentShader */
precision mediump float;
uniform sampler2D u_sampler;
varying vec2 v_textureCoord;
void main() {
gl_FragColor = texture2D(u_sampler, v_textureCoord);
}
纹理单元
webgl通过一种叫纹理单元的机制来同时使用多个纹理,每个纹理单元有一个单元编号来管理一张纹理图像。即使当前程序只需要一个纹理,也需要为其指定一个纹理单元。
系统支持的纹理单元数取决于硬件和浏览器的webgl实现,默认情况下webgl支持8个纹理单元,一些其他系统支持的更多。内置变量 gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2..., gl.TEXTURE7各表示一个纹理单元
纹理类型
- gl.TEXTURE_2D (二维纹理)
- gl.TEXTURE_CUBE_MAP (立方体纹理)
纹理数据的数据格式
| 格式 | 描述 |
|---|---|
| gl.UNSIGNED_BYTE | 无符号整型,每个颜色分量占1字节 |
| gl.UNSIGNED_SHORT_5_6_5 | RGB:每个分量分别占 5,6,5 比特 |
| gl.UNSIGNED_SHORT_4_4_4_4 | RGBA:每个分量分别占 4,4,4,4 比特 |
| gl.UNSIGNED_SHORT_5_5_5_1 | RGBA:每个分量分别占 5,5,5,1 比特 |
webgl API
| 名称 | 参数 | 描述 |
|---|---|---|
| createTexture | 无 | 创建纹理对象 |
| activeTexture | texUnit - 指定纹理单元 | 激活纹理单元 |
| bindTexture | target - 纹理类型 texture - 绑定的纹理单元 | 绑定纹理对象 |
| texParameteri | target pname param | 配置纹理图像 |
| texImage2D | target level internalformat format type image | 将纹理图像分配给纹理对象 |
实现纹理映射的大致过程
- 准备纹理图像 - new Image()
- 配置纹理映射方式 - 将纹理图像如何放置在图形上
- 加载纹理图像,并对其配置,以在webgl中使用
- 在片元着色器中将相应的纹素从纹理中取出,并将纹素的颜色赋给片元
纹理映射实现
效果
首先实现一个矩形
添加纹理映射
shader & js Code
/*
顶点着色器
a_pointPosition: 顶点坐标
a_textureCoord: 纹理坐标
u_canvasSize: canvas画布大小
v_textureCoord: 传递给片元着色器的纹理坐标
*/
attribute vec2 a_pointPosition;
attribute vec2 a_textureCoord;
uniform vec2 u_canvasSize;
varying vec2 v_textureCoord;
void main() {
float x = a_pointPosition.x;
float y = a_pointPosition.y;
x = x / u_canvasSize.x * 2.0 - 1.0;
y = 1.0 - y / u_canvasSize.y * 2.0;
gl_PointSize = 10.0;
gl_Position = vec4(x, y, 0.0, 1.0);
v_textureCoord = a_textureCoord;
}
/*
片元着色器
u_sampler: 取样器
v_textureCoord: 纹理坐标
*/
precision mediump float;
uniform sampler2D u_sampler;
varying vec2 v_textureCoord;
void main() {
gl_FragColor = texture2D(u_sampler, v_textureCoord);
}
// js主要逻辑
import WebGLProgram from '../gl/base.js'
import defineVertexShaderSource from './shader/vertexShader.js';
import defineFragmentShaderSource from './shader/fragmentShader.js';
import { getRandomColor } from '../utils/index.js'
const glProgram = new WebGLProgram({
id: 'root',
defineVertexShaderSource,
defineFragmentShaderSource
});
const { gl, program } = glProgram;
const root = document.getElementById('root');
// 获取glsl变量的内存地址
const positionLocation = gl.getAttribLocation(program, 'a_pointPosition');
const textureCoordLocation = gl.getAttribLocation(program, 'a_textureCoord')
const sizeLocation = gl.getUniformLocation(program, 'u_canvasSize');
const samplerLocation = gl.getUniformLocation(program, 'u_sampler');
// 将 canvas 画布大小传入到 顶点着色器中
gl.uniform2f(sizeLocation, root.width, root.height);
// 创建缓冲区对象
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// 创建缓冲区索引对象
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
const img = new Image();
img.src = './image/01.jpeg';
img.onload = function () {
// 创建纹理对象
const texture = gl.createTexture();
// 激活纹理单元
gl.activeTexture(gl.TEXTURE0);
// 将纹理对象绑定到 webgl 系统中
gl.bindTexture(gl.TEXTURE_2D, texture);
// 配置纹理图像 映射到 图形上的(放大,缩小,拉伸)方式
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
// 向片元着色器传递纹理单元的编号
gl.uniform1i(samplerLocation, 0);
render();
}
// 渲染函数
function render() {
// 向顶点缓冲区填充数据
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
50, 50, 0, 0,
50, 300, 0, 1,
400, 300, 1, 1,
400, 50, 1, 0,
]),
gl.STATIC_DRAW
);
// 向索引缓冲区填充数据
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array([
0, 1, 3, 2
]),
gl.STATIC_DRAW
)
// 指定缓冲区对象中 数据 与 顶点变量 的映射关系
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 4 * 4, 0);
// 指定缓冲区对象中 数据 与 纹理变量 的映射关系
gl.vertexAttribPointer(textureCoordLocation, 2, gl.FLOAT, false, 4 * 4, 4 * 2);
// 启用变量
gl.enableVertexAttribArray(positionLocation);
gl.enableVertexAttribArray(textureCoordLocation);
// 用三角带扇绘制矩形
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
}