cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320", "what resolution to perform the bloom effect at (independent of screen resolution)"};
cvar_t r_bloom_power = {CVAR_SAVE, "r_bloom_power", "2", "how much to darken the image before blurring to make the bloom effect"};
+cvar_t r_hdr = {CVAR_SAVE, "r_hdr", "0", "enables High Dynamic Range bloom effect (higher quality version of r_bloom)"};
+cvar_t r_hdr_scenebrightness = {CVAR_SAVE, "r_hdr_scenebrightness", "1", "global rendering brightness"};
+cvar_t r_hdr_bloomintensity = {CVAR_SAVE, "r_hdr_bloomintensity", "0.5", "amount of bloom"};
+cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"};
+
cvar_t r_smoothnormals_areaweighting = {0, "r_smoothnormals_areaweighting", "1", "uses significantly faster (and supposedly higher quality) area-weighted vertex normals and tangent vectors rather than summing normalized triangle normals and tangents"};
cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces a textures.log file containing names of skins and map textures the engine tried to load"};
"uniform vec3 Color_Shirt;\n"
"uniform vec3 FogColor;\n"
"\n"
+"uniform float GlowScale;\n"
+"uniform float SceneBrightness;\n"
+"\n"
"uniform float OffsetMapping_Scale;\n"
"uniform float OffsetMapping_Bias;\n"
"uniform float FogRangeRecip;\n"
" color *= gl_Color;\n"
"\n"
"#ifdef USEGLOW\n"
-" color.rgb += vec3(texture2D(Texture_Glow, TexCoord));\n"
+" color.rgb += vec3(texture2D(Texture_Glow, TexCoord)) * GlowScale;\n"
"#endif\n"
"\n"
"#ifdef USEFOG\n"
" color.rgb = color.rgb * fog + FogColor * (1.0 - fog);\n"
"#endif\n"
"\n"
+" color.rgb *= SceneBrightness;\n"
+"\n"
" gl_FragColor = color;\n"
"}\n"
"\n"
p->loc_DiffuseScale = qglGetUniformLocationARB(p->program, "DiffuseScale");
p->loc_SpecularPower = qglGetUniformLocationARB(p->program, "SpecularPower");
p->loc_SpecularScale = qglGetUniformLocationARB(p->program, "SpecularScale");
+ p->loc_GlowScale = qglGetUniformLocationARB(p->program, "GlowScale");
+ p->loc_SceneBrightness = qglGetUniformLocationARB(p->program, "SceneBrightness");
p->loc_OffsetMapping_Scale = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
p->loc_AmbientColor = qglGetUniformLocationARB(p->program, "AmbientColor");
p->loc_DiffuseColor = qglGetUniformLocationARB(p->program, "DiffuseColor");
//if (r_glsl_permutation->loc_Texture_Lightmap >= 0) R_Mesh_TexBind(7, R_GetTexture(r_texture_white));
//if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
if (r_glsl_permutation->loc_Texture_Glow >= 0) R_Mesh_TexBind(9, R_GetTexture(rsurface_texture->skin.glow));
+ if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
+ if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_view.colorscale);
if (r_glsl_permutation->loc_FogColor >= 0)
{
// additive passes are only darkened by fog, not tinted
Cvar_RegisterVariable(&r_bloom_blur);
Cvar_RegisterVariable(&r_bloom_resolution);
Cvar_RegisterVariable(&r_bloom_power);
+ Cvar_RegisterVariable(&r_hdr);
+ Cvar_RegisterVariable(&r_hdr_scenebrightness);
+ Cvar_RegisterVariable(&r_hdr_bloomintensity);
+ Cvar_RegisterVariable(&r_hdr_glowintensity);
Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
Cvar_RegisterVariable(&developer_texturelogging);
Cvar_RegisterVariable(&gl_lightmaps);
void R_View_Update(void)
{
+ R_View_SetFrustum();
+ R_View_WorldVisibility();
+ R_View_UpdateEntityVisible();
+}
+
+void R_ResetViewRendering(void)
+{
+ if (gl_support_fragment_shader)
+ {
+ qglUseProgramObjectARB(0);CHECKGLERROR
+ }
+
// GL is weird because it's bottom to top, r_view.y is top to bottom
qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
- R_View_SetFrustum();
- R_View_WorldVisibility();
- R_View_UpdateEntityVisible();
+ GL_ScissorTest(true);
+ GL_DepthMask(true);
+ GL_DepthTest(true);
+ R_Mesh_Matrix(&identitymatrix);
+ R_Mesh_ResetTextureState();
}
-static void R_BlendView(void)
+void R_RenderScene(void);
+
+void R_Bloom_MakeTexture(qboolean darken)
{
int screenwidth, screenheight;
- qboolean dobloom;
- qboolean doblend;
+ int screentexturewidth, screentextureheight;
+ int bloomtexturewidth, bloomtextureheight;
+ int bloomwidth, bloomheight, x, range;
+ float xoffset, yoffset, r;
float vertex3f[12];
float texcoord2f[3][8];
+ // set bloomwidth and bloomheight to the bloom resolution that will be
+ // used (often less than the screen resolution for faster rendering)
+ bloomwidth = bound(1, r_bloom_resolution.integer, r_view.width);
+ bloomheight = bound(1, bloomwidth * r_view.height / r_view.width, r_view.height);
+
// set the (poorly named) screenwidth and screenheight variables to
// a power of 2 at least as large as the screen, these will define the
// size of the texture to allocate
for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2);
for (screenheight = 1;screenheight < vid.height;screenheight *= 2);
- doblend = r_refdef.viewblend[3] >= 0.01f;
- dobloom = r_bloom.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+ r_refdef.stats.bloom++;
- if (!dobloom && !doblend)
- return;
+ // allocate textures as needed
+ // TODO: reallocate these when size settings change
+ if (!r_bloom_texture_screen)
+ r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+ if (!r_bloom_texture_bloom)
+ r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+
+ screentexturewidth = R_TextureWidth(r_bloom_texture_screen);
+ screentextureheight = R_TextureHeight(r_bloom_texture_screen);
+ bloomtexturewidth = R_TextureWidth(r_bloom_texture_bloom);
+ bloomtextureheight = R_TextureHeight(r_bloom_texture_bloom);
- GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
- GL_DepthMask(true);
- GL_DepthTest(false);
- R_Mesh_Matrix(&identitymatrix);
// vertex coordinates for a quad that covers the screen exactly
vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+
+ // set up a texcoord array for the full resolution screen image
+ // (we have to keep this around to copy back during final render)
+ texcoord2f[0][0] = 0;
+ texcoord2f[0][1] = (float)r_view.height / (float)screentextureheight;
+ texcoord2f[0][2] = (float)r_view.width / (float)screentexturewidth;
+ texcoord2f[0][3] = (float)r_view.height / (float)screentextureheight;
+ texcoord2f[0][4] = (float)r_view.width / (float)screentexturewidth;
+ texcoord2f[0][5] = 0;
+ texcoord2f[0][6] = 0;
+ texcoord2f[0][7] = 0;
+
+ // set up a texcoord array for the reduced resolution bloom image
+ // (which will be additive blended over the screen image)
+ texcoord2f[1][0] = 0;
+ texcoord2f[1][1] = (float)bloomheight / (float)bloomtextureheight;
+ texcoord2f[1][2] = (float)bloomwidth / (float)bloomtexturewidth;
+ texcoord2f[1][3] = (float)bloomheight / (float)bloomtextureheight;
+ texcoord2f[1][4] = (float)bloomwidth / (float)bloomtexturewidth;
+ texcoord2f[1][5] = 0;
+ texcoord2f[1][6] = 0;
+ texcoord2f[1][7] = 0;
+
+ R_ResetViewRendering();
+ GL_DepthTest(false);
R_Mesh_VertexPointer(vertex3f);
R_Mesh_ColorPointer(NULL);
- R_Mesh_ResetTextureState();
- if (dobloom)
+
+ R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
+ R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
+
+ // copy view into the screen texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
+
+ // now scale it down to the bloom texture size
+ CHECKGLERROR
+ qglViewport(r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ GL_Color(1, 1, 1, 1);
+ // TODO: optimize with multitexture or GLSL
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+
+ if (darken)
{
- int bloomwidth, bloomheight, x, range;
- float xoffset, yoffset, r;
- r_refdef.stats.bloom++;
- // allocate textures as needed
- if (!r_bloom_texture_screen)
- r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
- if (!r_bloom_texture_bloom)
- r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
- // set bloomwidth and bloomheight to the bloom resolution that will be
- // used (often less than the screen resolution for faster rendering)
- bloomwidth = min(r_view.width, r_bloom_resolution.integer);
- bloomheight = min(r_view.height, bloomwidth * r_view.height / r_view.width);
- // set up a texcoord array for the full resolution screen image
- // (we have to keep this around to copy back during final render)
- texcoord2f[0][0] = 0;
- texcoord2f[0][1] = (float)r_view.height / (float)screenheight;
- texcoord2f[0][2] = (float)r_view.width / (float)screenwidth;
- texcoord2f[0][3] = (float)r_view.height / (float)screenheight;
- texcoord2f[0][4] = (float)r_view.width / (float)screenwidth;
- texcoord2f[0][5] = 0;
- texcoord2f[0][6] = 0;
- texcoord2f[0][7] = 0;
- // set up a texcoord array for the reduced resolution bloom image
- // (which will be additive blended over the screen image)
- texcoord2f[1][0] = 0;
- texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
- texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
- texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
- texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
- texcoord2f[1][5] = 0;
- texcoord2f[1][6] = 0;
- texcoord2f[1][7] = 0;
- R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
- R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
- // copy view into the full resolution screen image texture
- GL_ActiveTexture(0);
- CHECKGLERROR
- qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
- r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
- // now scale it down to the bloom size and raise to a power of itself
- // to darken it (this leaves the really bright stuff bright, and
- // everything else becomes very dark)
- // TODO: optimize with multitexture or GLSL
- CHECKGLERROR
- qglViewport(r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
- GL_BlendFunc(GL_ONE, GL_ZERO);
- GL_Color(1, 1, 1, 1);
- R_Mesh_Draw(0, 4, 2, polygonelements);
- r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+ // raise to a power of itself to darken it (this leaves the really
+ // bright stuff bright, and everything else becomes very dark)
// render multiple times with a multiply blendfunc to raise to a power
GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
for (x = 1;x < r_bloom_power.integer;x++)
R_Mesh_Draw(0, 4, 2, polygonelements);
r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
}
- // we now have a darkened bloom image in the framebuffer, copy it into
- // the bloom image texture for more processing
+ }
+
+ // we now have a darkened bloom image in the framebuffer
+ // copy it into the bloom image texture for more processing
+ R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
+ R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]);
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+
+ // blend on at multiple vertical offsets to achieve a vertical blur
+ // TODO: do offset blends using GLSL
+ range = r_bloom_blur.integer * bloomwidth / 320;
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ for (x = -range;x <= range;x++)
+ {
+ xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
+ yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
+ // compute a texcoord array with the specified x and y offset
+ texcoord2f[2][0] = xoffset+0;
+ texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+ texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+ texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+ texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+ texcoord2f[2][5] = yoffset+0;
+ texcoord2f[2][6] = xoffset+0;
+ texcoord2f[2][7] = yoffset+0;
+ // this r value looks like a 'dot' particle, fading sharply to
+ // black at the edges
+ // (probably not realistic but looks good enough)
+ r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
+ if (r < 0.01f)
+ continue;
+ GL_Color(r, r, r, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ }
+
+ // copy the vertically blurred bloom view to a texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+
+ // blend the vertically blurred image at multiple offsets horizontally
+ // to finish the blur effect
+ // TODO: do offset blends using GLSL
+ range = r_bloom_blur.integer * bloomwidth / 320;
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ for (x = -range;x <= range;x++)
+ {
+ xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
+ yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
+ // compute a texcoord array with the specified x and y offset
+ texcoord2f[2][0] = xoffset+0;
+ texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+ texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+ texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+ texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+ texcoord2f[2][5] = yoffset+0;
+ texcoord2f[2][6] = xoffset+0;
+ texcoord2f[2][7] = yoffset+0;
+ // this r value looks like a 'dot' particle, fading sharply to
+ // black at the edges
+ // (probably not realistic but looks good enough)
+ r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
+ if (r < 0.01f)
+ continue;
+ GL_Color(r, r, r, 1);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+ GL_BlendFunc(GL_ONE, GL_ONE);
+ }
+
+ // copy the blurred bloom view to a texture
+ GL_ActiveTexture(0);
+ CHECKGLERROR
+ qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+ r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+}
+
+void R_HDR_RenderBloomTexture(void)
+{
+ int oldwidth, oldheight;
+
+ oldwidth = r_view.width;
+ oldheight = r_view.height;
+ r_view.width = bound(1, r_bloom_resolution.integer, min(r_view.width, gl_max_texture_size));
+ r_view.height = r_view.width * oldheight / oldwidth;
+
+ // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer? it might improve SLI performance.
+ // FIXME: change global lightmapintensity and light intensity according to r_hdr_bloomintensity cvar
+ // FIXME: change global lightmapintensity and light intensity according to r_hdr_scenebrightness cvar
+ // TODO: add exposure compensation features
+
+ r_view.colorscale = r_hdr_bloomintensity.value * r_hdr_scenebrightness.value;
+ R_RenderScene();
+
+ R_ResetViewRendering();
+
+ R_Bloom_MakeTexture(false);
+
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("clear");
+
+ // restore the view settings
+ r_view.width = oldwidth;
+ r_view.height = oldheight;
+
+ // go back to full view area
+ R_ResetViewRendering();
+}
+
+static void R_BlendView(void)
+{
+ int screenwidth, screenheight;
+ int bloomwidth, bloomheight;
+ qboolean dobloom;
+ qboolean dohdr;
+ qboolean doblend;
+ float vertex3f[12];
+ float texcoord2f[3][8];
+
+ // set the (poorly named) screenwidth and screenheight variables to
+ // a power of 2 at least as large as the screen, these will define the
+ // size of the texture to allocate
+ for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2);
+ for (screenheight = 1;screenheight < vid.height;screenheight *= 2);
+
+ doblend = r_refdef.viewblend[3] >= 0.01f;
+ dobloom = !r_hdr.integer && r_bloom.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+ dohdr = r_hdr.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+
+ if (!dobloom && !dohdr && !doblend)
+ return;
+
+ // vertex coordinates for a quad that covers the screen exactly
+ vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
+ vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
+ vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
+ vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+
+ // set bloomwidth and bloomheight to the bloom resolution that will be
+ // used (often less than the screen resolution for faster rendering)
+ bloomwidth = min(r_view.width, r_bloom_resolution.integer);
+ bloomheight = min(r_view.height, bloomwidth * r_view.height / r_view.width);
+ // set up a texcoord array for the full resolution screen image
+ // (we have to keep this around to copy back during final render)
+ texcoord2f[0][0] = 0;
+ texcoord2f[0][1] = (float)r_view.height / (float)screenheight;
+ texcoord2f[0][2] = (float)r_view.width / (float)screenwidth;
+ texcoord2f[0][3] = (float)r_view.height / (float)screenheight;
+ texcoord2f[0][4] = (float)r_view.width / (float)screenwidth;
+ texcoord2f[0][5] = 0;
+ texcoord2f[0][6] = 0;
+ texcoord2f[0][7] = 0;
+ // set up a texcoord array for the reduced resolution bloom image
+ // (which will be additive blended over the screen image)
+ texcoord2f[1][0] = 0;
+ texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
+ texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
+ texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
+ texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
+ texcoord2f[1][5] = 0;
+ texcoord2f[1][6] = 0;
+ texcoord2f[1][7] = 0;
+
+ if (dohdr)
+ {
+ // render high dynamic range bloom effect
+ // the bloom texture was made earlier this render, so we just need to
+ // blend it onto the screen...
+ R_ResetViewRendering();
+ GL_DepthTest(false);
+ R_Mesh_VertexPointer(vertex3f);
+ R_Mesh_ColorPointer(NULL);
+ GL_Color(1, 1, 1, 1);
+ GL_BlendFunc(GL_ONE, GL_ONE);
R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
- R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]);
- GL_ActiveTexture(0);
- CHECKGLERROR
- qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
- r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
- // blend on at multiple vertical offsets to achieve a vertical blur
- // TODO: do offset blends using GLSL
- range = r_bloom_blur.integer * bloomwidth / 320;
- GL_BlendFunc(GL_ONE, GL_ZERO);
- for (x = -range;x <= range;x++)
- {
- xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
- yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
- // compute a texcoord array with the specified x and y offset
- texcoord2f[2][0] = xoffset+0;
- texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
- texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
- texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
- texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
- texcoord2f[2][5] = yoffset+0;
- texcoord2f[2][6] = xoffset+0;
- texcoord2f[2][7] = yoffset+0;
- // this r value looks like a 'dot' particle, fading sharply to
- // black at the edges
- // (probably not realistic but looks good enough)
- r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
- if (r < 0.01f)
- continue;
- GL_Color(r, r, r, 1);
- R_Mesh_Draw(0, 4, 2, polygonelements);
- r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
- GL_BlendFunc(GL_ONE, GL_ONE);
- }
- // copy the vertically blurred bloom view to a texture
- GL_ActiveTexture(0);
- CHECKGLERROR
- qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
- r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
- // blend the vertically blurred image at multiple offsets horizontally
- // to finish the blur effect
- // TODO: do offset blends using GLSL
- range = r_bloom_blur.integer * bloomwidth / 320;
- GL_BlendFunc(GL_ONE, GL_ZERO);
- for (x = -range;x <= range;x++)
- {
- xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
- yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
- // compute a texcoord array with the specified x and y offset
- texcoord2f[2][0] = xoffset+0;
- texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
- texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
- texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
- texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
- texcoord2f[2][5] = yoffset+0;
- texcoord2f[2][6] = xoffset+0;
- texcoord2f[2][7] = yoffset+0;
- // this r value looks like a 'dot' particle, fading sharply to
- // black at the edges
- // (probably not realistic but looks good enough)
- r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
- if (r < 0.01f)
- continue;
- GL_Color(r, r, r, 1);
- R_Mesh_Draw(0, 4, 2, polygonelements);
- r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
- GL_BlendFunc(GL_ONE, GL_ONE);
- }
- // copy the blurred bloom view to a texture
- GL_ActiveTexture(0);
- CHECKGLERROR
- qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
- r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
- // go back to full view area
- qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+ R_Mesh_TexCoordPointer(0, 2, texcoord2f[1]);
+ R_Mesh_Draw(0, 4, 2, polygonelements);
+ r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height;
+ }
+ if (dobloom)
+ {
+ // render simple bloom effect
+ // make the bloom texture
+ R_Bloom_MakeTexture(true);
// put the original screen image back in place and blend the bloom
// texture on it
- GL_Color(1,1,1,1);
+ R_ResetViewRendering();
+ GL_DepthTest(false);
+ R_Mesh_VertexPointer(vertex3f);
+ R_Mesh_ColorPointer(NULL);
+ GL_Color(1, 1, 1, 1);
GL_BlendFunc(GL_ONE, GL_ZERO);
// do both in one pass if possible
R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
if (doblend)
{
// apply a color tint to the whole view
- R_Mesh_ResetTextureState();
+ R_ResetViewRendering();
+ GL_DepthTest(false);
+ R_Mesh_VertexPointer(vertex3f);
+ R_Mesh_ColorPointer(NULL);
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
R_Mesh_Draw(0, 4, 2, polygonelements);
return; //Host_Error ("R_RenderView: NULL worldmodel");
CHECKGLERROR
- GL_ScissorTest(true);
- GL_DepthMask(true);
if (r_timereport_active)
R_TimeReport("setup");
if (r_timereport_active)
R_TimeReport("visibility");
+ // GL is weird because it's bottom to top, r_view.y is top to bottom
+ R_ResetViewRendering();
+
R_ClearScreen();
if (r_timereport_active)
R_TimeReport("clear");
+ // this produces a bloom texture to be used in R_BlendView() later
+ if (r_hdr.integer)
+ R_HDR_RenderBloomTexture();
+
+ r_view.colorscale = r_hdr_scenebrightness.value;
R_RenderScene();
R_BlendView();
if (r_timereport_active)
R_TimeReport("visibility");
+ R_ResetViewRendering();
+
R_ClearScreen();
if (r_timereport_active)
R_TimeReport("clear");
//[515]: csqc
void CSQC_R_RenderScene (void)
{
+ R_ResetViewRendering();
+
+ R_ClearScreen();
+ if (r_timereport_active)
+ R_TimeReport("clear");
+
+ // this produces a bloom texture to be used in R_BlendView() later
+ if (r_hdr.integer)
+ R_HDR_RenderBloomTexture();
+
+ r_view.colorscale = r_hdr_scenebrightness.value;
R_RenderScene();
R_BlendView();
qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
+ R_ResetViewRendering();
+
R_MeshQueue_BeginScene();
if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows)
R_Mesh_ResetTextureState();
R_Mesh_TexBind(0, R_GetTexture(texture));
R_Mesh_TexCoordPointer(0, 2, spritetexcoord2f);
- GL_Color(cr * ifog, cg * ifog, cb * ifog, ca);
+ // FIXME: fixed function path can't properly handle r_view.colorscale > 1
+ GL_Color(cr * ifog * r_view.colorscale, cg * ifog * r_view.colorscale, cb * ifog * r_view.colorscale, ca);
R_Mesh_Draw(0, 4, 2, polygonelements);
if (blendfunc2 == GL_ONE_MINUS_SRC_ALPHA)
{
R_Mesh_TexBind(0, R_GetTexture(fogtexture));
GL_BlendFunc(blendfunc1, GL_ONE);
- GL_Color(r_refdef.fogcolor[0] * fog, r_refdef.fogcolor[1] * fog, r_refdef.fogcolor[2] * fog, ca);
+ GL_Color(r_refdef.fogcolor[0] * fog * r_view.colorscale, r_refdef.fogcolor[1] * fog * r_view.colorscale, r_refdef.fogcolor[2] * fog * r_view.colorscale, ca);
R_Mesh_Draw(0, 4, 2, polygonelements);
}
}
int i;
R_Mesh_VertexPointer(brush->points->v);
i = (int)(((size_t)brush) / sizeof(colbrushf_t));
- GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+ GL_Color((i & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_view.colorscale, 0.2f);
GL_LockArrays(0, brush->numpoints);
R_Mesh_Draw(0, brush->numpoints, brush->numtriangles, brush->elements);
GL_LockArrays(0, 0);
return;
R_Mesh_VertexPointer(surface->data_collisionvertex3f);
i = (int)(((size_t)surface) / sizeof(msurface_t));
- GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+ GL_Color((i & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_view.colorscale, 0.2f);
GL_LockArrays(0, surface->num_collisionvertices);
R_Mesh_Draw(0, surface->num_collisionvertices, surface->num_collisiontriangles, surface->data_collisionelement3i);
GL_LockArrays(0, 0);
layer->blendfunc2 = blendfunc2;
layer->texture = texture;
layer->texmatrix = *matrix;
- layer->color[0] = r;
- layer->color[1] = g;
- layer->color[2] = b;
+ layer->color[0] = r * r_view.colorscale;
+ layer->color[1] = g * r_view.colorscale;
+ layer->color[2] = b * r_view.colorscale;
layer->color[3] = a;
}
}
}
if (t->skin.glow != NULL)
- R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->skin.glow, &t->currenttexmatrix, 1, 1, 1, t->currentalpha);
+ R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->skin.glow, &t->currenttexmatrix, r_hdr_glowintensity.value, r_hdr_glowintensity.value, r_hdr_glowintensity.value, t->currentalpha);
if (r_refdef.fogenabled && !(t->currentmaterialflags & MATERIALFLAG_ADD))
{
// if this is opaque use alpha blend which will darken the earlier
{
const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
int k = (int)(((size_t)surface) / sizeof(msurface_t));
- GL_Color((k & 15) * (1.0f / 16.0f), ((k >> 4) & 15) * (1.0f / 16.0f), ((k >> 8) & 15) * (1.0f / 16.0f), 0.2f);
+ GL_Color((k & 15) * (1.0f / 16.0f) * r_view.colorscale, ((k >> 4) & 15) * (1.0f / 16.0f) * r_view.colorscale, ((k >> 8) & 15) * (1.0f / 16.0f) * r_view.colorscale, 0.2f);
GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
}
// level, so don't use it then either.
if (rsurface_model->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_viewcache.world_novis)
{
- GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
+ GL_Color(r_refdef.fogcolor[0] * r_view.colorscale, r_refdef.fogcolor[1] * r_view.colorscale, r_refdef.fogcolor[2] * r_view.colorscale, 1);
R_Mesh_ColorPointer(NULL);
R_Mesh_ResetTextureState();
if (skyrendermasked)
if (r_showtris.integer)
{
if (!rsurface_texture->currentlayers->depthmask)
- GL_Color(r_showtris.value, 0, 0, 1);
+ GL_Color(r_showtris.value * r_view.colorscale, 0, 0, 1);
else if (ent == r_refdef.worldentity)
- GL_Color(r_showtris.value, r_showtris.value, r_showtris.value, 1);
+ GL_Color(r_showtris.value * r_view.colorscale, r_showtris.value * r_view.colorscale, r_showtris.value * r_view.colorscale, 1);
else
- GL_Color(0, r_showtris.value, 0, 1);
+ GL_Color(0, r_showtris.value * r_view.colorscale, 0, 1);
elements = (ent->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
CHECKGLERROR
qglBegin(GL_LINES);
}
if (r_shownormals.integer)
{
- GL_Color(r_shownormals.value, 0, 0, 1);
+ GL_Color(r_shownormals.value * r_view.colorscale, 0, 0, 1);
qglBegin(GL_LINES);
for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
{
}
qglEnd();
CHECKGLERROR
- GL_Color(0, 0, r_shownormals.value, 1);
+ GL_Color(0, 0, r_shownormals.value * r_view.colorscale, 1);
qglBegin(GL_LINES);
for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
{
}
qglEnd();
CHECKGLERROR
- GL_Color(0, r_shownormals.value, 0, 1);
+ GL_Color(0, r_shownormals.value * r_view.colorscale, 0, 1);
qglBegin(GL_LINES);
for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
{
GL_DepthMask(false);
GL_DepthTest(!r_showdisabledepthtest.integer);
qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
- GL_Color(0.0, 0.0125, 0.1, 1);
+ GL_Color(0.0, 0.0125 * r_view.colorscale, 0.1 * r_view.colorscale, 1);
GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
qglDepthFunc(GL_GEQUAL);CHECKGLERROR
qglCullFace(GL_FRONT);CHECKGLERROR // this culls back
GL_DepthMask(false);
GL_DepthTest(!r_showdisabledepthtest.integer);
qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
- GL_Color(0.1, 0.0125, 0, 1);
+ GL_Color(0.1 * r_view.colorscale, 0.0125 * r_view.colorscale, 0, 1);
GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
if (transparent)
{
// if that mesh is not empty, check what area of the screen it covers
x1 = y1 = x2 = y2 = 0;
v[3] = 1.0f;
+ //Con_Printf("%i vertices to transform...\n", mesh.numvertices);
for (i = 0;i < mesh.numvertices;i++)
{
VectorCopy(mesh.vertex3f + i * 3, v);
return true;
// the light area is visible, set up the scissor rectangle
- GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
+ GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
//qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
//qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
r_refdef.stats.lights_scissored++;
static void R_Shadow_RenderSurfacesLighting_VisibleLighting(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
{
// used to display how many times a surface is lit for level design purposes
- GL_Color(0.1, 0.025, 0, 1);
+ GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
R_Mesh_ColorPointer(NULL);
R_Mesh_ResetTextureState();
RSurf_PrepareVerticesForBatch(false, false, numsurfaces, surfacelist);
RSurf_PrepareVerticesForBatch(true, true, numsurfaces, surfacelist);
R_Mesh_ColorPointer(NULL);
if (doambient)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
if (dodiffuse)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
if (dopants)
{
if (doambient)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
if (dodiffuse)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
}
if (doshirt)
{
if (doambient)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
if (dodiffuse)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
}
if (dospecular)
- R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(numsurfaces, surfacelist, lightcolorbase, glosstexture, normalmaptexture, specularscale);
+ R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(numsurfaces, surfacelist, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
}
void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, int numsurfaces, msurface_t **surfacelist, vec3_t diffusecolor2, vec3_t ambientcolor2)
float ambientcolorpants[3], diffusecolorpants[3];
float ambientcolorshirt[3], diffusecolorshirt[3];
rmeshstate_t m;
- VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2, ambientcolorbase);
- VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2, diffusecolorbase);
- VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2, ambientcolorpants);
- VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2, diffusecolorpants);
- VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
- VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, diffusecolorshirt);
+ VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorbase);
+ VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorbase);
+ VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorpants);
+ VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorpants);
+ VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
+ VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
R_Mesh_ColorPointer(rsurface_array_color4f);
memset(&m, 0, sizeof(m));