diff --git a/RenderCore/Assets/ModelCache.h b/RenderCore/Assets/ModelCache.h index c6a53db257..a66d497be7 100644 --- a/RenderCore/Assets/ModelCache.h +++ b/RenderCore/Assets/ModelCache.h @@ -59,7 +59,7 @@ namespace RenderCore { namespace Assets Config() : _modelScaffoldCount(2000), _materialScaffoldCount(2000) - , _rendererCount(100) {} + , _rendererCount(200) {} }; using ResChar = ::Assets::ResChar; diff --git a/RenderCore/Techniques/CommonBindings.h b/RenderCore/Techniques/CommonBindings.h index d590b8b118..4dd222f775 100644 --- a/RenderCore/Techniques/CommonBindings.h +++ b/RenderCore/Techniques/CommonBindings.h @@ -37,8 +37,9 @@ namespace RenderCore { namespace Techniques static const auto VisWireframe = 8u; static const auto WriteTriangleIndex = 9u; static const auto StochasticTransparency = 10u; + static const auto DepthWeightedTransparency = 11u; - static const auto Max = 11u; + static const auto Max = 12u; }; extern const ::Assets::ResChar* DefaultPredefinedCBLayout; diff --git a/RenderCore/Techniques/Techniques.cpp b/RenderCore/Techniques/Techniques.cpp index c183816c83..be1e61c6f7 100644 --- a/RenderCore/Techniques/Techniques.cpp +++ b/RenderCore/Techniques/Techniques.cpp @@ -759,6 +759,7 @@ namespace RenderCore { namespace Techniques // note -- lexographically sorted! { u("Deferred"), unsigned(TechniqueIndex::Deferred) }, { u("DepthOnly"), unsigned(TechniqueIndex::DepthOnly) }, + { u("DepthWeightedTransparency"), unsigned(TechniqueIndex::DepthWeightedTransparency) }, { u("Illum"), unsigned(TechniqueIndex::Forward) }, { u("OrderIndependentTransparency"), unsigned(TechniqueIndex::OrderIndependentTransparency) }, { u("PrepareVegetationSpawn"), unsigned(TechniqueIndex::PrepareVegetationSpawn) }, diff --git a/SceneEngine/DepthWeightedTransparency.cpp b/SceneEngine/DepthWeightedTransparency.cpp new file mode 100644 index 0000000000..aa2b4810f0 --- /dev/null +++ b/SceneEngine/DepthWeightedTransparency.cpp @@ -0,0 +1,169 @@ +// Copyright 2016 XLGAMES Inc. +// +// Distributed under the MIT License (See +// accompanying file "LICENSE" or the website +// http://www.opensource.org/licenses/mit-license.php) + +#include "DepthWeightedTransparency.h" +#include "GestaltResource.h" +#include "LightingParserContext.h" +#include "SceneEngineUtils.h" +#include "../RenderCore/Metal/Format.h" +#include "../RenderCore/Metal/State.h" +#include "../RenderCore/Metal/DeviceContext.h" +#include "../RenderCore/Metal/ShaderResource.h" +#include "../RenderCore/Metal/Shader.h" +#include "../RenderCore/Techniques/ResourceBox.h" +#include "../RenderCore/Techniques/CommonResources.h" +#include "../RenderCore/Techniques/Techniques.h" +#include "../BufferUploads/IBufferUploads.h" +#include "../Assets/Assets.h" + +#include "../RenderCore/DX11/Metal/IncludeDX11.h" + +namespace SceneEngine +{ + using namespace RenderCore; + + class DepthWeightedTransparencyBox + { + public: + class Desc + { + public: + unsigned _width, _height; + Desc(unsigned width, unsigned height) + { + _width = width; + _height = height; + } + }; + GestaltTypes::RTVSRV _accumulationBuffer; + GestaltTypes::RTVSRV _modulationBuffer; + GestaltTypes::RTVSRV _refractionBuffer; + + Metal::BlendState _blendState; + + DepthWeightedTransparencyBox(const Desc& desc); + }; + + DepthWeightedTransparencyBox::DepthWeightedTransparencyBox(const Desc& desc) + { + using namespace BufferUploads; + _accumulationBuffer = GestaltTypes::RTVSRV( + TextureDesc::Plain2D(desc._width, desc._height, Metal::NativeFormat::R32G32B32A32_FLOAT), + "TransAccBuffer", nullptr); + _modulationBuffer = GestaltTypes::RTVSRV( + TextureDesc::Plain2D(desc._width, desc._height, Metal::NativeFormat::R16G16B16A16_FLOAT), + "TransModulationBuffer", nullptr); + _refractionBuffer = GestaltTypes::RTVSRV( + TextureDesc::Plain2D(desc._width, desc._height, Metal::NativeFormat::R16G16B16A16_FLOAT), + "TransRefractBuffer", nullptr); + + D3D11_BLEND_DESC blendStateDesc; + blendStateDesc.AlphaToCoverageEnable = false; + blendStateDesc.IndependentBlendEnable = true; + for (unsigned c=0; c( + unsigned(viewport.Width), unsigned(viewport.Height)); + + _context->Clear(box._accumulationBuffer.RTV(), Float4(0.f, 0.f, 0.f, 0.f)); + _context->Clear(box._modulationBuffer.RTV(), Float4(1.f, 1.f, 1.f, 0.f)); + _context->Clear(box._refractionBuffer.RTV(), Float4(0.f, 0.f, 0.f, 0.f)); + _context->Bind( + MakeResourceList( + box._accumulationBuffer.RTV(), + box._modulationBuffer.RTV(), + box._refractionBuffer.RTV()), dsv); + _context->Bind(box._blendState); + _context->Bind(Techniques::CommonResources()._dssReadOnly); + + _box = &box; + } + + void DepthWeightedTransparencyOp::Resolve() + { + if (!_box) return; + + { + SetupVertexGeneratorShader(*_context); + auto& shader = ::Assets::GetAssetDep( + "game/xleres/basic2d.vsh:fullscreen:vs_*", + "game/xleres/forward/transparency/depthweighted.sh:resolve:ps_*"); + Metal::BoundUniforms uniforms(shader); + Techniques::TechniqueContext::BindGlobalUniforms(uniforms); + uniforms.BindShaderResources(1, {"Accumulator", "Modulator", "Refraction"}); + uniforms.Apply( + *_context, + _parserContext->GetGlobalUniformsStream(), + Metal::UniformsStream( + {}, + { + &_box->_accumulationBuffer.SRV(), + &_box->_modulationBuffer.SRV(), + &_box->_refractionBuffer.SRV() + })); + + _context->Bind(Techniques::CommonResources()._blendOneSrcAlpha); + _context->Bind(shader); + _context->Draw(4); + } + } + + DepthWeightedTransparencyOp::DepthWeightedTransparencyOp( + RenderCore::Metal::DeviceContext& context, + LightingParserContext& parserContext) + : _context(&context) + , _parserContext(&parserContext) + , _box(nullptr) + {} + + DepthWeightedTransparencyOp::~DepthWeightedTransparencyOp() + {} +} + + diff --git a/SceneEngine/DepthWeightedTransparency.h b/SceneEngine/DepthWeightedTransparency.h new file mode 100644 index 0000000000..f9dcab5890 --- /dev/null +++ b/SceneEngine/DepthWeightedTransparency.h @@ -0,0 +1,35 @@ +// Copyright 2016 XLGAMES Inc. +// +// Distributed under the MIT License (See +// accompanying file "LICENSE" or the website +// http://www.opensource.org/licenses/mit-license.php) + +#pragma once + +#include "../RenderCore/Metal/Forward.h" + +namespace SceneEngine +{ + class LightingParserContext; + class DepthWeightedTransparencyBox; + + class DepthWeightedTransparencyOp + { + public: + void PrepareFirstPass(const RenderCore::Metal::DepthStencilView* dsv); + void Resolve(); + + DepthWeightedTransparencyOp( + RenderCore::Metal::DeviceContext& context, + LightingParserContext& parserContext); + ~DepthWeightedTransparencyOp(); + + DepthWeightedTransparencyOp(const DepthWeightedTransparencyOp&) = delete; + DepthWeightedTransparencyOp& operator=(const DepthWeightedTransparencyOp&) = delete; + protected: + DepthWeightedTransparencyBox* _box; + RenderCore::Metal::DeviceContext* _context; + LightingParserContext* _parserContext; + }; +} + diff --git a/SceneEngine/LightingParser.cpp b/SceneEngine/LightingParser.cpp index b45e01cd9d..fa2e08cd9e 100644 --- a/SceneEngine/LightingParser.cpp +++ b/SceneEngine/LightingParser.cpp @@ -20,6 +20,7 @@ #include "RefractionsBuffer.h" #include "OrderIndependentTransparency.h" #include "StochasticTransparency.h" +#include "DepthWeightedTransparency.h" #include "Sky.h" #include "SunFlare.h" #include "Rain.h" @@ -290,6 +291,8 @@ namespace SceneEngine Metal::NativeFormat::Enum resolveFormat) { // todo -- support custom resolve (tone-map aware) + // See AMD post on this topic: + // http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/ context.GetUnderlying()->ResolveSubresource( destinationTexture, D3D11CalcSubresource(0,0,0), sourceTexture, D3D11CalcSubresource(0,0,0), @@ -562,10 +565,14 @@ namespace SceneEngine ////////////////////////////////////////////////////////////////////////////////////////////////// const bool hasOITrans = BatchHasContent(parserContext, SPS::BatchFilter::OITransparent); - const auto enabledSortedTrans = - Tweakable("UseOITrans", false) - && (mainTargets._desc._sampling._sampleCount <= 1) - && hasOITrans; + + enum OIMode { Unordered, Stochastic, DepthWeighted, SortedRef } oiMode; + switch (Tweakable("OITransMode", 1)) { + case 1: oiMode = OIMode::Stochastic; break; + case 2: oiMode = OIMode::DepthWeighted; break; + case 3: oiMode = (mainTargets._desc._sampling._sampleCount <= 1) ? OIMode::SortedRef : OIMode::Stochastic; break; + default: oiMode = OIMode::Unordered; break; + } // When enable OI transparency is enabled, we do a pre-depth pass // on all transparent geometry. @@ -577,7 +584,7 @@ namespace SceneEngine // // The depth pre-pass helps a little bit for stochastic transparency, // but it's not clear that it helps overall. - if (enabledSortedTrans && Tweakable("TransPrePass", false)) { + if (oiMode == OIMode::SortedRef && Tweakable("TransPrePass", false)) { StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._depthOnly); ExecuteScene( context, parserContext, SPS::BatchFilter::TransparentPreDepth, @@ -594,8 +601,8 @@ namespace SceneEngine } if (hasOITrans) { - StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._depthOnly); - if (enabledSortedTrans) { + if (oiMode == OIMode::SortedRef) { + StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._depthOnly); auto duplicatedDepthBuffer = BuildDuplicatedDepthBuffer(&metalContext, mainTargets._msaaDepthBufferTexture.get()); auto* transTargets = OrderIndependentTransparency_Prepare(metalContext, parserContext, duplicatedDepthBuffer); @@ -606,7 +613,8 @@ namespace SceneEngine // note; we use the main depth buffer for this call (not the duplicated buffer) OrderIndependentTransparency_Resolve(metalContext, parserContext, *transTargets, mainTargets._msaaDepthBufferSRV); - } else if (Tweakable("UseStochasticTrans", true)) { + } else if (oiMode == OIMode::Stochastic) { + StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._depthOnly); SavedTargets savedTargets(metalContext); auto resetMarker = savedTargets.MakeResetMarker(metalContext); @@ -632,6 +640,28 @@ namespace SceneEngine resetMarker = SavedTargets::ResetMarker(); // back to normal targets now stochTransOp.Resolve(); + } else if (oiMode == OIMode::DepthWeighted) { + StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._depthOnly); + SavedTargets savedTargets(metalContext); + auto resetMarker = savedTargets.MakeResetMarker(metalContext); + + DepthWeightedTransparencyOp transOp(metalContext, parserContext); + + Metal::DepthStencilView dsv(savedTargets.GetDepthStencilView()); + transOp.PrepareFirstPass(&dsv); + ExecuteScene( + context, parserContext, SPS::BatchFilter::OITransparent, + preparedScene, + TechniqueIndex_DepthWeightedTransparency, L"MainScene-PostGBuffer-OI"); + + resetMarker = SavedTargets::ResetMarker(); // back to normal targets now + transOp.Resolve(); + } else if (oiMode == OIMode::Unordered) { + StateSetChangeMarker marker(parserContext, GetStateSetResolvers()._forward); + ExecuteScene( + context, parserContext, SPS::BatchFilter::OITransparent, + preparedScene, + TechniqueIndex_General, L"MainScene-PostGBuffer-OI"); } } diff --git a/SceneEngine/Project/SceneEngine.vcxproj b/SceneEngine/Project/SceneEngine.vcxproj index 6e4e752b4a..9a470d144d 100644 --- a/SceneEngine/Project/SceneEngine.vcxproj +++ b/SceneEngine/Project/SceneEngine.vcxproj @@ -207,6 +207,7 @@ + @@ -259,6 +260,7 @@ + diff --git a/SceneEngine/Project/SceneEngine.vcxproj.filters b/SceneEngine/Project/SceneEngine.vcxproj.filters index 96f5549c0d..18b6105fa1 100644 --- a/SceneEngine/Project/SceneEngine.vcxproj.filters +++ b/SceneEngine/Project/SceneEngine.vcxproj.filters @@ -152,6 +152,9 @@ Lighting And Processing + + Lighting And Processing + @@ -323,6 +326,9 @@ + + Lighting And Processing + diff --git a/SceneEngine/SceneEngineUtils.h b/SceneEngine/SceneEngineUtils.h index 91b27182c9..c72bfa5fe0 100644 --- a/SceneEngine/SceneEngineUtils.h +++ b/SceneEngine/SceneEngineUtils.h @@ -120,6 +120,7 @@ namespace SceneEngine static const auto TechniqueIndex_OrderIndependentTransparency = RenderCore::Techniques::TechniqueIndex::OrderIndependentTransparency; static const auto TechniqueIndex_RTShadowGen = RenderCore::Techniques::TechniqueIndex::WriteTriangleIndex; static const auto TechniqueIndex_StochasticTransparency = RenderCore::Techniques::TechniqueIndex::StochasticTransparency; + static const auto TechniqueIndex_DepthWeightedTransparency = RenderCore::Techniques::TechniqueIndex::DepthWeightedTransparency; typedef intrusive_ptr ResourcePtr; ResourcePtr CreateResourceImmediate(const BufferUploads::BufferDesc& desc); diff --git a/Working/Game/xleres/Forward/Transparency/depthweighted.sh b/Working/Game/xleres/Forward/Transparency/depthweighted.sh new file mode 100644 index 0000000000..b232bd21fc --- /dev/null +++ b/Working/Game/xleres/Forward/Transparency/depthweighted.sh @@ -0,0 +1,112 @@ +// Copyright 2016 XLGAMES Inc. +// +// Distributed under the MIT License (See +// accompanying file "LICENSE" or the website +// http://www.opensource.org/licenses/mit-license.php) + +#include "../illum.psh" + +struct DepthWeightedOutput +{ + float4 accumulator : SV_Target0; + float4 modulator : SV_Target1; + float3 refraction : SV_Target2; +}; + +DepthWeightedOutput BuildDepthWeightedOutput(float4 color, VSOutput geo, SystemInputs sys) +{ + DepthWeightedOutput result; + + float coverage = 1.f; + float3 transmissionCoefficient = (1.0f - color.a).xxx; + float3 premultipliedReflectionAndEmission = color.rgb * color.a; + + result.modulator.rgb = coverage * (1.0.xxx - transmissionCoefficient); + coverage *= 1.0f - (transmissionCoefficient.r + transmissionCoefficient.g + transmissionCoefficient.b) * (1.0f / 3.0f); + + float tmp = 1.0f - geo.position.z * 0.99f; + float w = clamp(coverage * tmp * tmp * tmp * 1e3f, 1e-2f, 3e2f * 0.1f); + result.accumulator = float4(premultipliedReflectionAndEmission, coverage) * w; + + // float backgroundZ = csPosition.z - 4; + // Vector2 refractionOffset = (etaRatio == 1.0) ? Vector2(0) : computeRefractionOffset(backgroundZ, csNormal, csPosition, etaRatio); + // float trueBackgroundCSZ = _reconstructCSZ(texelFetch(_depthTexture.sampler, ivec2(gl_FragCoord.xy), 0).r, _clipInfo); + + // const float k_0 = 8.0; + // const float k_1 = 0.1; + + // _modulate.a = k_0 * coverage * (1.0 - collimation) * (1.0 - k_1 / (k_1 + csPosition.z - trueBackgroundCSZ)) / abs(csPosition.z); + // _modulate.a *= _modulate.a; + // if (_modulate.a > 0) { + // _modulate.a = max(_modulate.a, 1 / 256.0); + // } + + // _refraction = refractionOffset * coverage * 8.0; + + result.refraction = 0.0.xxx; + return result; +} + +[earlydepthstencil] +DepthWeightedOutput main_depth_weighted_oi(VSOutput geo, SystemInputs sys) +{ + // This is based on "A Phenomenological Scattering Model for Order-Independent Transparency" + // by McGuire and Mara. + + GBufferValues sample = IllumShader_PerPixel(geo); + + float3 directionToEye = 0.0.xxx; + #if (OUTPUT_WORLD_VIEW_VECTOR==1) + directionToEye = normalize(geo.worldViewVector); + #endif + + float4 result = float4( + ResolveLitColor( + sample, directionToEye, GetWorldPosition(geo), + LightScreenDest_Create(int2(geo.position.xy), GetSampleIndex(sys))), 1.f); + + #if OUTPUT_FOG_COLOR == 1 + result.rgb = geo.fogColor.rgb + result.rgb * geo.fogColor.a; + #endif + + result.a = sample.blendingAlpha; + + #if (OUTPUT_COLOUR>=1) && (MAT_VCOLOR_IS_ANIM_PARAM==0) + result.rgb *= geo.colour.rgb; + #endif + + #if MAT_SKIP_LIGHTING_SCALE==0 + // (note -- should we scale by this here? when using this shader with a + // basic lighting pipeline [eg, for material preview], the scale is unwanted) + result.rgb *= LightingScale; + #endif + + return BuildDepthWeightedOutput(result, geo, sys); +} + +Texture2D Accumulator; +Texture2D Modulator; +Texture2D Refraction; + +float maxComponent(float3 a) { return max(max(a.x, a.y), a.z); } + +float4 resolve(float4 pos : SV_Position) : SV_Target0 +{ + int2 pixelCoord = int2(pos.xy); + float4 mod = Modulator.Load(uint3(pixelCoord, 0)); + float3 backgroundModulation = mod.rgb; + if (maxComponent(backgroundModulation) == 1.f) discard; + + float4 acc = Accumulator.Load(uint3(pixelCoord, 0)); + + if (isinf(acc.a)) acc.a = maxComponent(acc.rgb); + if (isinf(maxComponent(acc.rgb))) acc = (isinf(acc.a) ? 1 : acc.a).xxxx; + + acc.rgb *= 0.5.xxx + backgroundModulation / max(0.01f, 2.0f * maxComponent(backgroundModulation)); + + float3 background = 0.0.xxx; + float3 result + = background * backgroundModulation + + (1.0.xxx - backgroundModulation) * acc.rgb / max(acc.a, 1e-5f); + return float4(result.rgb, maxComponent(backgroundModulation)); +} diff --git a/Working/Game/xleres/Techniques/Illum.tech b/Working/Game/xleres/Techniques/Illum.tech index 27b5707cf9..d5414bd841 100644 --- a/Working/Game/xleres/Techniques/Illum.tech +++ b/Working/Game/xleres/Techniques/Illum.tech @@ -169,3 +169,27 @@ STOCHASTIC_TRANS_OPACITY VertexShader=game/xleres/forward/illum.vsh:main PixelShader=game/xleres/forward/transparency/illum.psh:main_stochastic +~DepthWeightedTransparency + ~Inherit; shared.tech:CommonMaterial; shared.tech:Skinnable; shared.tech:VegetationSpawn; shared.tech:System + ~Parameters + ~Geometry + GEO_HAS_COLOUR + GEO_HAS_TEXCOORD + GEO_HAS_TANGENT_FRAME + GEO_HAS_BITANGENT + GEO_HAS_NORMAL + OUTPUT_WORLD_VIEW_VECTOR=1 + ~Material + MAT_TRANSMITTED_SPECULAR=0 + MAT_CLAMP_SPEC=0 + MAT_BLEND_FOG=1 + ~GlobalEnvironment + SKIP_MATERIAL_DIFFUSE=0 + SKY_PROJECTION + HAS_DIFFUSE_IBL + HAS_SPECULAR_IBL + ~Runtime + BASIC_LIGHT_COUNT=1 + SHADOW_CASCADE_MODE=2 + VertexShader=game/xleres/forward/illum.vsh:main + PixelShader=game/xleres/forward/transparency/depthweighted.sh:main_depth_weighted_oi