diff --git a/src/engine/renderer/glsl_source/lighttile_fp.glsl b/src/engine/renderer/glsl_source/lighttile_fp.glsl index 4548c07b9d..7afedfe398 100644 --- a/src/engine/renderer/glsl_source/lighttile_fp.glsl +++ b/src/engine/renderer/glsl_source/lighttile_fp.glsl @@ -130,6 +130,7 @@ void main() { or use compute shaders with atomics so we can have a variable amount of lights for each tile. */ for( uint i = uint( u_lightLayer ); i < uint( u_numLights ); i += uint( NUM_LIGHT_LAYERS ) ) { Light l = GetLight( i ); + vec3 center = ( u_ModelMatrix * vec4( l.center, 1.0 ) ).xyz; float radius = max( 2.0 * l.radius, 2.0 * 32.0 ); // Avoid artifacts with weak light sources diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 3c66a2d249..b0224b3759 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -39,6 +39,8 @@ static Cvar::Cvar r_clear( "r_clear", "Clear screen before painting over i Cvar::Cvar r_drawSky( "r_drawSky", "Draw the sky (clear the sky if disabled)", Cvar::NONE, true ); static Cvar::Cvar r_showEntityBounds( "r_showEntityBounds", "show bboxes used for culling (1: wireframe; 2: translucent solid)", Cvar::CHEAT, 0); +static Cvar::Cvar r_showDynamicLights( + "r_showDynamicLights", "visualize dynamic lights with tetrahedrons", Cvar::CHEAT, false ); void GL_Bind( image_t *image ) { @@ -1301,7 +1303,7 @@ static void RenderDepthTiles() { RB_PrepareForSamplingDepthMap(); } - + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); // 1st step @@ -1317,7 +1319,7 @@ static void RenderDepthTiles() gl_depthtile1Shader->SetUniform_zFar( zParams ); gl_depthtile1Shader->SetUniform_DepthMapBindless( - GL_BindToTMU( 0, tr.currentDepthImage ) + GL_BindToTMU( 0, tr.currentDepthImage ) ); matrix_t ortho; @@ -1727,7 +1729,7 @@ void RB_CameraPostFX() { gl_cameraEffectsShader->SetUniform_Tonemap( tonemap ); gl_cameraEffectsShader->SetUniform_CurrentMapBindless( - GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) + GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) ); if ( r_FXAA.Get() && gl_fxaaShader ) @@ -2312,6 +2314,125 @@ static void RB_RenderDebugUtils() Tess_End(); } + if ( r_showDynamicLights.Get() && backEnd.refdef.numLights > 0 ) + { + gl_genericShader->SetVertexSkinning( false ); + gl_genericShader->SetVertexAnimation( false ); + gl_genericShader->SetTCGenEnvironment( false ); + gl_genericShader->SetTCGenLightmap( false ); + gl_genericShader->SetDepthFade( false ); + gl_genericShader->SetDeform( 0 ); + gl_genericShader->BindProgram(); + + GL_State( GLS_DEFAULT ); + GL_Cull( cullType_t::CT_TWO_SIDED ); + + // set uniforms + gl_genericShader->SetUniform_AlphaTest( GLS_ATEST_NONE ); + SetUniform_ColorModulateColorGen( gl_genericShader, colorGen_t::CGEN_VERTEX, + alphaGen_t::AGEN_VERTEX ); + SetUniform_Color( gl_genericShader, Color::Black ); + gl_genericShader->SetUniform_ColorMapBindless( GL_BindToTMU( 0, tr.whiteImage ) ); + gl_genericShader->SetUniform_TextureMatrix( matrixIdentity ); + + // set up the transformation matrix + backEnd.orientation = backEnd.viewParms.world; + GL_LoadModelViewMatrix( backEnd.orientation.modelViewMatrix ); + gl_genericShader->SetUniform_ModelViewProjectionMatrix( + glState.modelViewProjectionMatrix[ glState.stackIndex ] ); + + Tess_Begin( Tess_StageIteratorDebug, nullptr, true, -1, 0 ); + GL_CheckErrors(); + + const refLight_t *lights = backEnd.refdef.lights; + for ( int i = 0; i < backEnd.refdef.numLights; ++i ) + { + const refLight_t &light = lights[ i ]; + // We can't really visualize directional lights since they don't + // have an origin or a radius. + if ( light.rlType >= refLightType_t::RL_DIRECTIONAL ) + { + continue; + } + + vec3_t baseOrigin; + VectorCopy( light.origin, baseOrigin ); + + if ( light.radius <= 0.0f ) + { + Log::Warn( "Light with index %d has no radius", i ); + } + + auto addArrow = [ & ]( const vec3_t dirInput, const Color::Color &arrowColor ) + { + vec3_t dir; + VectorCopy( dirInput, dir ); + if ( VectorNormalize( dir ) == 0.0f ) + { + VectorSet( dir, 0.0f, 0.0f, 1.0f ); + } + // idk why we need to negate here, but the arrow points the wrong way otherwise. + VectorNegate( dir, dir ); + + vec3_t tip; + VectorMA( baseOrigin, light.radius, dir, tip ); + + vec3_t tmp; + vec3_t tmp2; + vec3_t tmp3; + PerpendicularVector( tmp, dir ); + VectorScale( tmp, light.radius * 0.2f, tmp2 ); + VectorMA( tmp2, light.radius * 0.3f, dir, tmp2 ); + + vec4_t tetraVerts[ 4 ]; + for ( int k = 0; k < 3; k++ ) + { + RotatePointAroundVector( tmp3, dir, tmp2, k * 120.0f ); + VectorAdd( tmp3, baseOrigin, tmp3 ); + VectorCopy( tmp3, tetraVerts[ k ] ); + tetraVerts[ k ][ 3 ] = 1.0f; + } + + VectorCopy( baseOrigin, tetraVerts[ 3 ] ); + tetraVerts[ 3 ][ 3 ] = 1.0f; + Tess_AddTetrahedron( tetraVerts, arrowColor ); + + VectorCopy( tip, tetraVerts[ 3 ] ); + tetraVerts[ 3 ][ 3 ] = 1.0f; + + Tess_AddTetrahedron( tetraVerts, arrowColor ); + }; + + Color::Color color; + switch ( light.rlType ) + { + case refLightType_t::RL_PROJ: + color = Color::LtGrey; + addArrow( light.projTarget, color ); + break; + default: + color = Color::MdGrey; + { + static const vec3_t kOmniDirs[ 6 ] = { + { 1.0f, 0.0f, 0.0f }, + { -1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, -1.0f} + }; + for ( int dirIndex = 0; dirIndex < 6; ++dirIndex ) + { + addArrow( kOmniDirs[ dirIndex ], color ); + } + } + break; + } + } + + Tess_End(); + } + if ( r_showBspNodes->integer ) { if ( ( backEnd.refdef.rdflags & ( RDF_NOWORLDMODEL ) ) || !tr.world ) @@ -3486,6 +3607,7 @@ const RenderCommand *SetupLightsCommand::ExecuteSelf() const default: break; } + VectorNormalize( buffer[i].direction ); } glUnmapBuffer( bufferTarget ); diff --git a/src/engine/renderer/tr_scene.cpp b/src/engine/renderer/tr_scene.cpp index e95c50c898..fb84e5d785 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -366,7 +366,7 @@ static void RE_RenderCubeProbeFace( const refdef_t* originalRefdef ) { Log::Warn( "Cube probe face out of range! (%i/%i)", probeID, tr.cubeProbes.size() ); return; } - + refdef_t refdef{}; const int faceID = globalID % 6; @@ -487,6 +487,91 @@ static void RE_RenderCubeProbeFace( const refdef_t* originalRefdef ) { } +// Debug spot light (projected) injection +static Cvar::Cvar r_debugProjLight( "r_debugProjLight", "inject a directional sun light each frame", Cvar::CHEAT, false ); +static Cvar::Range> r_debugProjLightYaw( "r_debugProjLightYaw", "debug projected yaw in degrees", Cvar::NONE, 45.0f, -360.0f, 360.0f ); +static Cvar::Range> r_debugProjLightPitch( "r_debugProjLightPitch", "debug projected pitch in degrees", Cvar::NONE, -60.0f, -89.0f, 89.0f ); +static Cvar::Cvar r_debugProjLightRadius( "r_debugProjLightRadius", "debug projected radius (size)", Cvar::NONE, 100.0f ); +static Cvar::Range> r_debugProjLightR( "r_debugProjLightR", "debug projected color R", Cvar::NONE, 1.0f, 0.0f, 1.0f ); +static Cvar::Range> r_debugProjLightG( "r_debugProjLightG", "debug projected color G", Cvar::NONE, 1.0f, 0.0f, 1.0f ); +static Cvar::Range> r_debugProjLightB( "r_debugProjLightB", "debug projected color B", Cvar::NONE, 1.0f, 0.0f, 1.0f ); +static Cvar::Cvar r_debugProjLightOriginX( "r_debugProjLightOriginX", "debug projected origin X", Cvar::NONE, 0.0f ); +static Cvar::Cvar r_debugProjLightOriginY( "r_debugProjLightOriginY", "debug projected origin Y", Cvar::NONE, 0.0f ); +static Cvar::Cvar r_debugProjLightOriginZ( "r_debugProjLightOriginZ", "debug projected origin Z", Cvar::NONE, 0.0f ); +static Cvar::Cvar r_debugProjLightAngle( "r_debugProjLightAngle", "debug projected angle", + Cvar::NONE, 60.0f ); + +static void AddDebugProjectedLight() +{ + if ( r_numLights >= MAX_REF_LIGHTS ) + { + return; + } + refLight_t *light = &backEndData[ tr.smpFrame ]->lights[ r_numLights++ ]; + *light = {}; + light->rlType = refLightType_t::RL_PROJ; + // Compute direction from yaw/pitch cvars (in degrees) + float yaw = DEG2RAD( r_debugProjLightYaw.Get() ); + float pitch = DEG2RAD( r_debugProjLightPitch.Get() ); + // Right-handed: X forward, Y left, Z up. Direction vector components: + light->projTarget[ 0 ] = cosf( pitch ) * cosf( yaw ); + light->projTarget[ 1 ] = cosf( pitch ) * sinf( yaw ); + light->projTarget[ 2 ] = sinf( pitch ); + + vec3_t dir; + VectorCopy( light->projTarget, dir ); + VectorNormalize( dir ); + + PerpendicularVector( light->projUp, dir ); + + float upLen = VectorLength( light->projUp ); + float tgtLen = VectorLength( light->projTarget ); + + VectorScale( light->projUp, tanf( DEG2RAD( r_debugProjLightAngle.Get() ) ) / upLen / tgtLen, + light->projUp ); + + // Set properties + light->color[ 0 ] = r_debugProjLightR.Get(); + light->color[ 1 ] = r_debugProjLightG.Get(); + light->color[ 2 ] = r_debugProjLightB.Get(); + light->radius = r_debugProjLightRadius.Get(); + light->origin[ 0 ] = r_debugProjLightOriginX.Get(); + light->origin[ 1 ] = r_debugProjLightOriginY.Get(); + light->origin[ 2 ] = r_debugProjLightOriginZ.Get(); +} + +// Debug sun light (directional) injection +Cvar::Cvar r_debugSun( "r_debugSun", "inject a directional sun light each frame", Cvar::CHEAT, false ); +Cvar::Range> r_debugSunYaw( "r_debugSunYaw", "debug sun yaw in degrees", Cvar::NONE, 45.0f, -360.0f, 360.0f ); +Cvar::Range> r_debugSunPitch( "r_debugSunPitch", "debug sun pitch in degrees", Cvar::NONE, -60.0f, -89.0f, 89.0f ); +Cvar::Range> r_debugSunR( "r_debugSunR", "debug sun color R", Cvar::NONE, 1.0f, 0.0f, 10.0f ); +Cvar::Range> r_debugSunG( "r_debugSunG", "debug sun color G", Cvar::NONE, 1.0f, 0.0f, 10.0f ); +Cvar::Range> r_debugSunB( "r_debugSunB", "debug sun color B", Cvar::NONE, 1.0f, 0.0f, 10.0f ); + +static void AddDebugSunLight() +{ + if ( r_numLights >= MAX_REF_LIGHTS ) + { + return; + } + refLight_t *sun = &backEndData[ tr.smpFrame ]->lights[ r_numLights++ ]; + *sun = {}; + sun->rlType = refLightType_t::RL_DIRECTIONAL; + // Compute direction from yaw/pitch cvars (in degrees) + float yaw = DEG2RAD( r_debugSunYaw.Get() ); + float pitch = DEG2RAD( r_debugSunPitch.Get() ); + // Right-handed: X forward, Y left, Z up. Direction vector components: + sun->projTarget[ 0 ] = cosf( pitch ) * cosf( yaw ); + sun->projTarget[ 1 ] = cosf( pitch ) * sinf( yaw ); + sun->projTarget[ 2 ] = sinf( pitch ); + VectorNormalize( sun->projTarget ); + sun->color[ 0 ] = r_debugSunR.Get(); + sun->color[ 1 ] = r_debugSunG.Get(); + sun->color[ 2 ] = r_debugSunB.Get(); + // Max radius to ensure it is always included. + sun->radius = std::numeric_limits::max(); +} + /* @@@@@@@@@@@@@@@@@@@@@ RE_RenderScene @@ -562,6 +647,15 @@ void RE_RenderScene( const refdef_t *fd ) } } + if ( r_debugProjLight.Get() ) + { + AddDebugProjectedLight(); + } + if ( r_debugSun.Get() ) + { + AddDebugSunLight(); + } + // derived info if ( r_forceRendererTime.Get() >= 0 ) { tr.refdef.floatTime = float( double( r_forceRendererTime.Get() ) * 0.001 );