diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index f3f5a6af..df70144e 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "tr_local.h" +#include "tr_fbo.h" #include "tr_dsa.h" backEndData_t *backEndData; @@ -377,31 +378,20 @@ void RB_BeginDrawingView (void) { if (glRefConfig.framebufferObject) { + FBO_t *fbo = backEnd.viewParms.targetFbo; + // FIXME: HUGE HACK: render to the screen fbo if we've already postprocessed the frame and aren't drawing more world // drawing more world check is in case of double renders, such as skyportals - if (backEnd.viewParms.targetFbo == NULL) - { - if (!tr.renderFbo || (backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - else - { - FBO_Bind(backEnd.viewParms.targetFbo); + if (fbo == NULL && !(backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) + fbo = tr.renderFbo; - // FIXME: hack for cubemap testing - if (tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) - { - cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]; - //qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + backEnd.viewParms.targetFboLayer, backEnd.viewParms.targetFbo->colorImage[0]->texnum, 0); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + backEnd.viewParms.targetFboLayer, cubemap->image->texnum, 0); - } + if (tr.renderCubeFbo && fbo == tr.renderCubeFbo) + { + cubemap_t *cubemap = &tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]; + FBO_AttachImage(fbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, backEnd.viewParms.targetFboLayer); } + + FBO_Bind(fbo); } // @@ -780,14 +770,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * // FIXME: HUGE hack if (glRefConfig.framebufferObject) { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } + FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo); } RB_SetGL2D(); @@ -873,16 +856,7 @@ const void *RB_StretchPic ( const void *data ) { // FIXME: HUGE hack if (glRefConfig.framebufferObject) - { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(NULL); - } - else - { - FBO_Bind(tr.renderFbo); - } - } + FBO_Bind(backEnd.framePostProcessed ? NULL : tr.renderFbo); RB_SetGL2D(); @@ -1695,7 +1669,7 @@ const void *RB_ExportCubemaps(const void *data) for (j = 0; j < 6; j++) { - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, cubemap->image->texnum, 0); + FBO_AttachImage(tr.renderCubeFbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, j); qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p); p += sideSize; } diff --git a/code/renderergl2/tr_fbo.c b/code/renderergl2/tr_fbo.c index 6ee533f3..59b17032 100644 --- a/code/renderergl2/tr_fbo.c +++ b/code/renderergl2/tr_fbo.c @@ -208,13 +208,13 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) FBO_AttachImage ================= */ -void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment) +void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubemapside) { GLenum target = GL_TEXTURE_2D; int index; if (image->flags & IMGFLAG_CUBEMAP) - target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cubemapside; qglNamedFramebufferTexture2D(fbo->frameBuffer, attachment, target, image->texnum, 0); index = attachment - GL_COLOR_ATTACHMENT0_EXT; @@ -289,15 +289,15 @@ void FBO_Init(void) R_CheckFBO(tr.renderFbo); tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT); - FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT); + FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.msaaResolveFbo); } else if (r_hdr->integer) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT); - FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT); + FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.renderFbo); } @@ -312,8 +312,8 @@ void FBO_Init(void) if (r_drawSunRays->integer) { tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0_EXT); - FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT); + FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.sunRaysFbo); } @@ -323,7 +323,7 @@ void FBO_Init(void) for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) { tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); - FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0_EXT, 0); FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); R_CheckFBO(tr.pshadowFbos[i]); } @@ -334,56 +334,56 @@ void FBO_Init(void) for ( i = 0; i < 4; i++) { tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height); - FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT_EXT); + FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT_EXT, 0); R_CheckFBO(tr.sunShadowFbo[i]); } tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); - FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.screenShadowFbo); } for (i = 0; i < 2; i++) { tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); - FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.textureScratchFbo[i]); } { tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); - FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.calcLevelsFbo); } { tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); - FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.targetLevelsFbo); } for (i = 0; i < 2; i++) { tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); - FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.quarterFbo[i]); } if (r_ssao->integer) { tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); - FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.hdrDepthFbo); tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); - FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0_EXT, 0); R_CheckFBO(tr.screenSsaoFbo); } if (tr.renderCubeImage) { tr.renderCubeFbo = FBO_Create("_renderCubeFbo", tr.renderCubeImage->width, tr.renderCubeImage->height); - FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0_EXT); + FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0_EXT, 0); FBO_CreateBuffer(tr.renderCubeFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); R_CheckFBO(tr.renderCubeFbo); } @@ -474,7 +474,10 @@ void FBO_BlitFromTexture(struct image_s *src, ivec4_t inSrcBox, vec2_t inSrcTexS int width, height; if (!src) + { + ri.Printf(PRINT_WARNING, "Tried to blit from a NULL texture!\n"); return; + } if (inSrcBox) { diff --git a/code/renderergl2/tr_fbo.h b/code/renderergl2/tr_fbo.h index b5ab18c1..3f23a353 100644 --- a/code/renderergl2/tr_fbo.h +++ b/code/renderergl2/tr_fbo.h @@ -52,6 +52,7 @@ typedef struct FBO_s int height; } FBO_t; +void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubemapside); void FBO_Bind(FBO_t *fbo); void FBO_Init(void); void FBO_Shutdown(void); diff --git a/code/renderergl2/tr_postprocess.c b/code/renderergl2/tr_postprocess.c index 35982fce..1952ae96 100644 --- a/code/renderergl2/tr_postprocess.c +++ b/code/renderergl2/tr_postprocess.c @@ -183,7 +183,7 @@ void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0); } - FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, &tr.textureColorShader, NULL, 0); + FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0); } #else // higher quality blur, but slower else if (blur > 1.0f) @@ -217,7 +217,7 @@ void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float 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, dst, dstBox, &tr.textureColorShader, NULL, 0); + FBO_Blit(tr.quarterFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0); } #endif } @@ -232,49 +232,44 @@ static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretc 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); + alpha *= inc; + VectorSet4(color, alpha, alpha, alpha, 1.0f); + if (srcFbo) 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); + else + VectorSet4(srcBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - --passes; - scale = mul; - while (passes > 0) + VectorSet4(dstBox, x, y, w, h); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, 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); + + if (srcFbo) { - 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; - - if (srcFbo) - { - srcBox[0] = s0 * srcFbo->width; - srcBox[1] = t0 * srcFbo->height; - srcBox[2] = (s1 - s0) * srcFbo->width; - srcBox[3] = (t1 - t0) * srcFbo->height; - } - else - { - srcBox[0] = s0 * glConfig.vidWidth; - srcBox[1] = t0 * glConfig.vidHeight; - srcBox[2] = (s1 - s0) * glConfig.vidWidth; - srcBox[3] = (t1 - t0) * glConfig.vidHeight; - } - - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - scale *= mul; - --passes; + srcBox[0] = s0 * srcFbo->width; + srcBox[1] = t0 * srcFbo->height; + srcBox[2] = iscale * srcFbo->width; + srcBox[3] = iscale * srcFbo->height; } + else + { + srcBox[0] = s0 * glConfig.vidWidth; + srcBox[1] = t0 * glConfig.vidHeight; + srcBox[2] = iscale * glConfig.vidWidth; + srcBox[3] = iscale * glConfig.vidHeight; + } + + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + scale *= mul; + --passes; } } @@ -329,7 +324,7 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // From RB_DrawSun() { float dist; - mat4_t trans, model, mvp; + mat4_t trans, model; Mat4Translation( backEnd.viewParms.or.origin, trans ); Mat4Multiply( backEnd.viewParms.world.modelMatrix, trans, model ); @@ -353,12 +348,8 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // initialize quarter buffers { float mul = 1.f; - vec2_t texScale; ivec4_t rayBox, quarterBox; - texScale[0] = - texScale[1] = 1.0f; - VectorSet4(color, mul, mul, mul, 1); if (srcFbo) @@ -408,14 +399,10 @@ void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox) // 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); - FBO_Blit(tr.quarterFbo[0], NULL, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + FBO_Blit(tr.quarterFbo[0], NULL, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); } } @@ -443,31 +430,27 @@ static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean h { ivec4_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 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, 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 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, 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 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, 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 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, 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 ); + FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); } } @@ -492,10 +475,6 @@ void RB_GaussianBlur(float blur) { ivec4_t srcBox, dstBox; vec4_t color; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; VectorSet4(color, 1, 1, 1, 1); @@ -507,7 +486,7 @@ void RB_GaussianBlur(float blur) 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); + FBO_BlitFromTexture(tr.whiteImage, srcBox, NULL, tr.textureScratchFbo[0], dstBox, NULL, color, GLS_DEPTHTEST_DISABLE); qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // blur the tiny buffer horizontally and vertically @@ -518,6 +497,6 @@ void RB_GaussianBlur(float blur) VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); color[3] = factor; - FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, NULL, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + FBO_Blit(tr.textureScratchFbo[0], srcBox, NULL, NULL, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); } }