Paranoia2/cl_dll/render/gl_deferred.cpp

555 lines
16 KiB
C++

/*
gl_deferred.cpp - deferred rendering
Copyright (C) 2018 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "hud.h"
#include "cl_util.h"
#include "gl_local.h"
#include "gl_shader.h"
#include "gl_world.h"
#include "gl_grass.h"
enum
{
SCENE_PASS = 0,
LIGHT_PASS,
};
enum
{
BILATERAL_PASS0 = 0,
BILATERAL_PASS1,
};
void GL_InitDefSceneFBO( void )
{
GLenum MRTBuffers[5] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT, GL_COLOR_ATTACHMENT4_EXT };
int albedo_buffer;
int normal_buffer;
int smooth_buffer;
int light0_buffer;
int light1_buffer;
int depth_buffer;
if( !GL_Support( R_FRAMEBUFFER_OBJECT ) || !GL_Support( R_DRAW_BUFFERS_EXT ))
return;
// Create and set up the framebuffer
tr.defscene_fbo = GL_AllocDrawbuffer( "*defscene", glState.width, glState.height, 1 );
// create textures
albedo_buffer = CREATE_TEXTURE( "*albedo_rt", glState.width, glState.height, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
normal_buffer = CREATE_TEXTURE( "*normal_rt", glState.width, glState.height, NULL, TF_RT_NORMAL|TF_HAS_ALPHA );
smooth_buffer = CREATE_TEXTURE( "*smooth_rt", glState.width, glState.height, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
light0_buffer = CREATE_TEXTURE( "*light0_rt", glState.width, glState.height, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
light1_buffer = CREATE_TEXTURE( "*light1_rt", glState.width, glState.height, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
depth_buffer = CREATE_TEXTURE( "*depth_rt", glState.width, glState.height, NULL, TF_RT_DEPTH );
GL_AttachColorTextureToFBO( tr.defscene_fbo, albedo_buffer, 0 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, normal_buffer, 1 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, smooth_buffer, 2 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, light0_buffer, 3 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, light1_buffer, 4 );
GL_AttachDepthTextureToFBO( tr.defscene_fbo, depth_buffer );
pglDrawBuffersARB( ARRAYSIZE( MRTBuffers ), MRTBuffers );
pglReadBuffer( GL_NONE );
// check the framebuffer status
GL_CheckFBOStatus( tr.defscene_fbo );
}
void GL_VidInitDefSceneFBO( void )
{
// resize attached textures
GL_ResizeDrawbuffer( tr.defscene_fbo, glState.width, glState.height, 1 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->colortarget[0], 0 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->colortarget[1], 1 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->colortarget[2], 2 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->colortarget[3], 3 );
GL_AttachColorTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->colortarget[4], 4 );
GL_AttachDepthTextureToFBO( tr.defscene_fbo, tr.defscene_fbo->depthtarget );
}
void GL_InitDefLightFBO( void )
{
GLenum MRTBuffers[3] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT };
int normal_buffer;
int light0_buffer;
int light1_buffer;
int depth_buffer;
if( !GL_Support( R_FRAMEBUFFER_OBJECT ) || !GL_Support( R_DRAW_BUFFERS_EXT ))
return;
// Create and set up the framebuffer
tr.deflight_fbo = GL_AllocDrawbuffer( "*deflight", glState.defWidth, glState.defHeight, 1 );
// create textures
normal_buffer = CREATE_TEXTURE( "*normal_rs", glState.defWidth, glState.defHeight, NULL, TF_RT_NORMAL|TF_HAS_ALPHA );
light0_buffer = CREATE_TEXTURE( "*light0_rs", glState.defWidth, glState.defHeight, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
light1_buffer = CREATE_TEXTURE( "*light1_rs", glState.defWidth, glState.defHeight, NULL, TF_RT_COLOR|TF_HAS_ALPHA );
depth_buffer = CREATE_TEXTURE( "*depth_rs", glState.defWidth, glState.defHeight, NULL, TF_RT_DEPTH );
GL_AttachColorTextureToFBO( tr.deflight_fbo, normal_buffer, 0 );
GL_AttachColorTextureToFBO( tr.deflight_fbo, light0_buffer, 1 );
GL_AttachColorTextureToFBO( tr.deflight_fbo, light1_buffer, 2 );
GL_AttachDepthTextureToFBO( tr.deflight_fbo, depth_buffer );
pglDrawBuffersARB( ARRAYSIZE( MRTBuffers ), MRTBuffers );
pglReadBuffer( GL_NONE );
// check the framebuffer status
GL_CheckFBOStatus( tr.deflight_fbo );
}
void GL_VidInitDefLightFBO( void )
{
// resize attached textures
GL_ResizeDrawbuffer( tr.deflight_fbo, glState.defWidth, glState.defHeight, 1 );
GL_AttachColorTextureToFBO( tr.deflight_fbo, tr.deflight_fbo->colortarget[0], 0 );
GL_AttachColorTextureToFBO( tr.deflight_fbo, tr.deflight_fbo->colortarget[1], 1 );
GL_AttachColorTextureToFBO( tr.deflight_fbo, tr.deflight_fbo->colortarget[2], 2 );
GL_AttachDepthTextureToFBO( tr.deflight_fbo, tr.deflight_fbo->depthtarget );
}
void GL_VidInitDrawBuffers( void )
{
if( tr.defscene_fbo == NULL )
GL_InitDefSceneFBO();
else GL_VidInitDefSceneFBO();
if( tr.deflight_fbo == NULL )
GL_InitDefLightFBO();
else GL_VidInitDefLightFBO();
// unbind the framebuffer
GL_BindDrawbuffer( NULL );
}
void GL_SetupGBuffer( void )
{
// bind geometry fullscreen buffer
if( FBitSet( RI->params, RP_DEFERREDSCENE ))
GL_BindDrawbuffer( tr.defscene_fbo );
// bind light-pass downscaled buffer
if( FBitSet( RI->params, RP_DEFERREDLIGHT ))
GL_BindDrawbuffer( tr.deflight_fbo );
R_Clear( ~0 );
}
void GL_ResetGBuffer( void )
{
GL_BindDrawbuffer( NULL );
}
void GL_DrawScreenSpaceQuad( const vec3_t normals[4] )
{
pglBegin( GL_QUADS );
pglTexCoord2f( 0.0f, 1.0f );
pglNormal3fv( normals[0] );
pglVertex2f( 0.0f, 0.0f );
pglTexCoord2f( 1.0f, 1.0f );
pglNormal3fv( normals[3] );
pglVertex2f( RI->view.port[2], 0.0f );
pglTexCoord2f( 1.0f, 0.0f );
pglNormal3fv( normals[2] );
pglVertex2f( RI->view.port[2], RI->view.port[3] );
pglTexCoord2f( 0.0f, 0.0f );
pglNormal3fv( normals[1] );
pglVertex2f( 0.0f, RI->view.port[3] );
pglEnd();
}
/*
=================
GL_Setup2D
=================
*/
void GL_SetupDeferred( void )
{
// set up full screen workspace
pglMatrixMode( GL_PROJECTION );
pglLoadIdentity();
pglOrtho( 0, RI->view.port[2], RI->view.port[3], 0, -99999, 99999 );
pglMatrixMode( GL_MODELVIEW );
pglLoadIdentity();
GL_AlphaTest( GL_FALSE );
GL_DepthMask( GL_FALSE );
pglDisable( GL_DEPTH_TEST ); // older dirvers has issues with this
if( RI->currentlight != NULL )
{
GL_Blend( GL_TRUE );
pglBlendFunc( GL_ONE, GL_ONE );
}
else
{
GL_Blend( GL_FALSE );
}
GL_Cull( GL_FRONT );
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
pglViewport( 0, 0, RI->view.port[2], RI->view.port[3] );
}
static void GL_DrawDeferred( word hProgram, int pass )
{
if( hProgram <= 0 )
{
GL_BindShader( NULL );
return; // bad shader?
}
// prepare deferred pass
GL_SetupDeferred();
if( RI->currentshader != &glsl_programs[hProgram] )
{
// force to bind new shader
GL_BindShader( &glsl_programs[hProgram] );
}
glsl_program_t *shader = RI->currentshader;
CDynLight *pl = RI->currentlight; // may be NULL
Vector4D lightdir;
float *v;
// setup specified uniforms (and texture bindings)
for( int i = 0; i < shader->numUniforms; i++ )
{
uniform_t *u = &shader->uniforms[i];
switch( u->type )
{
case UT_COLORMAP:
u->SetValue( tr.defscene_fbo->colortarget[0] );
break;
case UT_NORMALMAP:
if( pass == LIGHT_PASS )
u->SetValue( tr.deflight_fbo->colortarget[0] );
else u->SetValue( tr.defscene_fbo->colortarget[1] );
break;
case UT_GLOSSMAP:
u->SetValue( tr.defscene_fbo->colortarget[2] );
break;
case UT_VISLIGHTMAP0:
if( pass == LIGHT_PASS )
u->SetValue( tr.deflight_fbo->colortarget[1] );
else u->SetValue( tr.defscene_fbo->colortarget[3] );
break;
case UT_VISLIGHTMAP1:
if( pass == LIGHT_PASS )
u->SetValue( tr.deflight_fbo->colortarget[2] );
else u->SetValue( tr.defscene_fbo->colortarget[4] );
break;
case UT_DEPTHMAP:
if( pass == LIGHT_PASS )
u->SetValue( tr.deflight_fbo->depthtarget );
else u->SetValue( tr.defscene_fbo->depthtarget );
break;
case UT_BSPPLANESMAP:
u->SetValue( tr.packed_planes_texture );
break;
case UT_BSPNODESMAP:
u->SetValue( tr.packed_nodes_texture );
break;
case UT_BSPLIGHTSMAP:
u->SetValue( tr.packed_lights_texture );
break;
case UT_BSPMODELSMAP:
u->SetValue( tr.packed_models_texture );
break;
case UT_SHADOWMAP:
if( pl ) u->SetValue( pl->shadowTexture[0] );
else u->SetValue( tr.fbo_light.GetTexture() );
break;
case UT_SHADOWMAP0:
if( pl ) u->SetValue( pl->shadowTexture[0] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP1:
if( pl ) u->SetValue( pl->shadowTexture[1] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP2:
if( pl ) u->SetValue( pl->shadowTexture[2] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP3:
if( pl ) u->SetValue( pl->shadowTexture[3] );
else u->SetValue( tr.depthTexture );
break;
case UT_PROJECTMAP:
if( pl && pl->type == LIGHT_SPOT )
u->SetValue( pl->spotlightTexture );
else u->SetValue( tr.whiteTexture );
break;
case UT_VIEWORIGIN:
u->SetValue( RI->view.matrix[3][0], RI->view.matrix[3][1], RI->view.matrix[3][2] );
break;
case UT_LIGHTSTYLEVALUES:
u->SetValue( &tr.lightstyle[0], MAX_LIGHTSTYLES );
break;
case UT_GAMMATABLE:
u->SetValue( &tr.gamma_table[0][0], 64 );
break;
case UT_LIGHTTHRESHOLD:
u->SetValue( tr.light_threshold );
break;
case UT_LIGHTSCALE:
u->SetValue( tr.direct_scale );
break;
case UT_LIGHTGAMMA:
u->SetValue( tr.light_gamma );
break;
case UT_ZFAR:
u->SetValue( RI->view.farClip );
break;
case UT_SCREENSIZEINV:
u->SetValue( 1.0f / (float)glState.width, 1.0f / (float)glState.height );
break;
case UT_DIFFUSEFACTOR:
u->SetValue( tr.diffuseFactor );
break;
case UT_AMBIENTFACTOR:
if( pl && pl->type == LIGHT_DIRECTIONAL )
u->SetValue( tr.sun_ambient );
else u->SetValue( tr.ambientFactor );
break;
case UT_SUNREFRACT:
u->SetValue( tr.sun_refract );
break;
case UT_REALTIME:
u->SetValue( (float)tr.time );
break;
case UT_FOGPARAMS:
u->SetValue( tr.fogColor[0], tr.fogColor[1], tr.fogColor[2], tr.fogDensity );
break;
case UT_SHADOWPARMS:
if( pl != NULL )
{
float shadowWidth = 1.0f / (float)RENDER_GET_PARM( PARM_TEX_WIDTH, pl->shadowTexture[0] );
float shadowHeight = 1.0f / (float)RENDER_GET_PARM( PARM_TEX_HEIGHT, pl->shadowTexture[0] );
// depth scale and bias and shadowmap resolution
u->SetValue( shadowWidth, shadowHeight, -pl->projectionMatrix[2][2], pl->projectionMatrix[3][2] );
}
else u->SetValue( 0.0f, 0.0f, 0.0f, 0.0f );
break;
case UT_SHADOWMATRIX:
if( pl ) u->SetValue( &pl->gl_shadowMatrix[0][0], MAX_SHADOWMAPS );
break;
case UT_SHADOWSPLITDIST:
v = RI->view.parallelSplitDistances;
u->SetValue( v[0], v[1], v[2], v[3] );
break;
case UT_TEXELSIZE:
u->SetValue( 1.0f / (float)sunSize[0], 1.0f / (float)sunSize[1], 1.0f / (float)sunSize[2], 1.0f / (float)sunSize[3] );
break;
case UT_LIGHTDIR:
if( pl )
{
if( pl->type == LIGHT_DIRECTIONAL ) lightdir = -tr.sky_normal;
else lightdir = pl->frustum.GetPlane( FRUSTUM_FAR )->normal;
u->SetValue( lightdir.x, lightdir.y, lightdir.z, pl->fov );
}
break;
case UT_LIGHTDIFFUSE:
if( pl ) u->SetValue( pl->color.x, pl->color.y, pl->color.z );
break;
case UT_LIGHTORIGIN:
if( pl ) u->SetValue( pl->origin.x, pl->origin.y, pl->origin.z, ( 1.0f / pl->radius ));
break;
case UT_LIGHTVIEWPROJMATRIX:
if( pl )
{
GLfloat gl_lightViewProjMatrix[16];
pl->lightviewProjMatrix.CopyToArray( gl_lightViewProjMatrix );
u->SetValue( &gl_lightViewProjMatrix[0] );
}
break;
case UT_NUMVISIBLEMODELS:
u->SetValue( world->num_visible_models );
break;
default:
ALERT( at_error, "%s: unhandled uniform %s\n", RI->currentshader->name, u->name );
break;
}
}
GL_DrawScreenSpaceQuad( tr.screen_normals );
}
static void GL_DrawBilateralFilter( word hProgram, int pass )
{
if( hProgram <= 0 )
{
GL_BindShader( NULL );
return; // bad shader?
}
if( RI->currentshader != &glsl_programs[hProgram] )
{
// force to bind new shader
GL_BindShader( &glsl_programs[hProgram] );
}
glsl_program_t *shader = RI->currentshader;
// setup specified uniforms (and texture bindings)
for( int i = 0; i < shader->numUniforms; i++ )
{
uniform_t *u = &shader->uniforms[i];
switch( u->type )
{
case UT_COLORMAP:
if( pass == BILATERAL_PASS0 )
u->SetValue( tr.fbo_light.GetTexture() );
else if( pass == BILATERAL_PASS1 )
u->SetValue( tr.fbo_filter.GetTexture() );
else u->SetValue( tr.defscene_fbo->colortarget[0] );
break;
case UT_DEPTHMAP:
u->SetValue( tr.defscene_fbo->depthtarget );
break;
case UT_ZFAR:
u->SetValue( RI->view.farClip );
break;
case UT_SCREENSIZEINV:
if( pass == BILATERAL_PASS0 )
u->SetValue( 1.0f / (float)glState.width, 0.0f );
else if( pass == BILATERAL_PASS1 )
u->SetValue( 0.0f, 1.0f / (float)glState.height );
else u->SetValue( 1.0f / (float)glState.width, 1.0f / (float)glState.height );
break;
case UT_SHADOWPARMS:
u->SetValue( RI->view.projectionMatrix[3][2], RI->view.projectionMatrix[2][2] );
break;
default:
ALERT( at_error, "%s: unhandled uniform %s\n", RI->currentshader->name, u->name );
break;
}
}
RenderFSQ( glState.width, glState.height );
}
void GL_SetupWorldLightPass( void )
{
tr.fbo_shadow.Bind(); // <- render to shadow texture
// FIXME! modelview isn't be changed anyway until current pass!!!
// pglMatrixMode( GL_MODELVIEW );
// pglLoadMatrixf( RI->glstate.modelviewMatrix );
GL_DrawDeferred( tr.defLightShader, LIGHT_PASS );
GL_CleanupAllTextureUnits();
GL_Setup2D();
tr.fbo_light.Bind(); // <- copy to fbo_light result of works complex light shader
GL_BindShader( NULL );
GL_BindTexture( GL_TEXTURE0, tr.fbo_shadow.GetTexture() );
RenderFSQ( glState.width, glState.height );
if( tr.bilateralShader )
{
// apply bilateral filter
tr.fbo_filter.Bind(); // <- bilateral pass0, filtering by width, store to fbo_filter
GL_DrawBilateralFilter( tr.bilateralShader, BILATERAL_PASS0 );
tr.fbo_light.Bind(); // <- bilateral pass1, filtering by height, store back to fbo_light
GL_DrawBilateralFilter( tr.bilateralShader, BILATERAL_PASS1 );
}
}
void GL_SetupDynamicPass( void )
{
if( !FBitSet( RI->view.flags, RF_HASDYNLIGHTS ))
return;
pglEnable( GL_SCISSOR_TEST );
CDynLight *pl = tr.dlights;
for( int i = 0; i < MAX_DLIGHTS; i++, pl++ )
{
// deferred path are ignored sunlights
if( pl->Expired( ) || pl->type == LIGHT_DIRECTIONAL )
continue;
if( !pl->Active( )) continue;
if( !Mod_CheckBoxVisible( pl->absmin, pl->absmax ))
continue;
if( R_CullFrustum( &pl->frustum ))
continue;
float y2 = (float)RI->view.port[3] - pl->h - pl->y;
pglScissor( pl->x, y2, pl->w, pl->h );
RI->currentlight = pl;
GL_DrawDeferred( tr.defDynLightShader[pl->type], SCENE_PASS );
}
pglDisable( GL_SCISSOR_TEST );
RI->currentlight = NULL;
}
void GL_SetupWorldScenePass( void )
{
mworldlight_t *wl;
int i;
// debug
for( i = 0, wl = world->worldlights; i < world->numworldlights; i++, wl++ )
{
if( wl->emittype == emit_ignored )
continue;
if( !CHECKVISBIT( RI->view.vislight, i ))
continue;
r_stats.c_worldlights++;
}
int type = CVAR_TO_BOOL( cv_deferred_full ) ? 1 : 0;
GL_DrawDeferred( tr.defSceneShader[type], SCENE_PASS );
// also render a standard dynamic lights
GL_SetupDynamicPass();
}
void GL_DrawDeferredPass( void )
{
if( !tr.defSceneShader || !tr.defLightShader )
return; // oops!
GL_ComputeScreenRays();
if( FBitSet( RI->params, RP_DEFERREDSCENE ))
GL_SetupWorldScenePass();
if( FBitSet( RI->params, RP_DEFERREDLIGHT ))
GL_SetupWorldLightPass();
GL_CleanupDrawState();
GL_BindFBO( FBO_MAIN );
GL_Setup3D();
}