2006-03-03 03:31:19 +00:00
/*
Copyright ( C ) 1997 - 2001 Id Software , Inc .
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 2
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
2010-01-17 06:42:30 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
2006-03-03 03:31:19 +00:00
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
2008-02-21 03:19:46 +00:00
// gl_bloom.c: 2D lighting post process effect
2006-03-03 03:31:19 +00:00
2012-01-17 07:57:46 +00:00
/*
info about bloom algo :
bloom is basically smudging .
screen is nearest - downsampled to some usable scale and filtered to remove low - value light ( this is what stops non - bright stuff from blooming )
this filtered image is then downsized multiple times
the downsized image is then blured
the downsized images are then blured horizontally , and then vertically .
final pass simply adds each blured level to the original image .
all samples are then added together for final rendering ( with some kind of tone mapping if you want proper hdr ) .
note : the horizontal / vertical bluring is a guassian filter
note : bloom comes from the fact that the most downsampled image doesn ' t have too many pixels . the pixels that it does have are spread over a large area .
http : //prideout.net/archive/bloom/ contains some sample code
*/
2006-03-03 03:31:19 +00:00
//http://www.quakesrc.org/forums/viewtopic.php?t=4340&start=0
# include "quakedef.h"
2007-05-25 22:16:29 +00:00
2016-07-28 13:18:22 +00:00
# if defined(GLQUAKE) || defined(VKQUAKE)
2012-01-17 07:57:46 +00:00
# include "shader.h"
# include "glquake.h"
2015-08-07 17:49:49 +00:00
# include "gl_draw.h"
2012-05-11 01:57:00 +00:00
cvar_t r_bloom = CVARAFD ( " r_bloom " , " 0 " , " gl_bloom " , CVAR_ARCHIVE , " Enables bloom (light bleeding from bright objects). Fractional values reduce the amount shown. " ) ;
2015-09-06 03:30:28 +00:00
cvar_t r_bloom_retain = CVARD ( " r_bloom_retain " , " 1 " , " How much of the regular scene to retain when bloom is active. " ) ;
2015-04-14 23:12:17 +00:00
cvar_t r_bloom_filter = CVARD ( " r_bloom_filter " , " 0.7 0.7 0.7 " , " Controls how bright the image must get before it will bloom (3 separate values, in RGB order). " ) ;
2015-07-14 14:47:00 +00:00
cvar_t r_bloom_size = CVARD ( " r_bloom_size " , " 4 " , " Target bloom kernel size (assuming a video width of 320). " ) ;
cvar_t r_bloom_downsize = CVARD ( " r_bloom_downsize " , " 0 " , " Technically more correct with a value of 1, but you probably won't notice. " ) ;
cvar_t r_bloom_initialscale = CVARD ( " r_bloom_initialscale " , " 1 " , " Initial scaling factor for bloom. should be either 1 or 0.5 " ) ;
2012-01-17 07:57:46 +00:00
static shader_t * bloomfilter ;
static shader_t * bloomrescale ;
static shader_t * bloomblur ;
static shader_t * bloomfinal ;
2015-07-14 14:47:00 +00:00
# define MAXLEVELS 16
2012-01-17 07:57:46 +00:00
texid_t pingtex [ 2 ] [ MAXLEVELS ] ;
2014-02-07 08:38:40 +00:00
fbostate_t fbo_bloom ;
2012-01-17 07:57:46 +00:00
static int scrwidth , scrheight ;
static int texwidth [ MAXLEVELS ] , texheight [ MAXLEVELS ] ;
2014-08-10 00:31:37 +00:00
static void R_InitBloomTextures ( void )
{
int i ;
2012-01-17 07:57:46 +00:00
2014-08-10 00:31:37 +00:00
bloomfilter = NULL ;
bloomblur = NULL ;
bloomfinal = NULL ;
scrwidth = 0 , scrheight = 0 ;
for ( i = 0 ; i < MAXLEVELS ; i + + )
{
pingtex [ 0 ] [ i ] = r_nulltex ;
pingtex [ 1 ] [ i ] = r_nulltex ;
}
}
2012-01-17 07:57:46 +00:00
void R_BloomRegister ( void )
{
Cvar_Register ( & r_bloom , " bloom " ) ;
2015-09-06 03:30:28 +00:00
Cvar_Register ( & r_bloom_retain , " bloom " ) ;
2012-05-11 01:57:00 +00:00
Cvar_Register ( & r_bloom_filter , " bloom " ) ;
2015-07-14 14:47:00 +00:00
Cvar_Register ( & r_bloom_size , " bloom " ) ;
Cvar_Register ( & r_bloom_downsize , " bloom " ) ;
Cvar_Register ( & r_bloom_initialscale , " bloom " ) ;
2012-01-17 07:57:46 +00:00
}
static void R_SetupBloomTextures ( int w , int h )
{
int i , j ;
2015-07-14 14:47:00 +00:00
if ( w = = scrwidth & & h = = scrheight & & ! r_bloom_initialscale . modified )
2012-01-17 07:57:46 +00:00
return ;
2015-07-14 14:47:00 +00:00
r_bloom_initialscale . modified = false ;
2012-01-17 07:57:46 +00:00
scrwidth = w ;
scrheight = h ;
2012-05-11 01:57:00 +00:00
//I'm depending on npot here
2015-07-14 14:47:00 +00:00
w * = r_bloom_initialscale . value ;
h * = r_bloom_initialscale . value ;
2012-01-17 07:57:46 +00:00
for ( i = 0 ; i < MAXLEVELS ; i + + )
{
/*I'm paranoid*/
if ( w < 4 )
w = 4 ;
if ( h < 4 )
h = 4 ;
texwidth [ i ] = w ;
texheight [ i ] = h ;
2015-07-14 14:47:00 +00:00
w / = 2 ;
h / = 2 ;
2012-01-17 07:57:46 +00:00
}
2015-07-14 14:47:00 +00:00
/*destroy textures for each level, to ensure they're created fresh as needed*/
2012-01-17 07:57:46 +00:00
for ( j = 0 ; j < MAXLEVELS ; j + + )
{
for ( i = 0 ; i < 2 ; i + + )
{
2015-07-14 14:47:00 +00:00
if ( TEXVALID ( pingtex [ i ] [ j ] ) )
Image_UnloadTexture ( pingtex [ i ] [ j ] ) ;
pingtex [ i ] [ j ] = r_nulltex ;
2012-01-17 07:57:46 +00:00
}
}
2013-08-21 07:14:39 +00:00
bloomfilter = R_RegisterShader ( " bloom_filter " , SUF_NONE ,
2012-01-17 07:57:46 +00:00
" { \n "
" cull none \n "
" program bloom_filter \n "
" { \n "
" map $sourcecolour \n "
" } \n "
" } \n " ) ;
2013-08-21 07:14:39 +00:00
bloomrescale = R_RegisterShader ( " bloom_rescale " , SUF_NONE ,
2012-01-17 07:57:46 +00:00
" { \n "
" cull none \n "
" program default2d \n "
" { \n "
" map $sourcecolour \n "
" } \n "
" } \n " ) ;
2013-08-21 07:14:39 +00:00
bloomblur = R_RegisterShader ( " bloom_blur " , SUF_NONE ,
2012-01-17 07:57:46 +00:00
" { \n "
" cull none \n "
" program bloom_blur \n "
" { \n "
" map $sourcecolour \n "
" } \n "
" } \n " ) ;
2013-08-21 07:14:39 +00:00
bloomfinal = R_RegisterShader ( " bloom_final " , SUF_NONE ,
2012-01-17 07:57:46 +00:00
" { \n "
" cull none \n "
" program bloom_final \n "
" { \n "
" map $sourcecolour \n "
" } \n "
" { \n "
" map $diffuse \n "
" } \n "
" { \n "
" map $loweroverlay \n "
" } \n "
" { \n "
" map $upperoverlay \n "
" } \n "
" } \n " ) ;
}
2014-08-19 06:08:23 +00:00
qboolean R_CanBloom ( void )
2012-01-17 07:57:46 +00:00
{
2014-08-19 06:08:23 +00:00
if ( ! r_bloom . value )
return false ;
2016-07-28 13:18:22 +00:00
switch ( qrenderer )
{
# ifdef GLQUAKE
case QR_OPENGL :
if ( ! gl_config . ext_framebuffer_objects )
return false ;
if ( ! gl_config . arb_shader_objects )
return false ;
if ( ! sh_config . texture_non_power_of_two_pic )
return false ;
break ;
# endif
# ifdef VKQUAKE
case QR_VULKAN :
break ;
# endif
default :
2014-08-19 06:08:23 +00:00
return false ;
2016-07-28 13:18:22 +00:00
}
2014-08-19 06:08:23 +00:00
return true ;
}
2016-07-28 13:18:22 +00:00
# ifdef VKQUAKE
# include "../vk/vkrenderer.h"
struct vk_rendertarg vk_rt_bloom [ 2 ] [ MAXLEVELS ] , vk_rt_filter ;
void VK_R_BloomBlend ( texid_t source , int x , int y , int w , int h )
{
int i ;
2017-09-20 11:27:13 +00:00
// struct vk_rendertarg *oldfbo = vk.rendertarg;
2016-07-28 13:18:22 +00:00
texid_t intex ;
int pixels = 1 ;
int targetpixels = r_bloom_size . value * vid . pixelwidth / 320 ;
targetpixels * = r_bloom_initialscale . value ;
/*whu?*/
if ( ! w | | ! h )
return ;
/*update textures if we need to resize them*/
R_SetupBloomTextures ( w , h ) ;
if ( R2D_Flush )
R2D_Flush ( ) ;
# if 1
/*filter the screen into a downscaled image*/
2017-07-28 01:49:25 +00:00
VKBE_RT_Gen ( & vk_rt_filter , texwidth [ 0 ] , texheight [ 0 ] , false , RT_IMAGEFLAGS ) ;
2016-07-28 13:18:22 +00:00
VKBE_RT_Begin ( & vk_rt_filter ) ;
vk . sourcecolour = source ;
R2D_ScalePic ( 0 , 0 , vid . width , vid . height , bloomfilter ) ;
2017-07-28 01:49:25 +00:00
VKBE_RT_End ( & vk_rt_filter ) ;
2016-07-28 13:18:22 +00:00
intex = & vk_rt_filter . q_colour ;
# else
intex = source ;
# endif
for ( pixels = 1 , i = 0 ; pixels < targetpixels & & i < MAXLEVELS ; i + + , pixels < < = 1 )
{
//downsize the blur, for added accuracy
/*if (i > 0 && r_bloom_downsize.ival)
{
//simple downscale that multiple times
VKBE_RT_Gen ( & vk_rt_bloom [ 0 ] [ i ] , texwidth [ i ] , texheight [ i ] , false ) ;
VKBE_RT_Begin ( & vk_rt_bloom [ 0 ] [ i ] ) ;
vk . sourcecolour = source ;
R2D_ScalePic ( 0 , vid . height , vid . width , - ( int ) vid . height , bloomrescale ) ;
if ( R2D_Flush )
R2D_Flush ( ) ;
intex = & vk_rt_bloom [ 0 ] [ i ] ;
r_worldentity . glowmod [ 0 ] = 1.0 / intex - > width ;
}
else */
r_worldentity . glowmod [ 0 ] = 2.0 / intex - > width ;
r_worldentity . glowmod [ 1 ] = 0 ;
2017-07-28 01:49:25 +00:00
VKBE_RT_Gen ( & vk_rt_bloom [ 1 ] [ i ] , texwidth [ i ] , texheight [ i ] , false , RT_IMAGEFLAGS ) ;
2016-07-28 13:18:22 +00:00
VKBE_RT_Begin ( & vk_rt_bloom [ 1 ] [ i ] ) ;
vk . sourcecolour = intex ;
BE_SelectEntity ( & r_worldentity ) ;
R2D_ScalePic ( 0 , 0 , vid . width , vid . height , bloomblur ) ;
2017-07-28 01:49:25 +00:00
VKBE_RT_End ( & vk_rt_bloom [ 1 ] [ i ] ) ;
2016-07-28 13:18:22 +00:00
r_worldentity . glowmod [ 0 ] = 0 ;
r_worldentity . glowmod [ 1 ] = 1.0 / texheight [ i ] ;
2017-07-28 01:49:25 +00:00
VKBE_RT_Gen ( & vk_rt_bloom [ 0 ] [ i ] , texwidth [ i ] , texheight [ i ] , false , RT_IMAGEFLAGS ) ;
2016-07-28 13:18:22 +00:00
VKBE_RT_Begin ( & vk_rt_bloom [ 0 ] [ i ] ) ;
vk . sourcecolour = & vk_rt_bloom [ 1 ] [ i ] . q_colour ;
BE_SelectEntity ( & r_worldentity ) ;
R2D_ScalePic ( 0 , 0 , vid . width , vid . height , bloomblur ) ;
2017-07-28 01:49:25 +00:00
VKBE_RT_End ( & vk_rt_bloom [ 0 ] [ i ] ) ;
2016-07-28 13:18:22 +00:00
intex = & vk_rt_bloom [ 0 ] [ i ] . q_colour ;
}
r_worldentity . glowmod [ 0 ] = 0 ;
r_worldentity . glowmod [ 1 ] = 0 ;
/*combine them onto the screen*/
bloomfinal - > defaulttextures - > base = intex ;
bloomfinal - > defaulttextures - > loweroverlay = ( i > = 2 ) ? & vk_rt_bloom [ 0 ] [ i - 2 ] . q_colour : 0 ;
bloomfinal - > defaulttextures - > upperoverlay = ( i > = 3 ) ? & vk_rt_bloom [ 0 ] [ i - 3 ] . q_colour : 0 ;
vk . sourcecolour = source ;
R2D_ScalePic ( x , y , w , h , bloomfinal ) ;
R2D_Flush ( ) ;
}
void VK_R_BloomShutdown ( void )
{
int i ;
for ( i = 0 ; i < MAXLEVELS ; i + + )
{
2017-07-28 01:49:25 +00:00
VKBE_RT_Gen ( & vk_rt_bloom [ 0 ] [ i ] , 0 , 0 , false , RT_IMAGEFLAGS ) ;
VKBE_RT_Gen ( & vk_rt_bloom [ 1 ] [ i ] , 0 , 0 , false , RT_IMAGEFLAGS ) ;
2016-07-28 13:18:22 +00:00
}
2017-07-28 01:49:25 +00:00
VKBE_RT_Gen ( & vk_rt_filter , 0 , 0 , false , RT_IMAGEFLAGS ) ;
2016-07-28 13:18:22 +00:00
R_InitBloomTextures ( ) ;
}
# endif
# ifdef GLQUAKE
2014-08-19 06:08:23 +00:00
void R_BloomBlend ( texid_t source , int x , int y , int w , int h )
{
int i ;
int oldfbo = 0 ;
2015-07-14 14:47:00 +00:00
texid_t intex ;
int pixels = 1 ;
int targetpixels = r_bloom_size . value * vid . pixelwidth / 320 ;
char name [ 64 ] ;
targetpixels * = r_bloom_initialscale . value ;
2012-01-17 07:57:46 +00:00
/*whu?*/
2014-08-19 06:08:23 +00:00
if ( ! w | | ! h )
2012-01-17 07:57:46 +00:00
return ;
/*update textures if we need to resize them*/
2014-08-19 06:08:23 +00:00
R_SetupBloomTextures ( w , h ) ;
2012-01-17 07:57:46 +00:00
2015-07-14 14:47:00 +00:00
/*filter the screen into a downscaled image*/
if ( ! TEXVALID ( pingtex [ 0 ] [ 0 ] ) )
2012-01-17 07:57:46 +00:00
{
2015-07-14 14:47:00 +00:00
sprintf ( name , " ***bloom*%c*%i*** " , ' a ' + 0 , 0 ) ;
TEXASSIGN ( pingtex [ 0 ] [ 0 ] , Image_CreateTexture ( name , NULL , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ) ;
Image_Upload ( pingtex [ 0 ] [ 0 ] , TF_RGBA32 , NULL , NULL , texwidth [ 0 ] , texheight [ 0 ] , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ;
}
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2015-07-14 14:47:00 +00:00
oldfbo = GLBE_FBO_Update ( & fbo_bloom , 0 , & pingtex [ 0 ] [ 0 ] , 1 , r_nulltex , 0 , 0 , 0 ) ;
GLBE_FBO_Sources ( source , r_nulltex ) ;
qglViewport ( 0 , 0 , texwidth [ 0 ] , texheight [ 0 ] ) ;
R2D_ScalePic ( 0 , vid . height , vid . width , - ( int ) vid . height , bloomfilter ) ;
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2015-07-14 14:47:00 +00:00
intex = pingtex [ 0 ] [ 0 ] ;
for ( pixels = 1 , i = 0 ; pixels < targetpixels & & i < MAXLEVELS ; i + + , pixels < < = 1 )
{
/*create any textures if they're not valid yet*/
if ( ! TEXVALID ( pingtex [ 0 ] [ i ] ) )
2013-03-12 23:18:37 +00:00
{
2015-07-14 14:47:00 +00:00
sprintf ( name , " ***bloom*%c*%i*** " , ' a ' + 0 , i ) ;
TEXASSIGN ( pingtex [ 0 ] [ i ] , Image_CreateTexture ( name , NULL , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ) ;
Image_Upload ( pingtex [ 0 ] [ i ] , TF_RGBA32 , NULL , NULL , texwidth [ i ] , texheight [ i ] , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ;
2013-03-12 23:18:37 +00:00
}
2015-07-14 14:47:00 +00:00
if ( ! TEXVALID ( pingtex [ 1 ] [ i ] ) )
{
sprintf ( name , " ***bloom*%c*%i*** " , ' a ' + 1 , i ) ;
TEXASSIGN ( pingtex [ 1 ] [ i ] , Image_CreateTexture ( name , NULL , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ) ;
Image_Upload ( pingtex [ 1 ] [ i ] , TF_RGBA32 , NULL , NULL , texwidth [ i ] , texheight [ i ] , IF_CLAMP | IF_NOMIPMAP | IF_NOPICMIP | IF_LINEAR ) ;
}
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2015-07-14 14:47:00 +00:00
//downsize the blur, for added accuracy
if ( i > 0 & & r_bloom_downsize . ival )
2013-03-12 23:18:37 +00:00
{
/*simple downscale that multiple times*/
2015-07-06 14:47:46 +00:00
GLBE_FBO_Update ( & fbo_bloom , 0 , & pingtex [ 0 ] [ i ] , 1 , r_nulltex , 0 , 0 , 0 ) ;
2014-02-07 08:38:40 +00:00
GLBE_FBO_Sources ( pingtex [ 0 ] [ i - 1 ] , r_nulltex ) ;
2013-03-12 23:18:37 +00:00
qglViewport ( 0 , 0 , texwidth [ i ] , texheight [ i ] ) ;
R2D_ScalePic ( 0 , vid . height , vid . width , - ( int ) vid . height , bloomrescale ) ;
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2015-07-14 14:47:00 +00:00
intex = pingtex [ 0 ] [ i ] ;
r_worldentity . glowmod [ 0 ] = 1.0 / intex - > width ;
2013-03-12 23:18:37 +00:00
}
2015-07-14 14:47:00 +00:00
else
r_worldentity . glowmod [ 0 ] = 2.0 / intex - > width ;
2012-01-17 07:57:46 +00:00
r_worldentity . glowmod [ 1 ] = 0 ;
2015-07-06 14:47:46 +00:00
GLBE_FBO_Update ( & fbo_bloom , 0 , & pingtex [ 1 ] [ i ] , 1 , r_nulltex , 0 , 0 , 0 ) ;
2015-07-14 14:47:00 +00:00
GLBE_FBO_Sources ( intex , r_nulltex ) ;
qglViewport ( 0 , 0 , pingtex [ 1 ] [ i ] - > width , pingtex [ 1 ] [ i ] - > height ) ;
2014-11-01 09:09:58 +00:00
BE_SelectEntity ( & r_worldentity ) ;
2012-01-17 07:57:46 +00:00
R2D_ScalePic ( 0 , vid . height , vid . width , - ( int ) vid . height , bloomblur ) ;
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2013-03-12 23:18:37 +00:00
2012-01-17 07:57:46 +00:00
r_worldentity . glowmod [ 0 ] = 0 ;
2015-07-14 14:47:00 +00:00
r_worldentity . glowmod [ 1 ] = 1.0 / pingtex [ 1 ] [ i ] - > height ;
2015-07-06 14:47:46 +00:00
GLBE_FBO_Update ( & fbo_bloom , 0 , & pingtex [ 0 ] [ i ] , 1 , r_nulltex , 0 , 0 , 0 ) ;
2014-02-07 08:38:40 +00:00
GLBE_FBO_Sources ( pingtex [ 1 ] [ i ] , r_nulltex ) ;
2015-07-14 14:47:00 +00:00
qglViewport ( 0 , 0 , pingtex [ 0 ] [ i ] - > width , pingtex [ 0 ] [ i ] - > height ) ;
2014-11-01 09:09:58 +00:00
BE_SelectEntity ( & r_worldentity ) ;
2012-01-17 07:57:46 +00:00
R2D_ScalePic ( 0 , vid . height , vid . width , - ( int ) vid . height , bloomblur ) ;
2015-07-14 14:47:00 +00:00
intex = pingtex [ 0 ] [ i ] ;
2012-01-17 07:57:46 +00:00
}
2013-11-21 23:02:28 +00:00
r_worldentity . glowmod [ 0 ] = 0 ;
r_worldentity . glowmod [ 1 ] = 0 ;
2012-01-17 07:57:46 +00:00
2015-08-07 17:49:49 +00:00
if ( R2D_Flush )
R2D_Flush ( ) ;
2012-01-17 07:57:46 +00:00
GL_Set2D ( false ) ;
2015-07-14 14:47:00 +00:00
bloomfinal - > defaulttextures - > base = intex ;
bloomfinal - > defaulttextures - > loweroverlay = ( i > = 2 ) ? pingtex [ 0 ] [ i - 2 ] : 0 ;
bloomfinal - > defaulttextures - > upperoverlay = ( i > = 3 ) ? pingtex [ 0 ] [ i - 3 ] : 0 ;
2012-01-17 07:57:46 +00:00
/*combine them onto the screen*/
2014-02-07 08:38:40 +00:00
GLBE_FBO_Pop ( oldfbo ) ;
2014-08-19 06:08:23 +00:00
GLBE_FBO_Sources ( source , r_nulltex ) ;
R2D_ScalePic ( x , y + h , w , - h , bloomfinal ) ;
2012-01-17 07:57:46 +00:00
}
2014-08-10 00:31:37 +00:00
void R_BloomShutdown ( void )
2012-01-17 07:57:46 +00:00
{
2014-08-10 00:31:37 +00:00
GLBE_FBO_Destroy ( & fbo_bloom ) ;
2012-01-17 07:57:46 +00:00
2014-08-10 00:31:37 +00:00
R_InitBloomTextures ( ) ;
2012-01-17 07:57:46 +00:00
}
2016-07-28 13:18:22 +00:00
# endif
2012-01-17 07:57:46 +00:00
2007-03-28 13:27:35 +00:00
# endif