Skip to content

Commit

Permalink
[Fix] HDR lightmap baking is working (#7247)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Valigursky <[email protected]>
  • Loading branch information
mvaligursky and Martin Valigursky authored Jan 6, 2025
1 parent c1034c3 commit 7a7f9d1
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 26 deletions.
26 changes: 22 additions & 4 deletions src/framework/lightmapper/lightmap-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class LightmapFilters {
this.pixelOffset = new Float32Array(2);

// denoise is optional and gets created only when needed
this.shaderDenoise = null;
this.shaderDenoise = [];
this.sigmas = null;
this.constantSigmas = null;
this.kernel = null;
Expand All @@ -35,10 +35,13 @@ class LightmapFilters {
this.constantPixelOffset.setValue(this.pixelOffset);
}

prepareDenoise(filterRange, filterSmoothness) {
prepareDenoise(filterRange, filterSmoothness, bakeHDR) {

if (!this.shaderDenoise) {
this.shaderDenoise = createShaderFromCode(this.device, shaderChunks.fullscreenQuadVS, shaderChunksLightmapper.bilateralDeNoisePS, 'lmBilateralDeNoise');
const index = bakeHDR ? 0 : 1;
if (!this.shaderDenoise[index]) {
const name = `lmBilateralDeNoise-${bakeHDR ? 'hdr' : 'rgbm'}`;
const define = bakeHDR ? '#define HDR\n' : '';
this.shaderDenoise[index] = createShaderFromCode(this.device, shaderChunks.fullscreenQuadVS, define + shaderChunksLightmapper.bilateralDeNoisePS, name);
this.sigmas = new Float32Array(2);
this.constantSigmas = this.device.scope.resolve('sigmas');
this.constantKernel = this.device.scope.resolve('kernel[0]');
Expand All @@ -52,6 +55,21 @@ class LightmapFilters {
this.evaluateDenoiseUniforms(filterRange, filterSmoothness);
}

getDenoise(bakeHDR) {
const index = bakeHDR ? 0 : 1;
return this.shaderDenoise[index];
}

getDilate(device, bakeHDR) {
const index = bakeHDR ? 0 : 1;
if (!this.shaderDilate[index]) {
const name = `lmDilate-${bakeHDR ? 'hdr' : 'rgbm'}`;
const define = bakeHDR ? '#define HDR\n' : '';
this.shaderDilate[index] = createShaderFromCode(device, shaderChunks.fullscreenQuadVS, define + shaderChunksLightmapper.dilatePS, name);
}
return this.shaderDilate[index];
}

evaluateDenoiseUniforms(filterRange, filterSmoothness) {

function normpdf(x, sigma) {
Expand Down
14 changes: 9 additions & 5 deletions src/framework/lightmapper/lightmapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class Lightmapper {

initBake(device) {

this.bakeHDR = this.scene.lightmapPixelFormat !== PIXELFORMAT_RGBA8;

// only initialize one time
if (!this._initCalled) {
this._initCalled = true;
Expand Down Expand Up @@ -251,7 +253,7 @@ class Lightmapper {
} else {
material.ambient = new Color(0, 0, 0); // don't bake ambient
}
material.chunks.basePS = shaderChunks.basePS + (scene.lightmapPixelFormat === PIXELFORMAT_RGBA8 ? '\n#define LIGHTMAP_RGBM\n' : '');
material.chunks.basePS = shaderChunks.basePS + (this.bakeHDR ? '' : '\n#define LIGHTMAP_RGBM\n');
material.chunks.endPS = bakeLmEndChunk;
material.lightMap = this.blackTex;
} else {
Expand Down Expand Up @@ -299,7 +301,7 @@ class Lightmapper {
height: size,
format: this.scene.lightmapPixelFormat,
mipmaps: false,
type: this.scene.lightmapPixelFormat === PIXELFORMAT_RGBA8 ? TEXTURETYPE_RGBM : TEXTURETYPE_DEFAULT,
type: this.bakeHDR ? TEXTURETYPE_DEFAULT : TEXTURETYPE_RGBM,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST,
addressU: ADDRESS_CLAMP_TO_EDGE,
Expand Down Expand Up @@ -876,12 +878,14 @@ class Lightmapper {
postprocessTextures(device, bakeNodes, passCount) {

const numDilates2x = 1; // 1 or 2 dilates (depending on filter being enabled)
const dilateShader = this.lightmapFilters.shaderDilate;
const dilateShader = this.lightmapFilters.getDilate(device, this.bakeHDR);
let denoiseShader;

// bilateral denoise filter - runs as a first pass, before dilate
const filterLightmap = this.scene.lightmapFilterEnabled;
if (filterLightmap) {
this.lightmapFilters.prepareDenoise(this.scene.lightmapFilterRange, this.scene.lightmapFilterSmoothness);
this.lightmapFilters.prepareDenoise(this.scene.lightmapFilterRange, this.scene.lightmapFilterSmoothness, this.bakeHDR);
denoiseShader = this.lightmapFilters.getDenoise(this.bakeHDR);
}

device.setBlendState(BlendState.NOBLEND);
Expand All @@ -908,7 +912,7 @@ class Lightmapper {

this.lightmapFilters.setSourceTexture(lightmap);
const bilateralFilterEnabled = filterLightmap && pass === 0 && i === 0;
drawQuadWithShader(device, tempRT, bilateralFilterEnabled ? this.lightmapFilters.shaderDenoise : dilateShader);
drawQuadWithShader(device, tempRT, bilateralFilterEnabled ? denoiseShader : dilateShader);

this.lightmapFilters.setSourceTexture(tempTex);
drawQuadWithShader(device, nodeRT, dilateShader);
Expand Down
40 changes: 31 additions & 9 deletions src/scene/shader-lib/chunks/lightmapper/frag/bilateralDeNoise.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ vec4 encodeRGBM(vec3 color) { // modified RGBM
return encoded;
}
vec3 decode(vec4 pixel) {
#if HDR
return pixel.rgb;
#else
return decodeRGBM(pixel);
#endif
}
bool isUsed(vec4 pixel) {
#if HDR
return any(greaterThan(pixel.rgb, vec3(0.0)));
#else
return pixel.a > 0.0;
#endif
}
// filter size
#define MSIZE 15
Expand All @@ -45,13 +61,13 @@ uniform float kernel[MSIZE];
void main(void) {
vec4 pixelRgbm = texture2DLod(source, vUv0, 0.0);
vec4 pixel = texture2DLod(source, vUv0, 0.0);
// lightmap specific optimization - skip pixels that were not baked
// this also allows dilate filter that work on the output of this to work correctly, as it depends on .a being zero
// to dilate, which the following blur filter would otherwise modify
if (pixelRgbm.a <= 0.0) {
gl_FragColor = pixelRgbm;
if (!isUsed(pixel)) {
gl_FragColor = pixel;
return ;
}
Expand All @@ -61,9 +77,9 @@ void main(void) {
// domain sigma - controls blurriness based on a pixel similarity (to preserve edges)
float bSigma = sigmas.y;
vec3 pixelHdr = decodeRGBM(pixelRgbm);
vec3 pixelHdr = decode(pixel);
vec3 accumulatedHdr = vec3(0.0);
float accumulatedFactor = 0.0;
float accumulatedFactor = 0.000001; // avoid division by zero
// read out the texels
const int kSize = (MSIZE-1)/2;
Expand All @@ -72,11 +88,11 @@ void main(void) {
// sample the pixel with offset
vec2 coord = vUv0 + vec2(float(i), float(j)) * pixelOffset;
vec4 rgbm = texture2DLod(source, coord, 0.0);
vec4 pix = texture2DLod(source, coord, 0.0);
// lightmap - only use baked pixels
if (rgbm.a > 0.0) {
vec3 hdr = decodeRGBM(rgbm);
if (isUsed(pix)) {
vec3 hdr = decode(pix);
// bilateral factors
float factor = kernel[kSize + j] * kernel[kSize + i];
Expand All @@ -89,6 +105,12 @@ void main(void) {
}
}
gl_FragColor = encodeRGBM(accumulatedHdr / accumulatedFactor);
vec3 finalHDR = accumulatedHdr / accumulatedFactor;
#if HDR
gl_FragColor = vec4(finalHDR, 1.0);
#else
gl_FragColor = encodeRGBM(finalHDR);
#endif
}
`;
24 changes: 16 additions & 8 deletions src/scene/shader-lib/chunks/lightmapper/frag/dilate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@ varying vec2 vUv0;
uniform sampler2D source;
uniform vec2 pixelOffset;
bool isUsed(vec4 pixel) {
#if HDR
return any(greaterThan(pixel.rgb, vec3(0.0)));
#else
return pixel.a > 0.0;
#endif
}
void main(void) {
vec4 c = texture2DLod(source, vUv0, 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 - pixelOffset, 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(0, -pixelOffset.y), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(pixelOffset.x, -pixelOffset.y), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(-pixelOffset.x, 0), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(pixelOffset.x, 0), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(-pixelOffset.x, pixelOffset.y), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + vec2(0, pixelOffset.y), 0.0);
c = c.a>0.0? c : texture2DLod(source, vUv0 + pixelOffset, 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 - pixelOffset, 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(0, -pixelOffset.y), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(pixelOffset.x, -pixelOffset.y), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(-pixelOffset.x, 0), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(pixelOffset.x, 0), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(-pixelOffset.x, pixelOffset.y), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + vec2(0, pixelOffset.y), 0.0);
c = isUsed(c) ? c : texture2DLod(source, vUv0 + pixelOffset, 0.0);
gl_FragColor = c;
}
`;

0 comments on commit 7a7f9d1

Please sign in to comment.