glDrawElements:绘制的源和目标纹理是相同的

时间:2018-06-09 19:28:49

标签: javascript webgl framebuffer blending glsles

我正忙着将一些代码从OpenGL传输到WebGL2(进行深度剥离),但我在控制台中收到一条警告,我无法理解,输出只是黑色。

我已经完成了单独绘制一些缓冲区的过程,发现当我进行几何遍历时,警告只出现在循环for (var p = 1; p < numPasses; p++)内。

我所依赖的着色器也广泛使用了gl_NormalMatrixgl_ModelViewMatrixgl_Vertex,我认为这也可能是黑色输出的结果。我假设只用gl_ModelViewMatrix * gl_Vertex替换uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0);会产生相同的结果。

(function() {
  var script = document.createElement("script");
  script.onload = function() {
    main();
  };
  script.src = "https://mdn.github.io/webgl-examples/tutorial/gl-matrix.js";
  document.head.appendChild(script);
})();

var initShader, peelShader, blendShader, finalShader;
var accumTex0, accumTex1;
var backBlenderFBO, peelingSingleFBO;
var depthTex = [], frontBlenderTex = [], backTempTex = [], backBlenderTex = [];
var quadVAO;
var drawBuffers;

function main() {
  const canvas = document.querySelector("#glcanvas");
  const gl = canvas.getContext("webgl2", { alpha: false });
  if (!gl) {
    alert("Unable to initialize WebGL. Your browser or machine may not support it.");
    return;
  }
  var ext = gl.getExtension("EXT_color_buffer_float");
  if (!ext) { alert("Unable to initialize WebGL. Your browser or machine may not support it."); return; }

  quadVAO = newMesh(gl);
  drawBuffers = [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT3, gl.COLOR_ATTACHMENT4, gl.COLOR_ATTACHMENT5, gl.COLOR_ATTACHMENT6];

  // Dual Peeling Render Targets
  backBlenderTex = newTexture(gl, gl.TEXTURE_2D, gl.RGBA32F, 640, 480, gl.RGBA, gl.FLOAT, null);
  backBlenderFBO = newFramebuffer(gl, [backBlenderTex]);

  depthTex[0] = newTexture(gl, gl.TEXTURE_2D, gl.RG32F, 640, 480, gl.RG, gl.FLOAT, null);
  frontBlenderTex[0] = newTexture(gl, gl.TEXTURE_2D, gl.RGBA32F, 640, 480, gl.RGBA, gl.FLOAT, null);
  backTempTex[0] = newTexture(gl, gl.TEXTURE_2D, gl.RGBA32F, 640, 480, gl.RGBA, gl.FLOAT, null);
  depthTex[1] = newTexture(gl, gl.TEXTURE_2D, gl.RG32F, 640, 480, gl.RG, gl.FLOAT, null);
  frontBlenderTex[1] = newTexture(gl, gl.TEXTURE_2D, gl.RGBA32F, 640, 480, gl.RGBA, gl.FLOAT, null);
  backTempTex[1] = newTexture(gl, gl.TEXTURE_2D, gl.RGBA32F, 640, 480, gl.RGBA, gl.FLOAT, null);
  peelingSingleFBO = newFramebuffer(gl, [depthTex[0], frontBlenderTex[0], backTempTex[0], depthTex[1], frontBlenderTex[1], backTempTex[1], backBlenderTex]);

  bindFramebuffer(gl, null);

  initShader = newShader(gl, vsInitSource, fsInitSource);
  peelShader = newShader(gl, vsPeelSource, fsPeelSource);
  blendShader = newShader(gl, vsBlendSource, fsBlendSource);
  finalShader = newShader(gl, vsFinalSource, fsFinalSource);

  gl.disable(gl.CULL_FACE);

  draw(gl);
}

// See below link to make sense of this function
// https://stackoverflow.com/questions/37381980/get-some-trounble-when-using-drawbuffers-in-webgl2
function getDrawBuffers(gl, ...idx) {
  var buffers = [gl.NONE, gl.NONE, gl.NONE, gl.NONE, gl.NONE, gl.NONE, gl.NONE];
  for (var i = 0; i < idx.length; i++) {
    if (i == idx[i]) buffers[i] = drawBuffers[i];
  }
  return buffers;
}

function draw(gl) {
  // setup MVP
  const proj = mat4.create();
  const cameraSize = 0.2;
  mat4.ortho(proj, 0.0, 1.0, 0.0, 1.0, 0.0001, 10.0);

  const view = mat4.create();
  mat4.lookAt(view, [0, 0, 2], [0, 0, 0], [0, 1, 0]);
  
  gl.disable(gl.DEPTH_TEST);
  gl.enable(gl.BLEND);

  bindFramebuffer(gl, peelingSingleFBO);

  gl.drawBuffers(getDrawBuffers(gl, 1, 2));
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  const maxDepth = 1;
  gl.drawBuffers(getDrawBuffers(gl, 0));
  gl.clearColor(-maxDepth, -maxDepth, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.blendEquation(gl.MAX);

  // init
  // bindFramebuffer(gl, null); // to test with
  // gl.drawBuffers([gl.BACK]); // to test with
  gl.useProgram(initShader);
  drawMesh(gl, initShader, proj, view, { x: 0.0, y: 0.0, z: 0.0 }, { r: 1.0, g: 0.0, b: 0.0, a: 1.0 });
  gl.useProgram(null);
  // return; // to test with

  // peeling & blending
  gl.drawBuffers(getDrawBuffers(gl, 6));
  var backgroundColor = [1, 1, 1];
  gl.clearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // return; // to test with

  // for each pass
  var numPasses = 4;
  var currID = 0;
  for (var p = 1; p < numPasses; p++) {
    currID = p % 2;
    var prevID = 1 - currID;
    var bufID = currID * 3;

    gl.drawBuffers(getDrawBuffers(gl, bufID + 1, bufID + 2));
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.drawBuffers(getDrawBuffers(gl, bufID));
    gl.clearColor(-maxDepth, -maxDepth, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // all three blending render targets
    gl.drawBuffers(getDrawBuffers(gl, bufID, bufID + 1, bufID + 2));
    gl.blendEquation(gl.MAX);

    gl.useProgram(peelShader);
    bindTexture(gl, gl.TEXTURE0, gl.TEXTURE_2D, depthTex[prevID]); // DepthBlenderTex
    bindTexture(gl, gl.TEXTURE1, gl.TEXTURE_2D, frontBlenderTex[prevID]); // FrontBlenderTex
    gl.uniform1f(gl.getUniformLocation(peelShader, "uAlpha"), 0.6);
    drawMesh(gl, peelShader, proj, view, { x: 0.0, y: 0.0, z: 0.0 }, { r: 1.0, g: 0.0, b: 0.0, a: 1.0 });
    gl.useProgram(null);

    // alpha blend the back color
    gl.drawBuffers(getDrawBuffers(gl, 6));
    gl.blendEquation(gl.FUNC_ADD);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

    gl.useProgram(blendShader);
    bindTexture(gl, gl.TEXTURE0, gl.TEXTURE_2D, backTempTex[currID]); // TempTex
    drawFullscreenQuad(gl);
    gl.useProgram(null);
  }

  gl.disable(gl.BLEND);

  // final pass
  bindFramebuffer(gl, null);
  gl.drawBuffers([gl.BACK]);

  gl.useProgram(finalShader);
  bindTexture(gl, gl.TEXTURE0, gl.TEXTURE_2D, depthTex[currID]); // DepthBlenderTex
  bindTexture(gl, gl.TEXTURE1, gl.TEXTURE_2D, frontBlenderTex[currID]); // FrontBlenderTex
  bindTexture(gl, gl.TEXTURE2, gl.TEXTURE_2D, backBlenderTex); // BackBlenderTex
  drawFullscreenQuad(gl);

  gl.useProgram(null);
}

function newShader(gl, vsSource, fsSource) {
  const vertexShader = loadSource(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadSource(gl, gl.FRAGMENT_SHADER, fsSource);
  const shaderProgram = gl.createProgram();

  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert("Unable to initialize the shader program: " + gl.getProgramInfoLog(shaderProgram));
    return null;
  }
  return shaderProgram;
}

function loadSource(gl, type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}

function newMesh(gl) {
  var vertices = [1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0];
  var indicies = [0, 1, 3, 1, 2, 3];

  const vao = gl.createVertexArray();
  gl.bindVertexArray(vao);

  const vb = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vb);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

  const eb = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, eb);
  gl.bufferData(
    gl.ELEMENT_ARRAY_BUFFER,
    new Uint16Array(indicies),
    gl.STATIC_DRAW
  );

  gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 2 * 4, 0);
  gl.enableVertexAttribArray(null);

  gl.bindVertexArray(null);

  return vao;
}

function drawFullscreenQuad(gl) {
  gl.bindVertexArray(quadVAO);
  gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
  gl.bindVertexArray(null);
}
function drawMesh(gl, prog, proj, view, pos, col) {
  gl.uniformMatrix4fv(gl.getUniformLocation(prog, "uProjMatrix"), false, proj);
  gl.uniformMatrix4fv(gl.getUniformLocation(prog, "uViewMatrix"), false, view);

  gl.bindVertexArray(quadVAO);

  const model = mat4.create();
  var trans = vec3.create();
  vec3.set(trans, pos.x, pos.y, pos.z);
  mat4.translate(model, model, trans);
  gl.uniform4fv(gl.getUniformLocation(prog, "uColor"), [col.r, col.g, col.b, col.a]);
  gl.uniformMatrix4fv(gl.getUniformLocation(prog, "uModelMatrix"), false, model);
  gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
  gl.bindVertexArray(null);
}

function newTexture(gl, target, internalFormat, height, width, format, type, pixels) {
  var tid = gl.createTexture();
  gl.bindTexture(target, tid);
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, pixels);
  return tid;
}

function bindTexture(gl, idx, target, id) {
  gl.activeTexture(idx);
  gl.bindTexture(target, id);
  // wait should I be doing glUniforml1(id, ...) here?
}

function newFramebuffer(gl, colorAttachments) {
  var fib = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fib);
  for (var i = 0; i < colorAttachments.length; i++) {
    gl.framebufferTexture2D(
      gl.FRAMEBUFFER,
      drawBuffers[i],
      gl.TEXTURE_2D,
      colorAttachments[i],
      0
    );
  }
  if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
    alert(gl.checkFramebufferStatus(gl.FRAMEBUFFER).toString(16));
  }
  return fib;
}

function bindFramebuffer(gl, fib) {
  gl.bindFramebuffer(gl.FRAMEBUFFER, fib);
}

const vsInitSource = `#version 300 es
layout(location=0) in vec3 inVertexPosition;

uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjMatrix;

void main(void) {
  gl_Position = uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0);
}`;
const fsInitSource = `#version 300 es
precision mediump float;

layout(location=0) out vec2 outColor;

void main(void) {
  // This seems very important because it is based on the near/far values
  // What is the correct value I can expect here?
	outColor.xy = vec2(-gl_FragCoord.z, gl_FragCoord.z);
}`;
const vsPeelSource = `#version 300 es
layout(location=0) in vec3 inVertexPosition;

uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjMatrix;

// I believe this is what gives the model the green and white stripes
// Not required?
// vec3 ShadeVertex() {
// 	float diffuse = abs(normalize(gl_NormalMatrix * gl_Normal).z);
// 	return vec3(gl_Vertex.xy, diffuse);
// }

void main(void) {
  gl_Position = uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0);
	//gl_TexCoord[0].xyz = ShadeVertex();
}`;
const fsPeelSource = `#version 300 es
precision mediump float;

uniform float uAlpha;

#define COLOR_FREQ 30.0
#define ALPHA_FREQ 30.0

vec4 ShadeFragment() {
	vec4 color;
	color.rgb = vec3(.4,.85,.0);
	color.a = uAlpha;
	return color;
}

uniform sampler2D DepthBlenderTex;
uniform sampler2D FrontBlenderTex;

#define MAX_DEPTH 1.0

layout(location=0) out vec4 outFragData0;
layout(location=1) out vec4 outFragData1;
layout(location=2) out vec4 outFragData2;

void main(void) {
	// window-space depth interpolated linearly in screen space
	float fragDepth = gl_FragCoord.z;

	vec2 depthBlender = texture(DepthBlenderTex, gl_FragCoord.xy).xy;
	vec4 forwardTemp = texture(FrontBlenderTex, gl_FragCoord.xy);
	
	// Depths and 1.0-alphaMult always increase
	// so we can use pass-through by default with MAX blending
	outFragData0.xy = depthBlender;
	
	// Front colors always increase (DST += SRC*ALPHA_MULT)
	// so we can use pass-through by default with MAX blending
	outFragData1 = forwardTemp;
	
	// Because over blending makes color increase or decrease,
	// we cannot pass-through by default.
	// Each pass, only one fragment writes a color greater than 0
	outFragData2 = vec4(0.0);

	float nearestDepth = -depthBlender.x;
	float farthestDepth = depthBlender.y;
	float alphaMultiplier = 1.0 - forwardTemp.w;

	if (fragDepth < nearestDepth || fragDepth > farthestDepth) {
		// Skip this depth in the peeling algorithm
		outFragData0.xy = vec2(-MAX_DEPTH);
		return;
	}
	
	if (fragDepth > nearestDepth && fragDepth < farthestDepth) {
		// This fragment needs to be peeled again
		outFragData0.xy = vec2(-fragDepth, fragDepth);
		return;
	}
	
	// If we made it here, this fragment is on the peeled layer from last pass
	// therefore, we need to shade it, and make sure it is not peeled any farther
	vec4 color = ShadeFragment();
	outFragData0.xy = vec2(-MAX_DEPTH);
	
	if (fragDepth == nearestDepth) {
		outFragData1.xyz += color.rgb * color.a * alphaMultiplier;
		outFragData1.w = 1.0 - alphaMultiplier * (1.0 - color.a);
	} else {
		outFragData2 += color;
	}
}`;
const vsBlendSource = `#version 300 es
layout(location=0) in vec3 inVertexPosition;

uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjMatrix;

void main(void) {
  gl_Position = uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0);
}`;
const fsBlendSource = `#version 300 es
precision mediump float;

uniform sampler2D TempTex;

layout(location=0) out vec4 outColor;

void main(void) {
	outColor = texture(TempTex, gl_FragCoord.xy);
	// for occlusion query
	if (outColor.a == 0.0) discard;
}`;
const vsFinalSource = `#version 300 es
layout(location=0) in vec3 inVertexPosition;

uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjMatrix;

void main(void) {
  gl_Position = uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0);
}`;
const fsFinalSource = `#version 300 es
precision mediump float;

uniform sampler2D DepthBlenderTex;
uniform sampler2D FrontBlenderTex;
uniform sampler2D BackBlenderTex;

layout(location=0) out vec4 outColor;

void main(void)
{
	vec4 frontColor = texture(FrontBlenderTex, gl_FragCoord.xy);
	vec3 backColor = texture(BackBlenderTex, gl_FragCoord.xy).rgb;
	float alphaMultiplier = 1.0 - frontColor.w;

	// front + back
	outColor.rgb = frontColor.rgb + backColor * alphaMultiplier;
	
	// front blender
	// outColor.rgb = frontColor.rgb + vec3(alphaMultiplier);
	
	// back blender
	// outColor.rgb = backColor;
}`;
<canvas id="glcanvas" width="640" height="480"></canvas>

1 个答案:

答案 0 :(得分:0)

不允许在帧缓冲区中使用相同的Texture进行读写操作(请参见https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.26的Point 6.27)

但是您可以创建两个具有单独纹理的FrameBuffer,然后交换每个Frame。因此,您可以从最后一个渲染中读取并写入第二个Texture,完成后只需交换它即可。