Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @file LLHeroProbeManager.cpp
* @brief LLHeroProbeManager class implementation
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2022, 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 "llheroprobemanager.h"
#include "llreflectionmapmanager.h"
#include "llviewercamera.h"
#include "llspatialpartition.h"
#include "llviewerregion.h"
#include "pipeline.h"
#include "llviewershadermgr.h"
#include "llviewercontrol.h"
#include "llenvironment.h"
#include "llstartup.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llviewerwindow.h"
#include "llviewerjoystick.h"
#include "llviewermediafocus.h"
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
extern BOOL gCubeSnapshot;
extern BOOL gTeleportDisplay;
// get the next highest power of two of v (or v if v is already a power of two)
//defined in llvertexbuffer.cpp
extern U32 nhpo2(U32 v);
static void touch_default_probe(LLReflectionMap* probe)
{
if (LLViewerCamera::getInstance())
{
LLVector3 origin = LLViewerCamera::getInstance()->getOrigin();
origin.mV[2] += 64.f;
probe->mOrigin.load3(origin.mV);
}
}
LLHeroProbeManager::LLHeroProbeManager()
{
}
// helper class to seed octree with probes
void LLHeroProbeManager::update()
{
if (!LLPipeline::sReflectionProbesEnabled || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE)
{
return;
}
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
llassert(!gCubeSnapshot); // assert a snapshot is not in progress
if (LLAppViewer::instance()->logoutRequestSent())
{
return;
}
initReflectionMaps();
if (!mRenderTarget.isComplete())
{
U32 color_fmt = GL_RGB16F;
U32 targetRes = mProbeResolution * 4; // super sample
mRenderTarget.allocate(targetRes, targetRes, color_fmt, true);
}
if (mMipChain.empty())
{
U32 res = mProbeResolution;
U32 count = log2((F32)res) + 0.5f;
mMipChain.resize(count);
for (int i = 0; i < count; ++i)
{
mMipChain[i].allocate(res, res, GL_RGB16F);
res /= 2;
}
}
llassert(mProbes[0] == mDefaultProbe);
LLVector4a probe_pos;
LLVector3 camera_pos = LLViewerCamera::instance().mOrigin;
if (mHeroVOList.size() > 0)
if (mNearestHero != nullptr && mNearestHero->mDrawable.notNull())
LLVector3 hero_pos = mNearestHero->mDrawable->mXform.getWorldPosition();
LLVector4a hit_pos;
LLVector3 focus_point;
LLQuaternion camera_rot;
F32 angleInRadians = 180 * DEG_TO_RAD;
LLMatrix4 rotationMatrix;
LLVector3 translatedPoint;
LLVector3 rotatedTranslatedPoint;
LLVector3 rotatedPoint;
switch (mNearestHero->mirrorPlacementMode()) {
case 0:
hero_pos.mV[1] = camera_pos.mV[1];
rotationMatrix.rotate(angleInRadians, LLVector4(1, 0, 0, 0));
translatedPoint = camera_pos - hero_pos;
rotatedTranslatedPoint = translatedPoint * rotationMatrix;
rotatedPoint = rotatedTranslatedPoint + hero_pos;
probe_pos.load3(rotatedPoint.mV);
hero_pos.mV[2] = camera_pos.mV[2];
rotationMatrix.rotate(angleInRadians, LLVector4(0, 1, 0, 0));
translatedPoint = camera_pos - hero_pos;
rotatedTranslatedPoint = translatedPoint * rotationMatrix;
rotatedPoint = rotatedTranslatedPoint + hero_pos;
probe_pos.load3(rotatedPoint.mV);
break;
hero_pos.mV[0] = camera_pos.mV[0];
rotationMatrix.rotate(angleInRadians, LLVector4(0, 0, 1, 0));
translatedPoint = camera_pos - hero_pos;
rotatedTranslatedPoint = translatedPoint * rotationMatrix;
rotatedPoint = rotatedTranslatedPoint + hero_pos;
probe_pos.load3(rotatedPoint.mV);
break;
mHeroProbeStrength = 1;
probe_pos.load3(camera_pos.mV);
}
static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1);
static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3);
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime");
// Probe 0 is always our mirror probe.
mProbes[0]->mOrigin = probe_pos;
bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass();
gPipeline.mReflectionMapManager.mRadiancePass = true;
for (U32 j = 0; j < mProbes.size(); j++)
for (U32 i = 0; i < 6; ++i)
updateProbeFace(mProbes[j], i);
gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass;
}
}
// Do the reflection map update render passes.
// For every 12 calls of this function, 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 LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face)
// Make our object invisible.
if (mNearestHero)
{
mNearestHero->setDrawableState(LLDrawable::FORCE_INVISIBLE, true);
gPipeline.markRebuild( mNearestHero->mDrawable, LLDrawable::REBUILD_ALL);
}
// hacky hot-swap of camera specific render targets
gPipeline.mRT = &gPipeline.mAuxillaryRT;
probe->update(mRenderTarget.getWidth(), face, true);
gPipeline.mRT = &gPipeline.mMainRT;
S32 sourceIdx = mReflectionProbeCount;
// Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes.
sourceIdx += 1;
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;
static LLStaticHashedString resScale("resScale");
static LLStaticHashedString direction("direction");
static LLStaticHashedString znear("znear");
static LLStaticHashedString zfar("zfar");
LLRenderTarget* screen_rt = &gPipeline.mAuxillaryRT.screen;
// perform a gaussian blur on the super sampled render before downsampling
{
gGaussianProgram.bind();
gGaussianProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2));
S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
// horizontal
gGaussianProgram.uniform2f(direction, 1.f, 0.f);
gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
mRenderTarget.bindTarget();
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
mRenderTarget.flush();
// vertical
gGaussianProgram.uniform2f(direction, 0.f, 1.f);
gGL.getTexUnit(diffuseChannel)->bind(&mRenderTarget);
screen_rt->bindTarget();
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
screen_rt->flush();
}
S32 mips = log2((F32)mProbeResolution) + 0.5f;
gReflectionMipProgram.bind();
S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
for (int i = 0; i < mMipChain.size(); ++i)
{
LL_PROFILE_GPU_ZONE("probe mip");
mMipChain[i].bindTarget();
if (i == 0)
{
gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
}
else
{
gGL.getTexUnit(diffuseChannel)->bind(&(mMipChain[i - 1]));
}
gReflectionMipProgram.uniform1f(resScale, 1.f/(mProbeResolution*2));
gPipeline.mScreenTriangleVB->setBuffer();
gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
res /= 2;
S32 mip = i - (mMipChain.size() - mips);
if (mip >= 0)
{
LL_PROFILE_GPU_ZONE("probe mip copy");
mTexture->bind(0);
//glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res);
glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res);
//if (i == 0)
//{
//glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, probe->mCubeIndex * 6 + face, 0, 0, res, res);
//}
mTexture->unbind();
}
mMipChain[i].flush();
}
gGL.popMatrix();
gGL.matrixMode(gGL.MM_MODELVIEW);
gGL.popMatrix();
gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE);
gReflectionMipProgram.unbind();
}
if (face == 5)
{
mMipChain[0].bindTarget();
static LLStaticHashedString sSourceIdx("sourceIdx");
{
//generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map)
gRadianceGenProgram.bind();
mVertexBuffer->setBuffer();
S32 channel = gRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
mTexture->bind(channel);
gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, mHeroProbeStrength);
U32 res = mMipChain[0].getWidth();
for (int i = 0; i < mMipChain.size(); ++i)
{
LL_PROFILE_GPU_ZONE("probe radiance gen");
static LLStaticHashedString sMipLevel("mipLevel");
static LLStaticHashedString sRoughness("roughness");
static LLStaticHashedString sWidth("u_width");
gRadianceGenProgram.uniform1f(sRoughness, (F32)i / (F32)(mMipChain.size() - 1));
gRadianceGenProgram.uniform1f(sMipLevel, i);
gRadianceGenProgram.uniform1i(sWidth, mProbeResolution);
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
for (int cf = 0; cf < 6; ++cf)
{ // for each cube face
LLCoordFrame frame;
frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]);
F32 mat[16];
frame.getOpenGLRotation(mat);
gGL.loadMatrix(mat);
mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res);
}
if (i != mMipChain.size() - 1)
{
res /= 2;
glViewport(0, 0, res, res);
}
}
gRadianceGenProgram.unbind();
}
mMipChain[0].flush();
}
if (mNearestHero)
{
mNearestHero->clearDrawableState(LLDrawable::FORCE_INVISIBLE, true);
gPipeline.markRebuild( mNearestHero->mDrawable, LLDrawable::REBUILD_ALL);
}
}
void LLHeroProbeManager::updateUniforms()
{
if (!LLPipeline::sReflectionProbesEnabled)
{
return;
}
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
struct HeroProbeData
{
LLVector4 heroPosition[1];
GLint heroProbeCount = 1;
};
HeroProbeData hpd;
LLMatrix4a modelview;
LLVector4a oa; // scratch space for transformed origin
oa.set(0, 0, 0, 0);
hpd.heroProbeCount = 1;
modelview.affineTransform(mProbes[0]->mOrigin, oa);
hpd.heroPosition[0].set(oa.getF32ptr());
//copy rpd into uniform buffer object
if (mUBO == 0)
{
glGenBuffers(1, &mUBO);
}
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
glBufferData(GL_UNIFORM_BUFFER, sizeof(HeroProbeData), &hpd, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
#if 0
if (!gCubeSnapshot)
{
for (auto& probe : mProbes)
{
LLViewerObject* vobj = probe->mViewerObject;
if (vobj)
{
F32 time = (F32)gFrameTimeSeconds - probe->mLastUpdateTime;
vobj->setDebugText(llformat("%d/%d/%d/%.1f - %.1f/%.1f", probe->mCubeIndex, probe->mProbeIndex, (U32) probe->mNeighbors.size(), probe->mMinDepth, probe->mMaxDepth, time), time > 1.f ? LLColor4::white : LLColor4::green);
}
}
}
#endif
}
void LLHeroProbeManager::setUniforms()
{
if (!LLPipeline::sReflectionProbesEnabled)
{
return;
}
if (mUBO == 0)
{
updateUniforms();
}
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
}
void LLHeroProbeManager::renderDebug()
{
gDebugProgram.bind();
for (auto& probe : mProbes)
{
renderReflectionProbe(probe);
}
gDebugProgram.unbind();
}
void LLHeroProbeManager::initReflectionMaps()
{
U32 count = LL_MAX_REFLECTION_PROBE_COUNT;
if (mTexture.isNull() || mReflectionProbeCount != count || mReset)
{
mReset = false;
mReflectionProbeCount = count;
mProbeResolution = nhpo2(1024);
mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // number of mips - 1
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);
if (mDefaultProbe.isNull())
{
llassert(mProbes.empty()); // default probe MUST be the first probe created
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;
mDefaultProbe->mDistance = 12.f;
mDefaultProbe->mRadius = 4096.f;
mDefaultProbe->mProbeIndex = 0;
touch_default_probe(mDefaultProbe);
mProbes.push_back(mDefaultProbe);
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
}
if (mVertexBuffer.isNull())
{
U32 mask = LLVertexBuffer::MAP_VERTEX;
LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask);
buff->allocateBuffer(4, 0);
LLStrider<LLVector3> v;
buff->getVertexStrider(v);
v[0] = LLVector3(-1, -1, -1);
v[1] = LLVector3(1, -1, -1);
v[2] = LLVector3(-1, 1, -1);
v[3] = LLVector3(1, 1, -1);
buff->unmapBuffer();
mVertexBuffer = buff;
}
}
void LLHeroProbeManager::cleanup()
{
mVertexBuffer = nullptr;
mRenderTarget.release();
mHeroRenderTarget.release();
mMipChain.clear();
mTexture = nullptr;
mProbes.clear();
mReflectionMaps.clear();
mDefaultProbe = nullptr;
mUpdatingProbe = nullptr;
glDeleteBuffers(1, &mUBO);
mUBO = 0;
mNearestHero = nullptr;
}
void LLHeroProbeManager::doOcclusion()
{
LLVector4a eye;
eye.load3(LLViewerCamera::instance().getOrigin().mV);
for (auto& probe : mProbes)
{
if (probe != nullptr && probe != mDefaultProbe)
{
probe->doOcclusion(eye);
}
}
}
void LLHeroProbeManager::registerHeroDrawable(LLVOVolume* drawablep)
mNearestHero = drawablep;
if (mHeroVOList.find(drawablep) == mHeroVOList.end())
{
mHeroVOList.insert(drawablep);
LL_INFOS() << "Mirror drawable registered." << LL_ENDL;
}
}
void LLHeroProbeManager::unregisterHeroDrawable(LLVOVolume* drawablep)
if (mHeroVOList.find(drawablep) != mHeroVOList.end())
mHeroVOList.erase(drawablep);