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 f3c23bf9e9..34c59389f0 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 = (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((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; 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..8cec6eaad8 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 *= 1.0f + tr.lightGridAverageCosine; + float total1Norm = totalColor.Red() + totalColor.Green() + totalColor.Blue(); + float directed1Norm = 3.0f * VectorLength( lightDir ); + 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; + VectorNormalize( lightDir ); VectorNegate( lightDir, lightDir ); diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index 3655ea0fb2..29cb4bb626 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,26 @@ 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). 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. + // 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 +3683,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_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_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..68328fde7d 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1690,7 +1690,7 @@ enum struct bspGridPoint1_t { byte color[3]; - byte ambientPart; + byte unused; }; struct bspGridPoint2_t { @@ -2498,6 +2498,7 @@ enum vec3_t ambientLight; bool ambientLightSet = false; + float lightGridAverageCosine; image_t *lightGrid1Image; image_t *lightGrid2Image; @@ -3263,9 +3264,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 ); /* ============================================================