Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions assets/shaders/decal.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Screen-space decal pass.
//
// Reads the scene depth buffer to reconstruct world-space positions, then for
// each active decal tests whether the position lies inside the decal OBB
// (represented as a unit cube in decal-local space). Matching pixels receive
// the decal colour blended over the existing scene colour via src-alpha blending
// in the pipeline blend state.

#define MAX_DECALS 16

struct DecalData {
float4x4 WorldToDecal; // Inverse world transform of the decal OBB
float4 DecalColor; // RGB tint + alpha
float4 Params; // x: blend factor, yzw: unused
};

[[vk::binding(0, 0)]] cbuffer DecalPassInfo : register(b0, space0)
{
uint DecalCount;
uint3 _pad;
DecalData Decals[MAX_DECALS];
}

// ViewInfo bound separately so this pass is independent of the default pass layout.
// Mirrors the SceneViewInfo C++ struct (world / view / viewProject / lastViewProject / invViewProject).
struct ViewInfo {
float4x4 World;
float4x4 View;
float4x4 ViewProj;
float4x4 LastViewProj;
float4x4 InvViewProj;
};

[[vk::binding(1, 0)]] cbuffer view : register(b1, space0)
{
ViewInfo View;
}

[[vk::binding(2, 0)]] Texture2D DepthBuffer : register(t0, space0);
[[vk::binding(3, 0)]] SamplerState DepthSampler : register(s0, space0);

// Full-screen triangle
const float2 FT_POSITIONS[3] = {
float2(-1.0, 3.0),
float2(-1.0, -1.0),
float2( 3.0, -1.0)
};

const float2 FT_UVS[3] = {
float2(0.0, 2.0),
float2(0.0, 0.0),
float2(2.0, 0.0)
};

struct VSOutput {
float4 Pos : SV_POSITION;
float2 UV : TEXCOORD;
};

VSOutput VSMain(uint vid : SV_VertexID)
{
VSOutput res;
res.Pos = float4(FT_POSITIONS[vid], 0.0, 1.0);
res.UV = FT_UVS[vid];
return res;
}

float4 FSMain(VSOutput input) : SV_TARGET
{
float depth = DepthBuffer.Sample(DepthSampler, input.UV).r;

// Discard sky / far-plane pixels (depth == 1 in non-reversed-z)
if (depth >= 1.0) {
discard;
}

// Reconstruct clip-space position and unproject to world space.
// NDC XY maps linearly from UV: (0,0) -> (-1,-1), (1,1) -> (1,1)
float2 ndcXY = input.UV * 2.0 - 1.0;
float4 clipPos = float4(ndcXY, depth, 1.0);

float4 worldPos4 = mul(View.InvViewProj, clipPos);
float3 worldPos = worldPos4.xyz / worldPos4.w;

float4 outColor = float4(0.0, 0.0, 0.0, 0.0);

for (uint i = 0; i < DecalCount; ++i)
{
// Transform world position into decal local space
float4 localPos = mul(Decals[i].WorldToDecal, float4(worldPos, 1.0));

// The decal OBB is a unit cube [-0.5, 0.5]^3 in local space
if (all(abs(localPos.xyz) <= 0.5))
{
// Map local XZ to UV [0,1]
float2 decalUV = localPos.xz + 0.5;

float4 decalColor = Decals[i].DecalColor;
float blend = decalColor.a * Decals[i].Params.x;

// Accumulate: later decals paint over earlier ones (painter's algorithm)
outColor = lerp(outColor, float4(decalColor.rgb, 1.0), blend);
}
}

return outColor;
}
1 change: 1 addition & 0 deletions assets/shaders/layout/default_global_view.hlslh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ struct ViewInfo {
float4x4 View;
float4x4 ViewProj;
float4x4 LastViewProj;
float4x4 InvViewProj;
};

[[vk::binding(0, 0)]] cbuffer global : register(b0, space0)
Expand Down
27 changes: 27 additions & 0 deletions assets/techniques/decal.tech
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"type": "graphics",
"shader": {
"path": "decal.hlsl",
"vertex": "VSMain",
"fragment": "FSMain"
},
"pass": {
"tag": "Decal"
},
"blend_state": [
{
"blendEnable": true,
"srcColor": "SRC_ALPHA",
"dstColor": "ONE_MINUS_SRC_ALPHA",
"srcAlpha": "ONE",
"dstAlpha": "ONE_MINUS_SRC_ALPHA"
}
],
"depth_stencil": {
"depthTestEnable": false,
"depthWriteEnable": false
},
"raster_state": {
"cullMode": "NONE"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// Created by blues on 2024/12/8.
//

#pragma once

#include <core/math/Transform.h>
#include <framework/world/Component.h>
#include <framework/interface/ITransformEvent.h>
#include <render/decal/DecalFeatureProcessor.h>

namespace sky {

struct DecalComponentData {
Vector4 color = {1.f, 1.f, 1.f, 1.f};
float blendFactor = 1.f;
};

/**
* Actor component that places a screen-space decal in the world.
* The decal is an oriented bounding box derived from the actor transform;
* scaling the actor controls the decal's extents in world space.
*/
class DecalComponent
: public ComponentAdaptor<DecalComponentData>
, public ITransformEvent {
public:
DecalComponent() = default;
~DecalComponent() override = default;

COMPONENT_RUNTIME_INFO(DecalComponent)

static void Reflect(SerializationContext *context);

void OnAttachToWorld() override;
void OnDetachFromWorld() override;

private:
void OnTransformChanged(const Transform &global, const Transform &local) override;
void UpdateDecal(const Transform &transform);

EventBinder<ITransformEvent> transformEvent;
DecalInstance *decal = nullptr;
};

} // namespace sky
39 changes: 39 additions & 0 deletions engine/render/adaptor/include/render/adaptor/pipeline/DecalPass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Created by blues on 2024/12/8.
//

#pragma once

#include <render/renderpass/RasterPass.h>
#include <render/adaptor/pipeline/DefaultPassConstants.h>

namespace sky {

/**
* Screen-space decal pass. Decoupled from forward/deferred pipelines:
* it only requires a depth buffer resource name and a color render target
* resource name which can be provided by any opaque pass.
*
* Algorithm:
* 1. Full-screen triangle reconstructs world position from depth using InvViewProj.
* 2. For each active decal the world position is transformed into the decal's
* OBB local space (WorldToDecal matrix). If the local position lies inside
* the unit cube [-0.5, 0.5]³ the decal colour is alpha-blended onto the output.
*/
class DecalPass : public FullScreenPass {
public:
/**
* @param tech Pre-loaded decal technique (decal.tech).
* @param colorName Name of the colour render target to write into (LoadOp::LOAD).
* @param depthName Name of the scene depth texture to read from (SRV).
*/
DecalPass(const RDGfxTechPtr &tech, const Name &colorName, const Name &depthName);
~DecalPass() override = default;

void Setup(rdg::RenderGraph &rdg, RenderScene &scene) override;

private:
RDResourceLayoutPtr decalLayout;
};

} // namespace sky
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <render/adaptor/pipeline/BRDFLutPass.h>
#include <render/adaptor/pipeline/ShadowMapPass.h>
#include <render/adaptor/pipeline/EmptyPass.h>
#include <render/adaptor/pipeline/DecalPass.h>
#include <memory>

namespace sky {
Expand Down Expand Up @@ -45,6 +46,7 @@ namespace sky {
std::unique_ptr<HizGenerator> hiz;
std::unique_ptr<ShadowMapPass> shadowMap;
std::unique_ptr<ForwardMSAAPass> forward;
std::unique_ptr<DecalPass> decal;
std::unique_ptr<PostProcessingPass> postProcess;
std::unique_ptr<PresentPass> present;
std::unique_ptr<BRDFLutPass> brdfLut;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace sky {

static constexpr std::string_view FWD_DS_RESOLVE = "ForwardDepthResolve";

static constexpr std::string_view DECAL_PASS_INFO = "DECAL_PASS_INFO";

static constexpr std::string_view SWAP_CHAIN = "SwapChain";

} // namespace sky
6 changes: 6 additions & 0 deletions engine/render/adaptor/src/RenderModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <render/adaptor/Reflection.h>
#include <render/adaptor/assets/TechniqueAsset.h>
#include <render/adaptor/components/CameraComponent.h>
#include <render/adaptor/components/DecalComponent.h>
#include <render/adaptor/components/LightComponent.h>
#include <render/adaptor/components/PrefabComponent.h>
#include <render/adaptor/components/SkeletalMeshComponent.h>
Expand All @@ -31,6 +32,7 @@
#include <render/env/EnvFeature.h>
#include <render/mesh/MeshFeature.h>
#include <render/text/TextFeature.h>
#include <render/decal/DecalFeatureProcessor.h>
#include <imgui/ImGuiFeature.h>

#include <render/RHI.h>
Expand All @@ -50,6 +52,7 @@ namespace sky {
SkeletalMeshComponent::Reflect(context);
SkyBoxComponent::Reflect(context);
PrefabComponent::Reflect(context);
DecalComponent::Reflect(context);

SkeletonDisplayComponent::Reflect(context);
AnimationPreviewComponent::Reflect(context);
Expand All @@ -63,6 +66,7 @@ namespace sky {
ComponentFactory::Get()->RegisterComponent<CameraComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<SkyBoxComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<PrefabComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<DecalComponent>(GROUP);
}

{
Expand Down Expand Up @@ -147,6 +151,8 @@ namespace sky {
LightFeature::Get()->Init();
EnvFeature::Get()->Init();

Renderer::Get()->RegisterRenderFeature<DecalFeatureProcessor>();

auto *am = AssetManager::Get();
{
auto guiAsset = am->LoadAssetFromPath<Technique>("techniques/gui.tech");
Expand Down
70 changes: 70 additions & 0 deletions engine/render/adaptor/src/components/DecalComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Created by blues on 2024/12/8.
//

#include <render/adaptor/components/DecalComponent.h>

#include <framework/serialization/JsonArchive.h>
#include <framework/serialization/PropertyCommon.h>
#include <framework/world/Actor.h>
#include <framework/world/TransformComponent.h>

#include <render/adaptor/Util.h>
#include <render/decal/DecalFeatureProcessor.h>

namespace sky {

void DecalComponent::Reflect(SerializationContext *context)
{
context->Register<DecalComponentData>("DecalComponentData")
.Member<&DecalComponentData::color>("Color")
.Member<&DecalComponentData::blendFactor>("BlendFactor");

REGISTER_BEGIN(DecalComponent, context)
}

void DecalComponent::OnAttachToWorld()
{
auto *df = GetFeatureProcessor<DecalFeatureProcessor>(actor);
if (df == nullptr) {
return;
}

decal = df->CreateDecal();
decal->SetColor(data.color);
decal->SetBlendFactor(data.blendFactor);

transformEvent.Bind(this, actor);

auto *transform = actor->GetComponent<TransformComponent>();
if (transform != nullptr) {
UpdateDecal(transform->GetWorldTransform());
}
}

void DecalComponent::OnDetachFromWorld()
{
auto *df = GetFeatureProcessor<DecalFeatureProcessor>(actor);
if (df != nullptr && decal != nullptr) {
df->RemoveDecal(decal);
}
decal = nullptr;
transformEvent.Reset();
}

void DecalComponent::OnTransformChanged(const Transform &global, const Transform &/*local*/)
{
UpdateDecal(global);
}

void DecalComponent::UpdateDecal(const Transform &transform)
{
if (decal == nullptr) {
return;
}
decal->SetWorldMatrix(transform.ToMatrix());
decal->SetColor(data.color);
decal->SetBlendFactor(data.blendFactor);
}

} // namespace sky
Loading