123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472 |
- /**
- * @file llreflectionmapmanager.cpp
- * @brief LLReflectionMap, LLReflectionMapManager and LLHeroProbeManager
- * classes implementation.
- *
- * $LicenseInfo:firstyear=2022&license=viewergpl$
- *
- * Copyright (c) 2022, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llreflectionmapmanager.h"
- #include "hbtracy.h"
- #include "llagent.h"
- #include "llappviewer.h"
- #if LL_RESET_HDRI_SKY_ON_REFLECTION_MAP_RESET
- # include "lldrawpoolwlsky.h" // For LLDrawPoolWLSky::resetHDRISky()
- #endif
- #include "llenvironment.h"
- #include "llpipeline.h"
- #include "llspatialpartition.h"
- #include "llstartup.h"
- #include "llviewercamera.h"
- #include "llviewercontrol.h"
- #include "llviewerdisplay.h" // For gTeleportDisplay, gCubeSnapshot
- #include "llviewerregion.h"
- #include "llviewershadermgr.h"
- #include "llviewerwindow.h"
- #include "llvovolume.h"
- #include "llworld.h"
- // Uniform names
- static LLStaticHashedString sDirection("direction");
- static LLStaticHashedString sMipLevel("mipLevel");
- static LLStaticHashedString sResScale("resScale");
- static LLStaticHashedString sRoughness("roughness");
- static LLStaticHashedString sSourceIdx("sourceIdx");
- static LLStaticHashedString sWidth("u_width");
- static LLStaticHashedString sZnear("znear");
- static LLStaticHashedString sZfar("zfar");
- static LLStaticHashedString sStrength("probe_strength");
- ///////////////////////////////////////////////////////////////////////////////
- // LLReflectionMap class
- ///////////////////////////////////////////////////////////////////////////////
- LLReflectionMap::LLReflectionMap()
- : mCubeIndex(-1),
- mDistance(-1.f),
- mMinDepth(-1.f),
- mMaxDepth(-1.f),
- mRadius(16.f),
- mLastUpdateTime(0.f),
- mLastBindTime(0.f),
- mFadeIn(0.f),
- mProbeIndex(-1),
- mPriority(0),
- mOcclusionQuery(0),
- mOcclusionPendingFrames(0),
- mOccluded(false),
- mComplete(false)
- {
- mOrigin.clear();
- }
- LLReflectionMap::~LLReflectionMap()
- {
- if (mOcclusionQuery)
- {
- glDeleteQueries(1, &mOcclusionQuery);
- }
- mGroup = NULL;
- mViewerObject = NULL;
- }
- void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic,
- F32 near_clip)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- if (!gUsePBRShaders || mCubeIndex == -1 || mCubeArray.isNull())
- {
- return;
- }
- mLastUpdateTime = gFrameTimeSeconds;
- // Make sure we do not walk off the edge of the render target
- while (resolution > gPipeline.mRT->mDeferredScreen.getWidth() ||
- resolution > gPipeline.mRT->mDeferredScreen.getHeight())
- {
- resolution /= 2;
- }
- if (near_clip <= 0.f)
- {
- near_clip = getNearClip();
- }
- gViewerWindowp->cubeSnapshot(LLVector3(mOrigin.getF32ptr()), mCubeArray,
- face, near_clip,
- force_dynamic || getIsDynamic());
- }
- void LLReflectionMap::autoAdjustOrigin()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- if (mComplete || mGroup.isNull() ||
- mGroup->hasState(LLViewerOctreeGroup::DEAD))
- {
- if (mViewerObject.notNull() && !mViewerObject->isDead())
- {
- mPriority = 1;
- mOrigin.load3(mViewerObject->getPositionAgent().mV);
- if (mViewerObject->getVolume())
- {
- LLVOVolume* vobjp = mViewerObject->asVolume();
- if (vobjp && vobjp->getReflectionProbeIsBox())
- {
- static const LLVector3 half(0.5f, 0.5f, 0.5f);
- mRadius = vobjp->getScale().scaledVec(half).length();
- return;
- }
- }
- mRadius = mViewerObject->getScale().mV[0] * 0.5f;
- }
- return;
- }
- LLSpatialPartition* partp = mGroup->getSpatialPartition();
- if (!partp || partp->mPartitionType != LLViewerRegion::PARTITION_VOLUME)
- {
- return;
- }
- mPriority = 0;
- if (!mGroup->getOctreeNode())
- {
- return;
- }
- // Cast a ray towards 8 corners of bounding box nudge origin towards center
- // of empty space
- const LLVector4a* bounds = mGroup->getBounds();
- mOrigin = bounds[0];
- LLVector4a size = bounds[1];
- LLVector4a corners[] =
- {
- { 1.f, 1.f, 1.f },
- { -1.f, 1.f, 1.f },
- { 1.f, -1.f, 1.f },
- { -1.f, -1.f, 1.f },
- { 1.f, 1.f, -1.f },
- { -1.f, 1.f, -1.f },
- { 1.f, -1.f, -1.f },
- { -1.f, -1.f, -1.f }
- };
- for (U32 i = 0; i < 8; ++i)
- {
- corners[i].mul(size);
- corners[i].add(bounds[0]);
- }
- LLVector4a extents[2];
- extents[0].setAdd(bounds[0], bounds[1]);
- extents[1].setSub(bounds[0], bounds[1]);
- // Prevent click-through exceptions to kick in during the calls to
- // lineSegmentIntersect(). HB
- gPickingProbe = true;
- LLVector4a intersection;
- bool hit = false;
- for (U32 i = 0; i < 8; ++i)
- {
- S32 face = -1;
- LLDrawable* drawablep =
- mGroup->lineSegmentIntersect(bounds[0], corners[i], false, false,
- &face, &intersection);
- if (drawablep)
- {
- hit = true;
- update_min_max(extents[0], extents[1], intersection);
- }
- else
- {
- update_min_max(extents[0], extents[1], corners[i]);
- }
- }
- gPickingProbe = false;
- if (hit)
- {
- mOrigin.setAdd(extents[0], extents[1]);
- mOrigin.mul(0.5f);
- }
- // Make sure origin is not under the ground
- F32* fp = mOrigin.getF32ptr();
- LLVector3 origin(fp);
- F32 height = gWorld.resolveLandHeightAgent(origin) + 2.f;
- fp[2] = llmax(fp[2], height);
- // Make sure radius encompasses all objects
- LLSimdScalar r2 = 0.0;
- for (S32 i = 0; i < 8; ++i)
- {
- LLVector4a v;
- v.setSub(corners[i], mOrigin);
- LLSimdScalar d = v.dot3(v);
- if (d > r2)
- {
- r2 = d;
- }
- }
- mRadius = llmax(sqrtf(r2.getF32()), 8.f);
- // Make sure near clip does not poke through ground
- fp[2] = llmax(fp[2], height + mRadius * 0.5f);
- }
- bool LLReflectionMap::intersects(LLReflectionMap* otherp)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- LLVector4a delta;
- delta.setSub(otherp->mOrigin, mOrigin);
- F32 r = mRadius + otherp->mRadius;
- return delta.dot3(delta).getF32() < r * r;
- }
- F32 LLReflectionMap::getAmbiance()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- F32 ret = 0.f;
- if (mViewerObject.notNull() && !mViewerObject->isDead() &&
- mViewerObject->getVolume())
- {
- LLVOVolume* vobjp = mViewerObject->asVolume();
- if (vobjp)
- {
- ret = vobjp->getReflectionProbeAmbiance();
- }
- }
- return ret;
- }
- F32 LLReflectionMap::getNearClip()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- F32 ret = 1.f; // Default to 1m for automatic terrain probes
- if (mViewerObject.notNull() && !mViewerObject->isDead() &&
- mViewerObject->getVolume())
- {
- LLVOVolume* vobjp = mViewerObject->asVolume();
- if (vobjp)
- {
- ret = vobjp->getReflectionProbeNearClip();
- }
- }
- else if (mGroup.notNull())
- {
- // Default to half radius for automatic object probes
- ret = mRadius * 0.5f;
- }
- constexpr F32 MINIMUM_NEAR_CLIP = 0.1f;
- return llmax(ret, MINIMUM_NEAR_CLIP);
- }
- bool LLReflectionMap::getIsDynamic()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- static LLCachedControl<U32> probe_detail(gSavedSettings,
- "RenderReflectionProbes");
- if (mViewerObject.isNull() || mViewerObject->isDead() ||
- !mViewerObject->getVolume() || (U32)probe_detail < STATIC_AND_DYNAMIC)
- {
- return false;
- }
- LLVOVolume* vovolp = mViewerObject->asVolume();
- return vovolp && vovolp->getReflectionProbeIsDynamic();
- }
- bool LLReflectionMap::getBox(LLMatrix4& box)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- if (mViewerObject.isNull() || mViewerObject->isDead() ||
- !mViewerObject->getVolume())
- {
- return false;
- }
- LLVOVolume* vobjp = mViewerObject->asVolume();
- if (!vobjp || !vobjp->mDrawable || vobjp->mDrawable->isDead() ||
- !vobjp->getReflectionProbeIsBox())
- {
- return false;
- }
- static const LLVector3 half(0.5f, 0.5f, 0.5f);
- LLVector3 s = vobjp->getScale().scaledVec(half);
- mRadius = s.length();
- // Object to agent space (no scale)
- LLMatrix4a scale;
- scale.setIdentity();
- scale.applyScaleAffine(s);
- scale.transpose();
- // Construct object to camera space (with scale)
- LLMatrix4a mv = gGLModelView;
- LLMatrix4a rm(vobjp->mDrawable->getWorldMatrix());
- mv.mul(rm);
- mv.mul(scale);
- // Inverse is camera space to object unit cube
- mv.invert();
- box.set(mv.getF32ptr());
- return true;
- }
- bool LLReflectionMap::isRelevant()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- static LLCachedControl<U32> probe_level(gSavedSettings,
- "RenderReflectionProbeLevel");
- static LLCachedControl<U32> reflections(gSavedSettings,
- "RenderReflectionProbes");
- U32 level = reflections ? probe_level : 0;
- if (mViewerObject.notNull() && !mViewerObject->isDead() && level > 0)
- {
- // Not an automatic probe
- return true;
- }
- if (level >= 3)
- {
- // All automatic probes are relevant
- return true;
- }
- if (level == 2)
- {
- // Terrain and water only, ignore probes that have a group
- return mGroup.isNull();
- }
- // No automatic probes, yes manual probes
- return mViewerObject.notNull() && !mViewerObject->isDead();
- }
- // Super sloppy, but we are doing an occlusion cull against a bounding cube of
- // a bounding sphere, pad radius so we assume if the eye is within the bounding
- // sphere of the bounding cube, the node is not culled.
- void LLReflectionMap::doOcclusion(const LLVector4a& eye)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_MAP);
- if (LLGLSLShader::sProfileEnabled)
- {
- return;
- }
- F32 dist = mRadius * F_SQRT3 + 1.f;
- LLVector4a o;
- o.setSub(mOrigin, eye);
- bool do_query = false;
- if (o.getLength3().getF32() < dist)
- {
- // Eye is inside radius, do not attempt to occlude
- mOccluded = false;
- return;
- }
- if (mOcclusionQuery == 0)
- {
- // No query was previously issued, allocate one and issue
- glGenQueries(1, &mOcclusionQuery);
- do_query = true;
- }
- else
- {
- // Query was previously issued, check it and only issue a new query
- // if previous query is available
- GLuint result = 0;
- glGetQueryObjectuiv(mOcclusionQuery, GL_QUERY_RESULT_AVAILABLE, &result);
- if (result > 0)
- {
- do_query = true;
- glGetQueryObjectuiv(mOcclusionQuery, GL_QUERY_RESULT, &result);
- mOccluded = result == 0;
- mOcclusionPendingFrames = 0;
- }
- else
- {
- ++mOcclusionPendingFrames;
- }
- }
- if (do_query)
- {
- glBeginQuery(GL_ANY_SAMPLES_PASSED, mOcclusionQuery);
- LLGLSLShader* shaderp = LLGLSLShader::sCurBoundShaderPtr;
- shaderp->uniform3fv(LLShaderMgr::BOX_CENTER, 1, mOrigin.getF32ptr());
- shaderp->uniform3f(LLShaderMgr::BOX_SIZE, mRadius, mRadius, mRadius);
- gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8,
- get_box_fan_indices(&gViewerCamera,
- mOrigin));
- glEndQuery(GL_ANY_SAMPLES_PASSED);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLReflectionMapManager class
- ///////////////////////////////////////////////////////////////////////////////
- static U32 sUpdateCount = 0;
- // Helper function
- static void touch_default_probe(LLReflectionMap* probep)
- {
- LLVector3 origin = gViewerCamera.getOrigin();
- origin.mV[2] += 64.f;
- probep->mOrigin.load3(origin.mV);
- }
- LLReflectionMapManager::LLReflectionMapManager()
- : mUpdatingProbe(NULL),
- mUBO(0),
- mUpdatingFace(0),
- mReflectionProbeCount(0),
- mProbeResolution(128),
- mOldProbeResolution(0),
- mMaxProbeLOD(6.f),
- mLightScale(1.f),
- mResumeTime(0.f),
- mReset(false),
- mPaused(false),
- mRadiancePass(false),
- mRealtimeRadiancePass(false)
- {
- initCubeFree();
- }
- void LLReflectionMapManager::initCubeFree()
- {
- // Start at 1 because index 0 is reserved for mDefaultProbe
- for (U32 i = 1; i < LL_MAX_REFLECTION_PROBE_COUNT; ++i)
- {
- mCubeFree.push_back(i);
- }
- }
- struct CompareProbeDistance
- {
- LL_INLINE bool operator()(const LLPointer<LLReflectionMap>& lhs,
- const LLPointer<LLReflectionMap>& rhs)
- {
- return lhs->mDistance < rhs->mDistance;
- }
- };
- static F32 update_score(LLReflectionMap* probep)
- {
- return gFrameTimeSeconds - probep->mLastUpdateTime -
- probep->mDistance * 0.1f;
- }
- // Returns true if a is higher priority for an update than b
- static bool check_priority(LLReflectionMap* a, LLReflectionMap* b)
- {
- if (a->mCubeIndex == -1)
- {
- // Not a candidate for updating
- return false;
- }
- if (b->mCubeIndex == -1)
- {
- // b is not a candidate for updating, a is higher priority by default
- return true;
- }
- if (!a->mComplete && !b->mComplete)
- {
- // Neither probe is complete, use distance
- return a->mDistance < b->mDistance;
- }
- if (a->mComplete && b->mComplete)
- {
- // Both probes are complete, use update_score metric
- return update_score(a) > update_score(b);
- }
- if (sUpdateCount % 3 == 0)
- {
- // a or b is not complete;every third update, allow complete probes to
- // cut in line in front of non-complete probes to avoid spammy probe
- // generators from deadlocking scheduler (SL-20258).
- return !b->mComplete;
- }
- // Prioritize incomplete probe
- return b->mComplete;
- }
- void LLReflectionMapManager::update()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!LLPipeline::sReflectionProbesEnabled || gCubeSnapshot ||
- gTeleportDisplay || gDisconnected || !LLStartUp::isLoggedIn() ||
- gAppViewerp->logoutRequestSent())
- {
- llassert(!gCubeSnapshot); // Assert a snapshot is not in progress
- return;
- }
- if (mPaused && gFrameTimeSeconds > mResumeTime)
- {
- mPaused = false;
- }
- initReflectionMaps();
- llassert(mProbes[0] == mDefaultProbe);
- LLVector4a camera_pos;
- camera_pos.load3(gViewerCamera.getOrigin().mV);
- // Process kill list
- for (U32 i = 0, count = mKillList.size(); i < count; ++i)
- {
- const auto& probep = mKillList[i];
- prmap_vec_t::const_iterator start = mProbes.begin();
- prmap_vec_t::const_iterator end = mProbes.end();
- prmap_vec_t::const_iterator iter = std::find(start, end, probep);
- if (iter != end)
- {
- deleteProbe(iter - start);
- }
- }
- mKillList.clear();
- // Process create list
- for (U32 i = 0, count = mCreateList.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mCreateList[i].get();
- if (probep) // Paranoia
- {
- mProbes.emplace_back(probep);
- }
- }
- mCreateList.clear();
- if (mProbes.empty())
- {
- return;
- }
- bool did_update = false;
- static LLCachedControl<U32> detail(gSavedSettings,
- "RenderReflectionProbes");
- static LLCachedControl<U32> probe_level(gSavedSettings,
- "RenderReflectionProbeLevel");
- U32 level = detail ? probe_level : 0;
- bool realtime = (U32)detail >= LLReflectionMap::REALTIME;
- if (mUpdatingProbe)
- {
- did_update = true;
- doProbeUpdate();
- }
- // Update distance to camera for all probes
- std::sort(mProbes.begin() + 1, mProbes.end(), CompareProbeDistance());
- llassert(mProbes[0] == mDefaultProbe && mProbes[0]->mCubeIndex == 0 &&
- mProbes[0]->mCubeArray == mTexture);
- // Make sure we are assigning cube slots to the closest probes
- // First free any cube indices for distant probes
- for (U32 i = mReflectionProbeCount, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep && probep->mCubeIndex != -1 && mUpdatingProbe != probep)
- {
- mCubeFree.push_back(probep->mCubeIndex);
- probep->mCubeArray = NULL;
- probep->mCubeIndex = -1;
- probep->mComplete = false;
- }
- }
- // Next distribute the free indices
- for (size_t i = 1, count = llmin(mReflectionProbeCount, mProbes.size());
- i < count && !mCubeFree.empty(); ++i)
- {
- // Find the closest probe that needs a cube index
- LLReflectionMap* probep = mProbes[i].get();
- if (probep && probep->mCubeIndex == -1)
- {
- S32 idx = allocateCubeIndex();
- if (!idx) // This should not happen
- {
- llwarns << "Could not allocate a new cube index." << llendl;
- llassert(false);
- }
- probep->mCubeArray = mTexture;
- probep->mCubeIndex = idx;
- }
- }
- LLReflectionMap* closest_dynamicp = NULL;
- LLReflectionMap* oldest_probep = NULL;
- LLReflectionMap* oldest_occludedp = NULL;
- LLVector4a d;
- for (size_t i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep->getNumRefs() == 1)
- {
- // No references held outside manager, delete this probe
- deleteProbe(i);
- --i;
- count = mProbes.size();
- continue;
- }
- if (probep != mDefaultProbe.get() &&
- (mPaused || !probep->isRelevant()))
- {
- // Skip irrelevant probes (or all non-default probes when paused).
- continue;
- }
- if (probep != mDefaultProbe.get())
- {
- LLViewerObject* objp = probep->mViewerObject.get();
- if (objp && !objp->isDead())
- {
- // Make sure probes track the object they are attached to.
- probep->mOrigin.load3(objp->getPositionAgent().mV);
- }
- d.setSub(camera_pos, probep->mOrigin);
- probep->mDistance = d.getLength3().getF32() - probep->mRadius;
- }
- else if (probep->mComplete)
- {
- // Make default probe have a distance of 64m for the purposes of
- // prioritization (if it is already been generated once).
- probep->mDistance = 64.f;
- }
- else
- {
- // Boost priority of default probe when it is not complete
- probep->mDistance = -4096.f;
- }
- if (probep->mComplete)
- {
- probep->autoAdjustOrigin();
- probep->mFadeIn = llmin(probep->mFadeIn + gFrameIntervalSeconds,
- 1.f);
- }
- if (probep->mOccluded && probep->mComplete)
- {
- if (!oldest_occludedp)
- {
- oldest_occludedp = probep;
- }
- else if (probep->mLastUpdateTime <
- oldest_occludedp->mLastUpdateTime)
- {
- oldest_occludedp = probep;
- }
- }
- else if (!did_update && i < mReflectionProbeCount &&
- (!oldest_probep || check_priority(probep, oldest_probep)))
- {
- oldest_probep = probep;
- }
- if (realtime && !closest_dynamicp && probep->mCubeIndex != -1 &&
- probep->getIsDynamic())
- {
- closest_dynamicp = probep;
- }
- }
- if (realtime && closest_dynamicp)
- {
- // Update the closest dynamic probe realtime; should do a full
- // irradiance pass on "odd" frames and a radiance pass on "even" frames
- closest_dynamicp->autoAdjustOrigin();
- // Store and override the value of "isRadiancePass"; parts of the
- // render pipeline rely on "isRadiancePass" to set lighting values etc.
- bool radiance_pass = isRadiancePass();
- mRadiancePass = mRealtimeRadiancePass;
- for (U32 i = 0; i < 6; ++i)
- {
- updateProbeFace(closest_dynamicp, i);
- }
- mRealtimeRadiancePass = !mRealtimeRadiancePass;
- // Restore "isRadiancePass"
- mRadiancePass = radiance_pass;
- }
- static LLCachedControl<U32> upd_period(gSavedSettings,
- "RenderDefaultProbeUpdatePeriod");
- F32 update_period = llclamp(U32(upd_period), 1, 30);
- if (gFrameTimeSeconds - mDefaultProbe->mLastUpdateTime < update_period)
- {
- if (!level)
- {
- // When probes are disabled do not update the default probe more
- // often than the prescribed update period.
- oldest_probep = NULL;
- }
- }
- else if (level)
- {
- // Wen probes are enabled do not update the default probe less often
- // than the prescribed update period.
- oldest_probep = mDefaultProbe.get();
- }
- // Switch to updating the next oldest probe
- if (!did_update && oldest_probep)
- {
- LLReflectionMap* probep = oldest_probep;
- llassert(probep->mCubeIndex != -1);
- probep->autoAdjustOrigin();
- ++sUpdateCount;
- mUpdatingProbe = probep;
- doProbeUpdate();
- }
- if (oldest_occludedp)
- {
- // As far as this occluded probe is concerned, an origin/radius update
- // is as good as a full update.
- oldest_occludedp->autoAdjustOrigin();
- oldest_occludedp->mLastUpdateTime = gFrameTimeSeconds;
- }
- }
- LLReflectionMap* LLReflectionMapManager::addProbe(LLSpatialGroup* groupp)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- LLReflectionMap* probep = new LLReflectionMap();
- probep->mGroup = groupp;
- if (mDefaultProbe.isNull())
- {
- // Safety check to make sure default probe is always first probe added
- mDefaultProbe = new LLReflectionMap();
- mProbes.push_back(mDefaultProbe);
- }
- llassert(mProbes[0] == mDefaultProbe);
- if (groupp)
- {
- probep->mOrigin = groupp->getOctreeNode()->getCenter();
- }
- if (gCubeSnapshot)
- {
- // Snapshot is in progress, mProbes is being iterated over: defer
- // insertion until next update.
- mCreateList.emplace_back(probep);
- }
- else
- {
- mProbes.emplace_back(probep);
- }
- return probep;
- }
- struct CompareProbeDepth
- {
- bool operator()(const LLReflectionMap* lhs, const LLReflectionMap* rhs)
- {
- return lhs->mMinDepth < rhs->mMinDepth;
- }
- };
- void LLReflectionMapManager::getReflectionMaps(std::vector<LLReflectionMap*>& maps)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- LLMatrix4a modelview = gGLModelView;
- LLVector4a oa; // Scratch space for transformed origin
- U32 count = 0;
- U32 last_idx = 0;
- const U32 maps_size = maps.size();
- for (U32 i = 0, probes = mProbes.size(); i < probes && count < maps_size;
- ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (!probep) continue; // Paranoia ?
- // Something wants to use this probe, so let's indicate it has been
- // requested.
- probep->mLastBindTime = gFrameTimeSeconds;
- if (probep->mCubeIndex != -1)
- {
- if (!probep->mOccluded && probep->mComplete)
- {
- maps[count++] = probep;
- modelview.affineTransform(probep->mOrigin, oa);
- F32 radius = probep->mRadius;
- probep->mMinDepth = -oa.getF32ptr()[2] - radius;
- probep->mMaxDepth = -oa.getF32ptr()[2] + radius;
- }
- }
- else
- {
- probep->mProbeIndex = -1;
- }
- last_idx = i;
- }
- // Set remaining probe indices to -1
- for (U32 i = last_idx + 1, n = mProbes.size(); i < n; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep) // Paranoia ?
- {
- probep->mProbeIndex = -1;
- }
- }
- if (count > 1)
- {
- std::sort(maps.begin(), maps.begin() + count, CompareProbeDepth());
- }
- for (U32 i = 0; i < count; ++i)
- {
- maps[i]->mProbeIndex = i;
- }
- // NULL-terminate list
- if (count < maps_size)
- {
- maps[count] = NULL;
- }
- }
- LLReflectionMap* LLReflectionMapManager::registerSpatialGroup(LLSpatialGroup* groupp)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!groupp)
- {
- return NULL;
- }
- LLSpatialPartition* partp = groupp->getSpatialPartition();
- if (!partp || partp->mPartitionType != LLViewerRegion::PARTITION_VOLUME)
- {
- return NULL;
- }
- OctreeNode* nodep = groupp->getOctreeNode();
- F32 size = nodep->getSize().getF32ptr()[0];
- if (size < 15.f || size > 17.f)
- {
- return NULL;
- }
- return addProbe(groupp);
- }
- LLReflectionMap* LLReflectionMapManager::registerViewerObject(LLViewerObject* vobjp)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!vobjp || vobjp->isDead())
- {
- return NULL;
- }
- LLReflectionMap* probep = new LLReflectionMap();
- probep->mViewerObject = vobjp;
- probep->mOrigin.load3(vobjp->getPositionAgent().mV);
- if (gCubeSnapshot)
- {
- // Snapshot is in progress, mProbes is being iterated over, defer
- // insertion until next update
- mCreateList.emplace_back(probep);
- }
- else
- {
- mProbes.emplace_back(probep);
- }
- return probep;
- }
- S32 LLReflectionMapManager::allocateCubeIndex()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (mCubeFree.empty())
- {
- return -1;
- }
- S32 ret = mCubeFree.front();
- mCubeFree.pop_front();
- return ret;
- }
- void LLReflectionMapManager::deleteProbe(U32 i)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- LLReflectionMap* probep = mProbes[i].get();
- if (probep == mDefaultProbe.get())
- {
- llwarns << "Attempt to remove the default probe. Aborted." << llendl;
- return;
- }
- if (probep->mCubeIndex != -1)
- {
- // Mark the cube index used by this probe as being free
- mCubeFree.push_back(probep->mCubeIndex);
- }
- if (mUpdatingProbe == probep)
- {
- mUpdatingProbe = NULL;
- mUpdatingFace = 0;
- }
- // Remove from any neighbors lists
- for (auto& otherp : probep->mNeighbors)
- {
- LLReflectionMap::reflmap_vec_t::iterator ne = otherp->mNeighbors.end();
- LLReflectionMap::reflmap_vec_t::iterator it =
- std::find(otherp->mNeighbors.begin(), ne, probep);
- if (it != ne)
- {
- otherp->mNeighbors.erase(it);
- }
- }
- mProbes.erase(mProbes.begin() + i);
- }
- void LLReflectionMapManager::doProbeUpdate()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!gUsePBRShaders)
- {
- return;
- }
- llassert(mUpdatingProbe != NULL);
- updateProbeFace(mUpdatingProbe, mUpdatingFace);
- if (++mUpdatingFace == 6)
- {
- updateNeighbors(mUpdatingProbe);
- mUpdatingFace = 0;
- if (isRadiancePass())
- {
- mUpdatingProbe->mComplete = true;
- mUpdatingProbe = NULL;
- mRadiancePass = false;
- }
- else
- {
- mRadiancePass = true;
- }
- }
- }
- // Do the reflection map update render passes. For every 12 calls to this
- // method, one complete reflection probe radiance map and irradiance map is
- // generated. First six passes render the scene with direct lighting only into
- // a scratch space cube map at the end of the cube map array and generate a
- // simple mip chain (not convolution filter). At the end of these passes, an
- // irradiance map is generated for this probe and placed into the irradiance
- // cube map array at the index for this probe. The next six passes render the
- // scene with both radiance and irradiance into the same scratch space cube map
- // and generate a simple mip chain. At the end of these passes, a radiance map
- // is generated for this probe and placed into the radiance cube map array at
- // the index for this probe. In effect this simulates single-bounce lighting.
- void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probep, U32 face)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!gUsePBRShaders)
- {
- return;
- }
- mLightScale = 1.f;
- static LLCachedControl<F32> max_amb(gSavedSettings,
- "RenderReflectionProbeMaxAmbiance");
- if (!isRadiancePass() && probep->getAmbiance() > (F32)max_amb)
- {
- mLightScale = max_amb / probep->getAmbiance();
- }
- // Hacky hot-swap of camera specific render targets
- auto saved_rt = gPipeline.mRT;
- gPipeline.mRT = &gPipeline.mAuxillaryRT;
- if (probep == mDefaultProbe.get())
- {
- touch_default_probe(probep);
- gPipeline.pushRenderTypeMask();
- // Only render sky, water, terrain, and clouds
- gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY,
- LLPipeline::RENDER_TYPE_WL_SKY,
- LLPipeline::RENDER_TYPE_WATER,
- LLPipeline::RENDER_TYPE_VOIDWATER,
- LLPipeline::RENDER_TYPE_CLOUDS,
- LLPipeline::RENDER_TYPE_TERRAIN,
- LLPipeline::END_RENDER_TYPES);
- probep->update(mRenderTarget.getWidth(), face);
- gPipeline.popRenderTypeMask();
- }
- else
- {
- probep->update(mRenderTarget.getWidth(), face);
- }
- gPipeline.mRT = saved_rt;
- S32 source_idx = mReflectionProbeCount;
- if (probep != mUpdatingProbe)
- {
- // This is the "realtime" probe that is updating every frame, use the
- // secondary scratch space channel
- ++source_idx;
- }
- gGL.setColorMask(true, true);
- LLGLDepthTest depth(GL_FALSE, GL_FALSE);
- LLGLDisable cull(GL_CULL_FACE);
- LLGLDisable blend(GL_BLEND);
- // Downsample to placeholder map
- gGL.matrixMode(gGL.MM_MODELVIEW);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.matrixMode(gGL.MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.flush();
- U32 res = mProbeResolution * 2;
- LLRenderTarget* screen_rt = &gPipeline.mAuxillaryRT.mScreen;
- // Perform a gaussian blur on the super sampled render before downsampling
- LLGLSLShader* shaderp = &gGaussianProgram;
- shaderp->bind();
- const F32 res_scale = 1.f / F32(mProbeResolution * 2);
- shaderp->uniform1f(sResScale, res_scale);
- S32 chan = shaderp->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE,
- LLTexUnit::TT_TEXTURE);
- // Horizontal
- shaderp->uniform2f(sDirection, 1.f, 0.f);
- gGL.getTexUnit(chan)->bind(screen_rt);
- mRenderTarget.bindTarget();
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- mRenderTarget.flush();
- // Vertical
- shaderp->uniform2f(sDirection, 0.f, 1.f);
- gGL.getTexUnit(chan)->bind(&mRenderTarget);
- screen_rt->bindTarget();
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- screen_rt->flush();
- S32 mips = S32(log2f((F32)mProbeResolution) + 0.5f);
- shaderp = &gReflectionMipProgram;
- shaderp->bind();
- chan = shaderp->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE,
- LLTexUnit::TT_TEXTURE);
- for (S32 i = 0, count = mMipChain.size(); i < count; ++i)
- {
- LLRenderTarget& target = mMipChain[i];
- target.bindTarget();
- if (i == 0)
- {
- gGL.getTexUnit(chan)->bind(screen_rt);
- }
- else
- {
- gGL.getTexUnit(chan)->bind(&(mMipChain[i - 1]));
- }
- shaderp->uniform1f(sResScale, res_scale);
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- res /= 2;
- S32 mip = i + mips - count;
- if (mip >= 0)
- {
- mTexture->bind(0);
- glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0,
- source_idx * 6 + face, 0, 0, res, res);
- mTexture->unbind();
- }
- target.flush();
- }
- gGL.popMatrix();
- gGL.matrixMode(gGL.MM_MODELVIEW);
- gGL.popMatrix();
- gGL.getTexUnit(chan)->unbind(LLTexUnit::TT_TEXTURE);
- shaderp->unbind();
- if (face != 5)
- {
- return; // We are done.
- }
- if (mMipChain.empty()) // Paranoia ?
- {
- llwarns_once << "mMipChain is empty !" << llendl;
- return;
- }
- if (!LLViewerShaderMgr::sHasIrrandiance)
- {
- // Cannot render this since the two gIrradianceGenProgram and
- // gRadianceGenProgram shaders have not loaded... HB
- return;
- }
- mMipChain[0].bindTarget();
- if (isRadiancePass())
- {
- // Generate radiance map (even if this is not the irradiance map, we
- // need the mip chain for the irradiance map).
- shaderp = &gRadianceGenProgram;
- shaderp->bind();
- mVertexBuffer->setBuffer();
- chan = shaderp->enableTexture(LLShaderMgr::REFLECTION_PROBES,
- LLTexUnit::TT_CUBE_MAP_ARRAY);
- mTexture->bind(chan);
- shaderp->uniform1i(sSourceIdx, source_idx);
- shaderp->uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD,
- mMaxProbeLOD);
- shaderp->uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, 1.f);
- U32 res = mMipChain[0].getWidth();
- LLCoordFrame frame;
- F32 mat[16];
- for (size_t i = 0, count = mMipChain.size(); i < count; ++i)
- {
- shaderp->uniform1f(sRoughness, F32(i) / F32(count - 1));
- shaderp->uniform1f(sMipLevel, i);
- shaderp->uniform1i(sWidth, mProbeResolution);
- for (U32 cf = 0; cf < 6; ++cf) // For each cube face
- {
- frame.lookAt(LLVector3::zero,
- LLCubeMapArray::sClipToCubeLookVecs[cf],
- LLCubeMapArray::sClipToCubeUpVecs[cf]);
- frame.getOpenGLRotation(mat);
- gGL.loadMatrix(mat);
- mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
- glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0,
- probep->mCubeIndex * 6 + cf, 0, 0,
- res, res);
- }
- if (i != count - 1)
- {
- res /= 2;
- glViewport(0, 0, res, res);
- }
- }
- }
- else
- {
- // Generate irradiance map
- shaderp = &gIrradianceGenProgram;
- shaderp->bind();
- chan = shaderp->enableTexture(LLShaderMgr::REFLECTION_PROBES,
- LLTexUnit::TT_CUBE_MAP_ARRAY);
- mTexture->bind(chan);
- shaderp->uniform1i(sSourceIdx, source_idx);
- shaderp->uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD,
- mMaxProbeLOD);
- mVertexBuffer->setBuffer();
- // Find the mip target to start with based on irradiance map resolution
- U32 start_mip = 0;
- U32 count = mMipChain.size();
- while (start_mip < count &&
- mMipChain[start_mip].getWidth() != LL_IRRADIANCE_MAP_RESOLUTION)
- {
- ++start_mip;
- }
- if (start_mip < count)
- {
- LLRenderTarget& target = mMipChain[start_mip];
- glViewport(0, 0, target.getWidth(), target.getHeight());
- F32 mat[16];
- for (U32 cf = 0; cf < 6; ++cf) // For each cube face
- {
- LLCoordFrame frame;
- frame.lookAt(LLVector3::zero,
- LLCubeMapArray::sClipToCubeLookVecs[cf],
- LLCubeMapArray::sClipToCubeUpVecs[cf]);
- frame.getOpenGLRotation(mat);
- gGL.loadMatrix(mat);
- mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
- S32 res = target.getWidth();
- mIrradianceMaps->bind(chan);
- glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, 0, 0,
- probep->mCubeIndex * 6 + cf, 0, 0,
- res, res);
- mTexture->bind(chan);
- }
- }
- }
- mMipChain[0].flush();
- shaderp->unbind();
- }
- void LLReflectionMapManager::pause(F32 duration)
- {
- mPaused = true;
- mResumeTime = gFrameTimeSeconds + duration;
- }
- void LLReflectionMapManager::shift(const LLVector4a& offset)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- for (U32 i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep) // Paranoia
- {
- probep->mOrigin.add(offset);
- }
- }
- }
- void LLReflectionMapManager::updateNeighbors(LLReflectionMap* probep)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (mDefaultProbe.get() == probep)
- {
- return;
- }
- // Remove from existing neighbors
- for (auto& otherp : probep->mNeighbors)
- {
- LLReflectionMap::reflmap_vec_t::iterator ne = otherp->mNeighbors.end();
- LLReflectionMap::reflmap_vec_t::iterator it =
- std::find(otherp->mNeighbors.begin(), ne, probep);
- if (it != ne)
- {
- otherp->mNeighbors.erase(it);
- }
- }
- probep->mNeighbors.clear();
- // Search for new neighbors
- if (probep->isRelevant())
- {
- for (U32 i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* otherp = mProbes[i].get();
- if (otherp != mDefaultProbe.get() && otherp != probep)
- {
- if (otherp->isRelevant() && probep->intersects(otherp))
- {
- probep->mNeighbors.push_back(otherp);
- otherp->mNeighbors.push_back(probep);
- }
- }
- }
- }
- }
- // Structure for packing uniform buffer object.
- // See class3/deferred/reflectionProbeF.glsl
- struct ReflectionProbeData
- {
- // For box probes, matrix that transforms from camera space to a [-1, 1]
- // cube representing the bounding box of the box probe
- LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
- LLMatrix4 heroBox;
- // For sphere probes, origin (xyz) and radius (w) of refmaps in clip space
- LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
- // Extra parameters
- // x - irradiance scale
- // y - radiance scale
- // z - fade in
- // w - znear
- LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT];
- LLVector4 heroSphere;
- // Indices used by probe:
- // [i][0] - cubemap array index for this probe
- // [i][1] - index into "refNeighbor" for probes that intersect this probe
- // [i][2] - number of probes that intersect this probe, or -1 for no
- // neighbors
- // [i][3] - priority (probe type stored in sign bit - positive for
- // spheres, negative for boxes)
- GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
- // List of neighbor indices
- GLint refNeighbor[4096];
- // Lookup table for which index to start with for the given Z depth
- GLint refBucket[256][4];
- // Numbrer of active refmaps
- GLint refmapCount;
- GLint heroShape;
- GLint heroMipCount;
- GLint heroProbeCount;
- };
- void LLReflectionMapManager::updateUniforms()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!LLPipeline::sReflectionProbesEnabled)
- {
- return;
- }
- mReflectionMaps.resize(mReflectionProbeCount);
- getReflectionMaps(mReflectionMaps);
- ReflectionProbeData rpd;
- static F32 min_depth[256];
- for (U32 i = 0; i < 256; ++i)
- {
- rpd.refBucket[i][0] = rpd.refBucket[i][1] = rpd.refBucket[i][2] =
- rpd.refBucket[i][3] = mReflectionProbeCount;
- min_depth[i] = FLT_MAX;
- }
- LLMatrix4a modelview = gGLModelView;
- LLVector4a oa; // Scratch space for transformed origin
- S32 count = 0;
- // Neighbor "cursor": index into refNeighbor to start writing the next
- // probe's list of neighbors
- U32 nc = 0;
- static LLCachedControl<bool> auto_adjust(gSavedSettings,
- "RenderSkyAutoAdjustLegacy");
- LLSettingsSky::ptr_t skyp = gEnvironment.getCurrentSky();
- F32 min_ambiance = skyp->getReflectionProbeAmbiance(auto_adjust);
- F32 ambscale, radscale;
- if (gCubeSnapshot && !isRadiancePass()) // Ambiance pass ?
- {
- ambscale = 0.f;
- radscale = 0.5f;
- }
- else
- {
- ambscale = radscale = 1.f;
- }
- for (U32 k = 0, nmaps = mReflectionMaps.size(); k < nmaps; ++k)
- {
- LLReflectionMap* refmapp = mReflectionMaps[k];
- if (!refmapp)
- {
- break;
- }
- if (refmapp != mDefaultProbe.get())
- {
- // Bucket search data. Theory of operation:
- // 1. Determine minimum and maximum depth of each influence volume
- // and store in mDepth (done in getReflectionMaps).
- // 2. Sort by minimum depth.
- // 3. Prepare a bucket for each 1m of depth out to 256m.
- // 4. For each bucket, store the index of the nearest probe that
- // might influence pixels in that bucket.
- // 5. In the shader, lookup the bucket for the pixel depth to get
- // the index of the first probe that could possibly influence
- // the current pixel.
- U32 depth_min = U32(llclamp(S32(refmapp->mMinDepth), 0, 255));
- U32 depth_max = U32(llclamp(S32(refmapp->mMaxDepth), 0, 255));
- for (U32 i = depth_min; i <= depth_max; ++i)
- {
- if (refmapp->mMinDepth < min_depth[i])
- {
- min_depth[i] = refmapp->mMinDepth;
- rpd.refBucket[i][0] = refmapp->mProbeIndex;
- }
- }
- }
- llassert(refmapp->mProbeIndex == count && refmapp->mCubeIndex >= 0 &&
- mReflectionMaps[refmapp->mProbeIndex] == refmapp);
- LLViewerObject* objp = refmapp->mViewerObject.get();
- if (objp && !objp->isDead() && objp->getVolume())
- {
- // Have active manual probes live-track the object they are
- // associated with
- refmapp->mOrigin.load3(objp->getPositionAgent().mV);
- LLVOVolume* vobjp = objp->asVolume();
- if (vobjp && vobjp->getReflectionProbeIsBox())
- {
- static const LLVector3 half(0.5f, 0.5f, 0.5f);
- refmapp->mRadius = vobjp->getScale().scaledVec(half).length();
- }
- else
- {
- refmapp->mRadius = objp->getScale().mV[0] * 0.5f;
- }
- }
- modelview.affineTransform(refmapp->mOrigin, oa);
- rpd.refSphere[count].set(oa.getF32ptr());
- rpd.refSphere[count].mV[3] = refmapp->mRadius;
- rpd.refIndex[count][0] = refmapp->mCubeIndex;
- llassert(nc % 4 == 0);
- rpd.refIndex[count][1] = nc / 4;
- rpd.refIndex[count][3] = refmapp->mPriority;
- // For objects that are reflection probes, use the volume as the
- // influence volume of the probe only possibile influence volumes are
- // boxes and spheres, so detect boxes and treat everything else as
- // spheres
- if (refmapp->getBox(rpd.refBox[count]))
- {
- // Negate priority to indicate this probe has a box influence
- // volume
- rpd.refIndex[count][3] *= -1;
- }
- rpd.refParams[count].set(llmax(min_ambiance,
- refmapp->getAmbiance()) * ambscale,
- radscale, // Radiance scale
- refmapp->mFadeIn, // Fade-in weight
- // Z near
- oa.getF32ptr()[2] - refmapp->mRadius);
- // Neighbor ("index"): index into refNeighbor to write indices for
- // current reflection probe's neighbors
- U32 ni = nc;
- // Pack neghbor list
- constexpr U32 MAX_NEIGHBORS = 64;
- U32 neighbor_count = 0;
- for (U32 n = 0, ncount = refmapp->mNeighbors.size();
- n < ncount && ni < 4096 && neighbor_count < MAX_NEIGHBORS; ++n)
- {
- LLReflectionMap* neighborp = refmapp->mNeighbors[n];
- GLint idx = neighborp->mProbeIndex;
- if (idx != -1 && !neighborp->mOccluded &&
- neighborp->mCubeIndex != -1)
- {
- // This neighbor may be sampled
- rpd.refNeighbor[ni++] = idx;
- ++neighbor_count;
- }
- }
- if (nc == ni)
- {
- // No neighbors, tag as empty
- rpd.refIndex[count][1] = -1;
- }
- else
- {
- rpd.refIndex[count][2] = ni - nc;
- // Move the cursor forward
- nc = ni;
- if (nc % 4 != 0)
- {
- // Jump to next power of 4 for compatibility with ivec4
- nc += 4 - (nc % 4);
- }
- }
- ++count;
- }
- rpd.refmapCount = count;
- LLHeroProbeManager& hero = gPipeline.mHeroProbeManager;
- hero.updateUniforms();
- // Get the hero probe data
- rpd.heroBox = hero.mHeroData.heroBox;
- rpd.heroSphere = hero.mHeroData.heroSphere;
- rpd.heroShape = hero.mHeroData.heroShape;
- rpd.heroMipCount = hero.mHeroData.heroMipCount;
- rpd.heroProbeCount = hero.mHeroData.heroProbeCount;
- // Copy rpd into uniform buffer object
- if (mUBO == 0)
- {
- glGenBuffers(1, &mUBO);
- }
- glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &rpd,
- GL_STREAM_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- }
- void LLReflectionMapManager::setUniforms()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (LLPipeline::sReflectionProbesEnabled)
- {
- if (mUBO == 0)
- {
- updateUniforms();
- }
- glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
- }
- }
- static void render_reflection_probe(LLReflectionMap* probep)
- {
- if (!probep || !probep->isRelevant())
- {
- return;
- }
- F32* po = probep->mOrigin.getF32ptr();
- // Draw orange line from probe to neighbors
- gGL.flush();
- gGL.diffuseColor4f(1.f, 0.5f, 0.f, 1.f);
- gGL.begin(gGL.LINES);
- for (U32 i = 0, count = probep->mNeighbors.size(); i < count; ++i)
- {
- LLReflectionMap* neighborp = probep->mNeighbors[i];
- if (!neighborp) continue; // Paranoia ?
- if (probep->mViewerObject.isNull() ||
- neighborp->mViewerObject.isNull())
- {
- gGL.vertex3fv(po);
- gGL.vertex3fv(neighborp->mOrigin.getF32ptr());
- }
- }
- gGL.end(true);
- gGL.diffuseColor4f(1.f, 1.f, 0.f, 1.f);
- gGL.begin(gGL.LINES);
- for (U32 i = 0, count = probep->mNeighbors.size(); i < count; ++i)
- {
- LLReflectionMap* neighborp = probep->mNeighbors[i];
- if (!neighborp) continue; // Paranoia ?
- if (probep->mViewerObject.notNull() &&
- neighborp->mViewerObject.notNull())
- {
- gGL.vertex3fv(po);
- gGL.vertex3fv(neighborp->mOrigin.getF32ptr());
- }
- }
- gGL.end(true);
- }
- void LLReflectionMapManager::renderDebug()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- gDebugProgram.bind();
- for (size_t i = 0, count = mProbes.size(); i < count; ++i)
- {
- render_reflection_probe(mProbes[i].get());
- }
- gDebugProgram.unbind();
- }
- void LLReflectionMapManager::initReflectionMaps()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!gUsePBRShaders)
- {
- return;
- }
- if (mReset || mTexture.isNull() ||
- mReflectionProbeCount != LL_MAX_REFLECTION_PROBE_COUNT)
- {
- mReset = false;
- #if LL_RESET_HDRI_SKY_ON_REFLECTION_MAP_RESET
- LLDrawPoolWLSky::resetHDRISky();
- #endif
- static LLCachedControl<U32> res(gSavedSettings,
- "RenderReflectionProbeResolution");
- mProbeResolution = nhpo2(llclamp((U32)res, 64, 512));
- mReflectionProbeCount = LL_MAX_REFLECTION_PROBE_COUNT;
- mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // Number of mips - 1
- if (mTexture.isNull() ||
- mTexture->getResolution() != mProbeResolution ||
- mTexture->getCount() != mReflectionProbeCount + 2)
- {
- mTexture = new LLCubeMapArray();
- // Store mReflectionProbeCount + 2 cube maps, final two cube maps
- // are used for render target and radiance map generation source).
- mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2);
- mIrradianceMaps = new LLCubeMapArray();
- mIrradianceMaps->allocate(LL_IRRADIANCE_MAP_RESOLUTION, 3,
- mReflectionProbeCount, false);
- }
- // Reset probe state
- mUpdatingFace = 0;
- mUpdatingProbe = NULL;
- mRadiancePass = mRealtimeRadiancePass = false;
- // If default probe already exists, remember whether or not it is
- // complete (SL-20498)
- bool default_complete = mDefaultProbe.notNull() &&
- mDefaultProbe->mComplete;
- for (U32 i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep) // Paranoia
- {
- probep->mLastUpdateTime = 0.f;
- probep->mComplete = false;
- probep->mProbeIndex = -1;
- probep->mCubeArray = NULL;
- probep->mCubeIndex = -1;
- probep->mNeighbors.clear();
- }
- }
- mCubeFree.clear();
- initCubeFree();
- if (mDefaultProbe.isNull())
- {
- // The default probe MUST be the first probe created
- llassert(mProbes.empty());
- mDefaultProbe = new LLReflectionMap();
- mProbes.push_back(mDefaultProbe);
- }
- llassert(mProbes[0] == mDefaultProbe);
- mDefaultProbe->mCubeIndex = 0;
- mDefaultProbe->mCubeArray = mTexture;
- mDefaultProbe->mDistance = 64.f;
- mDefaultProbe->mRadius = 4096.f;
- mDefaultProbe->mProbeIndex = 0;
- mDefaultProbe->mComplete = default_complete;
- touch_default_probe(mDefaultProbe);
- if (mProbeResolution != mOldProbeResolution)
- {
- mOldProbeResolution = mProbeResolution;
- mRenderTarget.release();
- mMipChain.clear();
- }
- }
- if (!mRenderTarget.isComplete())
- {
- U32 tgt_res = mProbeResolution * 4; // Super sample
- mRenderTarget.allocate(tgt_res, tgt_res, GL_RGB16F, true);
- }
- if (mMipChain.empty())
- {
- U32 res = mProbeResolution;
- U32 count = U32(log2f(F32(res)) + 0.5f);
- mMipChain.resize(count);
- for (U32 i = 0; i < count; ++i)
- {
- mMipChain[i].allocate(res, res, GL_RGB16F);
- res /= 2;
- }
- }
- if (mVertexBuffer.isNull())
- {
- constexpr U32 mask = LLVertexBuffer::MAP_VERTEX;
- mVertexBuffer = new LLVertexBuffer(mask);
- mVertexBuffer->allocateBuffer(4, 0);
- LLStrider<LLVector3> v;
- mVertexBuffer->getVertexStrider(v);
- v[0] = LLVector3(-1.f, -1.f, -1.f);
- v[1] = LLVector3(1.f, -1.f, -1.f);
- v[2] = LLVector3(-1.f, 1.f, -1.f);
- v[3] = LLVector3(1.f, 1.f, -1.f);
- mVertexBuffer->unmapBuffer();
- }
- }
- void LLReflectionMapManager::cleanup()
- {
- mVertexBuffer = NULL;
- mRenderTarget.release();
- mMipChain.clear();
- mTexture = NULL;
- mIrradianceMaps = NULL;
- mReflectionProbeCount = 0;
- mProbes.clear();
- mKillList.clear();
- mCreateList.clear();
- mReflectionMaps.clear();
- mUpdatingFace = 0;
- mDefaultProbe = NULL;
- mUpdatingProbe = NULL;
- glDeleteBuffers(1, &mUBO);
- mUBO = 0;
- // Note: also called on teleport (not just shutdown), so make sure we are
- // in a good "starting" state.
- initCubeFree();
- }
- void LLReflectionMapManager::doOcclusion()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!gUsePBRShaders)
- {
- return;
- }
- LLVector4a eye;
- eye.load3(gViewerCamera.getOrigin().mV);
- for (size_t i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep && probep != mDefaultProbe.get())
- {
- probep->doOcclusion(eye);
- }
- }
- }
- void LLReflectionMapManager::forceDefaultProbeAndUpdateUniforms(bool force)
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- static std::vector<bool> was_occluded;
- if (force)
- {
- was_occluded.clear();
- for (U32 i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- was_occluded.push_back(probep && probep->mOccluded);
- if (probep && probep != mDefaultProbe)
- {
- probep->mOccluded = true;
- }
- }
- updateUniforms();
- }
- else
- {
- for (U32 i = 0, count = llmin(mProbes.size(), was_occluded.size());
- i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- was_occluded.push_back(probep && probep->mOccluded);
- if (probep)
- {
- probep->mOccluded = was_occluded[i];
- }
- }
- was_occluded.clear();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLHeroProbeManager class
- ///////////////////////////////////////////////////////////////////////////////
- LLHeroProbeManager::LLHeroProbeManager()
- : mReflectionProbeCount(0),
- mLastUpdateTime(0.f),
- mCurrentProbeUpdateFrame(0),
- mProbeResolution(1024),
- mMaxProbeLOD(6.f),
- mNearestProbeDist(F32_MAX),
- mHeroProbeStrength(1.f),
- mIsInTransition(false),
- mReset(false),
- mRenderingMirror(false)
- {
- }
- LLHeroProbeManager::~LLHeroProbeManager()
- {
- cleanup();
- mHeroVOList.clear();
- mNearestHero = NULL;
- }
- void LLHeroProbeManager::cleanup()
- {
- mVertexBuffer = NULL;
- mRenderTarget.release();
- mMipChain.clear();
- mTexture = NULL;
- mReflectionProbeCount = 0;
- mProbes.clear();
- mDefaultProbe = NULL;
- }
- void LLHeroProbeManager::initReflectionMaps()
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- if (!LLPipeline::sRenderMirrors)
- {
- return;
- }
- if (mReset)
- {
- mReset = false;
- cleanup();
- }
- static LLCachedControl<U32> max_count(gSavedSettings,
- "RenderHeroProbesMaxCount");
- const U32 count = llclamp(U32(max_count), 1, LL_MAX_HERO_PROBE_COUNT);
- if (mTexture.isNull() || mReflectionProbeCount != count)
- {
- mReflectionProbeCount = count;
- static LLCachedControl<U32> probe_level(gSavedSettings,
- "RenderHeroProbeResLevel");
- switch ((U32)probe_level)
- {
- // Note: 0 now actually means hero probe/mirror disabled (256x256
- // is way too small and blurry for a mirror, anyway). HB
- case 0: mProbeResolution = 256; break;
- case 1: mProbeResolution = 512; break;
- case 2: mProbeResolution = 1024; break;
- default: mProbeResolution = 2048; break;
- }
- // Number of mips - 1
- mMaxProbeLOD = log2f(F32(mProbeResolution)) - 1.f;
- mTexture = new LLCubeMapArray();
- // We use two more cube maps for render target and radiance map
- // generation source.
- mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2);
- if (mDefaultProbe.isNull())
- {
- // The default probe MUST be the first probe created
- llassert_always(mProbes.empty());
- mDefaultProbe = new LLReflectionMap();
- mProbes.push_back(mDefaultProbe);
- }
- llassert(mProbes[0] == mDefaultProbe);
- // For hero probes, we treat this as the main mirror probe.
- mDefaultProbe->mCubeIndex = 0;
- mDefaultProbe->mCubeArray = mTexture;
- static LLCachedControl<U32> probe_dist(gSavedSettings,
- "RenderHeroProbeDistance");
- mDefaultProbe->mDistance = llclamp((F32)probe_dist, 2.f, 64.f);
- mDefaultProbe->mRadius = 4096.f;
- mDefaultProbe->mProbeIndex = 0;
- touch_default_probe(mDefaultProbe);
- mProbes.push_back(mDefaultProbe);
- }
- if (mVertexBuffer.isNull())
- {
- constexpr U32 mask = LLVertexBuffer::MAP_VERTEX;
- mVertexBuffer = new LLVertexBuffer(mask);
- mVertexBuffer->allocateBuffer(4, 0);
- LLStrider<LLVector3> v;
- mVertexBuffer->getVertexStrider(v);
- v[0] = LLVector3(-1.f, -1.f, -1.f);
- v[1] = LLVector3(1.f, -1.f, -1.f);
- v[2] = LLVector3(-1.f, 1.f, -1.f);
- v[3] = LLVector3(1.f, 1.f, -1.f);
- mVertexBuffer->unmapBuffer();
- }
- }
- void LLHeroProbeManager::update()
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- mNearestHero = NULL;
- mNearestProbeDist = F32_MAX;
- if (!LLPipeline::sRenderMirrors || gDisconnected ||
- !LLStartUp::isLoggedIn() || gAppViewerp->logoutRequestSent())
- {
- return;
- }
- initReflectionMaps();
- // Probe 0 is always our mirror probe.
- if (mProbes[0].isNull())
- {
- llwarns_sparse << "NULL default hero probe." << llendl;
- return;
- }
- llassert(mProbes[0] == mDefaultProbe);
- if (!mRenderTarget.isComplete())
- {
- mRenderTarget.allocate(mProbeResolution, mProbeResolution, GL_RGB16F,
- true);
- llinfos << "Allocated a render target for mirrors at size: "
- << mProbeResolution << "x" << mProbeResolution << llendl;
- }
- if (mMipChain.empty())
- {
- U32 res = mProbeResolution;
- U32 count = U32(log2f(F32(res)) + 0.5f);
- mMipChain.resize(count);
- for (U32 i = 0; i < count; ++i)
- {
- mMipChain[i].allocate(res, res, GL_RGB16F);
- res /= 2;
- }
- llinfos << "Allocated " << count << " mips for mirrors." << llendl;
- }
- F32 far_cam_dist = gViewerCamera.getFar();
- #if 0 // Wrong axis, LL... HB
- LLVector3 cam_dir = LLVector3::z_axis * gViewerCamera.getQuaternion();
- #else
- LLVector3 cam_dir = LLVector3::x_axis * gViewerCamera.getQuaternion();
- #endif
- const LLVector3& camera_pos = gViewerCamera.getOrigin();
- // Find the closest mirror, if any.
- F32 last_cam_dist = F32_MAX;
- LLVector4a center, size;
- for (volp_set_t::iterator it = mHeroVOList.begin(),
- end = mHeroVOList.end();
- it != end; ++it)
- {
- LLVOVolume* volp = it->get();
- if (!volp || volp->isDead() || volp->mDrawable.isNull() ||
- !volp->isReflectionProbe() || !volp->getReflectionProbeIsBox())
- {
- unregisterViewerObject(volp);
- continue;
- }
- const LLVector3& vol_pos = volp->getPositionAgent();
- LLVector3 offset = camera_pos - vol_pos;
- F32 distance = offset.length();
- if (distance > far_cam_dist || distance > mNearestProbeDist)
- {
- continue;
- }
- F32 cam_dist = cam_dir * -offset;
- if (cam_dist > last_cam_dist)
- {
- continue;
- }
- center.load3(vol_pos.mV);
- size.load3(volp->getScale().mV);
- if (gViewerCamera.AABBInFrustum(center, size))
- {
- // Check to see if the camera is in front of the +Z face of the
- // hero probe (if it is behind, then the probe is not interesting).
- LLVector3 probe_normal = LLVector3::z_axis *
- volp->mDrawable->getWorldRotation();
- #if 0 // Only the sign of the product is interesting... HB
- probe_normal.normalize();
- #endif
- if (probe_normal * offset >= 0.f)
- {
- // Yes, this probe is a candidate
- mNearestProbeDist = distance;
- last_cam_dist = cam_dist;
- mNearestHero = volp;
- }
- }
- }
- // No close, visible and valid/live hero probe. Abort.
- if (mNearestHero.isNull() || mNearestHero->isDead() ||
- mNearestHero->mDrawable.isNull())
- {
- // Set the tracked object for this probe to NULL.
- mProbes[0]->mViewerObject = NULL;
- mNearestHero = NULL;
- mNearestProbeDist = F32_MAX;
- return;
- }
- // Update the tracked object for this probe.
- mProbes[0]->mViewerObject = mNearestHero;
- mMirrorPosition = mNearestHero->getPositionAgent();
- mMirrorNormal.set(0.f, 0.f, 1.f);
- mMirrorNormal *= mNearestHero->mDrawable->getWorldRotation();
- mMirrorNormal.normalize();
- mCurrentClipPlane.setVec(mMirrorPosition, mMirrorNormal);
- LLVector3 offset = camera_pos - mMirrorPosition;
- LLVector3 project = mMirrorNormal * (offset * mMirrorNormal);
- LLVector3 reject = offset - project;
- LLVector3 point = (reject - project) + mMirrorPosition;
- mProbes[0]->mOrigin.load3(point.mV);
- // Detect visible faces of a cube based on camera direction and distance.
- static const LLVector3 faces[6] =
- {
- LLVector3::x_axis,
- LLVector3::x_axis_neg,
- LLVector3::y_axis,
- LLVector3::y_axis_neg,
- LLVector3::z_axis,
- LLVector3::z_axis_neg
- };
- static LLCachedControl<U32> mult(gSavedSettings,
- "RenderHeroProbeUpdateMult");
- F32 update_mult = llclamp((F32)mult, 2.f, 64.f);
- for (U32 i = 0; i < 6; ++i)
- {
- F32 cube_facing = 1.f - llclamp(cam_dir * faces[i], -1.f, 1.f);
- mFaceUpdateList[i] = (S32)llceil(cube_facing * update_mult);
- }
- mHeroProbeStrength = 1.f;
- }
- void LLHeroProbeManager::renderProbes()
- {
- if (mNearestHero.isNull())
- {
- return; // Nothing to do.
- }
- static LLCachedControl<U32> fps(gSavedSettings, "RenderHeroProbeMaxFPS");
- static LLCachedControl<U32> interval(gSavedSettings,
- "RenderHeroProbeMinFrameInterval");
- F32 delta = gFrameTimeSeconds - mLastUpdateTime;
- if (delta < 1.f / llclamp((F32)fps, 1.f, 60.f) ||
- delta < gFrameIntervalSeconds * (F32)interval)
- {
- return;
- }
- mLastUpdateTime = gFrameTimeSeconds;
- // Do not update mips when the nearest probe is beyond a distance from the
- // camera determined by the probe mDistance and a user-configurable cut-off
- // factor. This avoids excessive GPU consumption and lowered FPS rates when
- // there is no mirror close to the camera, or even no mirror currently
- // rendered at all ! HB
- static LLCachedControl<F32> cutoff(gSavedSettings,
- "RenderHeroProbeCutoff");
- if (cutoff > 1.f && mNearestProbeDist > mProbes[0]->mDistance * cutoff)
- {
- mProbes[0]->autoAdjustOrigin(); // Still adjust the position...
- return;
- }
- constexpr F32 NEAR_CLIP = 0.1f;
- bool is_dynamic = mNearestHero->getReflectionProbeIsDynamic();
- bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass();
- gPipeline.mReflectionMapManager.mRadiancePass = true;
- mRenderingMirror = true;
- doOcclusion();
- for (size_t j = 0, count = mProbes.size(); j < count; ++j)
- {
- for (size_t i = 0; i < 6; ++i)
- {
- if (mFaceUpdateList[i] > 0 &&
- !(mCurrentProbeUpdateFrame % mFaceUpdateList[i]))
- {
- updateProbeFace(mProbes[j], i, is_dynamic, NEAR_CLIP);
- mCurrentProbeUpdateFrame = 0;
- }
- }
- generateRadiance(mProbes[j]);
- }
- mRenderingMirror = false;
- gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass;
- mProbes[0]->autoAdjustOrigin();
- ++mCurrentProbeUpdateFrame;
- }
- // Does the reflection map update render passes. For every 12 calls of this
- // method, one complete reflection probe radiance map and irradiance map is
- // generated. First 6 passes render the scene with direct lighting only into a
- // scratch space cube map at the end of the cube a simple mip chain (not
- // convolution filter). At the end of these passes, an irradiance map is
- // generated for this probe and placed into the irradiance cube map array at
- // the index for this probe. The next six passes render the scene with both
- // radiance and irradiance into the same scratch space cube map and generate
- // a simple mip chain. Finally, a radiance map is generated for this probe and
- // placed into the radiance cube map array at the index for this probe. In
- // effect this simulates single-bounce lighting.
- void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probep, U32 face,
- bool is_dynamic, F32 near_clip)
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- // Hacky hot-swap of camera specific render targets
- auto saved_rt = gPipeline.mRT;
- gPipeline.mRT = &gPipeline.mHeroProbeRT;
- probep->update(mRenderTarget.getWidth(), face, is_dynamic, near_clip);
- gPipeline.mRT = saved_rt;
- gGL.setColorMask(true, true);
- LLGLDepthTest depth(GL_FALSE, GL_FALSE);
- LLGLDisable cull(GL_CULL_FACE);
- LLGLDisable blend(GL_BLEND);
- // Downsample to placeholder map
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.matrixMode(LLRender::MM_PROJECTION);
- gGL.pushMatrix();
- gGL.loadIdentity();
- gGL.flush();
- LLRenderTarget* screenp = &gPipeline.mHeroProbeRT.mScreen;
- // Perform a gaussian blur on the super sampled render before downsampling
- LLGLSLShader* shaderp = &gGaussianProgram;
- shaderp->bind();
- shaderp->uniform1f(sResScale, 0.5f / F32(mProbeResolution));
- S32 chan = shaderp->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE,
- LLTexUnit::TT_TEXTURE);
- // Horizontal
- shaderp->uniform2f(sDirection, 1.f, 0.f);
- gGL.getTexUnit(chan)->bind(screenp);
- mRenderTarget.bindTarget();
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- mRenderTarget.flush();
- // Vertical
- shaderp->uniform2f(sDirection, 0.f, 1.f);
- gGL.getTexUnit(chan)->bind(&mRenderTarget);
- screenp->bindTarget();
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- screenp->flush();
- shaderp->unbind();
- // Unlike the reflectionmap manager, all probes are considered "realtime"
- // for hero probes.
- U32 src_idx = mReflectionProbeCount + 1;
- S32 mips = S32(log2f((F32)mProbeResolution) + 0.5f);
- shaderp = &gReflectionMipProgram;
- shaderp->bind();
- chan = shaderp->enableTexture(LLShaderMgr::DEFERRED_DIFFUSE,
- LLTexUnit::TT_TEXTURE);
- LLTexUnit* diffunitp = gGL.getTexUnit(chan);
- U32 res = mProbeResolution;
- for (S32 i = 0, count = mMipChain.size(); i < count; ++i)
- {
- mMipChain[i].bindTarget();
- diffunitp->bind(i ? &(mMipChain[i - 1]) : screenp);
- shaderp->uniform1f(sResScale, 0.5f / F32(mProbeResolution));
- shaderp->uniform1f(sZnear, probep->getNearClip());
- shaderp->uniform1f(sZfar, MAX_FAR_CLIP);
- gPipeline.mScreenTriangleVB->setBuffer();
- gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- S32 mip = i - count + mips;
- if (mip >= 0)
- {
- mTexture->bind(0);
- glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0,
- src_idx * 6 + face, 0, 0, res, res);
- mTexture->unbind();
- }
- res /= 2;
- mMipChain[i].flush();
- }
- gGL.popMatrix();
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- gGL.popMatrix();
- if (diffunitp)
- {
- diffunitp->unbind(LLTexUnit::TT_TEXTURE);
- }
- shaderp->unbind();
- }
- // Radiance generation is done in a separate stage; this is to better enable
- // independent control over how we generate radiance vs. having it coupled with
- // processing the final face of the probe. Useful when we may not always be
- // rendering a full set of faces of the probe.
- void LLHeroProbeManager::generateRadiance(LLReflectionMap* probep)
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- // Unlike the reflectionmap manager, all probes are considered "realtime"
- // for hero probes.
- U32 src_idx = mReflectionProbeCount + 1;
- mMipChain[0].bindTarget();
- // Generate radiance map (even if this is not the irradiance map, we need
- // the mip chain for the irradiance map).
- LLGLSLShader& shader = gHeroRadianceGenProgram;
- shader.bind();
- mVertexBuffer->setBuffer();
- S32 chan = shader.enableTexture(LLShaderMgr::REFLECTION_PROBES,
- LLTexUnit::TT_CUBE_MAP_ARRAY);
- mTexture->bind(chan);
- shader.uniform1i(sSourceIdx, src_idx);
- shader.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
- shader.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH,
- mHeroProbeStrength);
- U32 res = mMipChain[0].getWidth();
- LLCoordFrame frame;
- F32 mat[16];
- size_t mips_chain_size = mMipChain.size();
- F32 roughness_factor = 1.f / F32(mips_chain_size - 1);
- for (size_t i = 0, count = mips_chain_size / 4; i < count; ++i)
- {
- shader.uniform1f(sRoughness, (F32)i * roughness_factor);
- shader.uniform1f(sMipLevel, (F32)i);
- shader.uniform1i(sWidth, mProbeResolution);
- shader.uniform1f(sStrength, 1.f);
- // For each cube face
- for (U32 cf = 0; cf < 6; ++cf)
- {
- frame.lookAt(LLVector3::zero,
- LLCubeMapArray::sClipToCubeLookVecs[cf],
- LLCubeMapArray::sClipToCubeUpVecs[cf]);
- frame.getOpenGLRotation(mat);
- gGL.loadMatrix(mat);
- mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
- glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0,
- probep->mCubeIndex * 6 + cf, 0, 0,
- res, res);
- }
- if (i != count - 1)
- {
- res /= 2;
- glViewport(0, 0, res, res);
- }
- }
- shader.unbind();
- mMipChain[0].flush();
- }
- void LLHeroProbeManager::updateUniforms()
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- if (!LLPipeline::sRenderMirrors)
- {
- mHeroData.heroProbeCount = 0;
- return;
- }
- mHeroData.heroProbeCount = 1;
- if (mNearestHero.notNull() && !mNearestHero->isDead() &&
- mDefaultProbe.notNull())
- {
- LLMatrix4a modelview = gGLModelView;
- LLVector4a oa; // Scratch space for transformed origin
- oa.set(0.f, 0.f, 0.f, 0.f);
- F32 radius;
- if (mNearestHero->getReflectionProbeIsBox())
- {
- static const LLVector3 half(0.5f, 0.5f, 0.5f);
- radius = mNearestHero->getScale().scaledVec(half).length();
- }
- else
- {
- radius = mNearestHero->getScale().mV[0] * 0.5f;
- }
- mDefaultProbe->mRadius = radius;
- modelview.affineTransform(mDefaultProbe->mOrigin, oa);
- mHeroData.heroSphere.set(oa.getF32ptr());
- mHeroData.heroSphere.mV[3] = radius;
- mHeroData.heroShape = mDefaultProbe->getBox(mHeroData.heroBox) ? 0 : 1;
- }
- mHeroData.heroMipCount = mMipChain.size();
- }
- void LLHeroProbeManager::renderDebug()
- {
- LL_TRACY_TIMER(TRC_HERO_PROBE);
- gDebugProgram.bind();
- for (size_t i = 0, count = mProbes.size(); i < count; ++i)
- {
- render_reflection_probe(mProbes[i].get());
- }
- gDebugProgram.unbind();
- }
- void LLHeroProbeManager::doOcclusion()
- {
- LL_TRACY_TIMER(TRC_REFLECTION_PROBE);
- if (!gUsePBRShaders)
- {
- return;
- }
- LLVector4a eye;
- eye.load3(gViewerCamera.getOrigin().mV);
- for (size_t i = 0, count = mProbes.size(); i < count; ++i)
- {
- LLReflectionMap* probep = mProbes[i].get();
- if (probep && probep != mDefaultProbe.get())
- {
- probep->doOcclusion(eye);
- }
- }
- }
- bool LLHeroProbeManager::registerViewerObject(LLVOVolume* volp)
- {
- if (!volp || volp->isDead() || volp->mDrawable.isNull())
- {
- return false; // Nope, we did not register this ! HB
- }
- mHeroVOList.emplace(volp);
- if (volp)
- {
- volp->mIsHeroProbe = true;
- }
- return true; // It is now indeed in our list. HB
- }
- void LLHeroProbeManager::unregisterViewerObject(LLVOVolume* volp)
- {
- mHeroVOList.erase(volp);
- if (volp)
- {
- volp->mIsHeroProbe = false;
- }
- }
|