diff options
Diffstat (limited to 'src/rend2/tr_postprocess.c')
-rw-r--r-- | src/rend2/tr_postprocess.c | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/src/rend2/tr_postprocess.c b/src/rend2/tr_postprocess.c new file mode 100644 index 00000000..0cb162b3 --- /dev/null +++ b/src/rend2/tr_postprocess.c @@ -0,0 +1,490 @@ +/* +=========================================================================== +Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete + +This file is part of Reaction source code. + +Reaction source code 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. + +Reaction source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Reaction source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +void RB_ToneMap(FBO_t *hdrFbo, int autoExposure) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + static int lastFrameCount = 0; + + if (autoExposure) + { + if (lastFrameCount == 0 || tr.frameCount < lastFrameCount || tr.frameCount - lastFrameCount > 5) + { + // determine average log luminance + FBO_t *srcFbo, *dstFbo, *tmp; + int size = 256; + + lastFrameCount = tr.frameCount; + + VectorSet4(dstBox, 0, 0, size, size); + + srcFbo = hdrFbo; + dstFbo = tr.textureScratchFbo[0]; + FBO_Blit(srcFbo, NULL, NULL, dstFbo, dstBox, &tr.calclevels4xShader[0], NULL, 0); + + srcFbo = tr.textureScratchFbo[0]; + dstFbo = tr.textureScratchFbo[1]; + + // downscale to 1x1 texture + while (size > 1) + { + VectorSet4(srcBox, 0, 0, size, size); + //size >>= 2; + size >>= 1; + VectorSet4(dstBox, 0, 0, size, size); + + if (size == 1) + dstFbo = tr.targetLevelsFbo; + + //FBO_Blit(targetFbo, srcBox, NULL, tr.textureScratchFbo[nextScratch], dstBox, &tr.calclevels4xShader[1], NULL, 0); + FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + tmp = srcFbo; + srcFbo = dstFbo; + dstFbo = tmp; + } + } + + // blend with old log luminance for gradual change + VectorSet4(srcBox, 0, 0, 0, 0); + + color[0] = + color[1] = + color[2] = 1.0f; + if (glRefConfig.textureFloat) + color[3] = 0.03f; + else + color[3] = 0.1f; + + FBO_Blit(tr.targetLevelsFbo, srcBox, NULL, tr.calcLevelsFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + + // tonemap + color[0] = + color[1] = + color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); + color[3] = 1.0f; + + if (autoExposure) + GL_BindToTMU(tr.calcLevelsImage, TB_LEVELSMAP); + else + GL_BindToTMU(tr.fixedLevelsImage, TB_LEVELSMAP); + + FBO_Blit(hdrFbo, NULL, NULL, tr.screenScratchFbo, NULL, &tr.tonemapShader, color, 0); +} + + +void RB_BokehBlur(float blur) +{ +// vec4i_t srcBox, dstBox; + vec4_t color; + + blur *= 10.0f; + + if (blur < 0.004f) + return; + + if (glRefConfig.framebufferObject) + { + // bokeh blur + if (blur > 0.0f) + { + // create a quarter texture + FBO_Blit(tr.screenScratchFbo, NULL, NULL, tr.quarterFbo[0], NULL, NULL, NULL, 0); + } + +#ifndef HQ_BLUR + if (blur > 1.0f) + { + // create a 1/16th texture + FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, NULL, 0); + } +#endif + + if (blur > 0.0f && blur <= 1.0f) + { + // Crossfade original with quarter texture + VectorSet4(color, 1, 1, 1, blur); + + FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } +#ifndef HQ_BLUR + // ok blur, but can see some pixelization + else if (blur > 1.0f && blur <= 2.0f) + { + // crossfade quarter texture with 1/16th texture + FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, NULL, 0); + + VectorSet4(color, 1, 1, 1, blur - 1.0f); + + FBO_Blit(tr.textureScratchFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + else if (blur > 2.0f) + { + // blur 1/16th texture then replace + int i; + + for (i = 0; i < 2; i++) + { + vec2_t blurTexScale; + float subblur; + + subblur = ((blur - 2.0f) / 2.0f) / 3.0f * (float)(i + 1); + + blurTexScale[0] = + blurTexScale[1] = subblur; + + color[0] = + color[1] = + color[2] = 0.5f; + color[3] = 1.0f; + + if (i != 0) + FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + else + FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0); + } + + FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); + } +#else // higher quality blur, but slower + else if (blur > 1.0f) + { + // blur quarter texture then replace + int i; + + src = tr.quarterFbo[0]; + dst = tr.quarterFbo[1]; + + VectorSet4(color, 0.5f, 0.5f, 0.5f, 1); + + for (i = 0; i < 2; i++) + { + vec2_t blurTexScale; + float subblur; + + subblur = (blur - 1.0f) / 2.0f * (float)(i + 1); + + blurTexScale[0] = + blurTexScale[1] = subblur; + + color[0] = + color[1] = + color[2] = 1.0f; + if (i != 0) + color[3] = 1.0f; + else + color[3] = 0.5f; + + FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + + FBO_Blit(tr.quarterFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); + } +#endif + } +} + + +#ifdef REACTION +static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretch, float x, float y, float w, float h, float xcenter, float ycenter, float alpha) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + const float inc = 1.f / passes; + const float mul = powf(stretch, inc); + float scale; + + { + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + alpha *= inc; + VectorSet4(color, alpha, alpha, alpha, 1.0f); + + VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); + VectorSet4(dstBox, x, y, w, h); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0); + + --passes; + scale = mul; + while (passes > 0) + { + float iscale = 1.f / scale; + float s0 = xcenter * (1.f - iscale); + float t0 = (1.0f - ycenter) * (1.f - iscale); + float s1 = iscale + s0; + float t1 = iscale + t0; + + srcBox[0] = s0 * srcFbo->width; + srcBox[1] = t0 * srcFbo->height; + srcBox[2] = (s1 - s0) * srcFbo->width; + srcBox[3] = (t1 - t0) * srcFbo->height; + + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + scale *= mul; + --passes; + } + } +} + + +static qboolean RB_UpdateSunFlareVis(void) +{ + GLuint sampleCount = 0; + if (!glRefConfig.occlusionQuery) + return qtrue; + + tr.sunFlareQueryIndex ^= 1; + if (!tr.sunFlareQueryActive[tr.sunFlareQueryIndex]) + return qtrue; + + /* debug code */ + if (0) + { + int iter; + for (iter=0 ; ; ++iter) + { + GLint available = 0; + qglGetQueryObjectivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_AVAILABLE_ARB, &available); + if (available) + break; + } + + ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter); + } + + qglGetQueryObjectuivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_ARB, &sampleCount); + return sampleCount > 0; +} + +void RB_GodRays(void) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + vec3_t dir; + float dot; + const float cutoff = 0.25f; + qboolean colorize = qtrue; + +// float w, h, w2, h2; + matrix_t mvp; + vec4_t pos, hpos; + + if (!backEnd.viewHasSunFlare) + return; + + VectorSubtract(backEnd.sunFlarePos, backEnd.viewParms.or.origin, dir); + VectorNormalize(dir); + + dot = DotProduct(dir, backEnd.viewParms.or.axis[0]); + if (dot < cutoff) + return; + + if (!RB_UpdateSunFlareVis()) + return; + + VectorCopy(backEnd.sunFlarePos, pos); + pos[3] = 1.f; + + // project sun point + Matrix16Multiply(backEnd.viewParms.projectionMatrix, backEnd.viewParms.world.modelMatrix, mvp); + Matrix16Transform(mvp, pos, hpos); + + // transform to UV coords + hpos[3] = 0.5f / hpos[3]; + + pos[0] = 0.5f + hpos[0] * hpos[3]; + pos[1] = 0.5f - hpos[1] * hpos[3]; + + // viewport dimensions + // JBravo: Apparently not used +/* w = glConfig.vidWidth; + h = glConfig.vidHeight; + w2 = glConfig.vidWidth / 2; + h2 = glConfig.vidHeight / 2; */ + + // initialize quarter buffers + { + float mul = 1.f; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, mul, mul, mul, 1); + + // first, downsample the framebuffer + VectorSet4(srcBox, 0, 0, tr.godRaysFbo->width, tr.godRaysFbo->height); + VectorSet4(dstBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + FBO_Blit(tr.godRaysFbo, srcBox, texScale, tr.quarterFbo[0], dstBox, &tr.textureColorShader, color, 0); + + if (colorize) + { + VectorSet4(srcBox, 0, 0, tr.screenScratchFbo->width, tr.screenScratchFbo->height); + FBO_Blit(tr.screenScratchFbo, srcBox, texScale, tr.quarterFbo[0], dstBox, &tr.textureColorShader, color, + GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO); + } + } + + // radial blur passes, ping-ponging between the two quarter-size buffers + { + const float stretch_add = 2.f/3.f; + float stretch = 1.f + stretch_add; + int i; + for (i=0; i<2; ++i) + { + RB_RadialBlur(tr.quarterFbo[i&1], tr.quarterFbo[(~i) & 1], 5, stretch, 0.f, 0.f, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height, pos[0], pos[1], 1.125f); + stretch += stretch_add; + } + } + + // add result back on top of the main buffer + { + float mul = 1.f; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, mul, mul, mul, 1); + + VectorSet4(srcBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + VectorSet4(dstBox, 0, 0, tr.screenScratchFbo->width, tr.screenScratchFbo->height); + FBO_Blit(tr.quarterFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + } +} +#endif + +static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean horizontal) +{ + float dx, dy; + float xmul, ymul; + float weights[3] = { + 0.227027027f, + 0.316216216f, + 0.070270270f, + }; + float offsets[3] = { + 0.f, + 1.3846153846f, + 3.2307692308f, + }; + + xmul = horizontal; + ymul = 1.f - xmul; + + xmul *= strength; + ymul *= strength; + + { + vec4i_t srcBox, dstBox; + vec4_t color; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, weights[0], weights[0], weights[0], 1.0f); + VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); + VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0 ); + + VectorSet4(color, weights[1], weights[1], weights[1], 1.0f); + dx = offsets[1] * xmul; + dy = offsets[1] * ymul; + VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + VectorSet4(color, weights[2], weights[2], weights[2], 1.0f); + dx = offsets[2] * xmul; + dy = offsets[2] * ymul; + VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + } +} + +static void RB_HBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) +{ + RB_BlurAxis(srcFbo, dstFbo, strength, qtrue); +} + +static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) +{ + RB_BlurAxis(srcFbo, dstFbo, strength, qfalse); +} + +void RB_GaussianBlur(float blur) +{ + //float mul = 1.f; + float factor = Com_Clamp(0.f, 1.f, blur); + + if (factor <= 0.f) + return; + + { + vec4i_t srcBox, dstBox; + vec4_t color; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, 1, 1, 1, 1); + + // first, downsample the framebuffer + VectorSet4(srcBox, 0, 0, tr.screenScratchFbo->width, tr.screenScratchFbo->height); + VectorSet4(dstBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + FBO_Blit(tr.screenScratchFbo, srcBox, texScale, tr.quarterFbo[0], dstBox, &tr.textureColorShader, color, 0); + + VectorSet4(srcBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); + FBO_Blit(tr.quarterFbo[0], srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, 0); + + // set the alpha channel + VectorSet4(srcBox, 0, 0, tr.whiteImage->width, tr.whiteImage->height); + VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); + qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + FBO_BlitFromTexture(tr.whiteImage, srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, GLS_DEPTHTEST_DISABLE); + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // blur the tiny buffer horizontally and vertically + RB_HBlur(tr.textureScratchFbo[0], tr.textureScratchFbo[1], factor); + RB_VBlur(tr.textureScratchFbo[1], tr.textureScratchFbo[0], factor); + + // finally, merge back to framebuffer + VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); + VectorSet4(dstBox, 0, 0, tr.screenScratchFbo->width, tr.screenScratchFbo->height); + color[3] = factor; + FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } +} |