/** * @file lldrawpoolalpha.cpp * @brief LLDrawPoolAlpha class implementation * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "lldrawpoolalpha.h" #include "llglheaders.h" #include "llviewercontrol.h" #include "llcriticaldamp.h" #include "llfasttimer.h" #include "llrender.h" #include "llcubemap.h" #include "llsky.h" #include "lldrawable.h" #include "llface.h" #include "llviewercamera.h" #include "llviewertexturelist.h" // For debugging #include "llviewerobjectlist.h" // For debugging #include "llviewerwindow.h" #include "pipeline.h" #include "llviewershadermgr.h" #include "llviewerregion.h" #include "lldrawpoolwater.h" #include "llspatialpartition.h" #include "llglcommonfunc.h" BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE; static BOOL deferred_render = FALSE; static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SETUP("Alpha Setup"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_GROUP_LOOP("Alpha Group"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_PUSH("Alpha Push Verts"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED("Alpha Deferred"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SETBUFFER("Alpha SetBuffer"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DRAW("Alpha Draw"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_TEX_BINDS("Alpha Tex Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MATS("Alpha Mat Tex Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_GLOW("Alpha Glow Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_SHADER_BINDS("Alpha Shader Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS("Alpha Def Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS("Alpha Def Tex Binds"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_MESH_REBUILD("Alpha Mesh Rebuild"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_EMISSIVE("Alpha Emissive"); static LLTrace::BlockTimerStatHandle FTM_RENDER_ALPHA_LIGHT_SETUP("Alpha Light Setup"); LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) : LLRenderPass(type), current_shader(NULL), target_shader(NULL), simple_shader(NULL), fullbright_shader(NULL), emissive_shader(NULL), mColorSFactor(LLRender::BF_UNDEF), mColorDFactor(LLRender::BF_UNDEF), mAlphaSFactor(LLRender::BF_UNDEF), mAlphaDFactor(LLRender::BF_UNDEF) { } LLDrawPoolAlpha::~LLDrawPoolAlpha() { } void LLDrawPoolAlpha::prerender() { mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_OBJECT); } S32 LLDrawPoolAlpha::getNumPostDeferredPasses() { if (LLPipeline::sImpostorRender) { //skip depth buffer filling pass when rendering impostors return 1; } else if (gSavedSettings.getBOOL("RenderDepthOfField")) { return 2; } else { return 1; } } void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED); F32 gamma = gSavedSettings.getF32("RenderDeferredDisplayGamma"); emissive_shader = (LLPipeline::sRenderDeferred) ? &gDeferredEmissiveProgram : (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram; emissive_shader->bind(); emissive_shader->uniform1i(LLShaderMgr::NO_ATMO, (LLPipeline::sRenderingHUDs) ? 1 : 0); emissive_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); emissive_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f)); if (pass == 0) { fullbright_shader = (LLPipeline::sImpostorRender) ? &gDeferredFullbrightProgram : (LLPipeline::sUnderWaterRender) ? &gDeferredFullbrightWaterProgram : &gDeferredFullbrightProgram; fullbright_shader->bind(); fullbright_shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); fullbright_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f)); fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); fullbright_shader->unbind(); simple_shader = (LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram : (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram; //prime simple shader (loads shadow relevant uniforms) gPipeline.bindDeferredShader(*simple_shader); simple_shader->uniform1f(LLShaderMgr::DISPLAY_GAMMA, (gamma > 0.1f) ? 1.0f / gamma : (1.0f/2.2f)); simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); } else if (!LLPipeline::sImpostorRender) { //update depth buffer sampler gPipeline.mScreen.flush(); gPipeline.mDeferredDepth.copyContents(gPipeline.mDeferredScreen, 0, 0, gPipeline.mDeferredScreen.getWidth(), gPipeline.mDeferredScreen.getHeight(), 0, 0, gPipeline.mDeferredDepth.getWidth(), gPipeline.mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); gPipeline.mDeferredDepth.bindTarget(); simple_shader = fullbright_shader = &gObjectFullbrightAlphaMaskProgram; gObjectFullbrightAlphaMaskProgram.bind(); gObjectFullbrightAlphaMaskProgram.setMinimumAlpha(0.33f); } deferred_render = TRUE; if (mShaderLevel > 0) { // Start out with no shaders. current_shader = target_shader = NULL; } gPipeline.enableLightsDynamic(); } void LLDrawPoolAlpha::endPostDeferredPass(S32 pass) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED); if (pass == 1 && !LLPipeline::sImpostorRender) { gPipeline.mDeferredDepth.flush(); gPipeline.mScreen.bindTarget(); gObjectFullbrightAlphaMaskProgram.unbind(); } deferred_render = FALSE; endRenderPass(pass); } void LLDrawPoolAlpha::renderPostDeferred(S32 pass) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED); render(pass); } void LLDrawPoolAlpha::beginRenderPass(S32 pass) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_SETUP); simple_shader = (LLPipeline::sImpostorRender) ? &gObjectSimpleImpostorProgram : (LLPipeline::sUnderWaterRender) ? &gObjectSimpleWaterProgram : &gObjectSimpleProgram; fullbright_shader = (LLPipeline::sImpostorRender) ? &gObjectFullbrightProgram : (LLPipeline::sUnderWaterRender) ? &gObjectFullbrightWaterProgram : &gObjectFullbrightProgram; emissive_shader = (LLPipeline::sImpostorRender) ? &gObjectEmissiveProgram : (LLPipeline::sUnderWaterRender) ? &gObjectEmissiveWaterProgram : &gObjectEmissiveProgram; if (LLPipeline::sImpostorRender) { if (mShaderLevel > 0) { fullbright_shader->bind(); fullbright_shader->setMinimumAlpha(0.5f); fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); simple_shader->bind(); simple_shader->setMinimumAlpha(0.5f); simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); } else { gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); //OK } } else { if (mShaderLevel > 0) { fullbright_shader->bind(); fullbright_shader->setMinimumAlpha(0.f); fullbright_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); simple_shader->bind(); simple_shader->setMinimumAlpha(0.f); simple_shader->uniform1i(LLShaderMgr::NO_ATMO, LLPipeline::sRenderingHUDs ? 1 : 0); } else { gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); //OK } } gPipeline.enableLightsDynamic(); LLGLSLShader::bindNoShader(); current_shader = NULL; } void LLDrawPoolAlpha::endRenderPass( S32 pass ) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_SETUP); LLRenderPass::endRenderPass(pass); if(gPipeline.canUseWindLightShaders()) { LLGLSLShader::bindNoShader(); } } void LLDrawPoolAlpha::render(S32 pass) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA); LLGLSPipelineAlpha gls_pipeline_alpha; if (deferred_render && pass == 1) { //depth only gGL.setColorMask(false, false); } else { gGL.setColorMask(true, true); } bool write_depth = LLDrawPoolWater::sSkipScreenCopy || (deferred_render && pass == 1) // we want depth written so that rendered alpha will // contribute to the alpha mask used for impostors || LLPipeline::sImpostorRenderAlphaDepthPass; LLGLDepthTest depth(GL_TRUE, write_depth ? GL_TRUE : GL_FALSE); if (deferred_render && pass == 1) { gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA); } else { mColorSFactor = LLRender::BF_SOURCE_ALPHA; // } regular alpha blend mColorDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } mAlphaSFactor = LLRender::BF_ZERO; // } glow suppression mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); } if (mShaderLevel > 0) { renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, pass); } else { renderAlpha(getVertexDataMask(), pass); } gGL.setColorMask(true, false); if (deferred_render && pass == 1) { gGL.setSceneBlendType(LLRender::BT_ALPHA); } if (sShowDebugAlpha) { BOOL shaders = gPipeline.canUseVertexShaders(); if(shaders) { gHighlightProgram.bind(); } else { gPipeline.enableLightsFullbright(); } gGL.diffuseColor4f(1,0,0,1); LLViewerFetchedTexture::sSmokeImagep->addTextureStats(1024.f*1024.f); gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep, TRUE) ; renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); pushBatches(LLRenderPass::PASS_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_ALPHA_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); // Material alpha mask gGL.diffuseColor4f(0, 0, 1, 1); pushBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_NORMMAP_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_SPECMAP_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_NORMSPEC_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); pushBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); gGL.diffuseColor4f(0, 1, 0, 1); pushBatches(LLRenderPass::PASS_INVISIBLE, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, FALSE); if(shaders) { gHighlightProgram.unbind(); } } } void LLDrawPoolAlpha::renderAlphaHighlight(U32 mask) { for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i) { LLSpatialGroup* group = *i; if (group->getSpatialPartition()->mRenderByGroup && !group->isDead()) { LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA]; for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo& params = **k; if (params.mParticle) { continue; } LLRenderPass::applyModelMatrix(params); if (params.mGroup) { params.mGroup->rebuildMesh(); } params.mVertexBuffer->setBuffer(mask); params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset); gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode); } } } } inline bool IsFullbright(LLDrawInfo& params) { return params.mFullbright; } inline bool IsMaterial(LLDrawInfo& params) { return params.mMaterial != nullptr; } inline bool IsEmissive(LLDrawInfo& params) { return params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE); } inline void Draw(LLDrawInfo* draw, U32 mask) { draw->mVertexBuffer->setBuffer(mask); LLRenderPass::applyModelMatrix(*draw); draw->mVertexBuffer->drawRange(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); gPipeline.addTrianglesDrawn(draw->mCount, draw->mDrawMode); } bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_shaders, bool use_material, LLGLSLShader* current_shader) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_TEX_BINDS); bool tex_setup = false; if (deferred_render && use_material && current_shader) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS); if (draw->mNormalMap) { draw->mNormalMap->addTextureStats(draw->mVSize); current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap); } if (draw->mSpecularMap) { draw->mSpecularMap->addTextureStats(draw->mVSize); current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap); } } else if (current_shader == simple_shader) { LLViewerFetchedTexture::sFlatNormalImagep->addTextureStats(draw->mVSize); LLViewerFetchedTexture::sWhiteImagep->addTextureStats(draw->mVSize); current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep); current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep); } if (use_shaders && draw->mTextureList.size() > 1) { for (U32 i = 0; i < draw->mTextureList.size(); ++i) { if (draw->mTextureList[i].notNull()) { gGL.getTexUnit(i)->bind(draw->mTextureList[i], TRUE); } } } else { //not batching textures or batch has only 1 texture -- might need a texture matrix if (draw->mTexture.notNull()) { draw->mTexture->addTextureStats(draw->mVSize); if (use_shaders && use_material) { current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture); } else { gGL.getTexUnit(0)->bind(draw->mTexture, TRUE) ; } if (draw->mTextureMatrix) { tex_setup = true; gGL.getTexUnit(0)->activate(); gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadMatrix((GLfloat*) draw->mTextureMatrix->mMatrix); gPipeline.mTextureMatrixOps++; } } else { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } return tex_setup; } void LLDrawPoolAlpha::RestoreTexSetup(bool tex_setup) { if (tex_setup) { gGL.getTexUnit(0)->activate(); gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.matrixMode(LLRender::MM_MODELVIEW); } } void LLDrawPoolAlpha::renderSimples(U32 mask, std::vector& simples) { gPipeline.enableLightsDynamic(); simple_shader->bind(); simple_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep); simple_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep); simple_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); simple_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, 0.0f); simple_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 0.0f); bool use_shaders = gPipeline.canUseVertexShaders(); for (LLDrawInfo* draw : simples) { bool tex_setup = TexSetup(draw, use_shaders, false, simple_shader); LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test); gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor); Draw(draw, mask); RestoreTexSetup(tex_setup); } simple_shader->unbind(); } void LLDrawPoolAlpha::renderFullbrights(U32 mask, std::vector& fullbrights) { gPipeline.enableLightsFullbright(); fullbright_shader->bind(); fullbright_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.0f); bool use_shaders = gPipeline.canUseVertexShaders(); for (LLDrawInfo* draw : fullbrights) { bool tex_setup = TexSetup(draw, use_shaders, false, fullbright_shader); LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test); gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor); Draw(draw, mask & ~(LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2)); RestoreTexSetup(tex_setup); } fullbright_shader->unbind(); } void LLDrawPoolAlpha::renderMaterials(U32 mask, std::vector& materials) { LLGLSLShader::bindNoShader(); current_shader = NULL; gPipeline.enableLightsDynamic(); bool use_shaders = gPipeline.canUseVertexShaders(); for (LLDrawInfo* draw : materials) { U32 mask = draw->mShaderMask; llassert(mask < LLMaterial::SHADER_COUNT); target_shader = (LLPipeline::sUnderWaterRender) ? &(gDeferredMaterialWaterProgram[mask]) : &(gDeferredMaterialProgram[mask]); if (current_shader != target_shader) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS); if (current_shader) { gPipeline.unbindDeferredShader(*current_shader); } gPipeline.bindDeferredShader(*target_shader); current_shader = target_shader; } bool tex_setup = TexSetup(draw, use_shaders, true, current_shader); current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, draw->mSpecColor.mV[0], draw->mSpecColor.mV[1], draw->mSpecColor.mV[2], draw->mSpecColor.mV[3]); current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, draw->mEnvIntensity); current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, draw->mFullbright ? 1.f : 0.f); { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_TEX_BINDS); if (draw->mNormalMap) { draw->mNormalMap->addTextureStats(draw->mVSize); current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap); } if (draw->mSpecularMap) { draw->mSpecularMap->addTextureStats(draw->mVSize); current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap); } } LLGLEnableFunc stencil_test(GL_STENCIL_TEST, draw->mSelected, &LLGLCommonFunc::selected_stencil_test); gGL.blendFunc((LLRender::eBlendFactor) draw->mBlendFuncSrc, (LLRender::eBlendFactor) draw->mBlendFuncDst, mAlphaSFactor, mAlphaDFactor); Draw(draw, mask); RestoreTexSetup(tex_setup); } } void LLDrawPoolAlpha::drawEmissive(U32 mask, LLDrawInfo* draw) { draw->mVertexBuffer->setBuffer((mask & ~LLVertexBuffer::MAP_COLOR) | LLVertexBuffer::MAP_EMISSIVE); draw->mVertexBuffer->drawRange(draw->mDrawMode, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); gPipeline.addTrianglesDrawn(draw->mCount, draw->mDrawMode); } void LLDrawPoolAlpha::drawEmissiveInline(U32 mask, LLDrawInfo* draw) { // install glow-accumulating blend mode gGL.blendFunc( LLRender::BF_ZERO, LLRender::BF_ONE, // don't touch color LLRender::BF_ONE, LLRender::BF_ONE); // add to alpha (glow) emissive_shader->bind(); drawEmissive(mask, draw); // restore our alpha blend mode gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); current_shader->bind(); } void LLDrawPoolAlpha::renderEmissives(U32 mask, std::vector& emissives) { emissive_shader->bind(); emissive_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, 1.f); gPipeline.enableLightsDynamic(); // install glow-accumulating blend mode // don't touch color, add to alpha (glow) gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, LLRender::BF_ONE, LLRender::BF_ONE); bool use_shaders = gPipeline.canUseVertexShaders(); for (LLDrawInfo* draw : emissives) { bool tex_setup = TexSetup(draw, use_shaders, false, emissive_shader); drawEmissive(mask, draw); RestoreTexSetup(tex_setup); } // restore our alpha blend mode gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); emissive_shader->unbind(); } void LLDrawPoolAlpha::renderAlpha(U32 mask, S32 pass) { BOOL batch_fullbrights = gSavedSettings.getBOOL("RenderAlphaBatchFullbrights"); BOOL batch_emissives = gSavedSettings.getBOOL("RenderAlphaBatchEmissives"); BOOL initialized_lighting = FALSE; BOOL light_enabled = TRUE; BOOL use_shaders = gPipeline.canUseVertexShaders(); for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i) { LLSpatialGroup* group = *i; llassert(group); llassert(group->getSpatialPartition()); if (group->getSpatialPartition()->mRenderByGroup && !group->isDead()) { std::vector emissives; std::vector fullbrights; bool is_particle_or_hud_particle = group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_PARTICLE || group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE; bool draw_glow_for_this_partition = mShaderLevel > 0; // no shaders = no glow. LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_GROUP_LOOP); bool disable_cull = is_particle_or_hud_particle; LLGLDisable cull(disable_cull ? GL_CULL_FACE : 0); LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA]; for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k) { LLDrawInfo& params = **k; U32 have_mask = params.mVertexBuffer->getTypeMask() & mask; if (have_mask != mask) { //FIXME! LL_WARNS_ONCE() << "Missing required components, expected mask: " << mask << " present: " << have_mask << ". Skipping render batch." << LL_ENDL; continue; } // Fix for bug - NORSPEC-271 // If the face is more than 90% transparent, then don't update the Depth buffer for Dof // We don't want the nearly invisible objects to cause of DoF effects if(pass == 1 && !LLPipeline::sImpostorRender) { LLFace* face = params.mFace; if(face) { const LLTextureEntry* tep = face->getTextureEntry(); if(tep) { if(tep->getColor().mV[3] < 0.1f) continue; } } } if (params.mFullbright && batch_fullbrights) { fullbrights.push_back(¶ms); continue; } LLRenderPass::applyModelMatrix(params); LLMaterial* mat = NULL; if (deferred_render) { mat = params.mMaterial; } if (params.mFullbright) { // Turn off lighting if it hasn't already been so. if (light_enabled || !initialized_lighting) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_LIGHT_SETUP); initialized_lighting = TRUE; if (use_shaders) { target_shader = fullbright_shader; } else { gPipeline.enableLightsFullbright(); } light_enabled = FALSE; } } // Turn on lighting if it isn't already. else if (!light_enabled || !initialized_lighting) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_LIGHT_SETUP); initialized_lighting = TRUE; if (use_shaders) { target_shader = simple_shader; } else { gPipeline.enableLightsDynamic(); } light_enabled = TRUE; } if (deferred_render && mat) { U32 mask = params.mShaderMask; llassert(mask < LLMaterial::SHADER_COUNT); target_shader = &(gDeferredMaterialProgram[mask]); if (LLPipeline::sUnderWaterRender) { target_shader = &(gDeferredMaterialWaterProgram[mask]); } if (current_shader != target_shader) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DEFERRED_SHADER_BINDS); gPipeline.bindDeferredShader(*target_shader); current_shader = target_shader; } } else if (!params.mFullbright) { target_shader = simple_shader; } else { target_shader = fullbright_shader; } if(use_shaders && (current_shader != target_shader)) {// If we need shaders, and we're not ALREADY using the proper shader, then bind it // (this way we won't rebind shaders unnecessarily). LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_SHADER_BINDS); current_shader = target_shader; current_shader->bind(); } else if (!use_shaders && current_shader != NULL) { LLGLSLShader::bindNoShader(); current_shader = NULL; } LLVector4 spec_color(1, 1, 1, 1); F32 env_intensity = 0.0f; F32 brightness = 1.0f; // We have a material. Supply the appropriate data here. if (use_shaders && mat && deferred_render) { spec_color = params.mSpecColor; env_intensity = params.mEnvIntensity; brightness = params.mFullbright ? 1.f : 0.f; } if (current_shader) { current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]); current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity); current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness); } if (params.mGroup) { params.mGroup->rebuildMesh(); } bool tex_setup = TexSetup(¶ms, use_shaders, use_shaders && (mat != nullptr), current_shader); { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_PUSH); LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test); gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor); params.mVertexBuffer->setBuffer(mask & ~(params.mFullbright ? (LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2) : 0)); { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_DRAW); params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset); gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode); } } // If this alpha mesh has glow, then draw it a second time to add the destination-alpha (=glow). Interleaving these state-changing calls is expensive, but glow must be drawn Z-sorted with alpha. if (current_shader && draw_glow_for_this_partition && params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE)) { LL_RECORD_BLOCK_TIME(FTM_RENDER_ALPHA_EMISSIVE); if (batch_emissives) { emissives.push_back(¶ms); } else { drawEmissiveInline(mask, ¶ms); } } if (tex_setup) { gGL.getTexUnit(0)->activate(); gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.matrixMode(LLRender::MM_MODELVIEW); } } if (batch_fullbrights) { light_enabled = false; renderFullbrights(mask, fullbrights); } if (batch_emissives) { light_enabled = true; renderEmissives(mask, emissives); } if (current_shader) { current_shader->bind(); } } } gGL.setSceneBlendType(LLRender::BT_ALPHA); LLVertexBuffer::unbind(); if (!light_enabled) { gPipeline.enableLightsDynamic(); } }