From 2df7b37300c6bccba732dd15708786679aaac841 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 16 Feb 2026 18:08:22 -0600 Subject: [PATCH 1/2] Rewrite light grid direction interpolation Redesign the scheme for storing light grid direction and directedness vs. ambientness information in the pixels of a 3D image. Note for context: the light grid does not store the separate directed and ambient colors; it just averages them. Before: Light dir is always stored as a normal vector. There is an "ambient part" byte which is supposed to indicate what fraction of the light is ambient and what fraction directed. After: Light dir is scaled according to how much ambient light there is. Amount of light which is ambient is determined by how long the direction vector is. Some issues with the old code which are fixed: - Wrong averaging of the directed and ambient colors. Previously directed and ambient colors were equally weighted, but ambient should have more weight since there is no dot product term which can decrease it. - Bad interpolation behavior. Example 1: if neighboring grid cells had nearly opposing directions, the resulting direction vector would be garbage. With the new code, this results in a small vector which causes most light to be automatically moved into directed instead of ambient. Example 2: if one cell has a large directed component and its neighbor has none, the directions are equally weighted despite the second one being meaningless. With the new code, directions are weighted according to how much directed light there is. - Excessive brightness when deluxe mapping is disabled, because it behaved in that case as if the normal vector were always perfectly aligned with the light direction. --- .../renderer/glsl_source/computeLight_fp.glsl | 16 ++-- .../renderer/glsl_source/lightMapping_fp.glsl | 19 +++-- src/engine/renderer/tr_backend.cpp | 15 ++-- src/engine/renderer/tr_bsp.cpp | 76 +++++++++---------- src/engine/renderer/tr_light.cpp | 27 +++---- src/engine/renderer/tr_local.h | 10 ++- 6 files changed, 85 insertions(+), 78 deletions(-) diff --git a/src/engine/renderer/glsl_source/computeLight_fp.glsl b/src/engine/renderer/glsl_source/computeLight_fp.glsl index f3c23bf9e9..d3508a9828 100644 --- a/src/engine/renderer/glsl_source/computeLight_fp.glsl +++ b/src/engine/renderer/glsl_source/computeLight_fp.glsl @@ -50,13 +50,19 @@ vec4 EnvironmentalSpecularFactor( vec3 viewDir, vec3 normal ) // lighting helper functions #if defined(USE_GRID_LIGHTING) || defined(USE_GRID_DELUXE_MAPPING) - void ReadLightGrid( in vec4 texel, in float lightFactor, out vec3 ambientColor, out vec3 lightColor ) { - float ambientScale = 2.0 * texel.a; - float directedScale = 2.0 - ambientScale; - ambientColor = ambientScale * texel.rgb; - lightColor = directedScale * texel.rgb; + void ReadLightGrid( in vec4 texel1, in vec4 texel2, in float lightFactor, out vec3 lightDir, out vec3 ambientColor, out vec3 lightColor ) { + vec3 totalColor = /*LIGHTGRID_MAX_LIGHT*/ 1.25 * texel1.rgb; + float total1Norm = totalColor.r + totalColor.g + totalColor.b; + vec3 scaledLightDir = 2.0 * (texel2.xyz - (128.0 / 255.0)); + float directed1Norm = 3.0 * length(scaledLightDir); + float directedFraction = clamp((0.25 * directed1Norm) / total1Norm, 0.0, 1.0); + float directedScale = 4.0 * directedFraction; + float ambientScale = 1.0 - directedFraction; + ambientColor = ambientScale * totalColor; + lightColor = directedScale * totalColor; ambientColor *= lightFactor; lightColor *= lightFactor; + lightDir = normalize(scaledLightDir); } #endif diff --git a/src/engine/renderer/glsl_source/lightMapping_fp.glsl b/src/engine/renderer/glsl_source/lightMapping_fp.glsl index da72f901e2..484098150c 100644 --- a/src/engine/renderer/glsl_source/lightMapping_fp.glsl +++ b/src/engine/renderer/glsl_source/lightMapping_fp.glsl @@ -130,19 +130,10 @@ void main() vec4 color; color.a = diffuse.a; - #if defined(USE_GRID_LIGHTING) || defined(USE_GRID_DELUXE_MAPPING) - // Compute light grid position. - vec3 lightGridPos = (var_Position - u_LightGridOrigin) * u_LightGridScale; - #endif - #if defined(USE_DELUXE_MAPPING) // Compute light direction in world space from deluxe map. vec4 deluxe = texture2D(u_DeluxeMap, var_TexLight); vec3 lightDir = normalize(2.0 * deluxe.xyz - 1.0); - #elif defined(USE_GRID_DELUXE_MAPPING) - // Compute light direction in world space from light grid. - vec4 texel = texture3D(u_LightGrid2, lightGridPos); - vec3 lightDir = normalize(texel.xyz - (128.0 / 255.0)); #endif float lightFactor = ColorModulateToLightFactor( u_ColorModulateColorGen ); @@ -156,8 +147,16 @@ void main() color.rgb = vec3(0.0); #else // Compute light color from lightgrid. + vec3 lightGridPos = (var_Position - u_LightGridOrigin) * u_LightGridScale; + vec4 texel1 = texture3D(u_LightGrid1, lightGridPos); + #if defined(USE_GRID_DELUXE_MAPPING) + vec4 texel2 = texture3D(u_LightGrid2, lightGridPos); + #else + vec4 texel2 = vec4(128.0 / 255.0); // zero direction vector + #endif + vec3 lightDir; vec3 ambientColor, lightColor; - ReadLightGrid(texture3D(u_LightGrid1, lightGridPos), lightFactor, ambientColor, lightColor); + ReadLightGrid(texel1, texel2, lightFactor, lightDir, ambientColor, lightColor); color.rgb = ambientColor * r_AmbientScale * diffuse.rgb; #endif diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index d8604cc632..6d80b8fa40 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -2223,14 +2223,19 @@ static void RB_RenderDebugUtils() int gridIndex = x + tr.world->lightGridBounds[ 0 ] * ( y + tr.world->lightGridBounds[ 1 ] * z ); const bspGridPoint1_t *gp1 = tr.world->lightGridData1 + gridIndex; const bspGridPoint2_t *gp2 = tr.world->lightGridData2 + gridIndex; - Color::Color generalColor = Color::Adapt( gp1->color ); - float ambientScale = 2.0f * unorm8ToFloat( gp1->ambientPart ); - float directedScale = 2.0f - ambientScale; - Color::Color ambientColor = generalColor * ambientScale; - Color::Color directedColor = generalColor * directedScale; lightDir[ 0 ] = snorm8ToFloat( gp2->direction[ 0 ] - 128 ); lightDir[ 1 ] = snorm8ToFloat( gp2->direction[ 1 ] - 128 ); lightDir[ 2 ] = snorm8ToFloat( gp2->direction[ 2 ] - 128 ); + Color::Color totalColor = Color::Adapt( gp1->color ); + totalColor *= LIGHTGRID_MAX_LIGHT; + float total1Norm = totalColor.Red() + totalColor.Green() + totalColor.Blue(); + float directed1Norm = 3.0f * VectorLength( lightDir ); + float directedFraction = ( 0.25 * directed1Norm ) / total1Norm; + float directedScale = 4.0 * directedFraction; + float ambientScale = 1.0 - directedFraction; + Color::Color ambientColor = totalColor * ambientScale; + Color::Color directedColor = totalColor * directedScale; + VectorNormalize( lightDir ); VectorNegate( lightDir, lightDir ); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index 3655ea0fb2..76d4f3a028 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3438,14 +3438,13 @@ static void R_SetConstantColorLightGrid( const byte color[3] ) bspGridPoint1_t *gridPoint1 = (bspGridPoint1_t *) ri.Hunk_Alloc( sizeof( bspGridPoint1_t ) + sizeof( bspGridPoint2_t ), ha_pref::h_low ); bspGridPoint2_t *gridPoint2 = (bspGridPoint2_t *) (gridPoint1 + w->numLightGridPoints); - // default some white light from above - gridPoint1->color[ 0 ] = color[0]; - gridPoint1->color[ 1 ] = color[1]; - gridPoint1->color[ 2 ] = color[2]; - gridPoint1->ambientPart = 128; + // directed part comes from above; ambient color and directed color are both `color` + VectorCopy( color, gridPoint1->color ); + gridPoint1->unused = 255; gridPoint2->direction[ 0 ] = 128 + floatToSnorm8( 0.0f ); gridPoint2->direction[ 1 ] = 128 + floatToSnorm8( 0.0f ); - gridPoint2->direction[ 2 ] = 128 + floatToSnorm8( 1.0f ); + gridPoint2->direction[ 2 ] = 128 + floatToSnorm8( + ( color[ 0 ] + color[ 1 ] + color[ 2 ] ) / ( 3.0f * 255 ) ); gridPoint2->isSet = 255; w->lightGridData1 = gridPoint1; @@ -3496,7 +3495,6 @@ void R_LoadLightGrid( lump_t *l ) float weights[ 3 ] = { 0.25f, 0.5f, 0.25f }; float *factors[ 3 ] = { weights, weights, weights }; vec3_t ambientColor, directedColor, direction; - float scale; if ( tr.ambientLightSet ) { const byte color[3]{ floatToUnorm8( tr.ambientLight[0] ), floatToUnorm8( tr.ambientLight[1] ), @@ -3637,19 +3635,28 @@ void R_LoadLightGrid( lump_t *l ) direction[ 1 ] = sinf( lng ) * sinf( lat ); direction[ 2 ] = cosf( lat ); - // Pack data into an bspGridPoint - gridPoint1->color[ 0 ] = floatToUnorm8( 0.5f * (ambientColor[ 0 ] + directedColor[ 0 ]) ); - gridPoint1->color[ 1 ] = floatToUnorm8( 0.5f * (ambientColor[ 1 ] + directedColor[ 1 ]) ); - gridPoint1->color[ 2 ] = floatToUnorm8( 0.5f * (ambientColor[ 2 ] + directedColor[ 2 ]) ); - - float ambientLength = VectorLength(ambientColor); - float directedLength = VectorLength(directedColor); - float length = ambientLength + directedLength; - gridPoint1->ambientPart = floatToUnorm8( ambientLength / length ); - - gridPoint2->direction[0] = 128 + floatToSnorm8( direction[ 0 ] ); - gridPoint2->direction[1] = 128 + floatToSnorm8( direction[ 1 ] ); - gridPoint2->direction[2] = 128 + floatToSnorm8( direction[ 2 ] ); + // Separate ambient and directed colors are not implemented, so this is the total average light + // (averaged over all direction vectors): ambient + 0.25 * directed. See TraceGrid in light.c in + // q3map2 for more information about this equation. Though if to take + // half-Lambert lighting's cosine modification into account, the 1/4 factor should be replaced + // by 1/3. The result is scaled down to fit in [0, 1]. + gridPoint1->color[ 0 ] = floatToUnorm8( ( ambientColor[ 0 ] + 0.25f * directedColor[ 0 ] ) / LIGHTGRID_MAX_LIGHT ); + gridPoint1->color[ 1 ] = floatToUnorm8( ( ambientColor[ 1 ] + 0.25f * directedColor[ 1 ] ) / LIGHTGRID_MAX_LIGHT ); + gridPoint1->color[ 2 ] = floatToUnorm8( ( ambientColor[ 2 ] + 0.25f * directedColor[ 2 ] ) / LIGHTGRID_MAX_LIGHT ); + gridPoint1->unused = 255; + + // The length of the direction vector is used to determine how much directed light there is. + // When adjacent grid points have opposing directions that (partially) cancel each other upon + // interpolation, the GLSL will see a smaller direction vector and put more light into the + // ambient part. "How much light" is determined using the 1-norm (Maybe it should be + // luma-aware?) since that is preserved under interpolation. + // TODO: send the directed contribution to zero if it is worse than random? If you had perfectly + // ambient white light with a total of sum of A, you'd get a directed color of 0.25*A and an ambient color + // of 0.1875*A. So if the directed to ambient ratio is lower than that, the direction is surely garbage. + float dirScale = ( directedColor[ 0 ] + directedColor[ 1 ] + directedColor[ 2 ] ) / 3.0f; + gridPoint2->direction[0] = 128 + floatToSnorm8( dirScale * direction[ 0 ] ); + gridPoint2->direction[1] = 128 + floatToSnorm8( dirScale * direction[ 1 ] ); + gridPoint2->direction[2] = 128 + floatToSnorm8( dirScale * direction[ 2 ] ); gridPoint2->isSet = 255; } @@ -3678,27 +3685,18 @@ void R_LoadLightGrid( lump_t *l ) continue; } - scale = R_InterpolateLightGrid( w, from, to, factors, - ambientColor, directedColor, - direction ); - if( scale > 0.0f ) { - scale = 1.0f / scale; - - VectorScale( ambientColor, scale, ambientColor ); - VectorScale( directedColor, scale, directedColor ); - VectorScale( direction, scale, direction ); + vec3_t interpolatedColor, interpolatedDir; + R_InterpolateLightGrid( w, from, to, factors, interpolatedColor, interpolatedDir ); + gridPoint1->color[0] = floatToUnorm8( interpolatedColor[ 0 ] ); + gridPoint1->color[1] = floatToUnorm8( interpolatedColor[ 1 ] ); + gridPoint1->color[2] = floatToUnorm8( interpolatedColor[ 2 ] ); + gridPoint1->unused = 255; - gridPoint1->color[0] = floatToUnorm8(0.5f * (ambientColor[0] + directedColor[0])); - gridPoint1->color[1] = floatToUnorm8(0.5f * (ambientColor[1] + directedColor[1])); - gridPoint1->color[2] = floatToUnorm8(0.5f * (ambientColor[2] + directedColor[2])); - gridPoint1->ambientPart = floatToUnorm8(VectorLength(ambientColor) / (VectorLength(ambientColor) + VectorLength(directedColor))); - - gridPoint2->direction[0] = 128 + floatToSnorm8(direction[0]); - gridPoint2->direction[1] = 128 + floatToSnorm8(direction[1]); - gridPoint2->direction[2] = 128 + floatToSnorm8(direction[2]); - gridPoint2->isSet = 255; - } + gridPoint2->direction[0] = 128 + floatToSnorm8( interpolatedDir[ 0 ] ); + gridPoint2->direction[1] = 128 + floatToSnorm8( interpolatedDir[ 1 ] ); + gridPoint2->direction[2] = 128 + floatToSnorm8( interpolatedDir[ 2 ] ); + gridPoint2->isSet = 255; } } } diff --git a/src/engine/renderer/tr_light.cpp b/src/engine/renderer/tr_light.cpp index f347419af0..5c870dc0f1 100644 --- a/src/engine/renderer/tr_light.cpp +++ b/src/engine/renderer/tr_light.cpp @@ -31,9 +31,9 @@ LIGHT SAMPLING ============================================================================= */ -float R_InterpolateLightGrid( world_t *w, int from[3], int to[3], - float *factors[3], vec3_t ambientLight, - vec3_t directedLight, vec3_t lightDir ) { +void R_InterpolateLightGrid( world_t *w, int from[3], int to[3], float *factors[3], + vec3_t lightColor, vec3_t lightDir ) +{ float totalFactor = 0.0f, factor; float *xFactor, *yFactor, *zFactor; int gridStep[ 3 ]; @@ -41,8 +41,7 @@ float R_InterpolateLightGrid( world_t *w, int from[3], int to[3], bspGridPoint1_t *gp1; bspGridPoint2_t *gp2; - VectorClear( ambientLight ); - VectorClear( directedLight ); + VectorClear( lightColor ); VectorClear( lightDir ); gridStep[ 0 ] = 1; @@ -80,18 +79,16 @@ float R_InterpolateLightGrid( world_t *w, int from[3], int to[3], lightDir[ 1 ] += factor * snorm8ToFloat( gp2->direction[ 1 ] - 128 ); lightDir[ 2 ] += factor * snorm8ToFloat( gp2->direction[ 2 ] - 128 ); - float ambientScale = 2.0f * unorm8ToFloat( gp1->ambientPart ); - float directedScale = 2.0f - ambientScale; - - ambientLight[ 0 ] += factor * ambientScale * unorm8ToFloat( gp1->color[ 0 ] ); - ambientLight[ 1 ] += factor * ambientScale * unorm8ToFloat( gp1->color[ 1 ] ); - ambientLight[ 2 ] += factor * ambientScale * unorm8ToFloat( gp1->color[ 2 ] ); - directedLight[ 0 ] += factor * directedScale * unorm8ToFloat( gp1->color[ 0 ] ); - directedLight[ 1 ] += factor * directedScale * unorm8ToFloat( gp1->color[ 1 ] ); - directedLight[ 2 ] += factor * directedScale * unorm8ToFloat( gp1->color[ 2 ] ); + lightColor[ 0 ] += factor * unorm8ToFloat( gp1->color[ 0 ] ); + lightColor[ 1 ] += factor * unorm8ToFloat( gp1->color[ 1 ] ); + lightColor[ 2 ] += factor * unorm8ToFloat( gp1->color[ 2 ] ); } } } - return totalFactor; + if ( totalFactor > 0.0f ) + { + VectorScale( lightDir, 1.0f / totalFactor, lightDir ); + VectorScale( lightColor, 1.0f / totalFactor, lightColor ); + } } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index c5570f8704..41097edacf 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1684,13 +1684,16 @@ enum bspSurface_t *firstSurface; }; +// max combined ambient+directed contribution to one color channel +#define LIGHTGRID_MAX_LIGHT 1.25f + // The ambient and directional colors are packed into four bytes, the color[3] is the // average of the ambient and directional colors and the ambientPart factor is the // proportion of ambient light in the total light struct bspGridPoint1_t { byte color[3]; - byte ambientPart; + byte unused; }; struct bspGridPoint2_t { @@ -3263,9 +3266,8 @@ void GLimp_LogComment_( std::string comment ); ============================================================ */ - float R_InterpolateLightGrid( world_t *w, int from[3], int to[3], - float *factors[3], vec3_t ambientLight, - vec3_t directedLight, vec3_t lightDir ); + void R_InterpolateLightGrid( world_t *w, int from[3], int to[3], float *factors[3], + vec3_t lightColor, vec3_t lightDir ); /* ============================================================ From 1e4d9952b36b3ee23cfa970d3d217b3400c3cb19 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 2 Mar 2026 19:24:50 -0600 Subject: [PATCH 2/2] Make models brighter with deluxe mapping off The previous commit fixed excessive brightness with grid lighting when deluxe mapping is disabled by using the average expected brightness (over all directions) instead of the maximum, but it went too far and made things dim. Boost the light a bit since light directions are positively correlated with normals in practice. --- src/engine/renderer/gl_shader.cpp | 2 ++ .../renderer/glsl_source/computeLight_fp.glsl | 6 +++--- src/engine/renderer/tr_backend.cpp | 6 +++--- src/engine/renderer/tr_bsp.cpp | 12 +++++------- src/engine/renderer/tr_init.cpp | 15 +++++++++++++++ src/engine/renderer/tr_local.h | 4 +--- 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/engine/renderer/gl_shader.cpp b/src/engine/renderer/gl_shader.cpp index f23055feb4..bd143ca82e 100644 --- a/src/engine/renderer/gl_shader.cpp +++ b/src/engine/renderer/gl_shader.cpp @@ -687,6 +687,8 @@ static std::string GenEngineConstants() { AddDefine( str, "r_tileStep", glState.tileStep[0], glState.tileStep[1] ); + AddDefine( str, "LIGHTGRID_AVERAGE_COSINE", tr.lightGridAverageCosine ); + if ( glConfig.realtimeLighting ) { AddDefine( str, "r_realtimeLighting", 1 ); diff --git a/src/engine/renderer/glsl_source/computeLight_fp.glsl b/src/engine/renderer/glsl_source/computeLight_fp.glsl index d3508a9828..34c59389f0 100644 --- a/src/engine/renderer/glsl_source/computeLight_fp.glsl +++ b/src/engine/renderer/glsl_source/computeLight_fp.glsl @@ -51,12 +51,12 @@ vec4 EnvironmentalSpecularFactor( vec3 viewDir, vec3 normal ) #if defined(USE_GRID_LIGHTING) || defined(USE_GRID_DELUXE_MAPPING) void ReadLightGrid( in vec4 texel1, in vec4 texel2, in float lightFactor, out vec3 lightDir, out vec3 ambientColor, out vec3 lightColor ) { - vec3 totalColor = /*LIGHTGRID_MAX_LIGHT*/ 1.25 * texel1.rgb; + vec3 totalColor = (1.0 + LIGHTGRID_AVERAGE_COSINE) * texel1.rgb; float total1Norm = totalColor.r + totalColor.g + totalColor.b; vec3 scaledLightDir = 2.0 * (texel2.xyz - (128.0 / 255.0)); float directed1Norm = 3.0 * length(scaledLightDir); - float directedFraction = clamp((0.25 * directed1Norm) / total1Norm, 0.0, 1.0); - float directedScale = 4.0 * directedFraction; + float directedFraction = clamp((LIGHTGRID_AVERAGE_COSINE * directed1Norm) / total1Norm, 0.0, 1.0); + float directedScale = directedFraction / LIGHTGRID_AVERAGE_COSINE; float ambientScale = 1.0 - directedFraction; ambientColor = ambientScale * totalColor; lightColor = directedScale * totalColor; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 6d80b8fa40..8cec6eaad8 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -2227,11 +2227,11 @@ static void RB_RenderDebugUtils() lightDir[ 1 ] = snorm8ToFloat( gp2->direction[ 1 ] - 128 ); lightDir[ 2 ] = snorm8ToFloat( gp2->direction[ 2 ] - 128 ); Color::Color totalColor = Color::Adapt( gp1->color ); - totalColor *= LIGHTGRID_MAX_LIGHT; + totalColor *= 1.0f + tr.lightGridAverageCosine; float total1Norm = totalColor.Red() + totalColor.Green() + totalColor.Blue(); float directed1Norm = 3.0f * VectorLength( lightDir ); - float directedFraction = ( 0.25 * directed1Norm ) / total1Norm; - float directedScale = 4.0 * directedFraction; + float directedFraction = ( tr.lightGridAverageCosine * directed1Norm ) / total1Norm; + float directedScale = directedFraction / tr.lightGridAverageCosine; float ambientScale = 1.0 - directedFraction; Color::Color ambientColor = totalColor * ambientScale; Color::Color directedColor = totalColor * directedScale; diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index 76d4f3a028..29cb4bb626 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3636,13 +3636,11 @@ void R_LoadLightGrid( lump_t *l ) direction[ 2 ] = cosf( lat ); // Separate ambient and directed colors are not implemented, so this is the total average light - // (averaged over all direction vectors): ambient + 0.25 * directed. See TraceGrid in light.c in - // q3map2 for more information about this equation. Though if to take - // half-Lambert lighting's cosine modification into account, the 1/4 factor should be replaced - // by 1/3. The result is scaled down to fit in [0, 1]. - gridPoint1->color[ 0 ] = floatToUnorm8( ( ambientColor[ 0 ] + 0.25f * directedColor[ 0 ] ) / LIGHTGRID_MAX_LIGHT ); - gridPoint1->color[ 1 ] = floatToUnorm8( ( ambientColor[ 1 ] + 0.25f * directedColor[ 1 ] ) / LIGHTGRID_MAX_LIGHT ); - gridPoint1->color[ 2 ] = floatToUnorm8( ( ambientColor[ 2 ] + 0.25f * directedColor[ 2 ] ) / LIGHTGRID_MAX_LIGHT ); + // (averaged over all direction vectors). The result is scaled down to fit in [0, 1]. + float colorScale = 1.0f / ( 1.0f + tr.lightGridAverageCosine ); + gridPoint1->color[ 0 ] = floatToUnorm8( ( ambientColor[ 0 ] + tr.lightGridAverageCosine * directedColor[ 0 ] ) * colorScale ); + gridPoint1->color[ 1 ] = floatToUnorm8( ( ambientColor[ 1 ] + tr.lightGridAverageCosine * directedColor[ 1 ] ) * colorScale ); + gridPoint1->color[ 2 ] = floatToUnorm8( ( ambientColor[ 2 ] + tr.lightGridAverageCosine * directedColor[ 2 ] ) * colorScale ); gridPoint1->unused = 255; // The length of the direction vector is used to determine how much directed light there is. diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 2885749bcd..8d496d1b47 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -1350,6 +1350,21 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p } } + if ( glConfig.deluxeMapping ) + { + // Average value of max(0, dot(random vector, light dir)) over the unit sphere, used to + // compare directed lighting's average contribution with ambient. See TraceGrid in light.c in + // q3map2 for more information about this calculation. Though if we took half-Lambert lighting's + // cosine modification into account it would be 1/3 instead of 1/4. + tr.lightGridAverageCosine = 0.25f; + } + else + { + // Boost it when deluxe is disabled because otherwise it's too dark. Normals tend to line up + // with the light direction better than chance. + tr.lightGridAverageCosine = 0.4f; + } + R_NoiseInit(); R_Register(); diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 41097edacf..68328fde7d 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1684,9 +1684,6 @@ enum bspSurface_t *firstSurface; }; -// max combined ambient+directed contribution to one color channel -#define LIGHTGRID_MAX_LIGHT 1.25f - // The ambient and directional colors are packed into four bytes, the color[3] is the // average of the ambient and directional colors and the ambientPart factor is the // proportion of ambient light in the total light @@ -2501,6 +2498,7 @@ enum vec3_t ambientLight; bool ambientLightSet = false; + float lightGridAverageCosine; image_t *lightGrid1Image; image_t *lightGrid2Image;