/* * RFB_AttachTextureToObject */ void RFB_AttachTextureToObject( int object, image_t *texture ) { r_fbo_t *fbo; int attachment; assert( object > 0 && object <= r_num_framebuffer_objects ); if( object <= 0 || object > r_num_framebuffer_objects ) { return; } assert( texture != NULL ); if( !texture ) { return; } fbo = r_framebuffer_objects + object - 1; qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo->objectID ); if( texture->flags & IT_DEPTH ) { attachment = GL_DEPTH_ATTACHMENT_EXT; fbo->depthTexture = texture; } else { attachment = GL_COLOR_ATTACHMENT0_EXT; fbo->colorTexture = texture; #ifndef GL_ES_VERSION_2_0 qglDrawBuffer( GL_COLOR_ATTACHMENT0_EXT ); qglReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); #endif } texture->fbo = object; // attach texture qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_2D, texture->texnum, 0 ); if( ( texture->flags & ( IT_DEPTH|IT_STENCIL ) ) == ( IT_DEPTH|IT_STENCIL ) ) { qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->texnum, 0 ); } qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, r_bound_framebuffer_objectID ? r_bound_framebuffer_object->objectID : 0 ); }
/* ============= RB_ExportCubemaps ============= */ const void *RB_ExportCubemaps(const void *data) { const exportCubemapsCommand_t *cmd = data; // finish any 2D drawing if needed if (tess.numIndexes) RB_EndSurface(); if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0) { // do nothing ri.Printf(PRINT_ALL, "Nothing to export!\n"); return (const void *)(cmd + 1); } if (cmd) { FBO_t *oldFbo = glState.currentFBO; int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4; byte *cubemapPixels = ri.Malloc(sideSize * 6); int i, j; FBO_Bind(tr.renderCubeFbo); for (i = 0; i < tr.numCubemaps; i++) { char filename[MAX_QPATH]; cubemap_t *cubemap = &tr.cubemaps[i]; char *p = cubemapPixels; 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); qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p); p += sideSize; } Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6); ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename); } FBO_Bind(oldFbo); ri.Free(cubemapPixels); } return (const void *)(cmd + 1); }
/* ================= R_AttachFBOTexture2D ================= */ void R_AttachFBOTexture2D(int target, int texId, int index) { if(target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) { ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid target %i\n", target); return; } if(index < 0 || index >= glRefConfig.maxColorAttachments) { ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid attachment index %i\n", index); return; } qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, texId, 0); }
GLvoid APIENTRY GLDSA_NamedFramebufferTexture2D(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer); qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, textarget, texture, level); }
/* ================= RB_BeginDrawingView Any mirrored or portaled views have already been drawn, so prepare to actually render the visible surfaces for this view ================= */ void RB_BeginDrawingView (void) { int clearBits = 0; // sync with gl if needed if ( r_finish->integer == 1 && !glState.finishCalled ) { qglFinish (); glState.finishCalled = qtrue; } if ( r_finish->integer == 0 ) { glState.finishCalled = qtrue; } // we will need to change the projection matrix before drawing // 2D images again backEnd.projection2D = qfalse; if (glRefConfig.framebufferObject) { // 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); // FIXME: hack for cubemap testing if (tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) { //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, tr.cubemaps[backEnd.viewParms.targetFboCubemapIndex]->texnum, 0); } } } // // set the modelview matrix for the viewer // SetViewportAndScissor(); // ensures that depth writes are enabled for the depth clear GL_State( GLS_DEFAULT ); // clear relevant buffers clearBits = GL_DEPTH_BUFFER_BIT; if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) { clearBits |= GL_STENCIL_BUFFER_BIT; } if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used #ifdef _DEBUG qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky #else qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky #endif } // clear to white for shadow maps if (backEnd.viewParms.flags & VPF_SHADOWMAP) { clearBits |= GL_COLOR_BUFFER_BIT; qglClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); } // clear to black for cube maps if (tr.renderCubeFbo && backEnd.viewParms.targetFbo == tr.renderCubeFbo) { clearBits |= GL_COLOR_BUFFER_BIT; qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); } qglClear( clearBits ); if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) { RB_Hyperspace(); return; } else { backEnd.isHyperspace = qfalse; } glState.faceCulling = -1; // force face culling to set next time // we will only draw a sun if there was sky rendered in this view backEnd.skyRenderedThisView = qfalse; // clip to the plane of the portal if ( backEnd.viewParms.isPortal ) { #if 0 float plane[4]; double plane2[4]; plane[0] = backEnd.viewParms.portalPlane.normal[0]; plane[1] = backEnd.viewParms.portalPlane.normal[1]; plane[2] = backEnd.viewParms.portalPlane.normal[2]; plane[3] = backEnd.viewParms.portalPlane.dist; plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; #endif GL_SetModelviewMatrix( s_flipMatrix ); } }
/* ================= R_AttachFBOTexturePackedDepthStencil ================= */ void R_AttachFBOTexturePackedDepthStencil(int texId) { qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); }
/* ================= R_AttachFBOTextureDepth ================= */ void R_AttachFBOTextureDepth(int texId) { qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); }
/** * @brief create a new framebuffer object * @param[in] width The width of the framebuffer * @param[in] height The height of the framebuffer * @param[in] ntextures The amount of textures for this framebuffer. See also the filters array. * @param[in] depth Also generate a depth buffer * @param[in] halfFloat Use half float pixel format * @param[in] filters Filters for the textures. Must have @c ntextures entries */ r_framebuffer_t * R_CreateFramebuffer (int width, int height, int ntextures, bool depth, bool halfFloat, unsigned int *filters) { r_framebuffer_t *buf; int i; if (!r_state.frameBufferObjectsInitialized) { Com_Printf("Warning: framebuffer creation failed; framebuffers not initialized!\n"); return NULL; } if (frameBufferObjectCount >= lengthof(frameBufferObjects)) { Com_Printf("Warning: framebuffer creation failed; already created too many framebuffers!\n"); return NULL; } buf = &frameBufferObjects[frameBufferObjectCount++]; OBJZERO(*buf); if (ntextures > r_config.maxDrawBuffers) { Com_Printf("Couldn't allocate requested number of drawBuffers in R_SetupFramebuffer!\n"); ntextures = r_config.maxDrawBuffers; } Vector4Clear(buf->clearColor); buf->width = width; buf->height = height; R_SetupViewport(buf, 0, 0, width, height); buf->nTextures = ntextures; buf->textures = Mem_AllocTypeN(unsigned int, ntextures); buf->pixelFormat = halfFloat ? GL_RGBA16F_ARB : GL_RGBA8; buf->byteFormat = halfFloat ? GL_HALF_FLOAT_ARB : GL_UNSIGNED_BYTE; /* Presence of depth buffer indicates render target that could use antialiasing*/ if (depth) { /** @todo also check if we are running on older (SM2.0) hardware, which doesn't support antialiased MRT */ if (qglRenderbufferStorageMultisampleEXT && qglBlitFramebuffer) { int samples = std::min(4, std::max(0, r_multisample->integer)); if (samples>1) buf->samples = samples; } } for (i = 0; i < buf->nTextures; i++) { buf->textures[i] = R_GetFreeFBOTexture(); glBindTexture(GL_TEXTURE_2D, buf->textures[i]); glTexImage2D(GL_TEXTURE_2D, 0, buf->pixelFormat, buf->width, buf->height, 0, GL_RGBA, buf->byteFormat, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filters[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); qglGenerateMipmapEXT(GL_TEXTURE_2D); if (r_config.anisotropic) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_config.maxAnisotropic); R_CheckError(); } glBindTexture(GL_TEXTURE_2D, 0); /* create FBO itself */ qglGenFramebuffersEXT(1, &buf->fbo); R_CheckError(); qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->fbo); /* create&attach depth renderbuffer */ if (depth) { qglGenRenderbuffersEXT(1, &buf->depth); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, buf->depth); if (buf->samples) qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, GL_DEPTH_COMPONENT, buf->width, buf->height); else qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, buf->width, buf->height); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, buf->depth); } else { buf->depth = 0; } /* create multisample color buffers if needed */ if (buf->samples) { /* generate color buffers */ for (i = 0; i < buf->nTextures; i++) { unsigned colorbuffer; qglGenRenderbuffersEXT(1, &colorbuffer); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorbuffer); qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, buf->samples, buf->pixelFormat, buf->width, buf->height); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_RENDERBUFFER_EXT, colorbuffer); R_CheckError(); } /* proxy framebuffer object for resolving MSAA */ qglGenFramebuffersEXT(1, &buf->proxyFBO); R_CheckError(); qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buf->proxyFBO); } /* Whether multisampling was enabled or not, current FBO should be populated with render-to-texture bindings */ for (i = 0; i < buf->nTextures; i++) { qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, colorAttachments[i], GL_TEXTURE_2D, buf->textures[i], 0); R_CheckError(); } R_CheckError(); /* unbind the framebuffer and return to default state */ qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); return buf; }
/* * RFB_AttachTextureToObject */ bool RFB_AttachTextureToObject( int object, bool depth, int target, image_t *texture ) { r_fbo_t *fbo; int attachment; GLuint texnum = 0; assert( object > 0 && object <= r_num_framebuffer_objects ); if( object <= 0 || object > r_num_framebuffer_objects ) { return false; } if( target < 0 || target >= MAX_FRAMEBUFFER_COLOR_ATTACHMENTS ) { return false; } if( target > 0 && !glConfig.ext.draw_buffers ) { return false; } fbo = r_framebuffer_objects + object - 1; qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo->objectID ); bind: if( depth ) { attachment = GL_DEPTH_ATTACHMENT_EXT; if( texture ) { assert( texture->flags & IT_DEPTH ); texnum = texture->texnum; texture->fbo = object; } } else { #ifndef GL_ES_VERSION_2_0 const GLenum fboBuffers[8] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT, GL_COLOR_ATTACHMENT4_EXT, GL_COLOR_ATTACHMENT5_EXT, GL_COLOR_ATTACHMENT6_EXT, GL_COLOR_ATTACHMENT7_EXT, }; #endif attachment = GL_COLOR_ATTACHMENT0_EXT + target; #ifndef GL_ES_VERSION_2_0 if( target > 0 && texture ) { qglDrawBuffersARB( target + 1, fboBuffers ); } else { if( glConfig.ext.draw_buffers ) { qglDrawBuffersARB( 0, fboBuffers ); } qglDrawBuffer( GL_COLOR_ATTACHMENT0_EXT ); qglReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); } #endif if( texture ) { assert( !( texture->flags & IT_DEPTH ) ); texnum = texture->texnum; texture->fbo = object; } } // attach texture qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_2D, texnum, 0 ); if( texture ) { if( ( texture->flags & ( IT_DEPTH | IT_STENCIL ) ) == ( IT_DEPTH | IT_STENCIL ) ) { qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texnum, 0 ); } } else { if( depth ) { qglFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texnum, 0 ); } } qglBindFramebufferEXT( GL_FRAMEBUFFER_EXT, r_bound_framebuffer_objectID ? r_bound_framebuffer_object->objectID : 0 ); // check framebuffer status and unbind if failed if( !RFB_CheckObjectStatus() ) { if( texture ) { texture = NULL; goto bind; } return false; } if( depth ) { fbo->depthTexture = texture; } else { fbo->colorTexture[target] = texture; } return true; }