如何在 webgl 中使用帧缓冲区?

2023-12-14

我一直在尝试了解 WebGL/OpenGL-ES 中的帧缓冲区。 我知道我们可以使用帧缓冲区混合多个纹理。

因此,为了理解我通过采用 1*1 纹理编写了一个示例,并尝试在其之上应用帧缓冲区逻辑。 但是,没有成功。

请参阅底部的片段,如果您单击“混合红色和蓝色”,图像不会渲染,我做错了什么吗?

Code : `

var canvas, gl, attrPosition, texture, program, vertexBuffer, textureBuffer, vertices, texVertices, attrPos, attrTexPos, textures = [], framebuffers = [];

canvas = document.getElementById('canvas');
    gl = getWebGL();
    vertices = new Float32Array([
        -1.0, -1.0,  
         1.0, -1.0, 
         1.0,  1.0, 
        -1.0,  1.0, 
        -1.0, -1.0, 
    ]);

    texVertices = new Float32Array([
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,
        0.0, 0.0
    ]);
    var getProgram = function () {
        var vs = createVertexShader([
            'attribute vec2 attrPos;',
            'attribute vec2 attrTexPos;',
            'varying highp vec2 vTexCoord;',
            'void main() {',
                '\tgl_Position = vec4(attrPos, 0.0, 1.0);',
            '}'
        ].join('\n'));

        var fs = createFragmentShader([
            'varying highp vec2 vTexCoord;',
            'uniform sampler2D uImage;',
            'void main() {',
                '\tgl_FragColor = texture2D(uImage, vTexCoord);',
            '}'
        ].join('\n'));
        return createAndLinkPrograms(vs, fs);
    };

    var render = function () {
        gl.clear(gl.DEPTH_BUFFER_BIT|gl.COLOR_BUFFER_BIT);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.vertexAttribPointer(attrPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
        gl.vertexAttribPointer(attrTexPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);
    };
    if (gl) {
        gl.clearColor(0.1, 0.5, 1.0, 1.0);
        render();
        program = getProgram();
        texture = createAndSetupTexture();
        vertexBuffer = createAndBindBuffer(vertices, gl.ARRAY_BUFFER);
        attrPos = gl.getUniformLocation(program, 'attrPos');
        gl.enableVertexAttribArray(attrPos);

        textureBuffer = createAndBindBuffer(texVertices, gl.ARRAY_BUFFER);
        attrTexPos = gl.getUniformLocation(program, 'attrTexPos');
        gl.enableVertexAttribArray(attrTexPos);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([123, 0, 60, 255]));
        render();
    }

    var initPingPongTextures = function(textures, framebuffers) {
        for (var i = 0; i < 2; ++i) {
            var tex = createAndSetupTexture(gl);
            textures.push(tex);
            // make the texture the same size as the image
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
            // Create a framebuffer
            var fbo = gl.createFramebuffer();
            framebuffers.push(fbo);
            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
            // Attach a texture to it.
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
        }
    }

    var setFramebuffer = function(fbo, width, height) {
        gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        gl.viewport(0, 0, width, height);
    };

    var mixRedAndBlue = function () {
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);

        setFramebuffer(framebuffers[0], 1, 1);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
        render();
        gl.bindTexture(gl.TEXTURE_2D, textures[0]);

        setFramebuffer(framebuffers[1], 1, 1);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 255, 0, 255]));
        render();
        gl.bindTexture(gl.TEXTURE_2D, textures[1]);

        setFramebuffer(null, 1, 1);
        render();
    };`
var getWebGLContext = function(canvas) {
	var webglContextParams = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
	var webglContext = null;
	for (var index = 0; index < webglContextParams.length; index++) {
		try {
			webglContext = canvas.getContext(webglContextParams[index]);
			if(webglContext) {
				//breaking as we got our context
				break;
			}
		} catch (E) {
			console.log(E);
		}
	}
	if(webglContext === null) {
		alert('WebGL is not supported on your browser.');
	} else {
		//WebGL is supported in your browser, lets render the texture
	}
	fillGLForCleanUp(webglContext);
	return webglContext;
}
var createVertexShader = function (vertexShaderSource) {
	console.log(vertexShaderSource);
	var vertexShader = gl.createShader(gl.VERTEX_SHADER);
	gl.shaderSource(vertexShader, vertexShaderSource);
	gl.compileShader(vertexShader);
	return vertexShader;
}

var createFragmentShader = function (fragmentShaderSource) {
	console.log(fragmentShaderSource);
	var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
	gl.shaderSource(fragmentShader, fragmentShaderSource);
	gl.compileShader(fragmentShader);
	return fragmentShader;
}


var createAndLinkPrograms = function (vertexShader, fragmentShader) {
	var program = gl.createProgram();
	gl.attachShader(program, vertexShader);
	gl.attachShader(program, fragmentShader);
	gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        alert('Could not initialise shaders');
    }
	gl.useProgram(program);
	return program;
}

var createAndBindBuffer = function (verticesOrIndices, bufferType) {
	var buffer = gl.createBuffer();
	gl.bindBuffer(bufferType, buffer);
	gl.bufferData(bufferType, verticesOrIndices, gl.STATIC_DRAW);
	//clear memory
	gl.bindBuffer(bufferType, null);
	return buffer;
}

var allowAllImageSizes = function() {
	  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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
	 // gl.bindTexture(gl.TEXTURE_2D, null);
} 

var createAndSetupTexture = function() {
	var texture = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, texture);
	allowAllImageSizes();
	gl.textures.push(texture);
	return texture;
}

var getWebGL = function (canvas, width, height) {
	if(!canvas) {
		canvas = document.createElement('canvas');
		canvas.id = 'canvas';
		canvas.width = !width ? 512 : width;
		canvas.height = !height ? 512 : height;
		document.body.appendChild(canvas);
	} else {
		canvas.width = !width ? 512 : width;
		canvas.height = !height ? 512 : height;
	}
	return getWebGLContext(canvas);
}

var fillGLForCleanUp = function (gl) {
	gl.textures = [];
	gl.framebuffers = [];
	gl.array_buffer = [];
	gl.element_array_buffers = [];
}


var canvas, gl, attrPosition, texture, program, 
vertexBuffer, textureBuffer, vertices, texVertices,
attrPos, attrTexPos, textures = [], framebuffers = [];
canvas = document.getElementById('canvas');
gl = getWebGL(canvas);
vertices = new Float32Array([
	-1.0, -1.0,  
	 1.0, -1.0, 
	 1.0,  1.0, 
	-1.0,  1.0, 
	-1.0, -1.0, 
]);

texVertices = new Float32Array([
	0.0, 0.0,
	1.0, 0.0,
	1.0, 1.0,
	0.0, 1.0,
	0.0, 0.0
]);
var getProgram = function () {
	var vs = createVertexShader([
		'attribute vec2 attrPos;',
		'attribute vec2 attrTexPos;',
		'varying highp vec2 vTexCoord;',
		'void main() {',
			'\tgl_Position = vec4(attrPos, 0.0, 1.0);',
		'}'
	].join('\n'));

	var fs = createFragmentShader([
		'varying highp vec2 vTexCoord;',
		'uniform sampler2D uImage;',
		'void main() {',
			'\tgl_FragColor = texture2D(uImage, vTexCoord);',
		'}'
	].join('\n'));
	return createAndLinkPrograms(vs, fs);
};

var render = function () {
	gl.clear(gl.DEPTH_BUFFER_BIT|gl.COLOR_BUFFER_BIT);
	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
	gl.vertexAttribPointer(attrPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
	gl.vertexAttribPointer(attrTexPos, 2, gl.FLOAT, gl.FALSE, 0, 0);
	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);
};
if (gl) {
	gl.clearColor(0.1, 0.5, 1.0, 1.0);
	render();
	program = getProgram();
	texture = createAndSetupTexture();
	vertexBuffer = createAndBindBuffer(vertices, gl.ARRAY_BUFFER);
	attrPos = gl.getUniformLocation(program, 'attrPos');
	gl.enableVertexAttribArray(attrPos);

	textureBuffer = createAndBindBuffer(texVertices, gl.ARRAY_BUFFER);
	attrTexPos = gl.getUniformLocation(program, 'attrTexPos');
	gl.enableVertexAttribArray(attrTexPos);

	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([123, 0, 60, 255]));
	render();
}

var initPingPongTextures = function(textures, framebuffers) {
	for (var i = 0; i < 2; ++i) {
		var tex = createAndSetupTexture(gl);
		textures.push(tex);
		// make the texture the same size as the image
		gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
		// Create a framebuffer
		var fbo = gl.createFramebuffer();
		framebuffers.push(fbo);
		gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
		// Attach a texture to it.
		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
	}
}

var setFramebuffer = function(fbo, width, height) {
	gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
	gl.viewport(0, 0, width, height);
};

var mixRedAndBlue = function () {
	gl.activeTexture(gl.TEXTURE0);
	gl.bindTexture(gl.TEXTURE_2D, texture);

	setFramebuffer(framebuffers[0], 1, 1);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
	render();
	gl.bindTexture(gl.TEXTURE_2D, textures[0]);

	setFramebuffer(framebuffers[1], 1, 1);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 255, 0, 255]));
	render();
	gl.bindTexture(gl.TEXTURE_2D, textures[1]);

	setFramebuffer(null, 1, 1);
	render();
};
<button id="redImg" onclick="mixRedAndBlue()">Mix Red and blue</button><hr/>
<canvas id="canvas" width=512 height=512></canvas>

Edit 1 :

我试图对具有多个片段着色器的多个程序实现相同的效果,因为不建议在片段着色器中使用 if/else 语句,因为它针对每个像素运行。

`
Shaders.prototype.VS_Base = [ '属性 vec3 顶点位置;', '属性 vec2 纹理位置;', '变化的 highp vec2 vTextureCoord;', '无效主(无效){', '\tgl_Position = vec4(verticesPosition * vec3(1.0, -1.0, 1.0), 0.5);', '\tvTextureCoord = 纹理位置;', '}' ].join('\n');

Shaders.prototype.FS_Base_Image_RED = [
        '#ifdef GL_ES',
        'precision highp float;',
        '#endif',
        'uniform sampler2D uImage;',
        'varying highp vec2 vTextureCoord;',
        'void main (void) {',
        '\tgl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);//texture2D(uImage, vTextureCoord);',
        '}'
].join('\n');

Shaders.prototype.FS_Base_Image_BLUE = [
        '#ifdef GL_ES',
        'precision highp float;',
        '#endif',
        'uniform sampler2D uImage;',
        'varying highp vec2 vTextureCoord;',
        'void main (void) {',
        '\tgl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);//texture2D(uImage, vTextureCoord);',
        '}'
].join('\n');`

现在我有两个单独的片段着色器程序,我需要使用帧缓冲区来混合红色和蓝色。我不是在寻找mix()因为实际场景非常复杂,这就是我使用带有片段着色器的多个程序来避免条件 if/else 语句的原因。


目前尚不清楚你想做什么。帧缓冲区只是附件(纹理和渲染缓冲区)的列表。您可以使用它们渲染到纹理和/或渲染缓冲区。然后,您可以使用刚刚渲染的纹理作为其他渲染的输入。

这是一个没有帧缓冲区的示例。它混合了 2 种纹理。

var vs = `
attribute vec4 position;

varying vec2 v_texcoord;

void main() {
  gl_Position = position;
  v_texcoord = position.xy * .5 + .5;
}
`;

var fs = `
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D tex1;
uniform sampler2D tex2;

void main() {
  vec4 color1 = texture2D(tex1, v_texcoord);
  vec4 color2 = texture2D(tex2, v_texcoord);
  gl_FragColor = mix(color1, color2, 0.5);
}
`;

const gl = document.querySelector("canvas").getContext("webgl");
const program = twgl.createProgramFromSources(gl, [vs, fs]);

// make 2 textures with canvas 2d
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 64;
ctx.canvas.height = 64;

// first texture has a circle
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, 64, 64);
ctx.strokeStyle = "yellow";
ctx.beginPath();
ctx.arc(32, 32, 20, 0, Math.PI * 2, false);
ctx.lineWidth = 12;
ctx.stroke();

const tex1 = createTextureFromCanvas(gl, ctx.canvas);

// second texture has a diamond (diagonal square)
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 64, 64);
ctx.fillStyle = "cyan";
ctx.beginPath();
ctx.moveTo(32, 6);
ctx.lineTo(58, 32);
ctx.lineTo(32, 58);
ctx.lineTo(6, 32);
ctx.lineTo(32, 6);
ctx.fill();

const tex2 = createTextureFromCanvas(gl, ctx.canvas);

const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1, -1,
   1, -1,
  -1,  1,
  -1,  1,
   1, -1,
   1,  1,
]), gl.STATIC_DRAW);


const positionLoc = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);

const tex1Loc = gl.getUniformLocation(program, "tex1");  
const tex2Loc = gl.getUniformLocation(program, "tex2");  
  
gl.useProgram(program);

gl.uniform1i(tex1Loc, 0);
gl.uniform1i(tex2Loc, 1);
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, tex1);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, tex2);

gl.drawArrays(gl.TRIANGLES, 0, 6);

function createTextureFromCanvas(gl, canvas) {
  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
  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);
  return tex;
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<canvas></canvas>

出于您的目的,混合部分没有区别,唯一的区别是纹理的来源。上面的纹理是使用 2d 画布创建的。相反,您可以使用帧缓冲区渲染到纹理。AFTER您已经渲染到纹理,然后可以在其他渲染中使用该纹理,就像上面一样。

要渲染到纹理,首先要创建一个帧缓冲区

var fb = gl.createFramebuffer();

然后你给它附加一个纹理

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);  
gl.framebufferTexture2D(
    gl.FRAMEBUFFER, 
    gl.COLOR_ATTACHMENT0,  // attach texture as COLOR_ATTACHMENT0
    gl.TEXTURE_2D,         // attach a 2D texture
    someTexture,           // the texture to attach
    0);                    // the mip level to render to (must be 0 in WebGL1)

根据您的附件,您应该检查它们是否有效。

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
  // these attachments don't work
}

WebGL 规范列出了 3 种保证有效的附件组合。下面的示例使用这 3 个之一,因此无需检查

现在如果你绑定帧缓冲区

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

然后当你调用任何gl.drawXXX函数或gl.clear它将绘制到someTexture而不是画布。要再次开始在画布上绘图,请绑定null

gl.bindFramebuffer(gl.FRAMEBUFFER, null);

请记住,如果画布和纹理的大小不同,您需要调用gl.viewport正确渲染

var vs = `
attribute vec4 position;

uniform mat4 matrix;

varying vec2 v_texcoord;

void main() {
  gl_Position = matrix * position;
  v_texcoord = position.xy * .5 + .5;
}
`;

var colorFS = `
precision mediump float;

uniform vec4 color;

void main() {
  gl_FragColor = color;
}
`;

var mixFS = `
precision mediump float;

varying vec2 v_texcoord;

uniform sampler2D tex1;
uniform sampler2D tex2;

void main() {
  // probably should use different texture coords for each
  // texture for more flexibility but I'm lazy
  vec4 color1 = texture2D(tex1, v_texcoord);
  vec4 color2 = texture2D(tex2, v_texcoord);
  gl_FragColor = mix(color1, color2, 0.5);
}
`;

const gl = document.querySelector("canvas").getContext("webgl");
const colorProgram = twgl.createProgramFromSources(gl, [vs, colorFS]);
const mixProgram = twgl.createProgramFromSources(gl, [vs, mixFS]);

// make 2 textures by attaching them to framebuffers and rendering to them
const texFbPair1 = createTextureAndFramebuffer(gl, 64, 64);
const texFbPair2 = createTextureAndFramebuffer(gl, 64, 64);

const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1, -1,
   1, -1,
  -1,  1,
  -1,  1,
   1, -1,
   1,  1,
]), gl.STATIC_DRAW);

function setAttributes(buf, positionLoc) {
  gl.enableVertexAttribArray(positionLoc);
  gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
}
  
const colorPrgPositionLoc = gl.getAttribLocation(colorProgram, "position");
setAttributes(buf, colorPrgPositionLoc);
const colorLoc = gl.getUniformLocation(colorProgram, "color");
const colorProgMatrixLoc = gl.getUniformLocation(colorProgram, "matrix");

// draw red rect to first texture through the framebuffer it's attached to
gl.useProgram(colorProgram);
  
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair1.fb);
gl.viewport(0, 0, 64, 64);
gl.uniform4fv(colorLoc, [1, 0, 0, 1]);
gl.uniformMatrix4fv(colorProgMatrixLoc, false, [
  0.5, 0, 0, 0,
    0,.25, 0, 0,
    0, 0, 1, 0,
   .2,.3, 0, 1,
]);

gl.drawArrays(gl.TRIANGLES, 0, 6);

// Draw a blue rect to the second texture through the framebuffer it's attached to
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair2.fb);
gl.viewport(0, 0, 64, 64);
gl.uniform4fv(colorLoc, [0, 0, 1, 1]);
gl.uniformMatrix4fv(colorProgMatrixLoc, false, [
  0.25, 0, 0, 0,
    0,.5, 0, 0,
    0, 0, 1, 0,
   .2,.3, 0, 1,
]);

gl.drawArrays(gl.TRIANGLES, 0, 6);

// Draw both textures to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
const mixPrgPositionLoc = gl.getAttribLocation(mixProgram, "position");
setAttributes(buf, mixPrgPositionLoc);
  
const mixProgMatrixLoc = gl.getUniformLocation(mixProgram, "matrix");

const tex1Loc = gl.getUniformLocation(mixProgram, "tex1");  
const tex2Loc = gl.getUniformLocation(mixProgram, "tex2");  
  
gl.useProgram(mixProgram);

gl.uniform1i(tex1Loc, 0);
gl.uniform1i(tex2Loc, 1);
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, texFbPair1.tex);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, texFbPair2.tex);
gl.uniformMatrix4fv(mixProgMatrixLoc, false, [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1,
]);  

gl.drawArrays(gl.TRIANGLES, 0, 6);

function createTextureAndFramebuffer(gl, width, height) {
  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, 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_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  gl.framebufferTexture2D(
     gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
  return {tex: tex, fb: fb};
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<canvas></canvas>

第一个程序和第二个程序之间唯一的功能区别是纹理如何获取数据。在第一个示例中,纹理从 2d 画布获取数据。在第二个示例中,纹理通过使用 WebGL 渲染来获取数据。

至于为什么您的示例不混合纹理,为了混合 2 个纹理,您需要一个使用两个纹理的着色器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 webgl 中使用帧缓冲区? 的相关文章

随机推荐

  • 由于缺少 sqlite3 gem,Rails 4.0rc1 应用程序未运行

    我第一次尝试安装 Rails 4 0 在带有 Ruby 2 0 x64 的 Windows 8 上 然 gem install rails version 4 0 0 rc1 no ri no rdoc 然后我运行 rails new te
  • 列表不可调用绘图

    这是给我错误的代码部分 我试图给绘图一个从 12 到 3 的范围 它是一个对数函数 所以它是 12 到 3 而不是 3 到 12 以防有人问 pp PdfPages BV V pdf plt plot BVcolor Vmag go plt
  • iPhone SDK 上的 PDF 操作

    我一直在阅读Apple的文档 他们推荐使用UIWebView 这一切都很好 直到您想要转到页码并搜索 PDF 是否有任何第三方库 免费或付费 可以执行此操作 至少我需要能够搜索并转到 PDF 中的页码 我查看过 PDFKit 不可用 并且
  • Laravel:让 Eloquent 创建嵌套 SELECT 的正确方法

    我试图雄辩地生成的查询是 SELECT SELECT COUNT comment id FROM comment AS c WHERE c approved true AND c blog fk b blog id AS comment c
  • 如何在 script# 中编写一个函数,以便使用任何对象调用,而不仅仅是定义它的类的实例?

    我正在 script 中编写 javascript 我想写一个看起来像的函数 function myFunc if this value gt 100 return true else return false 可以使用任何具有属性 val
  • Openshift - 我可以使用 Openshift 运行 docker 客户端命令(如 docker Push)吗?

    当您拥有 Docker 主机时 使用 Docker 会很容易 您将 Docker 客户端与 Docker 主机 引擎 连接 那么构建和部署 复杂的 docker 镜像的过程就像是 Jenkins 构建服务器上的这一系列命令 Maven cl
  • c中的for循环括号

    我编写了一个程序来使用 for 循环打印整数值 打印后 程序应该等待一秒钟 之后这些整数将被双空格字符覆盖 换句话说 程序的目的是在等待一秒后删除这些整数 这是程序 include
  • 在单个线性图中绘制多个字典/数据框

    我正在从多个词典中的某个来源收集数据 如下所示 d1 01 01 2018 15 02 01 2018 15 03 01 2018 15 d1 01 01 2018 20 02 01 2018 25 03 01 2018 56 d1 01
  • 在 .NET 中将字符串转换为 System.Color

    我正在尝试在 VB NET 上制作此应用程序 其中用户可以更改应用程序的背景颜色 当应用程序关闭时 颜色应保存到 XML 节省是小菜一碟 但现在真正的困境是如何转换字符串 Color white to a System Color 我已经用
  • addSnapShotListener 会触发所有依赖它的函数吗?

    据我了解 当 FireStore 中的数据发生变化时 AddsnapshotListener 会实时触发 但是 我不确定下面的代码中会触发哪个函数 只有函数C被触发吗 还是所有功能 如果所有函数都运行 函数 A 的参数会发生什么 func
  • Rails 不为生产或临时环境中的资产提供服务

    调试过程中这个问题 我尝试在本地生产模式下运行我的应用程序 但它不提供任何资产 另外 我有一个stagingHeroku 应用程序 与我的生产 Heroku 应用程序分开 中的环境现在也显示没有任何资产的 HTML 为了调试 我 杀死服务器
  • 如何向 symfony 会话添加额外的包

    我想为 symfony 会话添加一个额外的包 我在编译器通道中这样做 public function process ContainerBuilder container bag new AttributeBag my session at
  • 安装 python 时在 $PATH 中找不到可接受的 C 编译器

    我正在尝试在我的共享主机上安装新的 Python 环境 我按照中写的步骤操作这个帖子 mkdir src wget http www python org ftp python 2 7 1 Python 2 7 1 tgz tar zxvf
  • Oracle:加载一个大的 xml 文件?

    现在我有大量我感兴趣的 XML 数据 https blog stackoverflow com 2009 06 stack overflow creative commons data dump 我想将其加载到 Oracle 中来玩 如何将
  • 部分应用的功能[重复]

    这个问题在这里已经有答案了 在学习函数式编程时 部分应用函数的概念经常出现 在 Haskell 中 类似内置函数的东西take被认为是部分应用的 我仍然不清楚部分应用函数的确切含义或其使用 含义 函数本身不能 部分应用 或不 部分应用 这是
  • DjangoForeignKey 中的循环依赖?

    我在 Django 中有两个模型 A b ForeignKey B B a ForeignKey A 我希望这些外键不为空 但是 我无法创建对象 因为在我 save 之前它们没有 PrimaryKey 但如果没有其他对象 PrimaryKe
  • 使用“#”作为分隔符时 Read.table 不起作用?

    我有一个数据文件 符号作为分隔符 我想用read file命令 首先 这是一个大数据文件 我不想更改分隔符 因为 使用数据中已存在的不同分隔符的风险 注意 可以检查 但第 2 点使这变得更加复杂 我希望有更多这些数据文件 符号作为分隔符 所
  • 可扩展方式访问 ConcurrentHashMap 的每个元素一次

    我有 32 个机器线程和 1 个ConcurrentHashMap
  • 如何在canvas元素中使用html内容

    任何人都可以告诉我如何将我的 html 内容放在画布上 如果我们能做到这一点 这些元素的属性和事件是否有效 并且我还在该画布上绘制了动画 From MDN 上的这篇文章 您不能只将 HTML 绘制到画布中 相反 您需要使用 包含要渲染的内容
  • 如何在 webgl 中使用帧缓冲区?

    我一直在尝试了解 WebGL OpenGL ES 中的帧缓冲区 我知道我们可以使用帧缓冲区混合多个纹理 因此 为了理解我通过采用 1 1 纹理编写了一个示例 并尝试在其之上应用帧缓冲区逻辑 但是 没有成功 请参阅底部的片段 如果您单击 混合