WebGL:在带有深度模板纹理附件的帧缓冲区上未清除颜色

Posted

技术标签:

【中文标题】WebGL:在带有深度模板纹理附件的帧缓冲区上未清除颜色【英文标题】:WebGL: Color not cleared on framebuffer with depth stencil texture attachment 【发布时间】:2017-10-19 10:28:19 【问题描述】:

我尝试在启用模板的情况下绘制对象,一切正常。下面是我预期的工作结果的动画帧图片(从左到右)。

当我使用帧缓冲区时会出现问题。据我了解,WebGL 1.0 不支持单独附加模板和深度,既不支持渲染缓冲区也不支持纹理。可以通过WEBGL_depth_texture 扩展将模板和深度连接在一起来完成。我正在使用该扩展并使用帧缓冲区,绘制对象,但结果似乎并不清楚颜色。下面是结果的动画帧图片。

有人能解释一下发生了什么吗?

请浏览下面的完整代码。

(function() 
var gl;
var dtExt;

var gProgram;
var gRectShader;

var gVertexAttribLocation;
var gColorAttribLocation;

var gRectVertexAttribLocation;
var gRectTexcoordAttribLocation;

var gModelViewMatrixUniform;

var gTriangleVertexBuffer;
var gTriangleColorBuffer;
var gQuadVertexBuffer;
var gQuadColorBuffer;
var gQuadTexcoordBuffer;

var gFramebuffer;

var gColorTexture;
var gDepthStencilTexture;

var rotationMatrix = mat4.create();

function initGL() 
	var glcanvas = document.getElementById("glcanvas");
	gl = glcanvas.getContext("webgl", stencil:true);
	dtExt = gl.getExtension('WEBGL_depth_texture') || gl.getExtension('WEBKIT_WEBGL_depth_texture') || gl.getExtension('MOZ_WEBGL_depth_texture');


function initFramebuffers() 
	gFramebuffer = gl.createFramebuffer();

	gl.bindFramebuffer(gl.FRAMEBUFFER, gFramebuffer);

	gl.bindTexture(gl.TEXTURE_2D, gColorTexture);
	gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, gColorTexture, 0);

	gl.bindTexture(gl.TEXTURE_2D, gDepthStencilTexture);
	gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, gDepthStencilTexture, 0);

	gl.bindTexture(gl.TEXTURE_2D, null);
	gl.bindFramebuffer(gl.FRAMEBUFFER, null);


function createTexture() 
	var texture = gl.createTexture();

	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
	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.bindTexture(gl.TEXTURE_2D, null);

	return texture;


function initTextures() 
	gColorTexture = createTexture();
	gl.bindTexture(gl.TEXTURE_2D, gColorTexture);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

	gDepthStencilTexture = createTexture();
	gl.bindTexture(gl.TEXTURE_2D, gDepthStencilTexture);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_STENCIL, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.DEPTH_STENCIL, dtExt.UNSIGNED_INT_24_8_WEBGL, null);
	
	gl.bindTexture(gl.TEXTURE_2D, null);


function createAndCompileShader(type, source) 
	var shader = gl.createShader(type);

	gl.shaderSource(shader, source);
	gl.compileShader(shader);

	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) 
		throw new Error(gl.getShaderInfoLog(shader));
	

	return shader;


function createAndLinkProgram(glVertexShader, glFragmentShader) 
	var glProgram = gl.createProgram();

	gl.attachShader(glProgram, glVertexShader);
	gl.attachShader(glProgram, glFragmentShader);
	gl.linkProgram(glProgram);

	if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) 
	    throw new Error("Could not initialise shaders");
	

	return glProgram;


function initShaderPrograms() 
	var gVertexShader = createAndCompileShader(gl.VERTEX_SHADER, [
		"attribute vec3 a_vertex;",
		"attribute vec4 a_color;",

		"uniform mat4 u_modelViewMatrix;",

		"varying vec4 v_color;",

		"void main(void) ",
			"v_color = a_color;",
			"gl_Position = u_modelViewMatrix * vec4(a_vertex, 1.0);",
		""
	].join("\n"));

	var gFragmentShader = createAndCompileShader(gl.FRAGMENT_SHADER, [
		"precision mediump float;",

		"varying vec4 v_color;",
		"void main(void) ",
			"gl_FragColor = v_color;",
		""
	].join("\n"));

	gProgram = createAndLinkProgram(gVertexShader, gFragmentShader);

	var gVertexShader = createAndCompileShader(gl.VERTEX_SHADER, [
		"attribute vec3 a_vertex;",
		"attribute vec2 a_texcoord;",

		"varying vec2 v_texcoord;",

		"void main(void) ",
			"v_texcoord = a_texcoord;",
			"gl_Position = vec4(a_vertex, 1.0);",
		""
	].join("\n"));

	var gFragmentShader = createAndCompileShader(gl.FRAGMENT_SHADER, [
		"precision mediump float;",

	    "uniform sampler2D u_sampler0;",

		"varying vec2 v_texcoord;",
		"void main(void) ",
			"gl_FragColor = texture2D(u_sampler0, v_texcoord);",
		""
	].join("\n"));

	gRectShader = createAndLinkProgram(gVertexShader, gFragmentShader);


function initAttribAndUniformLocations() 
	gVertexAttribLocation = gl.getAttribLocation(gProgram, "a_vertex");
	gColorAttribLocation = gl.getAttribLocation(gProgram, "a_color");
	gModelViewMatrixUniform = gl.getUniformLocation(gProgram, 'u_modelViewMatrix');

	gRectVertexAttribLocation = gl.getAttribLocation(gRectShader, "a_vertex");
	gRectTexcoordAttribLocation = gl.getAttribLocation(gRectShader, "a_texcoord");


function initBuffers() 
	gTriangleVertexBuffer = gl.createBuffer();
	gTriangleColorBuffer = gl.createBuffer();
	gQuadVertexBuffer = gl.createBuffer();
	gQuadColorBuffer = gl.createBuffer();
	gQuadTexcoordBuffer = gl.createBuffer();

	gl.bindBuffer(gl.ARRAY_BUFFER, gTriangleVertexBuffer);
	var vertices = new Float32Array([

	     0.0, -1.0,  0.0,
	    -1.0,  1.0,  0.0,
	     1.0,  1.0,  0.0,

	     0.0,  1.0,  0.0,
	    -1.0, -1.0,  0.0,
	     1.0, -1.0,  0.0,
	]);
	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

	gl.bindBuffer(gl.ARRAY_BUFFER, gTriangleColorBuffer);
	var colors = new Float32Array([
	     0.0, 1.0,  0.0, 1.0,
	     0.0, 1.0,  0.0, 1.0,
	     0.0, 1.0,  0.0, 1.0,

	     0.0, 0.0,  1.0, 1.0,
	     0.0, 0.0,  1.0, 1.0,
	     0.0, 0.0,  1.0, 1.0
	]);
	gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);

	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadVertexBuffer);
	var vertices = new Float32Array([
	    -1.0,  1.0,  0.0,
	    -1.0, -1.0,  0.0,
	     1.0,  1.0,  0.0,
	     1.0, -1.0,  0.0
	]);
	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadColorBuffer);
	var colors = new Float32Array([
	     1.0, 0.0, 0.0, 1.0,
	     1.0, 0.0, 0.0, 1.0,
	     1.0, 0.0, 0.0, 1.0,
	     1.0, 0.0, 0.0, 1.0
	]);
	gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);

	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadTexcoordBuffer);
	var texcoords = new Float32Array([
	     0.0, 1.0,
	     0.0, 0.0,
	     1.0, 1.0,
	     1.0, 0.0
	]);
	gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.STATIC_DRAW);



function drawQuads() 
	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadVertexBuffer);
	gl.vertexAttribPointer(gVertexAttribLocation, 3, gl.FLOAT, false, 0, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadColorBuffer);
	gl.vertexAttribPointer(gColorAttribLocation, 4, gl.FLOAT, false, 0, 0);

	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);


function drawRectQuads() 
	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadVertexBuffer);
	gl.vertexAttribPointer(gRectVertexAttribLocation, 3, gl.FLOAT, false, 0, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER, gQuadTexcoordBuffer);
	gl.vertexAttribPointer(gRectTexcoordAttribLocation, 2, gl.FLOAT, false, 0, 0);

	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);


function drawTriagles() 
	gl.bindBuffer(gl.ARRAY_BUFFER, gTriangleVertexBuffer);
	gl.vertexAttribPointer(gVertexAttribLocation, 3, gl.FLOAT, false, 0, 0);

	gl.bindBuffer(gl.ARRAY_BUFFER, gTriangleColorBuffer);
	gl.vertexAttribPointer(gColorAttribLocation, 4, gl.FLOAT, false, 0, 0);

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



function renderScene() 
	var mvMatrix = mat4.create();

	gl.clearColor(0.5, 0.5, 0.5, 1.0);

	gl.bindFramebuffer(gl.FRAMEBUFFER, gFramebuffer);
	gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

	gl.enable(gl.STENCIL_TEST);
	gl.enable(gl.DEPTH_TEST);
	gl.enable(gl.CULL_FACE);

	gl.clear(gl.STENCIL_BUFFER_BIT);

	gl.useProgram(gProgram);

	gl.enableVertexAttribArray(gVertexAttribLocation);
	gl.enableVertexAttribArray(gColorAttribLocation);

	gl.disable(gl.DEPTH_TEST);
	gl.colorMask(false, false, false, false);

	gl.stencilFunc(gl.ALWAYS, 0, 0xff);
	gl.stencilMask(0xff);
	gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.INCR);
	gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.DECR);

	mat4.identity(mvMatrix);
	mat4.scale(mvMatrix, mvMatrix, [0.5, 0.5, 0.5]);
	mat4.multiply(mvMatrix, mvMatrix, rotationMatrix);

	gl.uniformMatrix4fv(gModelViewMatrixUniform, false, mvMatrix);

	gl.cullFace(gl.FRONT);
	drawTriagles();

	gl.cullFace(gl.BACK);
	drawTriagles();

	gl.stencilMask(0x00);
	gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);

	gl.enable(gl.DEPTH_TEST);
	gl.colorMask(true, true, true, true);

	mat4.identity(mvMatrix);
	mat4.scale(mvMatrix, mvMatrix, [0.75, 0.75, 0.75]);

	gl.uniformMatrix4fv(gModelViewMatrixUniform, false, mvMatrix);

	drawQuads();

	gl.disableVertexAttribArray(gVertexAttribLocation);
	gl.disableVertexAttribArray(gColorAttribLocation);

	gl.flush();

	gl.disable(gl.STENCIL_TEST);

	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
	gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

	gl.enable(gl.DEPTH_TEST);

	gl.bindTexture(gl.TEXTURE_2D, gColorTexture);

	gl.useProgram(gRectShader);

	gl.enableVertexAttribArray(gRectVertexAttribLocation);
	gl.enableVertexAttribArray(gRectTexcoordAttribLocation);

	drawRectQuads();

	gl.disableVertexAttribArray(gRectVertexAttribLocation);
	gl.disableVertexAttribArray(gRectTexcoordAttribLocation);

	gl.flush();


function step(timestamp) 
	renderScene();

	mat4.rotate(rotationMatrix, rotationMatrix, Math.PI / 360, [0, 0, 1])

    window.requestAnimationFrame(step);


initGL();
initShaderPrograms();
initAttribAndUniformLocations();
initTextures();
initFramebuffers();
initBuffers();
window.requestAnimationFrame(step);

());
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.4.0/gl-matrix-min.js"></script>

<canvas id="glcanvas"  >
		WebGL not supported!
</canvas>

【问题讨论】:

【参考方案1】:

在清除模板缓冲区之前需要设置模板掩码

gl.stencilMask(0xff);

另外,您不需要WEBGL_depth_texture 只是为帧缓冲区制作深度+模板附件。您可以使用 DEPTH_STENCIL 渲染缓冲区

const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb);

另外multiline template literals 可能会为您节省很多时间。

另外gl.flush 在代码中也没有任何意义。

【讨论】:

以上是关于WebGL:在带有深度模板纹理附件的帧缓冲区上未清除颜色的主要内容,如果未能解决你的问题,请参考以下文章

在 OpenGL ES 中为帧缓冲区使用深度和模板渲染缓冲区附件

Opengl 和 Webgl:从附加到当前帧缓冲区的纹理中采样

OpenGL 帧缓冲区 - 渲染到纹理创建带有主帧缓冲区内容的纹理

是否可以使用带有立方体贴图颜色附件的深度渲染缓冲区?

WebGL2:不完整的帧缓冲区

如何在 webgl2 中使用深度纹理