diff --git a/src/engine/renderer/glsl_source/lighttile_fp.glsl b/src/engine/renderer/glsl_source/lighttile_fp.glsl index 4548c07b9d..91a0bcbad7 100644 --- a/src/engine/renderer/glsl_source/lighttile_fp.glsl +++ b/src/engine/renderer/glsl_source/lighttile_fp.glsl @@ -130,6 +130,16 @@ 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 ); + // Directional lights (sun) have infinite extent, so always include them in tiles + if ( l.type == 2 ) { + pushIdxs( ( i / uint( NUM_LIGHT_LAYERS ) ) + 1u, lightCount, idxs ); + lightCount++; + if( lightCount == lightsPerLayer ) { + break; + } + continue; + } + 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 41f26a2de6..cce1d14ced 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -37,6 +37,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 ) { @@ -1303,7 +1305,7 @@ static void RenderDepthTiles() { RB_PrepareForSamplingDepthMap(); } - + TransitionMSAAToMain( GL_DEPTH_BUFFER_BIT ); // 1st step @@ -1319,7 +1321,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; @@ -1753,7 +1755,7 @@ void RB_CameraPostFX() { // tr.mainFBO R_BindNullFBO(); gl_cameraEffectsShader->SetUniform_CurrentMapBindless( - GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) + GL_BindToTMU( 0, tr.currentRenderImage[backEnd.currentMainFBO] ) ); if ( glConfig.colorGrading ) { @@ -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 ) @@ -2879,7 +3000,7 @@ static void RB_RenderPostProcess() static void SetFrameUniforms() { // This can happen with glsl_restart/vid_restart in R_SyncRenderThread() - if ( !stagingBuffer.Active() ) { + if ( !stagingBuffer.Active() || globalUBOProxy == nullptr ) { return; } @@ -3489,6 +3610,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 e14632ef18..07fe7e8c34 100644 --- a/src/engine/renderer/tr_scene.cpp +++ b/src/engine/renderer/tr_scene.cpp @@ -346,7 +346,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; @@ -467,6 +467,94 @@ 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::NONE, 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_debugProjLightIntensity( "r_debugProjLightIntensity", "debug projected intensity (scale)", Cvar::NONE, 1.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 + 1 >= 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 ); + + // Color and intensity + light->color[ 0 ] = r_debugProjLightR.Get(); + light->color[ 1 ] = r_debugProjLightG.Get(); + light->color[ 2 ] = r_debugProjLightB.Get(); + light->scale = r_debugProjLightIntensity.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::NONE, 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_debugSunIntensity( "r_debugSunIntensity", "debug sun intensity (scale)", Cvar::NONE, 1.0f, 0.0f, 100.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 + 1 >= 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 ); + // Color and intensity + sun->color[ 0 ] = r_debugSunR.Get(); + sun->color[ 1 ] = r_debugSunG.Get(); + sun->color[ 2 ] = r_debugSunB.Get(); + sun->scale = r_debugSunIntensity.Get(); +} + /* @@@@@@@@@@@@@@@@@@@@@ RE_RenderScene @@ -542,6 +630,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 ); diff --git a/src/engine/renderer/tr_shade.cpp b/src/engine/renderer/tr_shade.cpp index 7d1a1d5c15..4de7679828 100644 --- a/src/engine/renderer/tr_shade.cpp +++ b/src/engine/renderer/tr_shade.cpp @@ -198,7 +198,7 @@ static void EnableAvailableFeatures() glConfig.usingGeometryCache = glConfig.usingMaterialSystem && glConfig.geometryCacheAvailable; } -// For shaders that require map data for compile-time values +// For shaders that require map data for compile-time values void GLSL_InitWorldShaders() { // make sure the render thread is stopped R_SyncRenderThread(); @@ -256,11 +256,6 @@ static void GLSL_InitGPUShadersOrError() gl_depthReductionShader->MarkProgramForBuilding(); } - if ( tr.world ) // this only happens with /glsl_restart - { - GLSL_InitWorldShaders(); - } - if ( glConfig.realtimeLighting ) { gl_shaderManager.LoadShader( gl_depthtile1Shader ); @@ -344,7 +339,7 @@ static void GLSL_InitGPUShadersOrError() gl_contrastShader->MarkProgramForBuilding(); } - + // portal process effect gl_shaderManager.LoadShader( gl_portalShader ); @@ -397,6 +392,12 @@ static void GLSL_InitGPUShadersOrError() gl_shaderManager.PostProcessGlobalUniforms(); gl_shaderManager.InitShaders(); + // Init world shaders last so that everyhthing is already initialized. + if ( tr.world ) // this only happens with /glsl_restart + { + GLSL_InitWorldShaders(); + } + if ( r_lazyShaders.Get() == 0 ) { gl_shaderManager.BuildAll( false ); @@ -1481,7 +1482,7 @@ void Render_heatHaze( shaderStage_t *pStage ) // bind u_NormalMap gl_heatHazeShader->SetUniform_NormalMapBindless( - GL_BindToTMU( 0, pStage->bundle[TB_NORMALMAP].image[0] ) + GL_BindToTMU( 0, pStage->bundle[TB_NORMALMAP].image[0] ) ); if ( pStage->enableNormalMapping ) @@ -1497,7 +1498,7 @@ void Render_heatHaze( shaderStage_t *pStage ) // bind u_CurrentMap gl_heatHazeShader->SetUniform_CurrentMapBindless( - GL_BindToTMU( 1, tr.currentRenderImage[backEnd.currentMainFBO] ) + GL_BindToTMU( 1, tr.currentRenderImage[backEnd.currentMainFBO] ) ); gl_heatHazeShader->SetRequiredVertexPointers(); @@ -1507,7 +1508,7 @@ void Render_heatHaze( shaderStage_t *pStage ) // copy to foreground image R_BindFBO( tr.mainFBO[ backEnd.currentMainFBO ] ); gl_heatHazeShader->SetUniform_CurrentMapBindless( - GL_BindToTMU( 1, tr.currentRenderImage[1 - backEnd.currentMainFBO] ) + GL_BindToTMU( 1, tr.currentRenderImage[1 - backEnd.currentMainFBO] ) ); gl_heatHazeShader->SetUniform_DeformMagnitude( 0.0f ); Tess_DrawElements();