3

I'm busy transferring some code from OpenGL to WebGL2 (to do duel depth peeling) but I'm getting a warning in my console that I cannot make sense of and the output is just black.

I've gone through the process of drawing some of the buffers individually and discovered the warning only appears inside the loop for (var p = 1; p < numPasses; p++) when I do the geometry passes.

The shaders I based mine off also made extensive use of gl_NormalMatrix, gl_ModelViewMatrix, and gl_Vertex that I think could also be a result of the black output. I assume just replacing gl_ModelViewMatrix * gl_Vertex with uProjMatrix * uViewMatrix * uModelMatrix * vec4(inVertexPosition, 1.0); will yield the same result.

(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>
Fanus du Toit
  • 926
  • 1
  • 10
  • 26
  • 1
    It is not sufficient to bind the texture to a texture unit, the index of the texture unit has to be set to the texture sampler uniform, too. e.g. `gl.uniform1i(gl.getUniformLocation(peelShader, "DepthBlenderTex"), 0);` `gl.uniform1i(gl.getUniformLocation(peelShader, "FrontBlenderTex"), 1);` – Rabbid76 Jun 09 '18 at 20:13

1 Answers1

2

Using the same Texture for reading and writing in the Framebuffer is not alowed GLSL (see Point 6.27 at https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.26)

But you can create two FrameBuffers with seperate textures and swap then every Frame. So you can read from the last rendered and write to the second Texture and when done simply swap it.

Foxel
  • 21
  • 5