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

Posted

技术标签:

【中文标题】glDrawElements:绘制的源纹理和目标纹理相同【英文标题】:glDrawElements: Source and destination textures of the draw are the same 【发布时间】:2018-06-09 19:28:48 【问题描述】:

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

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

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

(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://***.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;
`;
&lt;canvas id="glcanvas" width="640" height="480"&gt;&lt;/canvas&gt;

【问题讨论】:

将纹理绑定到纹理单元是不够的,纹理单元的索引也必须设置为纹理采样器统一。例如gl.uniform1i(gl.getUniformLocation(peelShader, "DepthBlenderTex"), 0);gl.uniform1i(gl.getUniformLocation(peelShader, "FrontBlenderTex"), 1); 【参考方案1】:

GLSL 不允许在帧缓冲区中使用相同的纹理进行读取和写入(请参阅第 6.27 点https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.26)

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

【讨论】:

以上是关于glDrawElements:绘制的源纹理和目标纹理相同的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL - 使用 glDrawElements 错误地映射纹理

我的OpenGL学习进阶之旅使用glDrawElements绘制的时候,不绘制任何图元,报错 1282 即 GL_INVALID_OPERATION

我的OpenGL学习进阶之旅使用glDrawElements绘制的时候,不绘制任何图元,报错 1282 即 GL_INVALID_OPERATION

OpenGL:如果我将新的纹理绑定到另一个目标,纹理会发生啥?

应用纹理时 Collada 模型不渲染

将纹理应用到线框