Connect volumetric lighting to main directional light
This commit is contained in:
@@ -26,6 +26,12 @@ Shader "Builtin Volumetric"
|
||||
float4 gVolumeBoundsMax;
|
||||
};
|
||||
|
||||
cbuffer LightingConstants
|
||||
{
|
||||
float4 gMainLightDirectionAndIntensity;
|
||||
float4 gMainLightColorAndFlags;
|
||||
};
|
||||
|
||||
cbuffer MaterialConstants
|
||||
{
|
||||
float4 gVolumeTint;
|
||||
@@ -220,8 +226,18 @@ Shader "Builtin Volumetric"
|
||||
float transmittance = 1.0f;
|
||||
float accumulatedDensity = 0.0f;
|
||||
|
||||
float3 resolvedLightDirectionWS = gLightDirection.xyz;
|
||||
float3 resolvedLightRadiance = 1.0f.xxx;
|
||||
if (gMainLightColorAndFlags.a >= 0.5f &&
|
||||
length(gMainLightDirectionAndIntensity.xyz) > 0.0001f) {
|
||||
resolvedLightDirectionWS = gMainLightDirectionAndIntensity.xyz;
|
||||
resolvedLightRadiance =
|
||||
max(gMainLightColorAndFlags.rgb, 0.0f.xxx) *
|
||||
max(gMainLightDirectionAndIntensity.w, 0.0f);
|
||||
}
|
||||
|
||||
float3 localLightDirection =
|
||||
mul((float3x3)gInverseModelMatrix, gLightDirection.xyz);
|
||||
mul((float3x3)gInverseModelMatrix, resolvedLightDirectionWS);
|
||||
const float localLightLength = length(localLightDirection);
|
||||
if (localLightLength > 0.0001f) {
|
||||
localLightDirection /= localLightLength;
|
||||
@@ -267,7 +283,7 @@ Shader "Builtin Volumetric"
|
||||
localLightDirection,
|
||||
lightSamples,
|
||||
volume.accessor);
|
||||
const float3 S = sigmaS * shadow.xxx;
|
||||
const float3 S = sigmaS * shadow.xxx * resolvedLightRadiance;
|
||||
const float3 integratedSegment = (S - S * exp(-sigmaE * stepSize)) / sigmaE;
|
||||
integratedLight += transmittance * integratedSegment;
|
||||
transmittance *= exp(-sigmaE * stepSize);
|
||||
|
||||
@@ -29,6 +29,7 @@ class VolumeField;
|
||||
|
||||
namespace Rendering {
|
||||
struct VisibleVolumeItem;
|
||||
struct RenderLightingData;
|
||||
|
||||
namespace Passes {
|
||||
|
||||
@@ -54,6 +55,11 @@ private:
|
||||
Math::Vector4 localBoundsMax = Math::Vector4::Zero();
|
||||
};
|
||||
|
||||
struct LightingConstants {
|
||||
Math::Vector4 mainLightDirectionAndIntensity = Math::Vector4::Zero();
|
||||
Math::Vector4 mainLightColorAndFlags = Math::Vector4::Zero();
|
||||
};
|
||||
|
||||
struct OwnedDescriptorSet {
|
||||
RHI::RHIDescriptorPool* pool = nullptr;
|
||||
RHI::RHIDescriptorSet* set = nullptr;
|
||||
@@ -82,6 +88,7 @@ private:
|
||||
Core::uint32 descriptorSetCount = 0;
|
||||
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||
PassResourceBindingLocation perObject = {};
|
||||
PassResourceBindingLocation lighting = {};
|
||||
PassResourceBindingLocation material = {};
|
||||
PassResourceBindingLocation volumeField = {};
|
||||
};
|
||||
@@ -161,6 +168,7 @@ private:
|
||||
bool EnsureInitialized(const RenderContext& context);
|
||||
bool CreateResources(const RenderContext& context);
|
||||
void DestroyResources();
|
||||
static LightingConstants BuildLightingConstants(const RenderLightingData& lightingData);
|
||||
|
||||
ResolvedShaderPass ResolveVolumeShaderPass(
|
||||
const RenderSceneData& sceneData,
|
||||
@@ -185,6 +193,7 @@ private:
|
||||
const Resources::Material* material,
|
||||
const Resources::VolumeField* volumeField,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
const LightingConstants& lightingConstants,
|
||||
RHI::RHIResourceView* volumeFieldView);
|
||||
void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet);
|
||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||
|
||||
@@ -140,6 +140,26 @@ const char* BuiltinVolumetricPass::GetName() const {
|
||||
return "BuiltinVolumetricPass";
|
||||
}
|
||||
|
||||
BuiltinVolumetricPass::LightingConstants BuiltinVolumetricPass::BuildLightingConstants(
|
||||
const RenderLightingData& lightingData) {
|
||||
LightingConstants lightingConstants = {};
|
||||
if (!lightingData.HasMainDirectionalLight()) {
|
||||
return lightingConstants;
|
||||
}
|
||||
|
||||
lightingConstants.mainLightDirectionAndIntensity = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.direction.x,
|
||||
lightingData.mainDirectionalLight.direction.y,
|
||||
lightingData.mainDirectionalLight.direction.z,
|
||||
lightingData.mainDirectionalLight.intensity);
|
||||
lightingConstants.mainLightColorAndFlags = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.color.r,
|
||||
lightingData.mainDirectionalLight.color.g,
|
||||
lightingData.mainDirectionalLight.color.b,
|
||||
1.0f);
|
||||
return lightingConstants;
|
||||
}
|
||||
|
||||
RHI::InputLayoutDesc BuiltinVolumetricPass::BuildInputLayout() {
|
||||
RHI::InputLayoutDesc inputLayout = {};
|
||||
|
||||
@@ -357,6 +377,7 @@ BuiltinVolumetricPass::PassResourceLayout* BuiltinVolumetricPass::GetOrCreatePas
|
||||
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
|
||||
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||
passLayout.perObject = bindingPlan.perObject;
|
||||
passLayout.lighting = bindingPlan.lighting;
|
||||
passLayout.material = bindingPlan.material;
|
||||
passLayout.volumeField = bindingPlan.volumeField;
|
||||
|
||||
@@ -472,10 +493,11 @@ BuiltinVolumetricPass::CachedDescriptorSet* BuiltinVolumetricPass::GetOrCreateDy
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Resources::Material* material,
|
||||
const Resources::VolumeField* volumeField,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
RHI::RHIResourceView* volumeFieldView) {
|
||||
const Resources::Material* material,
|
||||
const Resources::VolumeField* volumeField,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
const LightingConstants& lightingConstants,
|
||||
RHI::RHIResourceView* volumeFieldView) {
|
||||
DynamicDescriptorSetKey key = {};
|
||||
key.passLayout = passLayoutKey;
|
||||
key.setIndex = setIndex;
|
||||
@@ -504,6 +526,17 @@ BuiltinVolumetricPass::CachedDescriptorSet* BuiltinVolumetricPass::GetOrCreateDy
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesLighting) {
|
||||
if (!passLayout.lighting.IsValid() || passLayout.lighting.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.lighting.binding,
|
||||
&lightingConstants,
|
||||
sizeof(lightingConstants));
|
||||
}
|
||||
|
||||
if (setLayout.usesVolumeField) {
|
||||
if (volumeFieldView == nullptr ||
|
||||
!passLayout.volumeField.IsValid() ||
|
||||
@@ -548,6 +581,7 @@ void BuiltinVolumetricPass::DestroyPassResourceLayout(PassResourceLayout& passLa
|
||||
passLayout.firstDescriptorSet = 0;
|
||||
passLayout.descriptorSetCount = 0;
|
||||
passLayout.perObject = {};
|
||||
passLayout.lighting = {};
|
||||
passLayout.material = {};
|
||||
passLayout.volumeField = {};
|
||||
}
|
||||
@@ -595,6 +629,7 @@ bool BuiltinVolumetricPass::DrawVisibleVolume(
|
||||
const Resources::MaterialRenderState effectiveRenderState =
|
||||
ResolveEffectiveRenderState(resolvedShaderPass.pass, material);
|
||||
const MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
|
||||
const LightingConstants lightingConstants = BuildLightingConstants(sceneData.lighting);
|
||||
if (passLayout->material.IsValid() && !materialConstants.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
@@ -630,7 +665,7 @@ bool BuiltinVolumetricPass::DrawVisibleVolume(
|
||||
}
|
||||
|
||||
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||
if (!(setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesVolumeField)) {
|
||||
if (!(setLayout.usesPerObject || setLayout.usesLighting || setLayout.usesMaterial || setLayout.usesVolumeField)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -651,6 +686,7 @@ bool BuiltinVolumetricPass::DrawVisibleVolume(
|
||||
materialKey,
|
||||
volumeFieldKey,
|
||||
materialConstants,
|
||||
lightingConstants,
|
||||
cachedVolume->shaderResourceView);
|
||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||
return false;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Components/LightComponent.h>
|
||||
#include <XCEngine/Components/VolumeRendererComponent.h>
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
@@ -29,7 +30,14 @@ private:
|
||||
void VolumeSceneTest::BuildScene() {
|
||||
mVolumeMaterial = CreateVolumetricMaterial(
|
||||
"VolumeMaterial",
|
||||
"Tests/Rendering/VolumeScene/Volume.material");
|
||||
"Tests/Rendering/VolumeScene/Volume.material",
|
||||
Vector4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
0.2f,
|
||||
1.0f,
|
||||
2000.0f,
|
||||
0.005f,
|
||||
Vector3(-0.72f, 0.16f, 0.67f),
|
||||
8.0f);
|
||||
ASSERT_NE(mVolumeMaterial, nullptr);
|
||||
|
||||
mVolumeField = LoadCloudVolumeField();
|
||||
@@ -45,6 +53,13 @@ void VolumeSceneTest::BuildScene() {
|
||||
cameraObject->GetTransform()->SetLocalPosition(Vector3(-10.0f, 300.0f, -1200.0f));
|
||||
cameraObject->GetTransform()->LookAt(Vector3(-10.0f, 73.0f, 0.0f));
|
||||
|
||||
GameObject* lightObject = mScene->CreateGameObject("MainDirectionalLight");
|
||||
auto* light = lightObject->AddComponent<LightComponent>();
|
||||
light->SetLightType(LightType::Directional);
|
||||
light->SetColor(XCEngine::Math::Color(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
light->SetIntensity(1.0f);
|
||||
lightObject->GetTransform()->LookAt(Vector3(-0.5f, -0.8f, -0.3f));
|
||||
|
||||
GameObject* volumeObject = mScene->CreateGameObject("CloudVolume");
|
||||
auto* volumeRenderer = volumeObject->AddComponent<VolumeRendererComponent>();
|
||||
volumeRenderer->SetVolumeField(mVolumeField);
|
||||
@@ -56,6 +71,7 @@ void VolumeSceneTest::BuildScene() {
|
||||
const RenderSceneData sceneData = extractor.Extract(*mScene, nullptr, kFrameWidth, kFrameHeight);
|
||||
ASSERT_EQ(sceneData.visibleItems.size(), 0u);
|
||||
ASSERT_EQ(sceneData.visibleVolumes.size(), 1u);
|
||||
ASSERT_TRUE(sceneData.lighting.HasMainDirectionalLight());
|
||||
}
|
||||
|
||||
void VolumeSceneTest::ReleaseSceneResources() {
|
||||
|
||||
@@ -687,19 +687,29 @@ TEST(BuiltinForwardPipeline_Test, BuiltinVolumetricShaderUsesAuthoringContract)
|
||||
|
||||
const ShaderPass* pass = shader->FindPass("Volumetric");
|
||||
ASSERT_NE(pass, nullptr);
|
||||
EXPECT_EQ(pass->resources.Size(), 3u);
|
||||
EXPECT_EQ(pass->resources.Size(), 4u);
|
||||
EXPECT_TRUE(pass->hasFixedFunctionState);
|
||||
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
|
||||
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);
|
||||
EXPECT_EQ(pass->fixedFunctionState.depthFunc, MaterialComparisonFunc::LessEqual);
|
||||
EXPECT_TRUE(pass->fixedFunctionState.blendEnable);
|
||||
|
||||
const ShaderResourceBindingDesc* lighting =
|
||||
shader->FindPassResourceBinding("Volumetric", "LightingConstants");
|
||||
ASSERT_NE(lighting, nullptr);
|
||||
EXPECT_EQ(lighting->type, ShaderResourceType::ConstantBuffer);
|
||||
EXPECT_EQ(lighting->set, 1u);
|
||||
EXPECT_EQ(lighting->binding, 0u);
|
||||
EXPECT_EQ(
|
||||
ResolveBuiltinPassResourceSemantic(*lighting),
|
||||
BuiltinPassResourceSemantic::Lighting);
|
||||
|
||||
const ShaderResourceBindingDesc* volumeData =
|
||||
shader->FindPassResourceBinding("Volumetric", "VolumeData");
|
||||
ASSERT_NE(volumeData, nullptr);
|
||||
EXPECT_EQ(volumeData->type, ShaderResourceType::StructuredBuffer);
|
||||
EXPECT_EQ(volumeData->set, 2u);
|
||||
EXPECT_EQ(volumeData->binding, 0u);
|
||||
EXPECT_EQ(volumeData->binding, 1u);
|
||||
EXPECT_EQ(
|
||||
ResolveBuiltinPassResourceSemantic(*volumeData),
|
||||
BuiltinPassResourceSemantic::VolumeField);
|
||||
@@ -722,27 +732,33 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromLoaded
|
||||
BuiltinPassResourceBindingPlan plan = {};
|
||||
String error;
|
||||
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(*pass, plan, &error)) << error.CStr();
|
||||
ASSERT_EQ(plan.bindings.Size(), 3u);
|
||||
ASSERT_EQ(plan.bindings.Size(), 4u);
|
||||
EXPECT_TRUE(plan.perObject.IsValid());
|
||||
EXPECT_TRUE(plan.lighting.IsValid());
|
||||
EXPECT_TRUE(plan.material.IsValid());
|
||||
EXPECT_TRUE(plan.volumeField.IsValid());
|
||||
EXPECT_EQ(plan.perObject.set, 0u);
|
||||
EXPECT_EQ(plan.material.set, 1u);
|
||||
EXPECT_EQ(plan.lighting.set, 1u);
|
||||
EXPECT_EQ(plan.material.set, 2u);
|
||||
EXPECT_EQ(plan.volumeField.set, 2u);
|
||||
EXPECT_EQ(plan.volumeField.binding, 0u);
|
||||
EXPECT_EQ(plan.volumeField.binding, 1u);
|
||||
|
||||
std::vector<BuiltinPassSetLayoutMetadata> setLayouts;
|
||||
ASSERT_TRUE(TryBuildBuiltinPassSetLayouts(plan, setLayouts, &error)) << error.CStr();
|
||||
ASSERT_EQ(setLayouts.size(), 3u);
|
||||
EXPECT_TRUE(setLayouts[0].usesPerObject);
|
||||
EXPECT_TRUE(setLayouts[1].usesMaterial);
|
||||
EXPECT_TRUE(setLayouts[1].usesLighting);
|
||||
EXPECT_TRUE(setLayouts[2].usesMaterial);
|
||||
EXPECT_TRUE(setLayouts[2].usesVolumeField);
|
||||
ASSERT_EQ(setLayouts[2].bindings.size(), 1u);
|
||||
ASSERT_EQ(setLayouts[2].bindings.size(), 2u);
|
||||
EXPECT_EQ(
|
||||
static_cast<DescriptorType>(setLayouts[2].bindings[0].type),
|
||||
DescriptorType::CBV);
|
||||
EXPECT_EQ(
|
||||
static_cast<DescriptorType>(setLayouts[2].bindings[1].type),
|
||||
DescriptorType::SRV);
|
||||
EXPECT_EQ(
|
||||
setLayouts[2].bindings[0].resourceDimension,
|
||||
setLayouts[2].bindings[1].resourceDimension,
|
||||
ResourceViewDimension::StructuredBuffer);
|
||||
|
||||
delete shader;
|
||||
@@ -972,8 +988,12 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringVolum
|
||||
"register(b0)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
d3d12Source,
|
||||
"cbuffer MaterialConstants",
|
||||
"cbuffer LightingConstants",
|
||||
"register(b1)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
d3d12Source,
|
||||
"cbuffer MaterialConstants",
|
||||
"register(b2)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
d3d12Source,
|
||||
"StructuredBuffer<uint> VolumeData",
|
||||
@@ -1000,12 +1020,16 @@ TEST(BuiltinForwardPipeline_Test, VulkanRuntimeCompileDescRewritesAuthoringVolum
|
||||
"register(b0, space0)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
vulkanSource,
|
||||
"cbuffer MaterialConstants",
|
||||
"cbuffer LightingConstants",
|
||||
"register(b0, space1)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
vulkanSource,
|
||||
"cbuffer MaterialConstants",
|
||||
"register(b0, space2)"));
|
||||
EXPECT_TRUE(SourceContainsRegisterBinding(
|
||||
vulkanSource,
|
||||
"StructuredBuffer<uint> VolumeData",
|
||||
"register(t0, space2)"));
|
||||
"register(t1, space2)"));
|
||||
|
||||
delete shader;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user