渲染到立方体贴图的非零 mip 级别

Render to a non-zero mip level of a cube map

我正在尝试将环境贴图渲染到单独的立方体贴图 mip 级别。

const levels = 8;
const width = 128;
const height = 128;

const internalFormat = gl.RGBA8;
const type = gl.UNSIGNED_BYTE;
const format = gl.RGBA;

const bindingPoint = gl.TEXTURE_CUBE_MAP;

const level = 1;
const target = gl.TEXTURE_CUBE_MAP_POSITIVE_X + 0;

const magFilter = gl.LINEAR;
const minFilter = gl.LINEAR_MIPMAP_LINEAR;
const wrapS = gl.CLAMP_TO_EDGE;
const wrapT = gl.CLAMP_TO_EDGE;

const texId = gl.createTexture();

gl.bindTexture(bindingPoint, texId);

gl.texParameteri(bindingPoint, gl.TEXTURE_WRAP_S, wrapS);
gl.texParameteri(bindingPoint, gl.TEXTURE_WRAP_T, wrapT);
gl.texParameteri(bindingPoint, gl.TEXTURE_MIN_FILTER, minFilter);
gl.texParameteri(bindingPoint, gl.TEXTURE_MAG_FILTER, magFilter);

gl.texStorage2D(bindingPoint, levels, internalFormat, width, height);

const framebuffer = gl.createFramebuffer();

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, texId, level);

const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);

console.assert(status === gl.FRAMEBUFFER_COMPLETE, 'incomplete!');

gl.viewport(0, 0, width/2, height/2);

// cube rendering

毕竟,我通过 textureLod() 传递级别作为第三个参数在片段着色器中读取它。但结果是黑色而不是适当的环境颜色。

根据 this 它必须起作用,但它不起作用。

在 Chromium 70.0.3538.110 版本和 Firefox 63.0.3 上测试。

我没有发现您发布的代码有任何问题。它对我有用。也许问题出在其他地方?还是您的驱动程序中的错误?

const gl = document.createElement('canvas').getContext('webgl2');
console.assert(gl !== null, 'no webgl2');
const levels = 8;
const width = 128;
const height = 128;

const internalFormat = gl.RGBA8;
const type = gl.UNSIGNED_BYTE;
const format = gl.RGBA;

const bindingPoint = gl.TEXTURE_CUBE_MAP;

const magFilter = gl.LINEAR;
const minFilter = gl.LINEAR_MIPMAP_LINEAR;
const wrapS = gl.CLAMP_TO_EDGE;
const wrapT = gl.CLAMP_TO_EDGE;

const texId = gl.createTexture();

gl.bindTexture(bindingPoint, texId);

gl.texParameteri(bindingPoint, gl.TEXTURE_WRAP_S, wrapS);
gl.texParameteri(bindingPoint, gl.TEXTURE_WRAP_T, wrapT);
gl.texParameteri(bindingPoint, gl.TEXTURE_MIN_FILTER, minFilter);
gl.texParameteri(bindingPoint, gl.TEXTURE_MAG_FILTER, magFilter);

gl.texStorage2D(bindingPoint, levels, internalFormat, width, height);

const levelFaceFBs = [];
for (let level = 0; level < levels; ++level) {
  const faceFBs = [];
  for (let face = 0; face < 6; ++face) {
    const framebuffer = gl.createFramebuffer();
    const target = gl.TEXTURE_CUBE_MAP_POSITIVE_X + face;

    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

    gl.framebufferTexture2D(
       gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 
       target, texId, level);

    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);

    console.assert(status === gl.FRAMEBUFFER_COMPLETE, 'incomplete!');
    
    const c = face + level;
    gl.clearColor(
       c & 1 ? 1 : 0,
       c & 2 ? 1 : 0,
       c & 4 ? 1 : 0,
       (level + 1) / levels);
    gl.clear(gl.COLOR_BUFFER_BIT);
    
    faceFBs.push(framebuffer);
  }
  levelFaceFBs.push(faceFBs);
}

log('--> based on clear');
dumpAll();
renderToAll();
log('--> based on render');
dumpAll();
log(gl.getError(), '<- 0 = no errors')

function renderToAll() {
  const vs = `
  void main() {
    gl_PointSize = 10.0;
    gl_Position = vec4(-1, -1, 0, 1);
  }
  `;
  const fs = `
  precision mediump float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
  `;
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
  gl.useProgram(programInfo.program);

  // render to every face at every level
  levelFaceFBs.forEach((faceFBs, l) => {
    faceFBs.forEach((faceFB, f) => {
      gl.bindFramebuffer(gl.FRAMEBUFFER, faceFB);
      gl.viewport(0, 0, width >> l, height >> l);
      const c = f + l;
      twgl.setUniforms(programInfo, {
        color: [
          c & 1 ? .2 : .7,
          c & 2 ? .2 : .7,
          c & 4 ? .2 : .7,
          (levels - l) / levels,
        ],
      });
      gl.drawArrays(gl.POINTS, 0, 1);
    });
  });
}

function dumpAll() {
  // get color of every face at every level
  levelFaceFBs.forEach((faceFBs, l) => {
    faceFBs.forEach((faceFB, f) => {
      gl.bindFramebuffer(gl.FRAMEBUFFER, faceFB);
      const pixel = new Uint8Array(4);
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
      log('level:', l, 'face:', f, 'color:', pixel);
    });
  });
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
pre { margin: 0; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>