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
3 changes: 3 additions & 0 deletions assets/shaders/layout/default_global_view.hlslh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ struct ViewInfo {
float4 MainLightColor; // rgb: color a: intensity
float4 MainLightDirection;
float4 Viewport;
uint PointLightCount;
uint SpotLightCount;
uint2 LightPadding;
}

#if VIEW_COUNT > 1
Expand Down
28 changes: 27 additions & 1 deletion assets/shaders/layout/default_pass.hlslh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,30 @@
[[vk::binding(9, 0)]] SamplerState PrefilteredMapSampler : register(s4, space0);

[[vk::binding(10, 0)]] Texture2D HizBuffer : register(t5, space0);
[[vk::binding(11, 0)]] SamplerState HizBufferSampler : register(s5, space0);
[[vk::binding(11, 0)]] SamplerState HizBufferSampler : register(s5, space0);

// Physically-based punctual light data structures
struct PointLightData {
float4 PositionRange; // xyz: world position, w: influence range
float4 ColorIntensity; // xyz: linear RGB color, w: luminous intensity (candela)
};

struct SpotLightData {
float4 PositionRange; // xyz: world position, w: influence range
float4 DirectionInner; // xyz: normalized direction, w: cos(innerConeAngle)
float4 ColorIntensity; // xyz: linear RGB color, w: luminous intensity (candela)
float4 OuterAnglePad; // x: cos(outerConeAngle), yzw: reserved
};

#define MAX_POINT_LIGHTS 8
#define MAX_SPOT_LIGHTS 4

[[vk::binding(12, 0)]] cbuffer pointLightBuf : register(b2, space0)
{
PointLightData PointLights[MAX_POINT_LIGHTS];
}

[[vk::binding(13, 0)]] cbuffer spotLightBuf : register(b3, space0)
{
SpotLightData SpotLights[MAX_SPOT_LIGHTS];
}
99 changes: 99 additions & 0 deletions assets/shaders/lighting/punctual_light.hlslh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Physically-based punctual light evaluation
// Reference: "Moving Frostbite to PBR" (Lagarde & de Rousiers, SIGGRAPH 2014)
// "Real Shading in Unreal Engine 4" (Karis, SIGGRAPH 2013)

// Smooth distance attenuation with inverse-square falloff and range windowing.
// Uses the UE4/Frostbite window function to ensure light influence drops to
// zero exactly at the specified range, avoiding abrupt pop-in artifacts.
// d - distance from surface to light
// range - maximum influence radius
float DistanceAttenuation(float d, float range)
{
float d2 = d * d;
float ratio2 = d2 / (range * range);
float factor = saturate(1.0 - ratio2 * ratio2); // window function
return (factor * factor) / max(d2, 1e-4); // inverse-square + window
}

// Angular attenuation for spot lights.
// Produces a smooth falloff between the inner and outer cone angles.
// cosAngle - dot(-L, spotDirection)
// cosInner - cosine of the inner (full-intensity) cone half-angle
// cosOuter - cosine of the outer (zero-intensity) cone half-angle
float SpotAttenuation(float cosAngle, float cosInner, float cosOuter)
{
float t = saturate((cosAngle - cosOuter) / max(cosInner - cosOuter, 1e-4));
return t * t;
}

// Evaluate a point light contribution using the Cook-Torrance BRDF.
float3 EvaluatePointLight(PointLightData light, float3 V, float3 N, float3 worldPos, StandardPBR param)
{
float3 lightPos = light.PositionRange.xyz;
float range = light.PositionRange.w;

float3 toLight = lightPos - worldPos;
float dist = length(toLight);
float3 L = toLight / max(dist, 1e-6);

float attenuation = DistanceAttenuation(dist, range);

float3 H = normalize(V + L);
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float NdotH = max(dot(N, H), 0.0);
float VdotH = max(dot(V, H), 0.0);

float3 F0 = lerp(float3(0.04, 0.04, 0.04), param.Albedo, param.Metallic);

float3 F = F_Schlick(VdotH, F0);
float D = D_GGX(NdotH, param.Roughness);
float G = G_SchlickSmithGGX(NdotL, NdotV, param.Roughness);

float specular = D * G / (4.0 * NdotV * NdotL + 0.001);
float3 specularColor = F * specular;

float3 kD = (1.0 - F) * (1.0 - param.Metallic);
float3 diffuse = kD / PI * param.Albedo;

float3 radiance = light.ColorIntensity.rgb * light.ColorIntensity.w * attenuation;
return (diffuse + specularColor) * radiance * NdotL;
}

// Evaluate a spot light contribution using the Cook-Torrance BRDF.
float3 EvaluateSpotLight(SpotLightData light, float3 V, float3 N, float3 worldPos, StandardPBR param)
{
float3 lightPos = light.PositionRange.xyz;
float range = light.PositionRange.w;
float3 spotDir = normalize(light.DirectionInner.xyz);
float cosInner = light.DirectionInner.w;
float cosOuter = light.OuterAnglePad.x;

float3 toLight = lightPos - worldPos;
float dist = length(toLight);
float3 L = toLight / max(dist, 1e-6);

float attenuation = DistanceAttenuation(dist, range);
float spotFactor = SpotAttenuation(dot(-L, spotDir), cosInner, cosOuter);

float3 H = normalize(V + L);
float NdotL = max(dot(N, L), 0.0);
float NdotV = max(dot(N, V), 0.0);
float NdotH = max(dot(N, H), 0.0);
float VdotH = max(dot(V, H), 0.0);

float3 F0 = lerp(float3(0.04, 0.04, 0.04), param.Albedo, param.Metallic);

float3 F = F_Schlick(VdotH, F0);
float D = D_GGX(NdotH, param.Roughness);
float G = G_SchlickSmithGGX(NdotL, NdotV, param.Roughness);

float specular = D * G / (4.0 * NdotV * NdotL + 0.001);
float3 specularColor = F * specular;

float3 kD = (1.0 - F) * (1.0 - param.Metallic);
float3 diffuse = kD / PI * param.Albedo;

float3 radiance = light.ColorIntensity.rgb * light.ColorIntensity.w * attenuation * spotFactor;
return (diffuse + specularColor) * radiance * NdotL;
}
15 changes: 15 additions & 0 deletions assets/shaders/standard_pbr.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#pragma option({"key": "ENABLE_IBL", "default": 0, "type": "Batch"})

#pragma option({"key": "ENABLE_SHADOW", "default": 0, "type": "Pass"})
#pragma option({"key": "ENABLE_PUNCTUAL_LIGHTS", "default": 0, "type": "Pass"})

#include "vertex/standard.hlslh"

Expand Down Expand Up @@ -266,6 +267,7 @@ void MSMain(

#include "layout/standard_shading.hlslh"
#include "lighting/pbr.hlslh"
#include "lighting/punctual_light.hlslh"

static const float4x4 biasMat = float4x4(
0.5, 0.0, 0.0, 0.5,
Expand Down Expand Up @@ -383,6 +385,19 @@ float4 FSMain(VSOutput input) : SV_TARGET
#endif
float3 e0 = BRDF(V, N, light, pbrParam) * shadow;

#if ENABLE_PUNCTUAL_LIGHTS
// Accumulate point light contributions
for (uint pi = 0; pi < PointLightCount && pi < MAX_POINT_LIGHTS; ++pi)
{
e0 += EvaluatePointLight(PointLights[pi], V, N, input.WorldPos, pbrParam);
}
// Accumulate spot light contributions
for (uint si = 0; si < SpotLightCount && si < MAX_SPOT_LIGHTS; ++si)
{
e0 += EvaluateSpotLight(SpotLights[si], V, N, input.WorldPos, pbrParam);
}
#endif

#if ENABLE_IBL
// Calculate view dot normal for Fresnel
float cosTheta = max(dot(N, V), 0.001); // Clamp to avoid zero at grazing angle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,88 @@ namespace sky {
EventBinder<ITransformEvent> binder;
};

struct PointLightData {
ColorRGB color = ColorRGB{1.f, 1.f, 1.f};
float intensity = 1.f;
float range = PointLight::DEFAULT_RANGE;
};

class PointLightComponent
: public ComponentAdaptor<PointLightData>
, public ITransformEvent {
public:
PointLightComponent() = default;
~PointLightComponent() override = default;

COMPONENT_RUNTIME_INFO(PointLightComponent)

static void Reflect(SerializationContext *context);

void SetColor(const ColorRGB &color);
const ColorRGB &GetColor() const { return data.color; }

void SetIntensity(float intensity);
float GetIntensity() const { return data.intensity; }

void SetRange(float range);
float GetRange() const { return data.range; }

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

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

PointLight *light = nullptr;
EventBinder<ITransformEvent> binder;
};

struct SpotLightComponentData {
ColorRGB color = ColorRGB{1.f, 1.f, 1.f};
float intensity = 1.f;
float range = SpotLight::DEFAULT_RANGE;
float innerAngle = SpotLight::DEFAULT_INNER_ANGLE;
float outerAngle = SpotLight::DEFAULT_OUTER_ANGLE;
};

class SpotLightComponent
: public ComponentAdaptor<SpotLightComponentData>
, public ITransformEvent {
public:
SpotLightComponent() = default;
~SpotLightComponent() override = default;

COMPONENT_RUNTIME_INFO(SpotLightComponent)

static void Reflect(SerializationContext *context);

void SetColor(const ColorRGB &color);
const ColorRGB &GetColor() const { return data.color; }

void SetIntensity(float intensity);
float GetIntensity() const { return data.intensity; }

void SetRange(float range);
float GetRange() const { return data.range; }

void SetInnerAngle(float angle);
float GetInnerAngle() const { return data.innerAngle; }

void SetOuterAngle(float angle);
float GetOuterAngle() const { return data.outerAngle; }

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

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

SpotLight *light = nullptr;
EventBinder<ITransformEvent> binder;
};

} // namespace sky


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace sky {

RDResourceLayoutPtr defaultRasterLayout;
RDUniformBufferPtr defaultGlobal;
RDUniformBufferPtr pointLightUbo;
RDUniformBufferPtr spotLightUbo;

rhi::ImagePtr hizDepth;
rhi::SamplerPtr pointSampler;
Expand Down
4 changes: 4 additions & 0 deletions engine/render/adaptor/src/RenderModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ namespace sky {
{
auto *context = SerializationContext::Get();
MainDirectLightComponent::Reflect(context);
PointLightComponent::Reflect(context);
SpotLightComponent::Reflect(context);
StaticMeshComponent::Reflect(context);
CameraComponent::Reflect(context);
SkeletalMeshComponent::Reflect(context);
Expand All @@ -58,6 +60,8 @@ namespace sky {
{
static std::string GROUP = "Render";
ComponentFactory::Get()->RegisterComponent<MainDirectLightComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<PointLightComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<SpotLightComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<StaticMeshComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<SkeletalMeshComponent>(GROUP);
ComponentFactory::Get()->RegisterComponent<CameraComponent>(GROUP);
Expand Down
Loading