GL_INVALID_OPERATION 由 samplerCube 引起

GL_INVALID_OPERATION caused by samplerCube

我目前正在学习 WebGL,并且每次都用新的东西扩展我的代码。但是,此错误不断抛出:

[.WebGLRenderingContext-0111BCC8]GL ERROR :GL_INVALID_OPERATION : glFramebufferTexture2D: <- error from previous GL command

在我的 javascript 代码中,我设置了一个 uniform bool,无论我正在渲染的对象是否有反射(在我的渲染代码的前面,我已经创建了一个立方体贴图并渲染了对其进行反思)。当它有反射时,我还设置了活动纹理单元,绑定立方体贴图并设置uniform textureCube uReflectionMap,如下所示:

if (obj.reflects && obj.reflectionMap != null) {
    this.gl.activeTexture(this.gl.TEXTURE10);
    this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, obj.reflectionMap.glTexture);
    this.gl.uniform1i(p.uniforms["uReflectionMap"], 10);
    this.gl.uniform1i(p.uniforms["uReflects"], true);
} else {
    this.gl.uniform1i(p.uniforms["uReflects"], false);
}

我仅为这部分代码使用纹理单元 10(仅用于反射立方体贴图)

片段着色器代码:

if(uReflects){
    vec3 lookup = reflect(eyeDirection, normal);
    color += textureCube(uReflectionMap, -lookup); //no errors when this line is commented
}

当我评论上面的 'highlighted' 行时,一切正常(除了明显没有反射)。因此,我预计 if(uReflects) 是错误的(因此即使没有设置 uReflectionMap,这段代码也会执行。只需将注释行更改为 color += vec4(1.),并且只更改我设置的对象反射完全是白色的。

之后我尝试解决问题,将 uReflectionMap 设置为纹理单元 10(else 语句中的 this.gl.uniform1i(p.uniforms["uReflectionMap"], 10);),而不管我的对象是否有 reflectionMap。

这对我来说似乎很奇怪,因为当 uReflects 为 false 时不执行 textureCube 函数,但当未设置 uReflectionMap 时仍然会产生错误。

我希望这个问题是可以理解的,我有很多代码并且不知道我应该向这个问题添加什么(因为可能有其他干扰,我已经监督过了)。

我在 Google Chrome 43.0.2357.134 m 中完成了所有这些测试。只是在 IE 中 运行 它,它似乎给出了更详细的错误消息,尽管它的荷兰语写得如此糟糕以至于我不知道如何解释它,更不用说 t运行slate 了。 现在将尝试使用 firefox。Firefox 没有给出任何警告,但它也不起作用...


回复@gman:

这是我创建立方体贴图纹理的代码 ():

var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);


var size = 1024;

this.reflectionFrameBuffers = [];
this.reflectionRenderBuffers = [];

for (var i = 0; i < 6; i++) {
    gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

    this.reflectionRenderBuffers[i] = gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER, this.reflectionRenderBuffers[i]);
    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);


    this.reflectionFrameBuffers[i] = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, this.reflectionFrameBuffers[i]);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, texture, 0);
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.reflectionRenderBuffers[i]);

    gl.checkFramebufferStatus(gl.FRAMEBUFFER);//this throws no errors
}

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

这件作品在渲染前清除了所有面:

this.gl.colorMask(true, true, true, true);
this.gl.clearColor(0, 0, 0, 1);
this.gl.cullFace(this.gl.BACK);

for (var j = 0; j < 6; j++) {
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, obj.reflectionFrameBuffers[j]);
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
}

这是渲染到立方体贴图的代码(它被其他 for 循环和代码包围以提供 reflectionMapper 着色器):

for (var k = 0; k < 6; k++) {
    this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, obj.reflectionFrameBuffers[k]);
    this.gl.uniformMatrix4fv(p.uniforms["uViewMatrix"], false, obj.reflectionViewMatrices[k].array);//these view matrices determine which direction to look in
    this.gl.drawArrays(this.gl.TRIANGLES, 0, mesh.facesArray.length / 9);
}

问题是您有 2 个不同类型的统一采样器指向同一个纹理单元。

着色器通常执行所有路径。我的意思是如果你在你的着色器中有这个

if (someCondition) {
  a = value1;
} else {
  a = value2;
}

着色器中真正发生的事情类似于

a = value1 * (someCondition ? 1 : 0) + value2 * (someCondition ? 0 : 1);

或更好

// mix will be 1.0 if someCondition is true, else 0.0
float mix = step(0.5, 1.0 - float(someCondition)); 

a = value1 * mix + value2 * (1 - mix);

目前没有分店。 GPU 不分支,这就是它们获得速度的方式。请注意,该示例是我编造的,但它显示了无论条件如何都使用 value1 和 value2 的要点。假设条件是可变的。在你的例子中

uniform bool uIsCube;
uniform sampler2D uTwoD;
uniform samplerCube uCube;

varying vec3 vTexCoord;

void main(void){
    if(uIsCube){
        gl_FragColor = textureCube(uCube, vTexCoord);
    } else {
        gl_FragColor = texture2D(uTwoD, vTexCoord.st);
    }
}

uIsCube 是可变的。我不能在编译时知道,只有运行时,所以 uCubeuTwoD 总是被使用,需要使用 gl.uniform1i

设置为不同的纹理单元

如果另一方面,如果条件是这样的常量

#define IsCube false

void main(void){
    if(IsCube){
        gl_FragColor = textureCube(uCube, vTexCoord);
    } else {
        gl_FragColor = texture2D(uTwoD, vTexCoord.st);
    }
}

然后在编译时编译器会可能删除其中一个采样器。我说 可能 因为 IIRC 规范不需要驱动程序来优化着色器中未使用的代码。

另请注意,uniforms 都默认为 0,因此如果您不设置 uniforms,则两个采样器都将指向纹理单元。

您可以通过检查制服的位置来检查制服是否已被优化掉

var uCubeLoc = gl.getUniformLocation(program, "uCube");
if (!uCubeLoc) {
  // uCubeLoc does not exist or was optimized out
}

要查看您的代码中是否存在与指向同一纹理单元的不同类型的多个采样器相关的错误,您可以在每次 gl.draw___ 调用之前调用这样的函数。

function checkForConflictingSamplers() {
  var prg = gl.getParameter(gl.CURRENT_PROGRAM);
  var units = {};
  var numUniforms = gl.getProgramParameter(prg, gl.ACTIVE_UNIFORMS);

  function checkUniform(name, type) {
    var unit = gl.getUniform(prg, gl.getUniformLocation(prg, name));
    var unitInfo = units[unit];
    if (unitInfo === undefined) {
      units[unit] = { 
        type: type,
        name: name,
      };
    } else if (unitInfo.type !== type) {
      console.error("unit " + unit + " is being used by conflicting samplers " + name + " and " + unitInfo.name);
    }
  }    

  for (var ii = 0; ii < numUniforms; ++ii) {
    var uniformInfo = gl.getActiveUniform(prg, ii);
    if (!uniformInfo) {
      continue;
    }
    var name = uniformInfo.name;
    var type = uniformInfo.type;
    var isArray = (uniformInfo.size > 1 && name.substr(-3) === "[0]");
    // remove the array suffix.
    if (name.substr(-3) === "[0]") {
      name = name.substr(0, name.length - 3);
    }

    if (type === gl.SAMPLER_2D || type === gl.SAMPLER_CUBE) {
      if (isArray) {
        for (var ii = 0; ii < uniformInfo.size; ++ii) {
          checkUniform(name + "[" + ii + "]", type);
        }
      } else {
        checkUniform(name, type);
      }
    }
  }
}