双pass渲染案例(离线渲染一个三角面,然后渲染到一个占满屏幕的矩阵上)
离线渲染如何需要开启深度测试的话,需要额外操作,这里不展开
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL 2 Offline Rendering and Texture Display</title>
</head>
<body>
<canvas id="glCanvas" width="800" height="600"></canvas>
<script>
// 将 WebGL 初始化和渲染逻辑封装到一个函数中
function initWebGL() {
// 获取 canvas 元素
const canvas = document.getElementById('glCanvas');
// 获取 WebGL 2 上下文
const gl = canvas.getContext('webgl2');
// 如果无法获取 WebGL 2 上下文,给出提示并结束函数
if (!gl) {
alert('无法初始化 WebGL 2,你的浏览器可能不支持。');
return;
}
// 离线渲染的着色器代码
const offlineVertexShaderSource = `#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}
`;
const offlineFragmentShaderSource = `#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
// 最终渲染到屏幕的着色器代码
const finalVertexShaderSource = `#version 300 es
in vec2 a_position;
in vec2 a_texCoord;
out vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
}
`;
const finalFragmentShaderSource = `#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord);
}
`;
// 创建着色器函数
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
// 创建着色器程序函数
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
// 创建离线渲染的着色器程序
const offlineVertexShader = createShader(gl, gl.VERTEX_SHADER, offlineVertexShaderSource);
const offlineFragmentShader = createShader(gl, gl.FRAGMENT_SHADER, offlineFragmentShaderSource);
const offlineProgram = createProgram(gl, offlineVertexShader, offlineFragmentShader);
// 创建最终渲染的着色器程序
const finalVertexShader = createShader(gl, gl.VERTEX_SHADER, finalVertexShaderSource);
const finalFragmentShader = createShader(gl, gl.FRAGMENT_SHADER, finalFragmentShaderSource);
const finalProgram = createProgram(gl, finalVertexShader, finalFragmentShader);
// 离线渲染的顶点数据
const offlinePositions = [
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5
];
// 创建离线渲染的顶点缓冲区
const offlinePositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offlinePositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(offlinePositions), gl.STATIC_DRAW);
// 获取离线渲染的顶点属性位置
const offlinePositionAttributeLocation = gl.getAttribLocation(offlineProgram, 'a_position');
gl.enableVertexAttribArray(offlinePositionAttributeLocation);
gl.vertexAttribPointer(offlinePositionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
// 创建帧缓冲区对象(FBO)
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
// 创建纹理对象
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_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.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// 检查帧缓冲区是否完整
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
console.error('帧缓冲区不完整');
}
// 离线渲染
gl.useProgram(offlineProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// 最终渲染到屏幕的顶点数据和纹理坐标
const finalPositions = [
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0
];
const finalTexCoords = [
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0
];
// 创建最终渲染的顶点缓冲区和纹理坐标缓冲区
const finalPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, finalPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(finalPositions), gl.STATIC_DRAW);
const finalTexCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, finalTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(finalTexCoords), gl.STATIC_DRAW);
// 获取最终渲染的顶点属性位置和纹理坐标属性位置
const finalPositionAttributeLocation = gl.getAttribLocation(finalProgram, 'a_position');
gl.enableVertexAttribArray(finalPositionAttributeLocation);
gl.vertexAttribPointer(finalPositionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
const finalTexCoordAttributeLocation = gl.getAttribLocation(finalProgram, 'a_texCoord');
gl.enableVertexAttribArray(finalTexCoordAttributeLocation);
gl.vertexAttribPointer(finalTexCoordAttributeLocation, 2, gl.FLOAT, false, 0, 0);
// 获取最终渲染的纹理采样器位置
const textureUniformLocation = gl.getUniformLocation(finalProgram, 'u_texture');
// 最终渲染到屏幕
gl.useProgram(finalProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(textureUniformLocation, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
// 调用封装好的函数来初始化和渲染 WebGL
initWebGL();
</script>
</body>
</html>