From 26471aaa774822d238d008b55830101aa3f6c88d Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 24 Mar 2019 18:18:44 -0500 Subject: [PATCH] WIP of timmy's changes merged in. Not properly initializing the probes/array slots just yet. --- .../source/T3D/lighting/reflectionProbe.cpp | 9 +- Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp | 113 +++++++- Engine/source/gfx/D3D11/gfxD3D11Cubemap.h | 7 +- Engine/source/gfx/Null/gfxNullDevice.cpp | 6 +- Engine/source/gfx/bitmap/imageUtils.cpp | 5 + Engine/source/gfx/bitmap/imageUtils.h | 5 + Engine/source/gfx/gfxCubemap.h | 25 +- .../source/renderInstance/renderProbeMgr.cpp | 170 ++++++++++-- Engine/source/renderInstance/renderProbeMgr.h | 48 +++- .../advanced/reflectionProbeArrayP.hlsl | 256 ++++++++++-------- 10 files changed, 485 insertions(+), 159 deletions(-) diff --git a/Engine/source/T3D/lighting/reflectionProbe.cpp b/Engine/source/T3D/lighting/reflectionProbe.cpp index 031381ee4..c4e79e34a 100644 --- a/Engine/source/T3D/lighting/reflectionProbe.cpp +++ b/Engine/source/T3D/lighting/reflectionProbe.cpp @@ -631,7 +631,7 @@ void ReflectionProbe::processStaticCubemap() IBLUtilities::SaveCubeMap(getPrefilterMapPath(), mPrefilterMap->mCubemap); } - mProbeInfo->mCubemap = mPrefilterMap->mCubemap; + mProbeInfo->mPrefilterCubemap = mPrefilterMap->mCubemap; mProbeInfo->mIrradianceCubemap = mIrridianceMap->mCubemap; } @@ -647,7 +647,7 @@ void ReflectionProbe::updateMaterial() { if (mPrefilterMap != nullptr && mPrefilterMap->mCubemap.isValid()) { - mProbeInfo->mCubemap = mPrefilterMap->mCubemap; + mProbeInfo->mPrefilterCubemap = mPrefilterMap->mCubemap; } else { @@ -667,7 +667,7 @@ void ReflectionProbe::updateMaterial() { if (mReflectionModeType == DynamicCubemap && !mDynamicCubemap.isNull()) { - mProbeInfo->mCubemap = mDynamicCubemap; + mProbeInfo->mPrefilterCubemap = mDynamicCubemap; mProbeInfo->mCubeReflector.registerReflector(this, reflectorDesc); //need to decide how we wanna do the reflectorDesc. static name or a field } @@ -684,6 +684,9 @@ void ReflectionProbe::updateMaterial() mProbeInfo->mIsEnabled = false; PROBEMGR->updateProbes(); + + if (mProbeInfo->mPrefilterCubemap->isInitialized() && mProbeInfo->mIrradianceCubemap->isInitialized()) + PROBEMGR->updateProbeTexture(mProbeInfo); } bool ReflectionProbe::createClientResources() diff --git a/Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp b/Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp index 565fa8173..cd5f528e5 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp @@ -141,7 +141,7 @@ void GFXD3D11Cubemap::initStatic(GFXTexHandle *faces) mSRView->GetDesc(&viewDesc); mMipMapLevels = viewDesc.TextureCube.MipLevels; } - mInitialized = true; + } void GFXD3D11Cubemap::initStatic(DDSFile *dds) @@ -208,7 +208,6 @@ void GFXD3D11Cubemap::initStatic(DDSFile *dds) { AssertFatal(false, "GFXD3D11Cubemap::initStatic(DDSFile *dds) - CreateTexture2D call failure"); } - mInitialized = true; } void GFXD3D11Cubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLevels) @@ -331,7 +330,7 @@ void GFXD3D11Cubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLeve } SAFE_RELEASE(depthTex); - mInitialized = true; + } //----------------------------------------------------------------------------- @@ -392,10 +391,11 @@ GFXD3D11CubemapArray::~GFXD3D11CubemapArray() SAFE_RELEASE(mTexture); } -void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) +//TODO: really need a common private 'init' function to avoid code double up with these init* functions +void GFXD3D11CubemapArray::init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) { - AssertFatal(cubemaps, "GFXD3D11CubemapArray - Got null GFXCubemapHandle!"); - AssertFatal(*cubemaps, "GFXD3D11CubemapArray - Got empty cubemap!"); + AssertFatal(cubemaps, "GFXD3D11CubemapArray::initStatic - Got null GFXCubemapHandle!"); + AssertFatal(*cubemaps, "GFXD3D11CubemapArray::initStatic - Got empty cubemap!"); //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap mSize = cubemaps[0]->getSize(); @@ -424,7 +424,7 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &mTexture); if (FAILED(hr)) - AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - CreateTexture2D failure"); + AssertFatal(false, "GFXD3D11CubemapArray::initStatic - CreateTexture2D failure"); for (U32 i = 0; i < cubemapCount; i++) { @@ -434,7 +434,7 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube { Con::printf("Trying to add an invalid Cubemap to a CubemapArray"); //destroy array here first - AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - invalid cubemap"); + AssertFatal(false, "GFXD3D11CubemapArray::initStatic - invalid cubemap"); } for (U32 face = 0; face < CubeFaces; face++) @@ -460,7 +460,102 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube hr = D3D11DEVICE->CreateShaderResourceView(mTexture, &SMViewDesc, &mSRView); if (FAILED(hr)) - AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - shader resource view creation failure"); + AssertFatal(false, "GFXD3D11CubemapArray::initStatic - shader resource view creation failure"); + +} + +//Just allocate the cubemap array but we don't upload any data +void GFXD3D11CubemapArray::init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) +{ + mSize = cubemapFaceSize; + mMipMapLevels = ImageUtil::getMaxMipCount(cubemapFaceSize, cubemapFaceSize); + mNumCubemaps = cubemapCount; + mFormat = format; + + //create texture object + UINT bindFlags = D3D11_BIND_SHADER_RESOURCE; + UINT miscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC)); + desc.Width = mSize; + desc.Height = mSize; + desc.MipLevels = mMipMapLevels; + desc.ArraySize = CubeFaces * cubemapCount; + desc.Format = GFXD3D11TextureFormat[mFormat]; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = bindFlags; + desc.MiscFlags = miscFlags; + desc.CPUAccessFlags = 0; + + HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &mTexture); + + if (FAILED(hr)) + AssertFatal(false, "GFXD3D11CubemapArray::initStatic - CreateTexture2D failure"); + + //create shader resource view + D3D11_SHADER_RESOURCE_VIEW_DESC SMViewDesc; + SMViewDesc.Format = GFXD3D11TextureFormat[mFormat]; + SMViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + SMViewDesc.TextureCubeArray.MipLevels = mMipMapLevels; + SMViewDesc.TextureCubeArray.MostDetailedMip = 0; + SMViewDesc.TextureCubeArray.NumCubes = mNumCubemaps; + SMViewDesc.TextureCubeArray.First2DArrayFace = 0; + + hr = D3D11DEVICE->CreateShaderResourceView(mTexture, &SMViewDesc, &mSRView); + if (FAILED(hr)) + AssertFatal(false, "GFXD3D11CubemapArray::initStatic - shader resource view creation failure"); + +} + +void GFXD3D11CubemapArray::updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) +{ + AssertFatal(slot <= mNumCubemaps, "GFXD3D11CubemapArray::updateTexture - trying to update a cubemap texture that is out of bounds!"); + + GFXD3D11Cubemap *pCubeObj = static_cast((GFXCubemap*)cubemap); + ID3D11Resource *pDstRes = pCubeObj->get2DTex(); + for (U32 face = 0; face < CubeFaces; face++) + { + const U32 arraySlice = face + CubeFaces * slot; + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + { + const U32 srcSubResource = D3D11CalcSubresource(currentMip, face, mMipMapLevels); + const U32 dstSubResource = D3D11CalcSubresource(currentMip, arraySlice, mMipMapLevels); + D3D11DEVICECONTEXT->CopySubresourceRegion(mTexture, dstSubResource, 0, 0, 0, pDstRes, srcSubResource, NULL); + } + } +} + +void GFXD3D11CubemapArray::copyTo(GFXCubemapArray *pDstCubemap) +{ + AssertFatal(pDstCubemap, "GFXD3D11CubemapArray::copyTo - Got null GFXCubemapArray"); + + const U32 dstCount = pDstCubemap->getNumCubemaps(); + const GFXFormat dstFmt = pDstCubemap->getFormat(); + const U32 dstSize = pDstCubemap->getSize(); + const U32 dstMips = pDstCubemap->getMipMapLevels(); + + AssertFatal(dstCount > mNumCubemaps, "GFXD3D11CubemapArray::copyTo - Destination too small"); + AssertFatal(dstFmt == mFormat, "GFXD3D11CubemapArray::copyTo - Destination format doesn't match"); + AssertFatal(dstSize == mSize, "GFXD3D11CubemapArray::copyTo - Destination size doesn't match"); + AssertFatal(dstMips == mMipMapLevels, "GFXD3D11CubemapArray::copyTo - Destination mip levels doesn't match"); + + GFXD3D11CubemapArray *pDstCube = static_cast(pDstCubemap); + ID3D11Resource *pDstRes = pDstCube->get2DTex(); + for (U32 cubeMap = 0; cubeMap < mNumCubemaps; cubeMap++) + { + for (U32 face = 0; face < CubeFaces; face++) + { + const U32 arraySlice = face + CubeFaces * cubeMap; + for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++) + { + const U32 subResource = D3D11CalcSubresource(currentMip, arraySlice, mMipMapLevels); + D3D11DEVICECONTEXT->CopySubresourceRegion(pDstRes, subResource, 0, 0, 0, mTexture, subResource, NULL); + } + } + } } diff --git a/Engine/source/gfx/D3D11/gfxD3D11Cubemap.h b/Engine/source/gfx/D3D11/gfxD3D11Cubemap.h index 6f2524622..02cc91627 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Cubemap.h +++ b/Engine/source/gfx/D3D11/gfxD3D11Cubemap.h @@ -48,6 +48,8 @@ public: virtual void zombify(); virtual void resurrect(); + virtual bool isInitialized() { return mTexture ? true : false; } + // Get functions ID3D11ShaderResourceView* getSRView(); ID3D11RenderTargetView* getRTView(U32 faceIdx, U32 mipIndex=0); @@ -81,7 +83,10 @@ class GFXD3D11CubemapArray : public GFXCubemapArray public: GFXD3D11CubemapArray(); virtual ~GFXD3D11CubemapArray(); - virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount); + virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount); + virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format); + virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot); + virtual void copyTo(GFXCubemapArray *pDstCubemap); virtual void setToTexUnit(U32 tuNum); ID3D11ShaderResourceView* getSRView() { return mSRView; } diff --git a/Engine/source/gfx/Null/gfxNullDevice.cpp b/Engine/source/gfx/Null/gfxNullDevice.cpp index fbf854d2b..83933733c 100644 --- a/Engine/source/gfx/Null/gfxNullDevice.cpp +++ b/Engine/source/gfx/Null/gfxNullDevice.cpp @@ -169,8 +169,10 @@ private: virtual void setToTexUnit(U32 tuNum) { }; public: - virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) { }; - + virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) { }; + virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) { }; + virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) { }; + virtual void copyTo(GFXCubemapArray *pDstCubemap) { } virtual ~GFXNullCubemapArray() {}; virtual void zombify() {} virtual void resurrect() {} diff --git a/Engine/source/gfx/bitmap/imageUtils.cpp b/Engine/source/gfx/bitmap/imageUtils.cpp index 0d550f704..2d9e7ab8d 100644 --- a/Engine/source/gfx/bitmap/imageUtils.cpp +++ b/Engine/source/gfx/bitmap/imageUtils.cpp @@ -301,4 +301,9 @@ namespace ImageUtil return format; }; } + + U32 getMaxMipCount(const U32 width, const U32 height) + { + return mFloor(mLog2(mMax(width, height))) + 1; + } } \ No newline at end of file diff --git a/Engine/source/gfx/bitmap/imageUtils.h b/Engine/source/gfx/bitmap/imageUtils.h index 2cbb278e6..62784aa85 100644 --- a/Engine/source/gfx/bitmap/imageUtils.h +++ b/Engine/source/gfx/bitmap/imageUtils.h @@ -29,6 +29,9 @@ #ifndef _GFXENUMS_H_ #include "gfx/gfxEnums.h" #endif +#ifndef _MMATHFN_H_ +#include "math/mMathFn.h" +#endif struct DDSFile; @@ -57,6 +60,8 @@ namespace ImageUtil //convert to sRGB format GFXFormat toSRGBFormat(const GFXFormat format); + + U32 getMaxMipCount(const U32 width, const U32 height); }; #endif \ No newline at end of file diff --git a/Engine/source/gfx/gfxCubemap.h b/Engine/source/gfx/gfxCubemap.h index f8ad9e31d..798251d12 100644 --- a/Engine/source/gfx/gfxCubemap.h +++ b/Engine/source/gfx/gfxCubemap.h @@ -49,6 +49,7 @@ protected: U32 mMipMapLevels; + bool mInitialized; public: @@ -62,6 +63,7 @@ public: virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8, U32 mipLevels = 0 ) = 0; void initNormalize(U32 size); + GFXCubemap(); virtual ~GFXCubemap(); @@ -71,6 +73,9 @@ public: /// Returns the face texture format. virtual GFXFormat getFormat() const = 0; + /// Returns if this cubemap has been initialized + virtual bool isInitialized() { return false; } + /// Returns the cubemap file path set at creation. const String& getPath() const { return mPath; } @@ -83,7 +88,6 @@ public: /// Get Z up face index of the cubemap. DDS files will be stored Y up static U32 zUpFaceIndex(const U32 index); - bool isInitialised() { return mInitialized; } }; @@ -102,32 +106,43 @@ public: void free() { StrongObjectRef::set( NULL ); } }; -/// Cubemap array +/// Cubemap array - data lives on the GPU only with this class, but the data is not immutable so it can be updated class GFXCubemapArray : public StrongRefBase, public GFXResource { friend class GFXDevice; friend class GFXTextureManager; protected: - // should only be called by GFXDevice + /// should only be called by GFXDevice virtual void setToTexUnit( U32 tuNum ) = 0; /// number of cubemaps in the array U32 mNumCubemaps; + /// cubemap face size U32 mSize; + /// number of mip levels U32 mMipMapLevels; + /// format GFXFormat mFormat; public: virtual ~GFXCubemapArray() {}; - - virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) = 0; + /// Initialize from an array of cubemaps + virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) = 0; + /// Initialize cubemapCount number of blank cubemaps in the array + virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) = 0; + /// Update cubemap in the array + virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) = 0; + /// Copy this cubemap to another - destination must be same format, same face size & at-least the same size(or larger) + virtual void copyTo(GFXCubemapArray *pDstCubemap) = 0; /// Return number of textures in the array const U32 getNumCubemaps() const { return mNumCubemaps; } /// Get the number of mip maps const U32 getMipMapLevels() const { return mMipMapLevels; } /// Returns the size of the faces. const U32 getSize() const { return mSize; } + /// Returns the format + const GFXFormat getFormat() const { return mFormat; } virtual const String describeSelf() const; }; diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 12e15b08d..4266255b3 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -78,20 +78,21 @@ ProbeRenderInst::ProbeRenderInst() : SystemInterface(), mDirty(false), mPriority(1.0f), mScore(0.0f), - mCubemap(NULL), + mPrefilterCubemap(NULL), mIrradianceCubemap(NULL), mRadius(1.0f), mProbeRefOffset(0, 0, 0), mProbeRefScale(1,1,1), - mAtten(0.0) + mAtten(0.0), + mCubemapIndex(0) { } ProbeRenderInst::~ProbeRenderInst() { - if (mCubemap && mCubemap.isValid()) + if (mPrefilterCubemap && mPrefilterCubemap.isValid()) { - mCubemap.free(); + mPrefilterCubemap.free(); } if (mIrradianceCubemap && mIrradianceCubemap.isValid()) { @@ -102,7 +103,7 @@ ProbeRenderInst::~ProbeRenderInst() void ProbeRenderInst::set(const ProbeRenderInst *probeInfo) { mTransform = probeInfo->mTransform; - mCubemap = probeInfo->mCubemap; + mPrefilterCubemap = probeInfo->mPrefilterCubemap; mIrradianceCubemap = probeInfo->mIrradianceCubemap; mRadius = probeInfo->mRadius; mProbeShapeType = probeInfo->mProbeShapeType; @@ -183,6 +184,13 @@ RenderProbeMgr::RenderProbeMgr() mProbeArrayEffect = nullptr; smProbeManager = this; + + mCubeMapCount = 0; + + for (U32 i = 0; i < PROBE_MAX_COUNT; i++) + { + mCubeMapSlots[i] = false; + } } RenderProbeMgr::RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder) @@ -203,6 +211,37 @@ RenderProbeMgr::~RenderProbeMgr() mConstantLookup.clear(); } +bool RenderProbeMgr::onAdd() +{ + if (!Parent::onAdd()) + return false; + + mIrradianceArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); + mPrefilterArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); + + //pre-allocate a few slots + mIrradianceArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_IRRAD_SIZE, PROBE_FORMAT); + mPrefilterArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_PREFILTER_SIZE, PROBE_FORMAT); + mCubeSlotCount = PROBE_ARRAY_SLOT_BUFFER_SIZE; + + //create our own default default skylight + mDefaultSkyLight = new ProbeRenderInst; + mDefaultSkyLight->mProbeShapeType = ProbeRenderInst::Skylight; + if (!mDefaultSkyLight->mIrradianceCubemap.set("core/art/pbr/default_irradiance.dds")) + { + Con::errorf("RenderProbeMgr::onAdd: Failed to load default irradiance cubemap"); + return false; + } + + if (!mDefaultSkyLight->mPrefilterCubemap.set("core/art/pbr/default_prefilter.dds")) + { + Con::errorf("RenderProbeMgr::onAdd: Failed to load default prefilter cubemap"); + return false; + } + + return true; +} + void RenderProbeMgr::onRemove() { Parent::onRemove(); @@ -246,6 +285,38 @@ void RenderProbeMgr::registerProbe(U32 probeIdx) mRegisteredProbes.push_back_unique(probeIdx); + const U32 cubeIndex = _findNextEmptyCubeSlot(); + if (cubeIndex == INVALID_CUBE_SLOT) + { + Con::warnf("RenderProbeMgr::addProbe: Invalid cubemap slot."); + return; + } + + //check if we need to resize the cubemap array + if (cubeIndex >= mCubeSlotCount) + { + //alloc temp array handles + GFXCubemapArrayHandle irr = GFXCubemapArrayHandle(GFX->createCubemapArray()); + GFXCubemapArrayHandle prefilter = GFXCubemapArrayHandle(GFX->createCubemapArray()); + + irr->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_IRRAD_SIZE, PROBE_FORMAT); + prefilter->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_PREFILTER_SIZE, PROBE_FORMAT); + + mIrradianceArray->copyTo(irr); + mPrefilterArray->copyTo(prefilter); + + //assign the temp handles to the new ones, this will destroy the old ones as well + mIrradianceArray = irr; + mPrefilterArray = prefilter; + + mCubeSlotCount += PROBE_ARRAY_SLOT_BUFFER_SIZE; + } + + ProbeRenderInst::all[probeIdx]->mCubemapIndex = cubeIndex; + //mark cubemap slot as taken + mCubeMapSlots[cubeIndex] = true; + mCubeMapCount++; + //rebuild our probe data _setupStaticParameters(); } @@ -258,6 +329,13 @@ void RenderProbeMgr::unregisterProbe(U32 probeIdx) mRegisteredProbes.remove(probeIdx); + if (ProbeRenderInst::all[probeIdx]->mCubemapIndex == INVALID_CUBE_SLOT) + return; + + //mark cubemap slot as available now + mCubeMapSlots[ProbeRenderInst::all[probeIdx]->mCubemapIndex] = false; + mCubeMapCount--; + //rebuild our probe data _setupStaticParameters(); } @@ -314,6 +392,16 @@ void RenderProbeMgr::_setupStaticParameters() cubeMaps.clear(); irradMaps.clear(); + if (probeCount != 0 && ProbeRenderInst::all[0]->mPrefilterCubemap != nullptr) + { + //Get our mipCount + mMipCount = ProbeRenderInst::all[0]->mPrefilterCubemap.getPointer()->getMipMapLevels(); + } + else + { + mMipCount = 1; + } + for (U32 i = 0; i < probeCount; i++) { if (mEffectiveProbeCount >= MAXPROBECOUNT) @@ -323,19 +411,23 @@ void RenderProbeMgr::_setupStaticParameters() if (!curEntry.mIsEnabled) continue; - if (curEntry.mCubemap.isNull() || curEntry.mIrradianceCubemap.isNull()) + if (curEntry.mIsSkylight) + { + skylightPos = curEntry.getPosition(); + skylightPrefilterMap = curEntry.mPrefilterCubemap; + skylightIrradMap = curEntry.mIrradianceCubemap; + hasSkylight = true; continue; + } - if (!curEntry.mCubemap->isInitialised()) - continue; - - if (!curEntry.mIrradianceCubemap->isInitialised()) - continue; - - //if (curEntry.mIsSkylight) + //if (curEntry.mCubemap.isNull() || curEntry.mIrradianceCubemap.isNull()) // continue; - mMipCount = curEntry.mCubemap.getPointer()->getMipMapLevels(); + //if (!curEntry.mCubemap->isInitialized()) + // continue; + + //if (!curEntry.mIrradianceCubemap->isInitialized()) + // continue; //Setup Point3F probePos = curEntry.getPosition(); @@ -354,22 +446,40 @@ void RenderProbeMgr::_setupStaticParameters() curEntry.mAtten, 1); - cubeMaps.push_back(curEntry.mCubemap); - irradMaps.push_back(curEntry.mIrradianceCubemap); + //cubeMaps.push_back(curEntry.mCubemap); + //irradMaps.push_back(curEntry.mIrradianceCubemap); mEffectiveProbeCount++; } if (mEffectiveProbeCount != 0) { - mCubemapArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); - mIrradArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); + //mPrefilterArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); + //mIrradianceArray = GFXCubemapArrayHandle(GFX->createCubemapArray()); - mCubemapArray->initStatic(cubeMaps.address(), cubeMaps.size()); - mIrradArray->initStatic(irradMaps.address(), irradMaps.size()); + //mPrefilterArray->initStatic(cubeMaps.address(), cubeMaps.size()); + //mIrradianceArray->initStatic(irradMaps.address(), irradMaps.size()); } } +void RenderProbeMgr::updateProbeTexture(ProbeRenderInst* probe) +{ + S32 probeIdx = ProbeRenderInst::all.find_next(probe); + + if (probeIdx != -1) //i mean, the opposite shouldn't even be possible + updateProbeTexture(probeIdx); +} + +void RenderProbeMgr::updateProbeTexture(U32 probeIdx) +{ + if (probeIdx >= ProbeRenderInst::all.size()) + return; + + const U32 cubeIndex = ProbeRenderInst::all[probeIdx]->mCubemapIndex; + mIrradianceArray->updateTexture(ProbeRenderInst::all[probeIdx]->mIrradianceCubemap, cubeIndex); + mPrefilterArray->updateTexture(ProbeRenderInst::all[probeIdx]->mPrefilterCubemap, cubeIndex); +} + void RenderProbeMgr::_setupPerFrameParameters(const SceneRenderState *state) { PROFILE_SCOPE(RenderProbeMgr_SetupPerFrameParameters); @@ -607,11 +717,11 @@ void RenderProbeMgr::render( SceneRenderState *state ) //updateProbes(); // Early out if nothing to draw. - if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || mEffectiveProbeCount == 0 - || !state->isDiffusePass() || cubeMaps.empty() || irradMaps.empty()) + if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (mEffectiveProbeCount == 0 + || mCubeMapCount != 0 && !hasSkylight)) { - getProbeArrayEffect()->setSkip(true); - return; + getProbeArrayEffect()->setSkip(true); + return; } GFXTransformSaver saver; @@ -625,10 +735,18 @@ void RenderProbeMgr::render( SceneRenderState *state ) //Array rendering //U32 probeCount = ProbeRenderInst::all.size(); + mProbeArrayEffect->setShaderConst("$hasSkylight", (float)hasSkylight); + if (hasSkylight) + { + mProbeArrayEffect->setCubemapTexture(6, skylightPrefilterMap); + mProbeArrayEffect->setCubemapTexture(7, skylightIrradMap); + + } + if (mEffectiveProbeCount != 0) { - mProbeArrayEffect->setCubemapArrayTexture(4, mCubemapArray); - mProbeArrayEffect->setCubemapArrayTexture(5, mIrradArray); + mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray); + mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray); String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0"); mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten); diff --git a/Engine/source/renderInstance/renderProbeMgr.h b/Engine/source/renderInstance/renderProbeMgr.h index a482f37be..2d796d4e4 100644 --- a/Engine/source/renderInstance/renderProbeMgr.h +++ b/Engine/source/renderInstance/renderProbeMgr.h @@ -72,7 +72,7 @@ struct ProbeRenderInst : public SystemInterface Point3F mProbeRefScale; F32 mAtten; - GFXCubemapHandle mCubemap; + GFXCubemapHandle mPrefilterCubemap; GFXCubemapHandle mIrradianceCubemap; //Utilized in dynamic reflections @@ -97,6 +97,8 @@ struct ProbeRenderInst : public SystemInterface ProbeShapeType mProbeShapeType; + U32 mCubemapIndex; + public: ProbeRenderInst(); @@ -161,6 +163,18 @@ class RenderProbeMgr : public RenderBinManager Vector mRegisteredProbes; + //maximum number of allowed probes + static const U32 PROBE_MAX_COUNT = 250; + //maximum number of rendered probes per frame adjust as needed + static const U32 PROBE_MAX_FRAME = 8; + //number of slots to allocate at once in the cubemap array + static const U32 PROBE_ARRAY_SLOT_BUFFER_SIZE = 10; + + static const U32 PROBE_IRRAD_SIZE = 32; + static const U32 PROBE_PREFILTER_SIZE = 128; + static const GFXFormat PROBE_FORMAT = GFXFormatR8G8B8A8;// when hdr fixed GFXFormatR16G16B16A16F; look into bc6h compression + static const U32 INVALID_CUBE_SLOT = U32_MAX; + //Array rendering U32 mEffectiveProbeCount; S32 mMipCount; @@ -173,6 +187,11 @@ class RenderProbeMgr : public RenderBinManager Vector cubeMaps; Vector irradMaps; + bool hasSkylight; + GFXCubemapHandle skylightIrradMap; + GFXCubemapHandle skylightPrefilterMap; + Point4F skylightPos; + AlignedArray mProbePositions; AlignedArray mProbeBBMin; AlignedArray mProbeBBMax; @@ -180,8 +199,15 @@ class RenderProbeMgr : public RenderBinManager AlignedArray mProbeRadius; AlignedArray mProbeAttenuation; - GFXCubemapArrayHandle mCubemapArray; - GFXCubemapArrayHandle mIrradArray; + //number of cubemaps + U32 mCubeMapCount; + //number of cubemap slots allocated + U32 mCubeSlotCount; + //array of cubemap slots, due to the editor these may be mixed around as probes are added and deleted + bool mCubeMapSlots[PROBE_MAX_COUNT]; + + GFXCubemapArrayHandle mPrefilterArray; + GFXCubemapArrayHandle mIrradianceArray; //Utilized in forward rendering ProbeConstantMap mConstantLookup; @@ -191,10 +217,14 @@ class RenderProbeMgr : public RenderBinManager // SimObjectPtr mProbeArrayEffect; + //Default skylight, used for shape editors, etc + ProbeRenderInst* mDefaultSkyLight; + public: RenderProbeMgr(); RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder); virtual ~RenderProbeMgr(); + virtual bool onAdd(); virtual void onRemove(); // ConsoleObject @@ -219,6 +249,7 @@ protected: GFXShaderConstBuffer *shaderConsts); void _setupStaticParameters(); + void updateProbeTexture(U32 probeIdx); void _setupPerFrameParameters(const SceneRenderState *state); virtual void addElement(RenderInst *inst); virtual void render(SceneRenderState * state); @@ -230,6 +261,7 @@ protected: public: // RenderBinMgr void updateProbes(); + void updateProbeTexture(ProbeRenderInst* probe); /// Returns the active LM. static inline RenderProbeMgr* getProbeManager(); @@ -249,6 +281,16 @@ public: void bakeProbe(ReflectionProbe *probeInfo); void bakeProbes(); + + U32 _findNextEmptyCubeSlot() + { + for (U32 i = 0; i < PROBE_MAX_COUNT; i++) + { + if (!mCubeMapSlots[i]) + return i; + } + return INVALID_CUBE_SLOT; + } }; RenderProbeMgr* RenderProbeMgr::getProbeManager() diff --git a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl index 600544501..9cee66918 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl @@ -29,9 +29,13 @@ uniform float4 bbMaxArray[MAX_PROBES]; uniform float4 probeConfigData[MAX_PROBES]; //r,g,b/mode,radius,atten #if DEBUGVIZ_CONTRIB -uniform float4 probeContribColors[MAX_PROBES]; +uniform float4 probeContribColors[MAX_PROBES]; #endif +TORQUE_UNIFORM_SAMPLERCUBE(skylightPrefilterMap, 6); +TORQUE_UNIFORM_SAMPLERCUBE(skylightIrradMap, 7); +uniform float hasSkylight; + //Probe IBL stuff struct ProbeData { @@ -64,17 +68,18 @@ float defineSphereSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeR float getDistBoxToPoint(float3 pt, float3 extents) { - float3 d = max(max(-extents - pt, 0), pt - extents); - return max(max(d.x,d.y),d.z); + float3 d = max(max(-extents - pt, 0), pt - extents); + return max(max(d.x, d.y), d.z); } float defineBoxSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay) { float3 surfPosLS = mul(probe.worldToLocal, float4(surface.P, 1.0)).xyz; - float atten = probe.attenuation; + float probeattenuationvalue = 0.5; // feed meh + float atten = 1.0 - probeattenuationvalue; float baseVal = 0.25; - float dist = getDistBoxToPoint(surfPosLS,float3(baseVal,baseVal,baseVal)); - return saturate(smoothstep(baseVal+0.0001,atten*baseVal,dist)); + float dist = getDistBoxToPoint(surfPosLS, float3(baseVal, baseVal, baseVal)); + return saturate(smoothstep(baseVal + 0.0001, atten*baseVal, dist)); } // Box Projected IBL Lighting @@ -82,12 +87,12 @@ float defineBoxSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay) // and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ float3 boxProject(Surface surface, ProbeData probe) { - float3 RayLS = mul(probe.worldToLocal, float4(surface.R,0.0)).xyz; - float3 PositionLS = mul( probe.worldToLocal, float4(surface.P,1.0)).xyz; - - float3 unit = probe.boxMax-probe.boxMin; - float3 plane1vec = (unit/2 - PositionLS) / RayLS; - float3 plane2vec = (-unit/2 - PositionLS) / RayLS; + float3 RayLS = mul(probe.worldToLocal, float4(surface.R, 0.0)).xyz; + float3 PositionLS = mul(probe.worldToLocal, float4(surface.P, 1.0)).xyz; + + float3 unit = probe.boxMax - probe.boxMin; + float3 plane1vec = (unit / 2 - PositionLS) / RayLS; + float3 plane2vec = (-unit / 2 - PositionLS) / RayLS; float3 furthestPlane = max(plane1vec, plane2vec); float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z); float3 posonbox = surface.P + surface.R * dist; @@ -99,8 +104,7 @@ float3 iblBoxDiffuse(Surface surface, ProbeData probe) { float3 dir = boxProject(surface, probe); - float lod = surface.roughness*cubeMips; - float3 color = TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, dir, probe.probeIdx, lod).xyz; + float3 color = TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, dir, probe.probeIdx, 0).xyz; if (probe.contribution>0) return color*probe.contribution; else @@ -154,129 +158,163 @@ float3 iblSkylightSpecular(Surface surface, ProbeData probe) return color; } -float4 main( PFXVertToPix IN ) : SV_TARGET +float4 main(PFXVertToPix IN) : SV_TARGET { //unpack normal and linear depth float4 normDepth = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, IN.uv0.xy); //create surface - Surface surface = createSurface( normDepth, TORQUE_SAMPLER2D_MAKEARG(colorBuffer),TORQUE_SAMPLER2D_MAKEARG(matInfoBuffer), - IN.uv0.xy, eyePosWorld, IN.wsEyeRay, cameraToWorld); + Surface surface = createSurface(normDepth, TORQUE_SAMPLER2D_MAKEARG(colorBuffer),TORQUE_SAMPLER2D_MAKEARG(matInfoBuffer), + IN.uv0.xy, eyePosWorld, IN.wsEyeRay, cameraToWorld); //early out if emissive if (getFlag(surface.matFlag, 0)) - { + { discard; - } + } + + float alpha = 1; int i = 0; float blendFactor[MAX_PROBES]; float blendSum = 0; float blendFacSum = 0; float invBlendSum = 0; - int skyID = 0; float probehits = 0; //Set up our struct data ProbeData probes[MAX_PROBES]; - //Process prooooobes - for (i = 0; i < numProbes; ++i) + if (alpha > 0) { - probes[i].wsPosition = inProbePosArray[i].xyz; - probes[i].radius = probeConfigData[i].g; - probes[i].boxMin = bbMinArray[i].xyz; - probes[i].boxMax = bbMaxArray[i].xyz; - probes[i].refPosition = inRefPosArray[i].xyz; - probes[i].attenuation = probeConfigData[i].b; - probes[i].worldToLocal = worldToObjArray[i]; - probes[i].probeIdx = i; - probes[i].type = probeConfigData[i].r; - probes[i].contribution = 0; - - if (probes[i].type == 0) //box - { - probes[i].contribution = defineBoxSpaceInfluence(surface, probes[i], IN.wsEyeRay); - probehits++; - } - else if (probes[i].type == 1) //sphere - { - probes[i].contribution = defineSphereSpaceInfluence(surface, probes[i], IN.wsEyeRay); - probehits++; - } - else //skylight - { - // - //probes[i].contribution = defineSkylightInfluence(surface, probes[i], IN.wsEyeRay); - skyID = i; - } - - if (probes[i].contribution>1 || probes[i].contribution<0) - probes[i].contribution = 0; - - blendSum += probes[i].contribution; - invBlendSum += (1.0f - probes[i].contribution); - } - - // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary. - // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). - // respect constraint B. - // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary - // and respect constraint A. - for (i = 0; i < numProbes; i++) - { - if (probehits>1.0) - { - blendFactor[i] = ((probes[i].contribution / blendSum)) / (probehits - 1); - blendFactor[i] *= ((probes[i].contribution) / invBlendSum); - blendFacSum += blendFactor[i]; - } - else - { - blendFactor[i] = probes[i].contribution; - blendFacSum = probes[i].contribution; - } - } - - // Normalize blendVal -#if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior - if (blendFacSum == 0.0f) // Possible with custom weight - { - blendFacSum = 1.0f; - } -#endif - //use probehits for sharp cuts when singular, - //blendSum when wanting blend on all edging - if (blendSum>1.0) - { - float invBlendSumWeighted = 1.0f / blendFacSum; + //Process prooooobes for (i = 0; i < numProbes; ++i) { - blendFactor[i] *= invBlendSumWeighted; - probes[i].contribution = blendFactor[i]; + probes[i].wsPosition = inProbePosArray[i].xyz; + probes[i].radius = probeConfigData[i].g; + probes[i].boxMin = bbMinArray[i].xyz; + probes[i].boxMax = bbMaxArray[i].xyz; + probes[i].refPosition = inRefPosArray[i].xyz; + probes[i].attenuation = probeConfigData[i].b; + probes[i].worldToLocal = worldToObjArray[i]; + probes[i].probeIdx = i; + probes[i].type = probeConfigData[i].r; + probes[i].contribution = 0; + + if (probes[i].type == 0) //box + { + probes[i].contribution = defineBoxSpaceInfluence(surface, probes[i], IN.wsEyeRay); + probehits++; + } + else if (probes[i].type == 1) //sphere + { + probes[i].contribution = defineSphereSpaceInfluence(surface, probes[i], IN.wsEyeRay); + probehits++; + } + + if (probes[i].contribution>1 || probes[i].contribution<0) + probes[i].contribution = 0; + + blendSum += probes[i].contribution; + invBlendSum += (1.0f - probes[i].contribution); + + alpha -= probes[i].contribution; } - } + + // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary. + // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). + // respect constraint B. + // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary + // and respect constraint A. + for (i = 0; i < numProbes; i++) + { + if (probehits>1.0) + { + blendFactor[i] = ((probes[i].contribution / blendSum)) / (probehits - 1); + blendFactor[i] *= ((probes[i].contribution) / invBlendSum); + blendFacSum += blendFactor[i]; + } + else + { + blendFactor[i] = probes[i].contribution; + blendFacSum = probes[i].contribution; + } + } + + + // Normalize blendVal +#if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior + if (blendFacSum == 0.0f) // Possible with custom weight + { + blendFacSum = 1.0f; + } +#endif + + //use probehits for sharp cuts when singular, + //blendSum when wanting blend on all edging + if (blendSum>1.0) + { + float invBlendSumWeighted = 1.0f / blendFacSum; + for (i = 0; i < numProbes; ++i) + { + blendFactor[i] *= invBlendSumWeighted; + probes[i].contribution = blendFactor[i]; + + alpha -= probes[i].contribution; + } + } + #if DEBUGVIZ_ATTENUATION == 1 - float attenVis = 0; - for (i = 0; i < numProbes; ++i) - { + /*float attenVis = 0; + for (i = 0; i < numProbes; ++i) + { attenVis += probes[i].contribution; - } - return float4(attenVis, attenVis, attenVis, 1); + } + return float4(attenVis, attenVis, attenVis, 1);*/ + return float4(alpha, alpha, alpha, 1); #endif #if DEBUGVIZ_CONTRIB == 1 - float3 finalContribColor = float3(0, 0, 0); - for (i = 0; i < numProbes; ++i) - { - if (probes[i].contribution == 0) - continue; + float3 finalContribColor = float3(0, 0, 0); + for (i = 0; i < numProbes; ++i) + { + if (probes[i].contribution == 0) + continue; - finalContribColor += probes[i].contribution * probeContribColors[i].rgb; + finalContribColor += probes[i].contribution * probeContribColors[i].rgb; + } + + return float4(finalContribColor, 1); +#endif } - return float4(finalContribColor, 1); + if (hasSkylight && alpha == 1) + { + float2 brdf = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.roughness, surface.NdotV, 0.0, 0.0)).xy; + float lod = surface.roughness*cubeMips; +#if DEBUGVIZ_SPECCUBEMAP == 0 && DEBUGVIZ_DIFFCUBEMAP == 0 + float3 specular = TORQUE_TEXCUBELOD(skylightPrefilterMap, float4(surface.R, lod)).xyz * (brdf.x + brdf.y); + float3 irradiance = TORQUE_TEXCUBELOD(skylightIrradMap, float4(surface.R, 0)).xyz; + + float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness); + + //energy conservation + float3 kD = 1.0.xxx - F; + kD *= 1.0 - surface.metalness; + + float3 diffuse = kD * irradiance * surface.baseColor.rgb; + float4 finalColor = float4(diffuse + specular * surface.ao, alpha); + return finalColor; +#elif DEBUGVIZ_SPECCUBEMAP == 1 && DEBUGVIZ_DIFFCUBEMAP == 0 + float3 specular = TORQUE_TEXCUBELOD(skylightPrefilterMap, float4(surface.R, lod)).xyz; + float4 finalColor = float4(specular, 1); + return finalColor; +#elif DEBUGVIZ_DIFFCUBEMAP == 1 + float3 irradiance = TORQUE_TEXCUBELOD(skylightIrradMap, float4(surface.R, 0)).xyz; + float4 finalColor = float4(irradiance, 1); + return finalColor; #endif + } #if DEBUGVIZ_SPECCUBEMAP == 0 && DEBUGVIZ_DIFFCUBEMAP == 0 @@ -292,17 +330,15 @@ float4 main( PFXVertToPix IN ) : SV_TARGET { if (probes[i].contribution == 0) continue; - + if (probes[i].type == 2) //skip skylight continue; - + irradiance += iblBoxDiffuse(surface, probes[i]); specular += F*iblBoxSpecular(surface, probes[i]); - contrib +=probes[i].contribution; + contrib += probes[i].contribution; } contrib = saturate(contrib); - irradiance = lerp(iblSkylightDiffuse(surface, probes[skyID]),irradiance,contrib); - specular = lerp(F*iblSkylightSpecular(surface, probes[skyID]),specular,contrib); //final diffuse color float3 diffuse = kD * irradiance * surface.baseColor.rgb; @@ -328,7 +364,7 @@ float4 main( PFXVertToPix IN ) : SV_TARGET return float4(cubeColor, 1); #elif DEBUGVIZ_DIFFCUBEMAP == 1 - + float3 cubeColor = float3(0, 0, 0); for (i = 0; i < numProbes; ++i) {