WebGLCubeRenderTarget : render to a specific mipmap level

I’m trying to bake cubemap in different mipmap levels. I found the option setRenderTarget on WebGLRenderer but it didn’t found a way to setup the WebGLCubeRenderTarget texture properly to support multi mipmal level.

In my example, the first Lod level is rendered but I have this error

[.WebGL-0x252803b8c700] GL_INVALID_FRAMEBUFFER_OPERATION: Framebuffer is incomplete: Attachment has zero size.

This certainly means there no allocate storage in texture for other mipmaps level.

 wrapCubeFromTexture(
    source: Texture,
    size: number,
    lodLayouts: CubemapWrapperLayout[]
  ) {
    this._material.uniforms.sourceTexture.value = source
    this._material.needsUpdate = true
    const oldRenderTarget = this.renderer.getRenderTarget()

    const textureLevels: CubeTexture[] = []
    const renderTarget = new WebGLCubeRenderTarget(size, {
      format: source.format,
      type: source.type,
      ...this.renderTargetOptions,
      depthBuffer: false,
      generateMipmaps: false,
    })


    for (let lod = 0; lod < lodLayouts.length; lod++) {
      const layout = lodLayouts[lod]
      const lodSize = size / Math.pow(2, lod)

      renderTarget.viewport.set(0, 0, lodSize, lodSize)

      const geometry = this.createGeometry(layout)
      const mesh = new Mesh(geometry, this._material)
      for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
        geometry.drawRange.start = faceIndex * 6
        this.renderer.setRenderTarget(renderTarget, faceIndex, lod)
        this.renderer.render(mesh, this._camera)
      }

      geometry.dispose()

      textureLevels.push(renderTarget.texture)
    }


    const cubemap = textureLevels.shift() as CubeTexture

    this.renderer.setRenderTarget(oldRenderTarget)
    console.log('cubemap', cubemap)
    return cubemap
  }
}

I think the official example webgl_materials_cubemap_render_to_mipmaps should do what you are looking for. Can you use it as a code template?

Thanks,

I didn’t have the last version of three JS, mipmap option was missing from cube camera. I didn’t want to use it as I’m just wrapping cubemaps from 2D texture faces sheet.

Here is my implementation which is quite different from the example use case.

wrapCubeLodFromTexture(
    source: Texture,
    cubeMapSize: number,
    lodLayouts: CubemapWrapperLayout[]
  ) {
    this._material.uniforms.sourceTexture.value = source

    const renderTarget = new WebGLCubeRenderTarget(cubeMapSize, {
      format: source.format,
      type: source.type,
      colorSpace: source.colorSpace,

      ...this.renderTargetOptions,
      
      magFilter: LinearFilter,
      // without setting Mipmap filter creating mipmap files
      minFilter: LinearMipMapLinearFilter,
      generateMipmaps: false,
      depthBuffer: false,
    })

    const camera = new CubeCamera(1, 50, renderTarget)
    const mesh = new Mesh(undefined, this._material)
    

    // no matter how many mipmaps you want to render
    // this has to be setup based on cube size
    // you just to be sure you cubemap is sized enough to match you requirements
    const mipLevels = Math.log(cubeMapSize) * Math.LOG2E + 1.0
    const mipmapCount = Math.floor(
      Math.log2(Math.max(cubeMapSize, cubeMapSize))
    )

   // I coudn't verified in details but most the time mipmapCount = mipLevels - 1

    for (let mip = 0; mip < mipLevels; mip++) {
      // tell three js to prepare storage for rendering
      renderTarget.texture.mipmaps.push({})
    }

    let geom: BufferGeometry
    
    // for each level create a cube with proper uvs to wrap source texture
    for (let lod = 0; lod < mipmapCount; lod++) {
      if (lod < lodLayouts.length) {
        const layout = lodLayouts[lod]
        geom = createCubeWrapperGeom(layout)
        mesh.geometry = geom
      }

      renderTarget.viewport.set(
        0,
        0,
        renderTarget.width >> lod,
        renderTarget.height >> lod
      )
      camera.activeMipmapLevel = lod
      // error in ts definition camera require a scene and not a mesh like the example
      camera.update(this.renderer, mesh as any)

      geom.dispose()
    }

    return renderTarget.texture
  }

1 Like