From aaff7d54980117a2dbf650516ffbdf3fa65016f8 Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 11 Jun 2006 23:24:18 +0000 Subject: [PATCH] added HDR bloom feature, not very different from bloom but looks better, runs a bit slower git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6469 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_particles.c | 12 +- client.h | 3 + gl_backend.c | 2 +- gl_rmain.c | 509 +++++++++++++++++++++++++++++++++---------------- gl_rsurf.c | 6 +- menu.c | 26 ++- r_explosion.c | 3 +- r_lightning.c | 7 +- r_shadow.c | 35 ++-- r_sky.c | 8 +- render.h | 4 + 11 files changed, 412 insertions(+), 203 deletions(-) diff --git a/cl_particles.c b/cl_particles.c index d8766f64..85ad184e 100644 --- a/cl_particles.c +++ b/cl_particles.c @@ -2045,9 +2045,9 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh blendmode = p->type->blendmode; - cr = p->color[0] * (1.0f / 255.0f); - cg = p->color[1] * (1.0f / 255.0f); - cb = p->color[2] * (1.0f / 255.0f); + cr = p->color[0] * (1.0f / 255.0f) * r_view.colorscale; + cg = p->color[1] * (1.0f / 255.0f) * r_view.colorscale; + cb = p->color[2] * (1.0f / 255.0f) * r_view.colorscale; ca = p->alpha * (1.0f / 255.0f); if (blendmode == PBLEND_MOD) { @@ -2077,9 +2077,9 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh cb = cb * ifog; if (blendmode == PBLEND_ALPHA) { - cr += r_refdef.fogcolor[0] * fog; - cg += r_refdef.fogcolor[1] * fog; - cb += r_refdef.fogcolor[2] * fog; + cr += r_refdef.fogcolor[0] * fog * r_view.colorscale; + cg += r_refdef.fogcolor[1] * fog * r_view.colorscale; + cb += r_refdef.fogcolor[2] * fog * r_view.colorscale; } } c4f[0] = c4f[4] = c4f[8] = c4f[12] = cr; diff --git a/client.h b/client.h index 7a9fdc7c..a35160d7 100644 --- a/client.h +++ b/client.h @@ -1265,6 +1265,9 @@ typedef struct r_view_s // which color components to allow (for anaglyph glasses) int colormask[4]; + + // global RGB color multiplier for rendering, this is required by HDR + float colorscale; } r_view_t; diff --git a/gl_backend.c b/gl_backend.c index 26b60847..348dd655 100644 --- a/gl_backend.c +++ b/gl_backend.c @@ -727,7 +727,7 @@ void GL_TransformToScreen(const vec4_t in, vec4_t out) Matrix4x4_Transform4 (&backend_projectmatrix, temp, out); iw = 1.0f / out[3]; out[0] = r_view.x + (out[0] * iw + 1.0f) * r_view.width * 0.5f; - out[1] = r_view.y + (out[1] * iw + 1.0f) * r_view.height * 0.5f; + out[1] = r_view.y + r_view.height - (out[1] * iw + 1.0f) * r_view.height * 0.5f; out[2] = r_view.z + (out[2] * iw + 1.0f) * r_view.depth * 0.5f; } diff --git a/gl_rmain.c b/gl_rmain.c index 0cd996a4..a566a73f 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -79,6 +79,11 @@ cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "4", "how large the glow is"}; 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"}; @@ -431,6 +436,9 @@ static const char *builtinshaderstring = "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" @@ -591,7 +599,7 @@ static const char *builtinshaderstring = " 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" @@ -600,6 +608,8 @@ static const char *builtinshaderstring = " color.rgb = color.rgb * fog + FogColor * (1.0 - fog);\n" "#endif\n" "\n" +" color.rgb *= SceneBrightness;\n" +"\n" " gl_FragColor = color;\n" "}\n" "\n" @@ -727,6 +737,8 @@ void R_GLSL_CompilePermutation(int permutation) 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"); @@ -873,6 +885,8 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting) //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 @@ -1007,6 +1021,10 @@ void GL_Main_Init(void) 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); @@ -1332,98 +1350,122 @@ static void R_View_SetFrustum(void) 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++) @@ -1431,86 +1473,204 @@ static void R_BlendView(void) 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)); @@ -1536,7 +1696,10 @@ static void R_BlendView(void) 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); @@ -1639,8 +1802,6 @@ void R_RenderView(void) return; //Host_Error ("R_RenderView: NULL worldmodel"); CHECKGLERROR - GL_ScissorTest(true); - GL_DepthMask(true); if (r_timereport_active) R_TimeReport("setup"); @@ -1648,10 +1809,18 @@ void R_RenderView(void) 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(); @@ -1679,6 +1848,8 @@ void CSQC_R_ClearScreen (void) if (r_timereport_active) R_TimeReport("visibility"); + R_ResetViewRendering(); + R_ClearScreen(); if (r_timereport_active) R_TimeReport("clear"); @@ -1688,6 +1859,17 @@ void CSQC_R_ClearScreen (void) //[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(); @@ -1717,6 +1899,8 @@ void R_RenderScene(void) 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) @@ -2031,14 +2215,15 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_ 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); } } @@ -2115,7 +2300,7 @@ static void R_DrawCollisionBrush(const colbrushf_t *brush) 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); @@ -2128,7 +2313,7 @@ static void R_DrawCollisionSurface(const entity_render_t *ent, const msurface_t 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); @@ -2144,9 +2329,9 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1, 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; } @@ -2290,7 +2475,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t) } } 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 @@ -2603,7 +2788,7 @@ static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **te { 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)); } @@ -2812,7 +2997,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, msurface_t **te // 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) @@ -3391,11 +3576,11 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) 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); @@ -3410,7 +3595,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) } 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++) { @@ -3421,7 +3606,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) } 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++) { @@ -3432,7 +3617,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces) } 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++) { diff --git a/gl_rsurf.c b/gl_rsurf.c index 99c8b4c5..117e1364 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -348,9 +348,9 @@ static void R_DrawPortal_Callback(const entity_render_t *ent, const rtlight_t *r R_Mesh_ResetTextureState(); i = surfacelist[0]; - GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f), - ((i & 0x0038) >> 3) * (1.0f / 7.0f), - ((i & 0x01C0) >> 6) * (1.0f / 7.0f), + GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f) * r_view.colorscale, + ((i & 0x0038) >> 3) * (1.0f / 7.0f) * r_view.colorscale, + ((i & 0x01C0) >> 6) * (1.0f / 7.0f) * r_view.colorscale, 0.125f); for (i = 0, v = vertex3f;i < numpoints;i++, v += 3) VectorCopy(portal->points[i].position, v); diff --git a/menu.c b/menu.c index 951fdeb0..b3562b2b 100644 --- a/menu.c +++ b/menu.c @@ -1962,7 +1962,7 @@ static void M_Options_Effects_Key (int k, char ascii) } -#define OPTIONS_GRAPHICS_ITEMS 14 +#define OPTIONS_GRAPHICS_ITEMS 18 static int options_graphics_cursor; @@ -1985,6 +1985,10 @@ extern cvar_t r_bloom_intensity; extern cvar_t r_bloom_power; extern cvar_t r_bloom_blur; extern cvar_t r_bloom_resolution; +extern cvar_t r_hdr; +extern cvar_t r_hdr_bloomintensity; +extern cvar_t r_hdr_scenebrightness; +extern cvar_t r_hdr_glowintensity; extern cvar_t gl_picmip; static void M_Menu_Options_Graphics_AdjustSliders (int dir) @@ -2002,10 +2006,14 @@ static void M_Menu_Options_Graphics_AdjustSliders (int dir) else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world_lightmaps, bound(0, r_shadow_realtime_world_lightmaps.value + dir * 0.1, 1)); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world_shadows, !r_shadow_realtime_world_shadows.integer); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom, !r_bloom.integer); - else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom_intensity, bound(1, r_bloom_intensity.value + dir * 1, 16)); + else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_hdr, !r_hdr.integer); + else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_hdr_bloomintensity, bound(0.125, r_hdr_bloomintensity.value + dir * 0.125, 4)); + else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_hdr_glowintensity, bound(0, r_hdr_glowintensity.value + dir * 0.25, 4)); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom_power, bound(1, r_bloom_power.value + dir * 1, 16)); + else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom_intensity, bound(1, r_bloom_intensity.value + dir * 0.25, 16)); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom_blur, bound(1, r_bloom_blur.value + dir * 1, 16)); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_bloom_resolution, bound(64, r_bloom_resolution.value + dir * 64, 2048)); + else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_hdr_scenebrightness, bound(0.25, r_hdr_scenebrightness.value + dir * 0.125, 4)); else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&gl_picmip, bound(0, gl_picmip.value - dir, 3)); else if (options_graphics_cursor == optnum++) Cbuf_AddText ("r_restart\n"); } @@ -2034,11 +2042,15 @@ static void M_Options_Graphics_Draw (void) M_Options_PrintCheckbox("RT World DLight Shadows", true, r_shadow_realtime_world_dlightshadows.integer); M_Options_PrintSlider( " RT World Lightmaps", true, r_shadow_realtime_world_lightmaps.value, 0, 1); M_Options_PrintCheckbox(" RT World Shadow", true, r_shadow_realtime_world_shadows.integer); - M_Options_PrintCheckbox(" Bloom Effect", true, r_bloom.integer); - M_Options_PrintSlider( " Bloom Intensity", true, r_bloom_intensity.value, 1, 16); - M_Options_PrintSlider( " Bloom Power", true, r_bloom_power.value, 1, 16); - M_Options_PrintSlider( " Bloom Blur", true, r_bloom_blur.value, 1, 16); - M_Options_PrintSlider( " Bloom Resolution", true, r_bloom_resolution.value, 64, 2048); + M_Options_PrintCheckbox(" Bloom Effect", !r_hdr.integer, r_bloom.integer); + M_Options_PrintCheckbox(" HDR Bloom Effect", r_hdr.integer, r_hdr.integer); + M_Options_PrintSlider( " HDR Bloom Intensity", r_hdr.integer, r_hdr_bloomintensity.value, 0.125, 4); + M_Options_PrintSlider( " HDR Glow Intensity", r_hdr.integer, r_hdr_glowintensity.value, 0, 4); + M_Options_PrintSlider( "Non-HDR Bloom Darkening", !r_hdr.integer && r_bloom.integer, r_bloom_power.value, 1, 16); + M_Options_PrintSlider( " Bloom Intensity", r_hdr.integer || r_bloom.integer, r_bloom_intensity.value, 1, 16); + M_Options_PrintSlider( " Bloom Blur", r_hdr.integer || r_bloom.integer, r_bloom_blur.value, 1, 16); + M_Options_PrintSlider( " Bloom Resolution", r_hdr.integer || r_bloom.integer, r_bloom_resolution.value, 64, 2048); + M_Options_PrintSlider( " Scene Brightness", true, r_hdr_scenebrightness.value, 0.25, 4); M_Options_PrintSlider( " Texture Quality", true, gl_picmip.value, 3, 0); M_Options_PrintCommand( " Restart Renderer", true); } diff --git a/r_explosion.c b/r_explosion.c index 6f2c36ec..283bbe81 100644 --- a/r_explosion.c +++ b/r_explosion.c @@ -199,7 +199,8 @@ static void R_DrawExplosion_TransparentCallback(const entity_render_t *ent, cons { const explosion_t *e = explosion + surfacelist[surfacelistindex]; R_Mesh_VertexPointer(e->vert[0]); - GL_Color(e->alpha, e->alpha, e->alpha, 1); + // FIXME: fixed function path can't properly handle r_view.colorscale > 1 + GL_Color(e->alpha * r_view.colorscale, e->alpha * r_view.colorscale, e->alpha * r_view.colorscale, 1); GL_LockArrays(0, numverts); R_Mesh_Draw(0, numverts, numtriangles, explosiontris[0]); GL_LockArrays(0, 0); diff --git a/r_lightning.c b/r_lightning.c index eabf768f..9fb871e1 100644 --- a/r_lightning.c +++ b/r_lightning.c @@ -246,17 +246,18 @@ void R_DrawLightningBeam_TransparentCallback(const entity_render_t *ent, const r r_lightningbeams_setuptexture(); R_Mesh_VertexPointer(vertex3f); + // FIXME: fixed function path can't properly handle r_view.colorscale > 1 if (r_refdef.fogenabled) { // per vertex colors if fog is used R_Mesh_ColorPointer(color4f); - R_FogLightningBeam_Vertex3f_Color4f(vertex3f, color4f, 12, r_lightningbeam_color_red.value, r_lightningbeam_color_green.value, r_lightningbeam_color_blue.value, 1); + R_FogLightningBeam_Vertex3f_Color4f(vertex3f, color4f, 12, r_lightningbeam_color_red.value * r_view.colorscale, r_lightningbeam_color_green.value * r_view.colorscale, r_lightningbeam_color_blue.value * r_view.colorscale, 1); } else { // solid color if fog is not used R_Mesh_ColorPointer(NULL); - GL_Color(r_lightningbeam_color_red.value, r_lightningbeam_color_green.value, r_lightningbeam_color_blue.value, 1); + GL_Color(r_lightningbeam_color_red.value * r_view.colorscale, r_lightningbeam_color_green.value * r_view.colorscale, r_lightningbeam_color_blue.value * r_view.colorscale, 1); } memset(&m, 0, sizeof(m)); if (r_lightningbeam_qmbtexture.integer) @@ -271,7 +272,7 @@ void R_DrawLightningBeam_TransparentCallback(const entity_render_t *ent, const r const beam_t *b = cl.beams + surfacelist[surfacelistindex]; vec3_t beamdir, right, up, offset, start, end; float length, t1, t2; - + CL_Beam_CalculatePositions(b, start, end); // calculate beam direction (beamdir) vector and beam length diff --git a/r_shadow.c b/r_shadow.c index 791e073e..2c384f33 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -962,7 +962,7 @@ void R_Shadow_RenderMode_VisibleShadowVolumes(void) 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 @@ -979,7 +979,7 @@ void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transpar 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) { @@ -1075,6 +1075,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs) // 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); @@ -1112,7 +1113,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs) 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++; @@ -1275,7 +1276,7 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int numsurfaces, msurfa 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); @@ -1793,25 +1794,25 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3(int numsurfaces, msurface 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) @@ -1928,12 +1929,12 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex(int numsurfaces, msurfa 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)); diff --git a/r_sky.c b/r_sky.c index 487ebb5c..41b473a6 100644 --- a/r_sky.c +++ b/r_sky.c @@ -268,7 +268,8 @@ int skyboxelements[6*2*3] = static void R_SkyBox(void) { int i; - GL_Color(1, 1, 1, 1); + // FIXME: fixed function path can't properly handle r_view.colorscale > 1 + GL_Color(1 * r_view.colorscale, 1 * r_view.colorscale, 1 * r_view.colorscale, 1); GL_BlendFunc(GL_ONE, GL_ZERO); GL_DepthMask(false); GL_DepthTest(false); // don't modify or read zbuffer @@ -365,7 +366,8 @@ static void R_SkySphere(void) speedscale -= (int)speedscale; Matrix4x4_CreateTranslate(&scroll2matrix, speedscale, speedscale, 0); - GL_Color(1, 1, 1, 1); + // FIXME: fixed function path can't properly handle r_view.colorscale > 1 + GL_Color(1 * r_view.colorscale, 1 * r_view.colorscale, 1 * r_view.colorscale, 1); GL_BlendFunc(GL_ONE, GL_ZERO); GL_DepthMask(true); GL_DepthTest(false); // don't modify or read zbuffer @@ -375,7 +377,7 @@ static void R_SkySphere(void) R_Mesh_TexBind(0, R_GetTexture(r_refdef.worldmodel->brush.solidskytexture)); R_Mesh_TexCoordPointer(0, 2, skysphere_texcoord2f); R_Mesh_TexMatrix(0, &scroll1matrix); - if (r_textureunits.integer >= 2) + if (r_textureunits.integer >= 2 && r_view.colorscale == 1) { // one pass using GL_DECAL or GL_INTERPOLATE_ARB for alpha layer R_Mesh_TexBind(1, R_GetTexture(r_refdef.worldmodel->brush.alphaskytexture)); diff --git a/render.h b/render.h index dd0f0411..66bd58b2 100644 --- a/render.h +++ b/render.h @@ -153,6 +153,8 @@ extern cvar_t gl_dither; extern cvar_t r_smoothnormals_areaweighting; +extern cvar_t r_test; + #include "gl_backend.h" #include "r_light.h" @@ -267,6 +269,8 @@ typedef struct r_glsl_permutation_s int loc_DiffuseScale; int loc_SpecularScale; int loc_SpecularPower; + int loc_GlowScale; + int loc_SceneBrightness; int loc_OffsetMapping_Scale; int loc_AmbientColor; int loc_DiffuseColor; -- 2.39.2