diff --git a/.gitignore b/.gitignore index 71ce0cece37..4ae3259c10d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ cmake-build-*/ /mingw/ /.cache/ /_skbuild/ - +/buildrelease/ # Meta data of macOS's Finder.app .DS_Store diff --git a/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp b/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp index 3542b3eef0f..8ca427f7397 100644 --- a/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp +++ b/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp @@ -97,6 +97,8 @@ bool CookTorranceLighting::createCpuSubPrograms(ProgramSet* programSet) // add the lighting computation auto mrparams = psMain->resolveLocalParameter(GCT_FLOAT2, "metalRoughness"); + auto ao = psMain->resolveLocalParameter(GCT_FLOAT1, "ao"); + fstage.assign(1.0f, ao); //Default to no AO. if(!mMetalRoughnessMapName.empty()) { auto metalRoughnessSampler = @@ -106,6 +108,7 @@ bool CookTorranceLighting::createCpuSubPrograms(ProgramSet* programSet) // This layout intentionally reserves the 'r' channel for (optional) occlusion map data fstage.sampleTexture(metalRoughnessSampler, psInTexcoord, mrSample); fstage.assign(In(mrSample).mask(Operand::OPM_YZ), mrparams); + fstage.assign(In(mrSample).x(), ao); } else { @@ -124,7 +127,7 @@ bool CookTorranceLighting::createCpuSubPrograms(ProgramSet* programSet) fstage.assign(Vector3(0), Out(outDiffuse).xyz()); fstage.mul(In(diffuse).w(), In(outDiffuse).w(), Out(outDiffuse).w()); // forward alpha - fstage.callFunction("PBR_MakeParams", {In(baseColor), In(mrparams), InOut(pixelParams)}); + fstage.callFunction("PBR_MakeParams", {In(baseColor), In(mrparams), In(ao), InOut(pixelParams)}); fstage = psMain->getStage(FFP_PS_PBR_LIGHTING_END); if(mLightCount > 0) diff --git a/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.cpp b/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.cpp index bc6b273a4fe..3ada804f9aa 100644 --- a/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.cpp +++ b/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.cpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: MIT #include "OgreShaderPrecompiledHeaders.h" +#include "OgreRenderTarget.h" #ifdef RTSHADER_SYSTEM_BUILD_CORE_SHADERS namespace Ogre @@ -24,7 +25,7 @@ int ImageBasedLighting::getExecutionOrder() const { return FFP_LIGHTING + 10; } bool ImageBasedLighting::setParameter(const String& name, const String& value) { - if (name == "texture" && !value.empty()) + if ((name == "texture" || name == "texture_diffuse") && !value.empty()) { mEnvMapName = value; return true; @@ -34,6 +35,27 @@ bool ImageBasedLighting::setParameter(const String& name, const String& value) mIsLuminanceParamDirty = true; return StringConverter::parse(value, mLuminance); } + else if (name == "texture_specular" && !value.empty()) + { + //If set, the shader will use seperate "diffuse irradiance" and "specular ggx" cubemaps for IBL. + mEnvMapNameSpecular = value; + return true; + } + + //Enables local probes. + else if (name == "local_probe_sh_atlas" && !value.empty()) + { + mIsLocalProbesEnabled = true; + mLocalProbeShAtlas = value; + return true; + } + + //Enables masking by vertex colour. Black = global , white = local. + else if (name == "vertex_colour_probe_mask") + { + mUseVertexColourProbeMask = StringConverter::parseBool(value); + return true; + } return false; } @@ -42,7 +64,12 @@ void ImageBasedLighting::copyFrom(const SubRenderState& rhs) { const ImageBasedLighting& rhsIBL = static_cast(rhs); mEnvMapName = rhsIBL.mEnvMapName; + mEnvMapNameSpecular = rhsIBL.mEnvMapNameSpecular; mLuminance = rhsIBL.mLuminance; + mIsLocalProbesEnabled = rhsIBL.mIsLocalProbesEnabled; + mLocalProbeShAtlas = rhsIBL.mLocalProbeShAtlas; + mLocalBlendWeight = rhsIBL.mLocalBlendWeight; + mUseVertexColourProbeMask = rhsIBL.mUseVertexColourProbeMask; } bool ImageBasedLighting::preAddToRenderState(const RenderState* renderState, Pass* srcPass, Pass* dstPass) @@ -66,6 +93,27 @@ bool ImageBasedLighting::preAddToRenderState(const RenderState* renderState, Pas tus->setHardwareGammaEnabled(true); mEnvMapSamplerIndex = dstPass->getNumTextureUnitStates() - 1; + // Check for dual cubemaps (diffuse irradiance plus specular convoluted). Add second texture if applicable. + if (!mEnvMapNameSpecular.empty()) + { + tus = dstPass->createTextureUnitState(); + tus->setTextureName(mEnvMapNameSpecular, TEX_TYPE_CUBE_MAP); + tus->setHardwareGammaEnabled(true); + mEnvMapSamplerIndexSpecular = dstPass->getNumTextureUnitStates() - 1; + } + + //Add local probe spherical harmionics coefficient atlas. + if (mIsLocalProbesEnabled) + { + tus = dstPass->createTextureUnitState(); + tus->setTextureName(mLocalProbeShAtlas, TEX_TYPE_2D); + tus->setHardwareGammaEnabled(false); + tus->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + tus->setTextureFiltering(TFO_NONE); + tus->setNumMipmaps(0); + mLocalProbeShAtlasSamplerIndex = dstPass->getNumTextureUnitStates() - 1; + } + return true; } @@ -79,8 +127,23 @@ bool ImageBasedLighting::createCpuSubPrograms(ProgramSet* programSet) auto vsOutViewPos = vsMain->resolveOutputParameter(Parameter::SPC_POSITION_VIEW_SPACE); auto viewPos = psMain->resolveInputParameter(vsOutViewPos); + //World position for light probes + auto vsInObjectPos = vsMain->resolveInputParameter(Parameter::SPC_POSITION_OBJECT_SPACE); + + auto worldMat = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLD_MATRIX); + + auto vsOutWorldPos = vsMain->resolveOutputParameter(Parameter::SPS_TEXTURE_COORDINATES, 7, + Parameter::SPC_POSITION_WORLD_SPACE, GCT_FLOAT3); + + auto worldPos = psMain->resolveInputParameter(vsOutWorldPos); + + auto stage = vsMain->getStage(FFP_VS_TRANSFORM + 1); + + stage.callFunction(FFP_FUNC_TRANSFORM, worldMat, vsInObjectPos, vsOutWorldPos); + auto pixel = psMain->getLocalParameter("pixel"); mLuminanceParam = psProgram->resolveParameter(GCT_FLOAT1, "luminance"); + mBlendWeightParam = NULL; if (!pixel) { @@ -101,16 +164,169 @@ bool ImageBasedLighting::createCpuSubPrograms(ProgramSet* programSet) auto dfgLUTSampler = psProgram->resolveParameter(GCT_SAMPLER2D, "dfgLUTSampler", mDfgLUTSamplerIndex); auto iblEnvSampler = psProgram->resolveParameter(GCT_SAMPLERCUBE, "iblEnvSampler", mEnvMapSamplerIndex); + Ogre::RTShader::UniformParameterPtr iblEnvSpecularSampler = NULL; + Ogre::RTShader::UniformParameterPtr iblEnvSize = NULL; + + // Add secondary cubemap if applicable. + if (mEnvMapNameSpecular != "") + { + psProgram->addPreprocessorDefines("HAS_DUAL_TEXTURE"); + iblEnvSpecularSampler = psProgram->resolveParameter(GCT_SAMPLERCUBE, "iblEnvSpecularSampler", mEnvMapSamplerIndexSpecular); + iblEnvSize = psProgram->resolveParameter(GpuProgramParameters::ACT_TEXTURE_SIZE, mEnvMapSamplerIndexSpecular); + } + else + { + iblEnvSize = psProgram->resolveParameter(GpuProgramParameters::ACT_TEXTURE_SIZE, mEnvMapSamplerIndex); + } + + //Add required local probe parameters and texture, if local probes are enabled. + ParameterPtr psInputVertexColour; + ParameterPtr probeMaskParam; + + if (mIsLocalProbesEnabled && mUseVertexColourProbeMask) + { + // Feed through vertex colours as they are used for probe masking. + // Black = exterior/global, white = interior/local. + ParameterPtr vsInputVertexColour = vsMain->resolveInputParameter(Parameter::SPC_COLOR_DIFFUSE); + + ParameterPtr vsOutputVertexColour = vsMain->resolveOutputParameter(Parameter::SPC_COLOR_DIFFUSE); + + psInputVertexColour = psMain->resolveInputParameter(vsOutputVertexColour); + + auto vsColourStage = vsMain->getStage(FFP_VS_COLOUR); + vsColourStage.assign(vsInputVertexColour, vsOutputVertexColour); + + psProgram->addPreprocessorDefines("HAS_VERTEX_COLOUR_PROBE_MASK"); + } + + if (mIsLocalProbesEnabled) + { + + //Define has local probes + psProgram->addPreprocessorDefines("HAS_LOCAL_PROBE"); + + //SH Coefficients texture atlas. Row = probe ID. 9 px wide for 9 SH Coeffs. + mLocalProbeShAtlasSampler = psProgram->resolveParameter(GCT_SAMPLER2D, "iblLocalShAtlasSampler", mLocalProbeShAtlasSamplerIndex); + + mShAtlasSizeParam = psProgram->resolveParameter(GpuProgramParameters::ACT_TEXTURE_SIZE, mLocalProbeShAtlasSamplerIndex); + + //Local probe 1 + mLocalProbeParams1_1 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_1_1"); //x, y, z = position. w = probe ID for SH atlas reference. + mLocalProbeParams1_2 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_1_2"); //x, y, z = box extent. w = blend distance. + mLocalProbeParams1_3 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_1_3"); //Quaternoin for orientation of probe + + // Local probe 2 + mLocalProbeParams2_1 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_2_1"); + mLocalProbeParams2_2 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_2_2"); + mLocalProbeParams2_3 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_2_3"); + + // Local probe 3 + mLocalProbeParams3_1 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_3_1"); + mLocalProbeParams3_2 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_3_2"); + mLocalProbeParams3_3 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_3_3"); + + // Local probe 4 + mLocalProbeParams4_1 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_4_1"); + mLocalProbeParams4_2 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_4_2"); + mLocalProbeParams4_3 = psProgram->resolveParameter(GCT_FLOAT4, "IBL_LocalProbe_4_3"); + } - auto iblEnvSize = psProgram->resolveParameter(GpuProgramParameters::ACT_TEXTURE_SIZE, mEnvMapSamplerIndex); auto invViewMat = psProgram->resolveParameter(GpuProgramParameters::ACT_INVERSE_VIEW_MATRIX); auto fstage = psMain->getStage(FFP_PS_PBR_LIGHTING_BEGIN + 5); - fstage.callFunction("evaluateIBL", - {InOut(pixel), In(viewNormal), In(viewPos), In(invViewMat), In(dfgLUTSampler), In(iblEnvSampler), - In(iblEnvSize).w(), In(mLuminanceParam), InOut(outDiffuse).xyz()}); - + //Add second texture if set. + if (mIsLocalProbesEnabled) + { + if (mUseVertexColourProbeMask) + { + fstage.callFunction("evaluateIBL", {InOut(pixel), + In(viewNormal), + In(viewPos), + In(worldPos), + In(invViewMat), + In(dfgLUTSampler), + In(iblEnvSampler), + In(iblEnvSpecularSampler), + In(iblEnvSize).w(), + In(mLuminanceParam), + + In(mLocalProbeShAtlasSampler), + In(mShAtlasSizeParam).xy(), + + In(mLocalProbeParams1_1), + In(mLocalProbeParams1_2), + In(mLocalProbeParams1_3), + + In(mLocalProbeParams2_1), + In(mLocalProbeParams2_2), + In(mLocalProbeParams2_3), + + In(mLocalProbeParams3_1), + In(mLocalProbeParams3_2), + In(mLocalProbeParams3_3), + + In(mLocalProbeParams4_1), + In(mLocalProbeParams4_2), + In(mLocalProbeParams4_3), + + In(psInputVertexColour).x(), // NEW + + InOut(outDiffuse).xyz()}); + } + else + { + fstage.callFunction("evaluateIBL", {InOut(pixel), + In(viewNormal), + In(viewPos), + In(worldPos), + In(invViewMat), + In(dfgLUTSampler), + In(iblEnvSampler), + In(iblEnvSpecularSampler), + In(iblEnvSize).w(), + In(mLuminanceParam), + + In(mLocalProbeShAtlasSampler), + In(mShAtlasSizeParam).xy(), + + In(mLocalProbeParams1_1), + In(mLocalProbeParams1_2), + In(mLocalProbeParams1_3), + + In(mLocalProbeParams2_1), + In(mLocalProbeParams2_2), + In(mLocalProbeParams2_3), + + In(mLocalProbeParams3_1), + In(mLocalProbeParams3_2), + In(mLocalProbeParams3_3), + + In(mLocalProbeParams4_1), + In(mLocalProbeParams4_2), + In(mLocalProbeParams4_3), + + InOut(outDiffuse).xyz()}); + } + } + else + { + //No local probes + if (mEnvMapNameSpecular == "") + { + //No local, no specular - old behaviour + fstage.callFunction("evaluateIBL", + {InOut(pixel), In(viewNormal), In(viewPos), In(invViewMat), In(dfgLUTSampler), + In(iblEnvSampler), In(iblEnvSize).w(), In(mLuminanceParam), InOut(outDiffuse).xyz()}); + } + else + { + //No local, specular edition. + fstage.callFunction("evaluateIBL", {InOut(pixel), In(viewNormal), In(viewPos), In(invViewMat), + In(dfgLUTSampler), In(iblEnvSampler), In(iblEnvSpecularSampler), + In(iblEnvSize).w(), In(mLuminanceParam), InOut(outDiffuse).xyz()}); + } + } return true; } @@ -121,6 +337,100 @@ void ImageBasedLighting::updateGpuProgramsParams(Renderable* rend, const Pass* p mLuminanceParam->setGpuParameter(mLuminance); mIsLuminanceParamDirty = false; } + + //Update local probe data if enabled + if (mIsLocalProbesEnabled && rend) + { + Vector4 probeData; + + //Local probe 1 + probeData = Vector4(0, 0, 0, -1); // Default to -1/no probe. + if (rend->hasCustomParameter(0)) + { + probeData = rend->getCustomParameter(0); + } + mLocalProbeParams1_1->setGpuParameter(probeData); + + probeData = Vector4(1, 1, 1, 0); + if (rend->hasCustomParameter(1)) + { + probeData = rend->getCustomParameter(1); + } + mLocalProbeParams1_2->setGpuParameter(probeData); + + probeData = Vector4(0, 0, 0, 1); + if (rend->hasCustomParameter(2)) + { + probeData = rend->getCustomParameter(2); + } + mLocalProbeParams1_3->setGpuParameter(probeData); + + // Local probe 2 + probeData = Vector4(0, 0, 0, -1); // Default to -1/no probe. + if (rend->hasCustomParameter(3)) + { + probeData = rend->getCustomParameter(3); + } + mLocalProbeParams2_1->setGpuParameter(probeData); + + probeData = Vector4(1, 1, 1, 0); + if (rend->hasCustomParameter(4)) + { + probeData = rend->getCustomParameter(4); + } + mLocalProbeParams2_2->setGpuParameter(probeData); + + probeData = Vector4(0, 0, 0, 1); + if (rend->hasCustomParameter(5)) + { + probeData = rend->getCustomParameter(5); + } + mLocalProbeParams2_3->setGpuParameter(probeData); + + // Local probe 3 + probeData = Vector4(0, 0, 0, -1); // Default to -1/no probe. + if (rend->hasCustomParameter(6)) + { + probeData = rend->getCustomParameter(6); + } + mLocalProbeParams3_1->setGpuParameter(probeData); + + probeData = Vector4(1, 1, 1, 0); + if (rend->hasCustomParameter(7)) + { + probeData = rend->getCustomParameter(7); + } + mLocalProbeParams3_2->setGpuParameter(probeData); + + probeData = Vector4(0, 0, 0, 1); + if (rend->hasCustomParameter(8)) + { + probeData = rend->getCustomParameter(8); + } + mLocalProbeParams3_3->setGpuParameter(probeData); + + // Local probe 4 + probeData = Vector4(0, 0, 0, -1); // Default to -1/no probe. + if (rend->hasCustomParameter(9)) + { + probeData = rend->getCustomParameter(9); + } + mLocalProbeParams4_1->setGpuParameter(probeData); + + probeData = Vector4(1, 1, 1, 0); + if (rend->hasCustomParameter(10)) + { + probeData = rend->getCustomParameter(10); + } + mLocalProbeParams4_2->setGpuParameter(probeData); + + probeData = Vector4(0, 0, 0, 1); + if (rend->hasCustomParameter(11)) + { + probeData = rend->getCustomParameter(11); + } + mLocalProbeParams4_3->setGpuParameter(probeData); + } } @@ -134,13 +444,13 @@ SubRenderState* ImageBasedLightingFactory::createInstance(const ScriptProperty& if (prop.name != "image_based_lighting" || prop.values.size() < 2) return NULL; - if(prop.values[0] != "texture") + if (prop.values[0] != "texture" && prop.values[0] != "texture_diffuse") { translator->emitError(); return NULL; } auto ret = static_cast(createOrRetrieveInstance(translator)); - ret->setParameter("texture", prop.values[1]); + ret->setParameter(prop.values[0], prop.values[1]); if (prop.values.size() < 4) return ret; @@ -153,6 +463,40 @@ SubRenderState* ImageBasedLightingFactory::createInstance(const ScriptProperty& ret->setParameter("luminance", prop.values[3]); + if (prop.values.size() < 6) + return ret; + + //If this is set "texture" is then used as a diffuse texture only + if (prop.values[4] != "texture_specular") + { + translator->emitError(); + return NULL; + } + + ret->setParameter("texture_specular", prop.values[5]); + + if (prop.values.size() < 8) + return ret; + + //If set then enable local probes and use value for sh coefficients. + if (prop.values[6] != "local_probe_sh_atlas") + { + translator->emitError(); + return NULL; + } + ret->setParameter("local_probe_sh_atlas", prop.values[7]); + + if (prop.values.size() < 10) + return ret; + + if (prop.values[8] != "vertex_colour_probe_mask") + { + translator->emitError(); + return NULL; + } + + ret->setParameter("vertex_colour_probe_mask", prop.values[9]); + return ret; } @@ -174,4 +518,4 @@ SubRenderState* ImageBasedLightingFactory::createInstanceImpl() { return OGRE_NE } // namespace RTShader } // namespace Ogre -#endif +#endif \ No newline at end of file diff --git a/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.h b/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.h index c9b38775d64..e817373d3de 100644 --- a/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.h +++ b/Components/RTShaderSystem/src/OgreShaderImageBasedLighting.h @@ -30,10 +30,42 @@ class ImageBasedLighting : public SubRenderState friend class ImageBasedLightingFactory; int mDfgLUTSamplerIndex = 0; int mEnvMapSamplerIndex = 0; + int mEnvMapSamplerIndexSpecular = 0; + int mLocalProbeShAtlasSamplerIndex = 0; float mLuminance = 1.0f; + float mLocalBlendWeight = 0.5f; String mEnvMapName; + String mEnvMapNameSpecular; + String mLocalProbeShAtlas; + UniformParameterPtr mLuminanceParam; + UniformParameterPtr mBlendWeightParam; + + UniformParameterPtr mLocalProbeShAtlasSampler; //Stores all probe SH coefficients. Row = probe ID. Colums = SH coefficients. + UniformParameterPtr mShAtlasSizeParam; + + UniformParameterPtr mLocalProbeParams1_1; //Stores probe position (x, y, z), plus probe ID for SH atlas reference (w). + UniformParameterPtr mLocalProbeParams1_2; //Stores probe box extents (x, y, z), plus probe blend distance (w). + UniformParameterPtr mLocalProbeParams1_3; //Quaternoin for orientation of probe + + UniformParameterPtr mLocalProbeParams2_1; + UniformParameterPtr mLocalProbeParams2_2; + UniformParameterPtr mLocalProbeParams2_3; + + UniformParameterPtr mLocalProbeParams3_1; + UniformParameterPtr mLocalProbeParams3_2; + UniformParameterPtr mLocalProbeParams3_3; + + UniformParameterPtr mLocalProbeParams4_1; + UniformParameterPtr mLocalProbeParams4_2; + UniformParameterPtr mLocalProbeParams4_3; + + UniformParameterPtr mProbeMaskParam; + bool mIsLuminanceParamDirty = true; + bool mUseVertexColourProbeMask = false; + bool mIsLocalProbesEnabled = false; + public: const String& getType() const override; int getExecutionOrder() const override; @@ -65,4 +97,4 @@ class ImageBasedLightingFactory : public SubRenderStateFactory } // namespace Ogre #endif -#endif +#endif \ No newline at end of file diff --git a/Media/RTShaderLib/RTSLib_IBL.glsl b/Media/RTShaderLib/RTSLib_IBL.glsl index 927f53e10ae..c8ec5354cc9 100644 --- a/Media/RTShaderLib/RTSLib_IBL.glsl +++ b/Media/RTShaderLib/RTSLib_IBL.glsl @@ -2,6 +2,11 @@ // code adapted from Google Filament // SPDX-License-Identifier: Apache-2.0 +void IBL_ComputeWorldPos(in mat4 worldMat, in vec4 objectPos, out vec3 worldPos) +{ + worldPos = mul(worldMat, objectPos).xyz; +} + vec3 specularDFG(const PixelParams pixel) { return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0); } @@ -33,58 +38,274 @@ vec3 prefilteredRadiance(samplerCube light_iblSpecular, const vec3 r, float perc return decodeDataForIBL(textureCubeLod(light_iblSpecular, r, lod)); } +vec3 IrradianceMap(samplerCube iblDiffuse, const vec3 n) { + return decodeDataForIBL(textureCubeLod(iblDiffuse, n, 0.0)); +} + vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { return mix(r, n, roughness * roughness); } +float probeWeightBox(vec3 worldPos, vec3 probePos, vec3 extents, float blendDistWorld) +{ + vec3 delta = worldPos - probePos; + + vec3 ad; + ad.x = (delta.x < 0.0) ? -delta.x : delta.x; + ad.y = (delta.y < 0.0) ? -delta.y : delta.y; + ad.z = (delta.z < 0.0) ? -delta.z : delta.z; + + vec3 d = ad - extents; // <=0 inside + + vec3 dpos; + dpos.x = (d.x > 0.0) ? d.x : 0.0; + dpos.y = (d.y > 0.0) ? d.y : 0.0; + dpos.z = (d.z > 0.0) ? d.z : 0.0; + + float outsideDist = sqrt(dot(dpos, dpos)); + + float bd = (blendDistWorld > 0.0001) ? blendDistWorld : 0.0001; + + float w = 1.0 - smoothstep(0.0, bd, outsideDist); + + return clamp(w, 0.0, 1.0); +} + +vec3 evalSH9Diffuse(sampler2D shAtlasTex, vec2 shAtlasSize, float pid, vec3 n) +{ + vec2 invSize = 1.0 / shAtlasSize; + vec2 base = vec2(0.0, pid + 0.5) * invSize; + + vec3 sh0 = texture2DLod(shAtlasTex, (vec2(0.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh1 = texture2DLod(shAtlasTex, (vec2(1.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh2 = texture2DLod(shAtlasTex, (vec2(2.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh3 = texture2DLod(shAtlasTex, (vec2(3.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh4 = texture2DLod(shAtlasTex, (vec2(4.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh5 = texture2DLod(shAtlasTex, (vec2(5.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh6 = texture2DLod(shAtlasTex, (vec2(6.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh7 = texture2DLod(shAtlasTex, (vec2(7.5, pid + 0.5)) * invSize, 0.0).rgb; + vec3 sh8 = texture2DLod(shAtlasTex, (vec2(8.5, pid + 0.5)) * invSize, 0.0).rgb; + + float x = n.x, y = n.y, z = n.z; + + return + sh0 * 0.2820947918 + + sh1 * (0.4886025119 * y) + + sh2 * (0.4886025119 * z) + + sh3 * (0.4886025119 * x) + + sh4 * (1.0925484306 * x * y) + + sh5 * (1.0925484306 * y * z) + + sh6 * (0.3153915653 * (3.0 * z * z - 1.0)) + + sh7 * (1.0925484306 * x * z) + + sh8 * (0.5462742153 * (x * x - y * y)); +} + +#ifdef HAS_LOCAL_PROBE +float boxProbeWeight(f32vec3 localPos, + f32vec3 boxExtents, + float blendDistance) +{ + f32vec3 p = abs(localPos) / max(boxExtents, f32vec3(0.001f, 0.001f, 0.001f)); + + //0 at centre, 1 at box edge + float edgeAmount = max(p.x, max(p.y, p.z)); + + if (edgeAmount >= 1.0f) + return 0.0f; + + //Center weighted. This makes overlapping probes blend across the overlap, not just in a thin line at the edge. + float w = 1.0f - edgeAmount; + + return max(w * w, 0.001f); +} + +f32vec3 quatRotateInverse(f32vec4 q, f32vec3 v) +{ + f32vec3 t = 2.0f * cross(-q.xyz, v); + return v + q.w * t + cross(-q.xyz, t); +} + +//f32Vec3 used throughout - important for precision. +void accumulateLocalProbe( + sampler2D shAtlasTex, + vec2 shAtlasSize, + vec3 worldNormal, + f32vec3 worldPos, + f32vec4 probePosID, + f32vec4 probeExtentsBlend, + f32vec4 probeOrientation, + inout vec3 localIrr, + inout float totalW) +{ + float probeId = probePosID.w; + + if (probeId < 0.0f) + return; + + f32vec3 probePos = probePosID.xyz; + f32vec3 boxExtents = probeExtentsBlend.xyz; + float blendDistance = probeExtentsBlend.w; + + //Convert world position into probe-local space + f32vec3 probeDelta = worldPos - probePos; + f32vec3 probeLocalPos = quatRotateInverse(probeOrientation, probeDelta); + + //Box test in local probe space + float w = boxProbeWeight(probeLocalPos, boxExtents, blendDistance); + + if (w <= 0.0001f) + return; + + vec3 irr = evalSH9Diffuse(shAtlasTex, shAtlasSize, probeId, -worldNormal); + + localIrr += irr * w; + totalW += w; +} +#endif + void evaluateIBL(inout PixelParams pixel, in vec3 vNormal, in vec3 viewPos, + #ifdef HAS_LOCAL_PROBE + in f32vec3 worldPos, + #endif in mat4 invViewMat, in sampler2D dfgTex, in samplerCube iblEnvTex, + #ifdef HAS_DUAL_TEXTURE + in samplerCube iblEnvTexSpecular, + #endif in float iblRoughnessOneLevel, in float iblLuminance, + + #ifdef HAS_LOCAL_PROBE + in sampler2D shAtlasTex, + in vec2 shAtlasSize, + + //Local probe 1 data + in f32vec4 localProbePosID_1, + in f32vec4 localProbExtentsBlend_1, + in f32vec4 localProbeOrientation_1, + + in f32vec4 localProbePosID_2, + in f32vec4 localProbExtentsBlend_2, + in f32vec4 localProbeOrientation_2, + + in f32vec4 localProbePosID_3, + in f32vec4 localProbExtentsBlend_3, + in f32vec4 localProbeOrientation_3, + + in f32vec4 localProbePosID_4, + in f32vec4 localProbExtentsBlend_4, + in f32vec4 localProbeOrientation_4, + + #ifdef HAS_VERTEX_COLOUR_PROBE_MASK + in float vertexProbeMask, + #endif + #endif + inout vec3 color) { vec3 shading_normal = normalize(vNormal); vec3 shading_view = -normalize(viewPos); float shading_NoV = clampNoV(abs(dot(shading_normal, shading_view))); - // the above is currently duplicated with CookTorrance - vec3 shading_reflected = reflect(-shading_view, shading_normal); - // Pre-filtered DFG term used for image-based lighting + //Pre-filtered DFG term pixel.dfg = PrefilteredDFG_LUT(dfgTex, pixel.perceptualRoughness, shading_NoV); vec3 E = specularDFG(pixel); vec3 r = getSpecularDominantDirection(shading_normal, shading_reflected, pixel.roughness); - // OGRE specific: convert r and n back to world space for texture sampling + //Convert to world space r = normalize(mul(invViewMat, vec4(r, 0.0)).xyz); r.z *= -1.0; shading_normal = normalize(mul(invViewMat, vec4(shading_normal, 0.0)).xyz); - // specular layer - vec3 Fr = E * prefilteredRadiance(iblEnvTex, r, pixel.perceptualRoughness, iblRoughnessOneLevel); + //Global IBL + vec3 Fr_global; + vec3 diffuseIrr_global; - vec3 diffuseIrradiance = Irradiance_RoughnessOne(iblEnvTex, shading_normal, iblRoughnessOneLevel); - vec3 Fd = pixel.diffuseColor * diffuseIrradiance * (1.0 - E); + #ifdef HAS_DUAL_TEXTURE + Fr_global = E * prefilteredRadiance( + iblEnvTexSpecular, r, pixel.perceptualRoughness, iblRoughnessOneLevel); + diffuseIrr_global = IrradianceMap(iblEnvTex, shading_normal) * 0.5f; + #else + Fr_global = E * prefilteredRadiance( + iblEnvTex, r, pixel.perceptualRoughness, iblRoughnessOneLevel); + diffuseIrr_global = Irradiance_RoughnessOne( + iblEnvTex, shading_normal, iblRoughnessOneLevel); + #endif - Fr *= iblLuminance; - Fd *= iblLuminance; + vec3 Fd_global = pixel.diffuseColor * diffuseIrr_global * (1.0 - E) * pixel.ao; - // Combine all terms - // Note: iblLuminance is already premultiplied by the exposure -#ifndef USE_LINEAR_COLOURS - color = pow(color, vec3_splat(2.2)); // gamma to linear -#endif + Fr_global *= iblLuminance; + Fr_global *= mix(pixel.ao, 1.0, 0.7); + Fd_global *= iblLuminance; + + vec3 Fr = Fr_global; + vec3 Fd = Fd_global; + + #ifdef HAS_LOCAL_PROBE + { + vec3 localIrr = vec3_splat(0.0); + float totalW = 0.0; + + accumulateLocalProbe(shAtlasTex, shAtlasSize, shading_normal, worldPos, + localProbePosID_1, localProbExtentsBlend_1, localProbeOrientation_1, localIrr, totalW); + + accumulateLocalProbe(shAtlasTex, shAtlasSize, shading_normal, worldPos, + localProbePosID_2, localProbExtentsBlend_2, localProbeOrientation_2, localIrr, totalW); + + accumulateLocalProbe(shAtlasTex, shAtlasSize, shading_normal, worldPos, + localProbePosID_3, localProbExtentsBlend_3, localProbeOrientation_3, localIrr, totalW); + + accumulateLocalProbe(shAtlasTex, shAtlasSize, shading_normal, worldPos, + localProbePosID_4, localProbExtentsBlend_4, localProbeOrientation_4, localIrr, totalW); + + #ifdef HAS_VERTEX_COLOUR_PROBE_MASK + float localOnly = 1.0f - saturate(vertexProbeMask); // black = local + #else + float localOnly = 0.0f; + #endif + + if (totalW > 0.0001f) + { + //This is the actual probe-to-probe blend. It normalises all overlapping local probes. + localIrr /= totalW; + + vec3 Fd_local = + pixel.diffuseColor * localIrr * (1.0f - E) * iblLuminance * pixel.ao; + + #ifdef HAS_VERTEX_COLOUR_PROBE_MASK + if (localOnly > 0.5f) + { + //Interior/local-only surface - no global contribution while any local probe exists. + Fd = Fd_local; + } + else + { + //Outdoor/mixed surface - normal local vs global blending + float localBlend = saturate(totalW); + Fd = mix(Fd_global, Fd_local, localBlend); + } + #else + float localBlend = saturate(totalW); + Fd = mix(Fd_global, Fd_local, localBlend); + #endif + } + } + #endif + + #ifndef USE_LINEAR_COLOURS + color = pow(color, vec3_splat(2.2)); + #endif color += Fr + Fd; -#ifndef USE_LINEAR_COLOURS - color = pow(color, vec3_splat(1.0/2.2)); // linear to gamma - color = saturate(color); -#endif + #ifndef USE_LINEAR_COLOURS + color = pow(color, vec3_splat(1.0 / 2.2)); + color = saturate(color); + #endif } \ No newline at end of file diff --git a/Media/RTShaderLib/SGXLib_CookTorrance.glsl b/Media/RTShaderLib/SGXLib_CookTorrance.glsl index bdede2a5606..3a9f9dd2a1b 100644 --- a/Media/RTShaderLib/SGXLib_CookTorrance.glsl +++ b/Media/RTShaderLib/SGXLib_CookTorrance.glsl @@ -29,6 +29,7 @@ struct PixelParams vec3 f0; vec3 dfg; vec3 energyCompensation; + float ao; }; float clampNoV(float NoV) { @@ -65,6 +66,14 @@ float perceptualRoughnessToRoughness(float perceptualRoughness) { return perceptualRoughness * perceptualRoughness; } +// https://github.com/google/filament/blob/7701e7a65afb14c081d2aebbb426c646cc6cb2b9/shaders/src/surface_lighting.fs +float computeMicroShadowing(float NoL, float visibility) { + // Chan 2018, "Material Advances in Call of Duty: WWII" + float aperture = inversesqrt(1.0 - min(visibility, 0.9999)); + float microShadow = saturate(NoL * aperture); + return microShadow * microShadow; +} + // https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg) float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) { // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" @@ -133,7 +142,6 @@ vec3 evaluateLight( f32vec3 vNormalView = normalize(vNormal); float NoL = saturate(dot(vNormalView, vLightView)); - if(NoL <= 0.0) return vec3_splat(0.0); // not lit by this light @@ -154,8 +162,13 @@ vec3 evaluateLight( vec3 Fr = (D * V) * F; vec3 Fd = pixel.diffuseColor * Fd_Lambert(); - // https://google.github.io/filament/Filament.md.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance - vec3 color = NoL * lightColor * (Fr * pixel.energyCompensation + Fd); + //AO / micro shadowing + float microShadow = computeMicroShadowing(NoL, pixel.ao); + vec3 diffuseTerm = Fd * microShadow; + + vec3 specularTerm = Fr * pixel.energyCompensation; + + vec3 color = NoL * lightColor * (specularTerm + diffuseTerm); color *= getDistanceAttenuation(pointParams.yzw, fLightD); @@ -167,9 +180,10 @@ vec3 evaluateLight( return color; } -void PBR_MakeParams(in vec3 baseColor, in vec2 mrParam, inout PixelParams pixel) +void PBR_MakeParams(in vec3 baseColor, in vec2 mrParam, in float ao, inout PixelParams pixel) { pixel.baseColor = baseColor; + pixel.ao = ao; float perceptualRoughness = mrParam.x; // Clamp the roughness to a minimum value to avoid divisions by 0 during lighting @@ -233,6 +247,6 @@ void PBR_Lights( vOutColour += lightVal; } - vOutColour += pixel.baseColor * pow(ambient.rgb, vec3_splat(2.2)); + vOutColour += pixel.baseColor * pow(ambient.rgb, vec3_splat(2.2)) * pixel.ao; } #endif \ No newline at end of file diff --git a/RenderSystems/Direct3D11/src/OgreD3D11Mappings.cpp b/RenderSystems/Direct3D11/src/OgreD3D11Mappings.cpp index cba7e2bfb3d..f7350c208ec 100644 --- a/RenderSystems/Direct3D11/src/OgreD3D11Mappings.cpp +++ b/RenderSystems/Direct3D11/src/OgreD3D11Mappings.cpp @@ -745,8 +745,8 @@ namespace Ogre UINT flags = 0; - if((bindflags & D3D11_BIND_SHADER_RESOURCE) && (bindflags & D3D11_BIND_RENDER_TARGET)) - flags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; + if((bindflags & D3D11_BIND_SHADER_RESOURCE) && (bindflags & D3D11_BIND_RENDER_TARGET) && (usage & TU_AUTOMIPMAP)) + flags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; if(textype == TEX_TYPE_CUBE_MAP) flags |= D3D11_RESOURCE_MISC_TEXTURECUBE;