gl.DEPTH_COMPONENT可以用作立方体贴图的格式吗?

时间:2017-04-26 13:01:15

标签: webgl

在尝试创建可在移动平台上运行的VSM阴影时,我正在探索24位深度纹理存储瞬间的可能性(某些移动平台不支持浮点纹理)。

问题是我需要带阴影的全景灯,这意味着我需要立方体贴图(理想情况下)。至少firefox似乎不支持这一点,将Error: WebGL warning: texImage2D: With format DEPTH_COMPONENT24, this function may only be called with target=TEXTURE_2D, data=null, and level=0.打印到控制台。

我用DEPTH_COMPONENT作为格式和内部格式调用gl.texImage2D。对于类型I,尝试过gl.UNSIGNED_SHORT,gl.UNSIGNED_INT和ext.UNSIGNED_INT_24_8_WEBGL,都无济于事。

我可以将立方体的边缘映射到2d纹理,并为每边添加边距以避免插值伪影,但这似乎过于复杂且难以维护。

是否有其他解决方法可以使用DEPTH_COMPONENT格式的采样器多维数据集?

这适用于WebGL 1

编辑:我在gman的答案中对代码做了一些修改,以更好地反映我的问题。 Here是一个小人物。它似乎适用于chrome(红色背景上的深红色立方体),但不适用于Firefox(一切都是黑色)。

1 个答案:

答案 0 :(得分:2)

如果要使用深度纹理,则需要尝试启用WEBGL_depth_texture extension。请注意many mobile devices don't support depth textures。 (单击左上角的过滤器)

然后,according to the spec,您不能将DEPTH_COMPONENT24传递给texImage2D。在传递DEPTH_COMPONENT和类型gl.UNSIGNED_SHORTgl.UNSIGNED_INT中,实现选择位深度。您可以通过致电gl.getParameter(gl.DEPTH_BITS);

来查看您获得的分辨率



function main() {
  const m4 = twgl.m4;
  const v3 = twgl.v3;
  const gl = document.querySelector("canvas").getContext("webgl");
  const ext = gl.getExtension("WEBGL_depth_texture");
  if (!ext) {
    alert("Need WEBGL_depth_texture");
    return;
  }

  const width = 128;
  const height = 128;
  const depthTex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, depthTex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, 
                gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
  // calls gl.bindTexture, gl.texParameteri
  twgl.setTextureParameters(gl, depthTex, {
    minMag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
  });

  // calls gl.createTexture, gl.bindTexture, gl.texImage2D, gl.texParameteri
  const cubeTex = twgl.createTexture(gl, {
    target: gl.TEXTURE_CUBE_MAP,
    minMag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
    width: width,
    height: height,
  });
  const faces = [
    gl.TEXTURE_CUBE_MAP_POSITIVE_X,  
    gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
  ];
  const fbs = faces.map(face => {
    const fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, cubeTex, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);
    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status !== gl.FRAMEBUFFER_COMPLETE) {
      console.log("can't use this framebuffer attachment combo");
    }
    return fb;
  });  

  const vs = `
  attribute vec4 position;
  attribute vec3 normal;
  uniform mat4 u_worldViewProjection;
  uniform mat4 u_worldInverseTranspose;
  varying vec3 v_normal;
  void main() {
    gl_Position = u_worldViewProjection * position;
    v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
  }
  `;
  const fs = `
  precision mediump float; 
  uniform vec3 u_color;
  uniform vec3 u_lightDir;
  varying vec3 v_normal;
  void main() {
    float light = dot(u_lightDir, normalize(v_normal)) * .5 + .5;
    gl_FragColor = vec4(u_color * light, 1);
  }
  `;
  
  const vs2 = `
  attribute vec4 position;
  uniform mat4 u_matrix;
  varying vec3 v_texcoord;
  void main() {
    gl_Position = u_matrix * position;
    v_texcoord = position.xyz;
  }
  `;
  const fs2 = `
  precision mediump float; 
  uniform samplerCube u_cube;
  varying vec3 v_texcoord;
  void main() {
    gl_FragColor = textureCube(u_cube, normalize(v_texcoord));
  }
  `;


  // compile shaders, links program, looks up locations
  const colorProgramInfo = twgl.createProgramInfo(gl, [vs, fs]);
  // compile shaders, links program, looks up locations
  const cubeProgramInfo = twgl.createProgramInfo(gl, [vs2, fs2]);
  // calls gl.createBuffer, gl.bindBuffer, gl.bufferData
  const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl);
  
  function render(time) {
    time *= 0.001;  // seconds
    
    gl.enable(gl.DEPTH_TEST);

    gl.useProgram(colorProgramInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
    twgl.setBuffersAndAttributes(gl, colorProgramInfo, cubeBufferInfo);

    // draw a different color on each face
    faces.forEach((face, ndx) => {
      const c = ndx + 1;
      const color = [
        (c & 0x1) ? 1 : 0,
        (c & 0x2) ? 1 : 0,
        (c & 0x4) ? 1 : 0,
      ];      
      gl.bindFramebuffer(gl.FRAMEBUFFER, fbs[ndx]);
      gl.viewport(0, 0, width, height);
      gl.clearColor(1 - color[0], 1 - color[1], 1 - color[2], 1);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      const fov = Math.PI * 0.25;
      const aspect = width / height;
      const zNear = 0.001;
      const zFar = 100;
      const projection = m4.perspective(fov, aspect, zNear, zFar);
      const world = m4.translation([0, 0, -3]);
      m4.rotateY(world, Math.PI * .1  * c * time, world);
      m4.rotateX(world, Math.PI * .15 * c * time, world);

      // calls gl.uniformXXX
      twgl.setUniforms(colorProgramInfo, {
         u_color: color,
         u_lightDir: v3.normalize([1, 5, 10]),
         u_worldViewProjection: m4.multiply(projection, world),
         u_worldInverseTranspose: m4.transpose(m4.inverse(world)),
      });

      // calls gl.drawArrays or gl.drawElements
      twgl.drawBufferInfo(gl, cubeBufferInfo);
    });

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(cubeProgramInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
    twgl.setBuffersAndAttributes(gl, cubeProgramInfo, cubeBufferInfo);

    const fov = Math.PI * 0.25;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.001;
    const zFar = 10;
    const mat = m4.perspective(fov, aspect, zNear, zFar);
    m4.translate(mat, [0, 0, -2], mat);
    m4.rotateY(mat, Math.PI * .25 * time, mat);
    m4.rotateX(mat, Math.PI * .25 * time, mat);

    twgl.setUniforms(cubeProgramInfo, {
      u_cube: cubeTex,
      u_matrix: mat,
    });

    // calls gl.drawArrays or gl.drawElements
    twgl.drawBufferInfo(gl, cubeBufferInfo);
    
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();

canvas { border: 1px solid black; }

<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
&#13;
&#13;
&#13;

否则您可以使用深度渲染缓冲区。 Where's an example code is herecode that creates the framebuffers for the cubemap is here

the spec

更新

对于立方体贴图深度纹理here,具体说只支持TEXTURE_2D

  

在以下情况下会生成错误INVALID_OPERATION:

     
      使用DEPTH_COMPONENT的格式和内部格式调用
  • texImage2D   或DEPTH_STENCIL和目标不是TEXTURE_2D,
  •   

您可能必须切换到WebGL2。它适用于firefox和chrome

&#13;
&#13;
function main() {
  const m4 = twgl.m4;
  const v3 = twgl.v3;
  const gl = document.querySelector("canvas").getContext("webgl2");

  const width = 128;
  const height = 128;
  const colorTex = twgl.createTexture(gl, {
    target: gl.TEXTURE_CUBE_MAP,
    minMag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
    width: width,
    height: height,
  });

  // calls gl.createTexture, gl.bindTexture, gl.texImage2D, gl.texParameteri
  const depthTex = twgl.createTexture(gl, {
    target: gl.TEXTURE_CUBE_MAP,
    internalFormat: gl.DEPTH_COMPONENT24,
    format: gl.DEPTH_COMPONENT,
    type: gl.UNSIGNED_INT,
    width: width,
    height: height,
    wrap: gl.CLAMP_TO_EDGE,
    minMax: gl.NEAREST,
  });

  const faces = [
    gl.TEXTURE_CUBE_MAP_POSITIVE_X,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
    gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
    gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
  ];

  const fbs = faces.map(face => {
    const fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, colorTex, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, face, depthTex, 0);
    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status !== gl.FRAMEBUFFER_COMPLETE) {
      console.log("can't use this framebuffer attachment combo");
    }
    return fb;
  });

  const vs = `
attribute vec4 position;
attribute vec3 normal;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * position;
gl_Position.z = 0.5;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
}
`;
  const fs = `
precision mediump float; 
uniform vec3 u_color;
uniform vec3 u_lightDir;
varying vec3 v_normal;
void main() {
float light = dot(u_lightDir, normalize(v_normal)) * .5 + .5;
gl_FragColor = vec4(u_color * light, 1);
}
`;

  const vs2 = `
attribute vec4 position;
uniform mat4 u_matrix;
varying vec3 v_texcoord;
void main() {
gl_Position = u_matrix * position;
v_texcoord = position.xyz;
}
`;
  const fs2 = `
precision mediump float; 
uniform samplerCube u_cube;
varying vec3 v_texcoord;
void main() {
gl_FragColor = textureCube(u_cube, normalize(v_texcoord)) / vec4(2.0, 1.0, 1.0, 1.0);
}
`;


  // compile shaders, links program, looks up locations
  const colorProgramInfo = twgl.createProgramInfo(gl, [vs, fs]);
  // compile shaders, links program, looks up locations
  const cubeProgramInfo = twgl.createProgramInfo(gl, [vs2, fs2]);
  // calls gl.createBuffer, gl.bindBuffer, gl.bufferData
  const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl);

  function render(time) {
    time *= 0.001; // seconds

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

    gl.useProgram(colorProgramInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
    twgl.setBuffersAndAttributes(gl, colorProgramInfo, cubeBufferInfo);

    // draw a different color on each face
    faces.forEach((face, ndx) => {
      const c = ndx + 1;
      const color = [
        (c & 0x1) ? 1 : 0,
        (c & 0x2) ? 1 : 0,
        (c & 0x4) ? 1 : 0,
      ];
      gl.bindFramebuffer(gl.FRAMEBUFFER, fbs[ndx]);
      gl.viewport(0, 0, width, height);
      gl.clearColor(1 - color[0], 1 - color[1], 1 - color[2], 1);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      const fov = Math.PI * 0.25;
      const aspect = width / height;
      const zNear = 0.001;
      const zFar = 100;
      const projection = m4.perspective(fov, aspect, zNear, zFar);
      const world = m4.translation([0, 0, -3]);
      m4.rotateY(world, Math.PI * .1 * c * time, world);
      m4.rotateX(world, Math.PI * .15 * c * time, world);

      // calls gl.uniformXXX
      twgl.setUniforms(colorProgramInfo, {
        u_color: color,
        u_lightDir: v3.normalize([1, 5, 10]),
        u_worldViewProjection: m4.multiply(projection, world),
        u_worldInverseTranspose: m4.transpose(m4.inverse(world)),
      });

      // calls gl.drawArrays or gl.drawElements
      twgl.drawBufferInfo(gl, cubeBufferInfo);
    });

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(cubeProgramInfo.program);
    // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
    twgl.setBuffersAndAttributes(gl, cubeProgramInfo, cubeBufferInfo);

    const fov = Math.PI * 0.25;
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.001;
    const zFar = 10;
    const mat = m4.perspective(fov, aspect, zNear, zFar);
    m4.translate(mat, [0, 0, -2], mat);
    m4.rotateY(mat, Math.PI * .25 * time, mat);
    m4.rotateX(mat, Math.PI * .25 * time, mat);

    twgl.setUniforms(cubeProgramInfo, {
      u_cube: colorTex,
      u_matrix: mat,
    });

    // calls gl.drawArrays or gl.drawElements
    twgl.drawBufferInfo(gl, cubeBufferInfo);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();
&#13;
canvas { border: 1px solid black; }
&#13;
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
&#13;
&#13;
&#13;