Published
Edited
Jul 13, 2022
1 fork
Insert cell
md`# WebGL Triangle #7: FBO`
Insert cell
canvas = DOM.canvas(width, height);
Insert cell
{
// clear
gl.clearColor(1., 1., 1., 1.);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const offscreenContainer = createCanvasTextureAndFrameBuffer(width, height);
// render to FBO
gl.bindFramebuffer(gl.FRAMEBUFFER, offscreenContainer.frameBuffer);
draw()
// render to screen
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, offscreenContainer.texture);
drawTexture(offscreenContainer.texture);
}
Insert cell
function createCanvasTextureAndFrameBuffer(width, height) {
// create to render to
const targetTextureWidth = width;
const targetTextureHeight = height;
const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);

{
// define size and format of level 0
const level = 0;
const internalFormat = gl.RGBA;
const border = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
const data = null;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
targetTextureWidth, targetTextureHeight, border,
format, type, data);

// set the filtering so we don't need mips
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);
}
// Create and bind the framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

// attach the texture as the first color attachment
const attachmentPoint = gl.COLOR_ATTACHMENT0;
const level = 0;
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);

return {
texture: targetTexture,
frameBuffer: fb
}
}
Insert cell
function drawTexture(texture) {
// use program
gl.useProgram(program);

// clear
gl.clearColor(1., 1., 1., 1.);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const positionBuffer = createBuffer(gl, [
0, 0, width, 0, 0, height,
0, height, width, 0, width, height
]);
const texCoordBuffer = createBuffer(gl, [
0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 1,
])

{
// connect buffers and attributes
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
attributeLocations.position,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(attributeLocations.position);
}
{
// connect buffers and attributes
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(
attributeLocations.texCoord,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(attributeLocations.texCoord);
}
{
// connect uniforms
gl.uniform2f(uniformLocations.resolution, width, height);
gl.uniform4fv(uniformLocations.color, new Float32Array(data.color));
gl.uniformMatrix4fv(uniformLocations.transform, false, new Float32Array(transform));

}

{
// bind textures
const textureUnit = 0;
// Bind the texture to texture unit 0
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(uniformLocations.sampler, textureUnit);
}

// draw
const offset = 0;
const vertexCount = 6;
// gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
gl.drawArrays(gl.TRIANGLES, offset, vertexCount)
}
Insert cell
function draw() {

// use program
gl.useProgram(program);
// clear
gl.clearColor(1., 1., 1., 1.);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

{
// connect buffers and attributes
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
attributeLocations.position,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(attributeLocations.position);
}
{
// connect buffers and attributes
const numComponents = 2;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(
attributeLocations.texCoord,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(attributeLocations.texCoord);
}
{
// connect uniforms
gl.uniform2f(uniformLocations.resolution, width, height);
gl.uniform4fv(uniformLocations.color, new Float32Array(data.color));
gl.uniformMatrix4fv(uniformLocations.transform, false, new Float32Array(transform));

}

{
// bind textures
const textureUnit = 0;
// Bind the texture to texture unit 0
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(uniformLocations.sampler, textureUnit);
}

// draw
const offset = 0;
const vertexCount = 3;
// gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
gl.drawArrays(gl.TRIANGLES, offset, vertexCount)
}
Insert cell
texture = loadTexture(gl,
'https://brickmaker.static.observableusercontent.com/files/63737287a6c197978aa17abbc1e7e0db452e1303fe10d9efdacada95dccb25fc8bdd73c77c4a95d0ed7920d02feea995f4ce5558d3c6f7dc77bdce4a62f28de8?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27%25E4%25B8%2589%25E5%259B%25BD%25E6%259D%2580%25E7%2595%258C%25E5%2588%2598%25E5%25A4%2587.jpg&Expires=1657756800&Key-Pair-Id=APKAIQVKCV7GBKVBR2KA&Signature=YIlEurNqAJpVmCHiRgfXrdoTNq7WWvMmU6qarzJzvKqxdtBqbr26Q0taR02HL44dgRj~xWEXj~8WVRUoKX-AneKn14d2lytp7ditGEydTsUxluzJnSPVB8NrRsta5cCgWUUz9rotyWUJcxcJ6umOCog4mCOn7KDepPRzU12A8JtZCcX~f~bK4pYRkzoD1uIiZEOEp~65GrNShCUtXvZjNFCkxOy5v7VFXMqgILoTgF3n~MEWQxVv0uvJ407kRea8j4ZI5vI3XNh8z25yty1SNSiYF6F66PWeSovL4BdrTKIudmqg7XUpN8aJS3xsjUozTkufdTuEWI7H2UkUurwE8g__')
Insert cell
Insert cell
Insert cell
positionBuffer = createBuffer(gl, data.positions)
Insert cell
texCoords = [0, 1, 1, 1, 0.5, 0.]
Insert cell
texCoordBuffer = createBuffer(gl, texCoords)
Insert cell
attributeLocations = {
return {
position: gl.getAttribLocation(program, "in_position"),
texCoord: gl.getAttribLocation(program, "in_texCoord")
}
}
Insert cell
uniformLocations = {
return {
sampler: gl.getUniformLocation(program, 'u_sampler'),
resolution: gl.getUniformLocation(program, "u_resolution"),
transform: gl.getUniformLocation(program, "u_transform"),
color: gl.getUniformLocation(program, "u_color")
}
}
Insert cell
program = createProgram(gl, vertexShaderSource, fragmentShaderSource)
Insert cell
gl = canvas.getContext('webgl2')
Insert cell
height = width * 0.75
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more