【WebGL】纹理
背景
记录下纹理相关容易忘记的地方
texImage2D
根据数据源不同,有多个重载函数
上传 HTML 元素(图片、视频、Canvas 等)
gl.texImage2D(target, level, internalformat, format, type, source);
参数说明:
- target: 目标纹理类型(如 gl.TEXTURE_2D)。
- level: Mipmap 级别(通常为 0)。
- internalformat: 纹理的内部格式(如 gl.RGBA)。
- format: 数据的格式(如 gl.RGBA)。
- type: 数据类型(如 gl.UNSIGNED_BYTE)。
- source: HTML 元素(如 HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, 或 ImageBitmap)
const image = new Image();
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
};
image.src = 'texture.png';
上传 TypedArray 数据
gl.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
参数说明:
- target: 目标纹理类型(如 gl.TEXTURE_2D)。
- level: Mipmap 级别(通常为 0)。
- internalformat: 纹理的内部格式(如 gl.RGBA)。
- width: 纹理的宽度。
- height: 纹理的高度。
- border: 边框大小(在 WebGL 中必须为 0)。
- format: 数据的格式(如 gl.RGBA)。
- type: 数据类型(如 gl.UNSIGNED_BYTE)。
- pixels: 包含像素数据的 TypedArray(如 Uint8Array, Float32Array 等)或 null。
const width = 256;
const height = 256;
const data = new Uint8Array(width * height * 4); // RGBA 数据
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
activeTexture
gl.activeTexture(textureUnit);
- textureUnit:指定要激活的纹理单元。
通常为 gl.TEXTURE0 到 gl.TEXTURE31,表示可以同时激活的 32 个纹理单元之一。
gl.TEXTURE0 是默认的活动纹理单元。意思你不执行这行代码,默认也是gl.TEXTURE0被激活的
完整案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL Textured Triangle</title>
<style>
canvas {
display: block;
margin: auto;
background: #000;
}
</style>
</head>
<body>
<canvas id="webgl-canvas" width="800" height="600"></canvas>
<script>
const vertexShaderSource = `
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord; // Pass texture coordinates to the fragment shader
}
`;
const fragmentShaderSource = `
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vTexCoord); // Sample the texture
}
`;
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
return program;
}
function initWebGL() {
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl2');
if (!gl) {
console.error('WebGL not supported');
return;
}
// Compile shaders and link program
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(gl, vertexShader, fragmentShader);
// Triangle vertices and texture coordinates
const vertices = new Float32Array([
// Positions // Texture coordinates
0.0, 0.5, 0.5, 1.0, // Top vertex
-0.5, -0.5, 0.0, 0.0, // Bottom-left vertex
0.5, -0.5, 1.0, 0.0 // Bottom-right vertex
]);
// Set up buffers
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const aPosition = gl.getAttribLocation(program, 'aPosition');
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 4 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.enableVertexAttribArray(aPosition);
const aTexCoord = gl.getAttribLocation(program, 'aTexCoord');
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 4 * Float32Array.BYTES_PER_ELEMENT, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(aTexCoord);
// Load texture
const texture = gl.createTexture();
const image = new Image();
image.crossOrigin = 'anonymous'; // Allow cross-origin for external images
image.src = 'https://webglfundamentals.org/webgl/resources/leaves.jpg'; // Replace with your texture image URL
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
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);
render(); // Start rendering after the texture is loaded
};
function render() {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.bindVertexArray(vao);
//gl.activeTexture(gl.TEXTURE0);
//gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(program, 'uTexture'), 0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Request the next frame
requestAnimationFrame(render);
}
}
initWebGL();
</script>
</body>
</html>