一.实现效果
二.逻辑
为了在WebGL中给图片添加背景,主要的逻辑步骤包括初始化WebGL上下文、编写和编译着色器、创建和绑定缓冲区、加载和配置纹理以及绘制场景。以下是代码逻辑的详细说明:
1. 获取WebGL上下文
首先,通过获取<canvas>
元素并调用getContext('webgl')
方法来初始化WebGL上下文。
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
2. 定义顶点着色器和片段着色器
编写顶点着色器(vertex shader)和片段着色器(fragment shader)。顶点着色器负责处理顶点数据,并将纹理坐标传递给片段着色器;片段着色器则使用纹理坐标来获取纹理颜色,并将其绘制到屏幕上。
const vsSource = `
attribute vec4 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = aPosition;
vTexCoord = aTexCoord;
}
`;
const fsSource = `
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uSampler;
void main() {
gl_FragColor = texture2D(uSampler, vTexCoord);
}
`;
3. 初始化WebGL着色器程序
编译和链接着色器程序,然后使用该程序。
const program = initShader(gl, vsSource, fsSource);
gl.useProgram(program);
4. 定义顶点数据和索引数据
定义包含顶点位置和纹理坐标的顶点数据,以及用于绘制四边形的索引数据。顶点数据每个顶点包含5个元素(x, y, z, s, t),其中(x, y, z)是顶点位置,(s, t)是纹理坐标。
const vertices = new Float32Array([
-1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, 1.0, 0.0, 1.0, 1.0,
1.0, -1.0, 0.0, 1.0, 0.0,
]);
const indices = new Uint16Array([0, 1, 2, 1, 2, 3]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
5. 获取着色器中的变量位置并启用顶点属性数组
获取着色器中属性和统一变量的位置,并启用顶点属性数组。
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aTexCoord = gl.getAttribLocation(program, 'aTexCoord');
const uSampler = gl.getUniformLocation(program, 'uSampler');
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 20, 12);
gl.enableVertexAttribArray(aTexCoord);
6. 创建纹理对象并加载图片
创建纹理对象,加载图片并配置纹理参数。当图片加载完成时,配置纹理并调用绘制函数。
const texture = gl.createTexture();
const image = new Image();
image.src = '../asset/border.png'; // 这里需要指定背景图片的路径
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 翻转图片Y轴
// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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);
gl.uniform1i(uSampler, 0);
// 绘制场景
drawScene();
}
7. 绘制场景
清除颜色缓冲区,激活纹理单元并绑定纹理,绘制包含纹理的四边形。
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.activeTexture(gl.TEXTURE0); // 激活纹理单元
gl.bindTexture(gl.TEXTURE_2D, texture);
// 绘制背景
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
}
三.完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebGL Background Image</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
<script src="test.js"></script>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script>
// 获取WebGL上下文
const canvas = document.getElementById('canvas')
const gl = canvas.getContext('webgl')
if (!gl) {
console.error('Unable to initialize WebGL.')
} else {
// 定义顶点着色器和片段着色器
const vsSource = `
attribute vec4 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = aPosition;
vTexCoord = aTexCoord;
}
`
const fsSource = `
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uSampler;
void main() {
gl_FragColor = texture2D(uSampler, vTexCoord);
}
`
const program = initShader(gl, vsSource, fsSource)
gl.useProgram(program)
// 定义顶点数据
const vertices = new Float32Array([
-1.0, 1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, -1.0, 0.0, 1.0, 0.0
])
const indices = new Uint16Array([0, 1, 2, 1, 2, 3])
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
const indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)
// 获取着色器中的变量位置
const aPosition = gl.getAttribLocation(program, 'aPosition')
const aTexCoord = gl.getAttribLocation(program, 'aTexCoord')
const uSampler = gl.getUniformLocation(program, 'uSampler')
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 20, 0)
gl.enableVertexAttribArray(aPosition)
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 20, 12)
gl.enableVertexAttribArray(aTexCoord)
// 创建纹理对象
const texture = gl.createTexture()
const image = new Image()
image.src = '../asset/border.png' // 这里需要指定背景图片的路径
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1) // 翻转图片Y轴
// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
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)
gl.uniform1i(uSampler, 0)
// 绘制场景
drawScene()
}
// 绘制场景
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT)
gl.activeTexture(gl.TEXTURE0) // 激活纹理单元
gl.bindTexture(gl.TEXTURE_2D, texture)
// 绘制背景
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)
}
}
</script>
</body>
</html>