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