Newer
Older
// for sphere probes, origin (xyz) and radius (w) of refmaps in clip space
LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
// x - irradiance scale
// y - radiance scale
// z - fade in
David Parks
committed
// 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
David Parks
committed
GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
// numbrer of active refmaps
GLint refmapCount;
GLint heroShape;
GLint heroMipCount;
GLint heroProbeCount;
mReflectionMaps.resize(mReflectionProbeCount);
getReflectionMaps(mReflectionMaps);
ReflectionProbeData rpd;
David Parks
committed
F32 minDepth[256];
for (int i = 0; i < 256; ++i)
{
rpd.refBucket[i][0] = mReflectionProbeCount;
rpd.refBucket[i][1] = mReflectionProbeCount;
rpd.refBucket[i][2] = mReflectionProbeCount;
rpd.refBucket[i][3] = mReflectionProbeCount;
minDepth[i] = FLT_MAX;
}
// load modelview matrix into matrix 4a
LLVector4a oa; // scratch space for transformed origin
S32 count = 0;
U32 nc = 0; // neighbor "cursor" - index into refNeighbor to start writing the next probe's list of neighbors
LLEnvironment& environment = LLEnvironment::instance();
LLSettingsSky::ptr_t psky = environment.getCurrentSky();
David Parks
committed
static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
David Parks
committed
F32 minimum_ambiance = psky->getReflectionProbeAmbiance(should_auto_adjust);
David Parks
committed
bool is_ambiance_pass = gCubeSnapshot && !isRadiancePass();
F32 ambscale = is_ambiance_pass ? 0.f : 1.f;
F32 radscale = is_ambiance_pass ? 0.5f : 1.f;
for (auto* refmap : mReflectionMaps)
{
if (refmap == nullptr)
{
break;
}
if (refmap != mDefaultProbe.get())
David Parks
committed
{
// 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.
unsigned int depth_min = llclamp(llfloor(refmap->mMinDepth), 0, 255);
unsigned int depth_max = llclamp(llfloor(refmap->mMaxDepth), 0, 255);
David Parks
committed
for (U32 i = depth_min; i <= depth_max; ++i)
{
if (refmap->mMinDepth < minDepth[i])
{
minDepth[i] = refmap->mMinDepth;
rpd.refBucket[i][0] = refmap->mProbeIndex;
}
}
}
llassert(refmap->mProbeIndex == count);
llassert(mReflectionMaps[refmap->mProbeIndex] == refmap);
llassert(refmap->mCubeIndex >= 0); // should always be true, if not, getReflectionMaps is bugged
{
if (refmap->mViewerObject && refmap->mViewerObject->getVolume())
David Parks
committed
{ // have active manual probes live-track the object they're associated with
LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject;
refmap->mOrigin.load3(vobj->getPositionAgent().mV);
if (vobj->getReflectionProbeIsBox())
{
LLVector3 s = vobj->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
refmap->mRadius = s.magVec();
}
else
{
refmap->mRadius = refmap->mViewerObject->getScale().mV[0] * 0.5f;
}
David Parks
committed
}
modelview.affineTransform(refmap->mOrigin, oa);
rpd.refSphere[count].set(oa.getF32ptr());
rpd.refSphere[count].mV[3] = refmap->mRadius;
}
rpd.refIndex[count][0] = refmap->mCubeIndex;
llassert(nc % 4 == 0);
rpd.refIndex[count][1] = nc / 4;
rpd.refIndex[count][3] = refmap->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 (refmap->getBox(rpd.refBox[count]))
{ // negate priority to indicate this probe has a box influence volume
rpd.refIndex[count][3] = -rpd.refIndex[count][3];
}
David Parks
committed
rpd.refParams[count].set(
llmax(minimum_ambiance, refmap->getAmbiance())*ambscale, // ambiance scale
radscale, // radiance scale
refmap->mFadeIn, // fade in weight
oa.getF32ptr()[2] - refmap->mRadius); // z near
David Parks
committed
S32 ni = nc; // neighbor ("index") - index into refNeighbor to write indices for current reflection probe's neighbors
{
//LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - refNeighbors");
//pack neghbor list
const U32 max_neighbors = 64;
U32 neighbor_count = 0;
for (auto& neighbor : refmap->mNeighbors)
{
if (ni >= 4096)
{ // out of space
break;
}
GLint idx = neighbor->mProbeIndex;
David Parks
committed
if (idx == -1 || neighbor->mOccluded || neighbor->mCubeIndex == -1)
{
continue;
}
// this neighbor may be sampled
rpd.refNeighbor[ni++] = idx;
neighbor_count++;
if (neighbor_count >= max_neighbors)
{
break;
}
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++;
}
David Parks
committed
#if 0
{
// fill in gaps in refBucket
S32 probe_idx = mReflectionProbeCount;
David Parks
committed
for (int i = 0; i < 256; ++i)
{
if (i < count)
{ // for debugging, store depth of mReflectionsMaps[i]
rpd.refBucket[i][1] = (S32) (mReflectionMaps[i]->mDepth * 10);
}
if (rpd.refBucket[i][0] == mReflectionProbeCount)
{
rpd.refBucket[i][0] = probe_idx;
}
else
{
probe_idx = rpd.refBucket[i][0];
}
}
}
#endif
rpd.refmapCount = count;
gPipeline.mHeroProbeManager.updateUniforms();
// Get the hero data.
rpd.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox;
rpd.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere;
rpd.heroShape = gPipeline.mHeroProbeManager.mHeroData.heroShape;
rpd.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount;
rpd.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount;
//copy rpd into uniform buffer object
if (mUBO == 0)
{
}
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("rmmsu - update buffer");
glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ReflectionProbeData), &rpd, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
David Parks
committed
#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 LLReflectionMapManager::setUniforms()
{
if (!LLPipeline::sReflectionProbesEnabled)
{
return;
}
if (mUBO == 0)
updateUniforms();
}
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
}
void renderReflectionProbe(LLReflectionMap* probe)
{
David Parks
committed
if (probe->isRelevant())
David Parks
committed
F32* po = probe->mOrigin.getF32ptr();
David Parks
committed
//draw orange line from probe to neighbors
gGL.flush();
gGL.diffuseColor4f(1, 0.5f, 0, 1);
gGL.begin(gGL.LINES);
for (auto& neighbor : probe->mNeighbors)
David Parks
committed
if (probe->mViewerObject && neighbor->mViewerObject)
{
continue;
}
gGL.vertex3fv(po);
gGL.vertex3fv(neighbor->mOrigin.getF32ptr());
}
David Parks
committed
gGL.end();
gGL.flush();
gGL.diffuseColor4f(1, 1, 0, 1);
gGL.begin(gGL.LINES);
for (auto& neighbor : probe->mNeighbors)
{
if (probe->mViewerObject && neighbor->mViewerObject)
{
gGL.vertex3fv(po);
gGL.vertex3fv(neighbor->mOrigin.getF32ptr());
}
}
gGL.end();
gGL.flush();
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
#if 0
LLSpatialGroup* group = probe->mGroup;
if (group)
{ // draw lines from corners of object aabb to reflection probe
const LLVector4a* bounds = group->getBounds();
LLVector4a o = bounds[0];
gGL.flush();
gGL.diffuseColor4f(0, 0, 1, 1);
F32* c = o.getF32ptr();
const F32* bc = bounds[0].getF32ptr();
const F32* bs = bounds[1].getF32ptr();
// daaw blue lines from corners to center of node
gGL.begin(gGL.LINES);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] + bs[0], bc[1] + bs[1], bc[2] + bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] - bs[0], bc[1] + bs[1], bc[2] + bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] + bs[0], bc[1] - bs[1], bc[2] + bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] - bs[0], bc[1] - bs[1], bc[2] + bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] + bs[0], bc[1] + bs[1], bc[2] - bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] - bs[0], bc[1] + bs[1], bc[2] - bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] + bs[0], bc[1] - bs[1], bc[2] - bs[2]);
gGL.vertex3fv(c);
gGL.vertex3f(bc[0] - bs[0], bc[1] - bs[1], bc[2] - bs[2]);
gGL.end();
//draw yellow line from center of node to reflection probe origin
gGL.flush();
gGL.diffuseColor4f(1, 1, 0, 1);
gGL.begin(gGL.LINES);
gGL.vertex3fv(c);
gGL.vertex3fv(po);
gGL.end();
gGL.flush();
}
#endif
}
void LLReflectionMapManager::renderDebug()
{
gDebugProgram.bind();
for (auto& probe : mProbes)
{
renderReflectionProbe(probe);
}
gDebugProgram.unbind();
}
void LLReflectionMapManager::initReflectionMaps()
{
U32 count = LL_MAX_REFLECTION_PROBE_COUNT;
David Parks
committed
static LLCachedControl<U32> ref_probe_res(gSavedSettings, "RenderReflectionProbeResolution", 128U);
U32 probe_resolution = nhpo2(llclamp(ref_probe_res(), (U32)64, (U32)512));
if (mTexture.isNull() || mReflectionProbeCount != count || mProbeResolution != probe_resolution || mReset)
{
if(mProbeResolution != probe_resolution)
{
mRenderTarget.release();
mMipChain.clear();
}
mReset = false;
David Parks
committed
mReflectionProbeCount = count;
mProbeResolution = probe_resolution;
mMaxProbeLOD = log2f((F32)mProbeResolution) - 1.f; // number of mips - 1
if (mTexture.isNull() ||
mTexture->getWidth() != mProbeResolution ||
mReflectionProbeCount + 2 != mTexture->getCount())
{
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);
}
David Parks
committed
// reset probe state
mUpdatingFace = 0;
mUpdatingProbe = nullptr;
mRadiancePass = false;
mRealtimeRadiancePass = false;
// if default probe already exists, remember whether or not it's complete (SL-20498)
bool default_complete = mDefaultProbe.isNull() ? false : mDefaultProbe->mComplete;
David Parks
committed
for (auto& probe : mProbes)
{
probe->mLastUpdateTime = 0.f;
David Parks
committed
probe->mComplete = false;
probe->mProbeIndex = -1;
probe->mCubeArray = nullptr;
probe->mCubeIndex = -1;
David Parks
committed
probe->mNeighbors.clear();
David Parks
committed
}
David Parks
committed
mCubeFree.clear();
initCubeFree();
if (mDefaultProbe.isNull())
David Parks
committed
{
David Parks
committed
llassert(mProbes.empty()); // default probe MUST be the first probe created
mDefaultProbe = new LLReflectionMap();
mProbes.push_back(mDefaultProbe);
David Parks
committed
}
David Parks
committed
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;
David Parks
committed
touch_default_probe(mDefaultProbe);
David Parks
committed
if (mVertexBuffer.isNull())
{
U32 mask = LLVertexBuffer::MAP_VERTEX;
LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask);
buff->allocateBuffer(4, 0);
David Parks
committed
LLStrider<LLVector3> v;
David Parks
committed
buff->getVertexStrider(v);
David Parks
committed
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();
David Parks
committed
mVertexBuffer = buff;
}
}
void LLReflectionMapManager::cleanup()
{
mVertexBuffer = nullptr;
mRenderTarget.release();
mMipChain.clear();
mTexture = nullptr;
mIrradianceMaps = nullptr;
mProbes.clear();
mKillList.clear();
mCreateList.clear();
mReflectionMaps.clear();
mUpdatingFace = 0;
mDefaultProbe = nullptr;
glDeleteBuffers(1, &mUBO);
mUBO = 0;
// note: also called on teleport (not just shutdown), so make sure we're in a good "starting" state
initCubeFree();
David Parks
committed
void LLReflectionMapManager::doOcclusion()
{
LLVector4a eye;
eye.load3(LLViewerCamera::instance().getOrigin().mV);
for (auto& probe : mProbes)
{
if (probe != nullptr && probe != mDefaultProbe)
David Parks
committed
{
probe->doOcclusion(eye);
}
}
}
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
void LLReflectionMapManager::forceDefaultProbeAndUpdateUniforms(bool force)
{
static std::vector<bool> mProbeWasOccluded;
if (force)
{
llassert(mProbeWasOccluded.empty());
for (size_t i = 0; i < mProbes.size(); ++i)
{
auto& probe = mProbes[i];
mProbeWasOccluded.push_back(probe->mOccluded);
if (probe != nullptr && probe != mDefaultProbe)
{
probe->mOccluded = true;
}
}
updateUniforms();
}
else
{
llassert(mProbes.size() == mProbeWasOccluded.size());
const size_t n = llmin(mProbes.size(), mProbeWasOccluded.size());
for (size_t i = 0; i < n; ++i)
{
auto& probe = mProbes[i];
llassert(probe->mOccluded == (probe != mDefaultProbe));
probe->mOccluded = mProbeWasOccluded[i];
}
mProbeWasOccluded.clear();
mProbeWasOccluded.shrink_to_fit();
}
}