Point Light PBR Support.

This commit is contained in:
Anish Bhobe 2024-07-24 19:51:26 +02:00
parent cc67b17ae4
commit 3e30dfbac9
5 changed files with 176 additions and 69 deletions

View File

@ -46,7 +46,7 @@ struct VertexData
vec4 m_Normal; vec4 m_Normal;
vec2 m_TexCoord0 = vec2{0.0f, 0.0f}; vec2 m_TexCoord0 = vec2{0.0f, 0.0f};
vec2 m_TexCoord1 = vec2{0.0f, 0.0f}; vec2 m_TexCoord1 = vec2{0.0f, 0.0f};
vec4 m_Color0 = vec4{1.0f, 0.0f, 1.0f, 1.0f}; vec4 m_Color0 = vec4{1.0f, 1.0f, 1.0f, 1.0f};
}; };
struct Model struct Model

View File

@ -90,7 +90,7 @@ main(int, char **)
.m_Position = vec4{0.0f, 2.0f, 2.0f, 1.0f}, .m_Position = vec4{0.0f, 2.0f, 2.0f, 1.0f},
}; };
lightManager.AddDirectional(vec3(0.0f, -1.0f, 0.0f), {0.0f, 1.0f, 0.0f}); //lightManager.AddDirectional(vec3(0.0f, -1.0f, 0.0f), {0.0f, 1.0f, 0.0f});
lightManager.AddPoint(vec3{2.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 15.0f); lightManager.AddPoint(vec3{2.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 15.0f);
lightManager.AddPoint(vec3{-2.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, 15.0f); lightManager.AddPoint(vec3{-2.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, 15.0f);

View File

@ -42,7 +42,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou
.binding = 0, .binding = 0,
.descriptorType = vk::DescriptorType::eUniformBuffer, .descriptorType = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1, .descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex, .stageFlags = vk::ShaderStageFlagBits::eAll,
}, },
}; };
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {

View File

@ -39,7 +39,7 @@ struct Block
uint NodeIdx; uint NodeIdx;
}; };
struct Light struct PointLight
{ {
float Position[3]; float Position[3];
float Range; float Range;
@ -47,6 +47,14 @@ struct Light
float Intensity; float Intensity;
}; };
struct DirectionalLight
{
float Direction[3];
float Validity_;
uint Color;
float Intensity;
};
struct CameraData struct CameraData
{ {
float4x4 View; float4x4 View;
@ -60,11 +68,14 @@ struct CameraData
#define INVALID_HANDLE 0xFFFFFFFF #define INVALID_HANDLE 0xFFFFFFFF
static const float PI = 3.14159265f;
[[vk::binding(0, 0)]] StructuredBuffer<float4> VertexBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<float4> VertexBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<MaterialData> MaterialsBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<MaterialData> MaterialsBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<TransformData> NodeBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<TransformData> NodeBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<Light> LightBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<PointLight> PointLightBuffer[];
[[vk::binding(0, 0)]] StructuredBuffer<DirectionalLight> DirectionalLightBuffer[];
[[vk::binding(1, 0)]] Texture2D<float4> Textures[]; [[vk::binding(1, 0)]] Texture2D<float4> Textures[];
[[vk::binding(1, 0)]] SamplerState ImmutableSamplers[]; [[vk::binding(1, 0)]] SamplerState ImmutableSamplers[];

View File

@ -13,102 +13,198 @@ struct FS_Output
float4 ColorTarget : SV_Target0; float4 ColorTarget : SV_Target0;
}; };
float4 GetAlbedo(int MaterialIdx, float2 UV) float4 GetAlbedo(float2 UV, float4 InColor)
{ {
uint AlbedoTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][MaterialIdx].AlbedoTex; float4 AlbedoFactor = (float4) MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].AlbedoFactor;
if (AlbedoTexId == INVALID_HANDLE) uint AlbedoTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].AlbedoTex;
{
return (float4) MaterialsBuffer[PushConstant.MaterialBufferHandle][MaterialIdx].AlbedoFactor; return AlbedoFactor * InColor * (AlbedoTexId != INVALID_HANDLE ? Textures[AlbedoTexId].Sample(ImmutableSamplers[AlbedoTexId], UV) : 1.0f.xxxx);
}
else
{
return Textures[AlbedoTexId].Sample(ImmutableSamplers[AlbedoTexId], UV);
}
} }
float2 GetMetalRough(int MaterialIdx, float2 UV) float3 GetEmissive(float2 UV)
{ {
uint MetalRoughTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][MaterialIdx].MetalRoughTex; float3 EmissionFactor = (float3) MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].EmissionFactor;
if (MetalRoughTexId == INVALID_HANDLE) uint EmissionTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].EmissionTex;
{
return float2(MaterialsBuffer[PushConstant.MaterialBufferHandle][MaterialIdx].MetalFactor, MaterialsBuffer[PushConstant.MaterialBufferHandle][MaterialIdx].RoughFactor); return EmissionFactor * (EmissionTexId != INVALID_HANDLE ? Textures[EmissionTexId].Sample(ImmutableSamplers[EmissionTexId], UV).rgb : 1.0f.xxx);
}
else
{
return Textures[MetalRoughTexId].Sample(ImmutableSamplers[MetalRoughTexId], UV).bg; // Metal in Blue, Roughness in Green
}
} }
float3 GetDirectionalLightInfluence(float3 Normal) float3 GetNormal(float3 Position, float3 Normal, float2 UV)
{
float3 N = normalize(Normal);
uint NormalTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].NormalTex;
if (NormalTexId == INVALID_HANDLE)
{
return N;
}
float3 TangentSpaceNormal = Textures[NormalTexId].Sample(ImmutableSamplers[NormalTexId], UV).xyz * 2.0f - 1.0f;
float3 q1 = ddx(Position);
float3 q2 = ddy(Position);
float2 st1 = ddx(UV);
float2 st2 = ddy(UV);
float3 T = normalize(q1 * st2.y - q2 * st1.y).xyz;
float3 B = -normalize(cross(N, T));
float3x3 TBN = float3x3(T, B, N); // Construction is Row by Row.
return normalize(mul(TangentSpaceNormal, TBN)); // Post multiple to avoid transpose.
}
float2 GetMetalRough(float2 UV)
{
float2 MetalRoughFactors = float2(MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].MetalFactor, MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].RoughFactor);
uint MetalRoughTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].MetalRoughTex;
return MetalRoughFactors * (MetalRoughTexId != INVALID_HANDLE ? Textures[MetalRoughTexId].Sample(ImmutableSamplers[MetalRoughTexId], UV).bg : 1.0f.xx); // Metal is B, Rough is G.
}
float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness)
{
float Coeff = Roughness * Roughness;
float Alpha2 = Coeff * Coeff;
float NdotH = max(dot(Normal, Halfway), 0.0f);
float NdotH2 = NdotH * NdotH;
float Numerator = Alpha2;
float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f;
Denominator = PI * Denominator * Denominator;
return Numerator / Denominator;
}
float GeometrySchlickGGX(float NdotV, float Roughness)
{
float R = Roughness + 1.0f;
float K = (R * R) / 8.0f;
float Numerator = NdotV;
float Denominator = NdotV * (1.0f - K) + K;
return Numerator / Denominator;
}
float GeometrySmith(float NdotV, float NdotL, float Roughness)
{
float GGX1 = GeometrySchlickGGX(NdotV, Roughness);
float GGX2 = GeometrySchlickGGX(NdotL, Roughness);
return GGX1 * GGX2;
}
// https://en.wikipedia.org/wiki/Schlick%27s_approximation
float3 FresnelSchlick(float cosine, float3 F_0)
{
return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
}
float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
{ {
if (PushConstant.LightHandle == INVALID_HANDLE) if (PushConstant.LightHandle == INVALID_HANDLE)
return float3(0.0f, 0.0f, 0.0f); return 0.0f.xxx;
uint Count = IndexerCount(PushConstant.DirectionLightIndexer);
float3 Contrib = 0.0f;
for (uint i = 0; i < Count; ++i)
{
Light Light = LightBuffer[PushConstant.LightHandle][i];
float3 LightDir = - (float3) Light.Position; // Position is actually direction for directionalLight; LightDir is Direction towards the light (-direction)
float DiffuseFactor = max(dot(Normal, LightDir), 0.0f);
float R = (Light.Color & 0xFF000000) >> 24;
float G = (Light.Color & 0x00FF0000) >> 16;
float B = (Light.Color & 0x0000FF00) >> 8;
float3 Color = float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
float3 Diffuse = DiffuseFactor * Color;
Contrib += (Light.Range < 0 ? float3(0.0f, 0.0f, 0.0f) : Diffuse);
}
return Contrib;
}
float3 GetPointLightInfluence(float3 Position, float3 Normal)
{
if (PushConstant.LightHandle == INVALID_HANDLE)
return float3(0.0f, 0.0f, 0.0f);
uint Offset = IndexerOffset(PushConstant.PointLightIndexer); uint Offset = IndexerOffset(PushConstant.PointLightIndexer);
uint Count = IndexerCount(PushConstant.PointLightIndexer); uint Count = IndexerCount(PushConstant.PointLightIndexer);
float3 ViewDir = normalize(Camera.Position.xyz - Position);
float Metallic = MetalRough.r;
float Roughness = MetalRough.g;
// Dielectric F_0 based on LearnOpenGL.
// TODO: Cite
float3 F_0 = 0.04f.xxx;
F_0 = lerp(F_0, Albedo, Metallic);
float3 LightAmp = float3(23.47f, 21.31f, 20.79f);
float3 Contrib = 0.0f; float3 Contrib = 0.0f;
for (uint i = 0; i < Count; ++i) for (uint i = 0; i < Count; ++i)
{ {
Light Light = LightBuffer[PushConstant.LightHandle][i + Offset]; PointLight Light = PointLightBuffer[PushConstant.LightHandle][i + Offset];
float3 LightDir = normalize(((float3)Light.Position) - Position);
float DiffuseFactor = max(dot(Normal, LightDir), 0.0f); if (Light.Range < 0.0f)
continue;
float3 LightDir = float3(Light.Position) - Position;
float LightDistance = length(LightDir);
if (LightDistance > Light.Range)
continue;
LightDir /= LightDistance; // Normalization
// Color Unpack
float R = (Light.Color & 0xFF000000) >> 24; float R = (Light.Color & 0xFF000000) >> 24;
float G = (Light.Color & 0x00FF0000) >> 16; float G = (Light.Color & 0x00FF0000) >> 16;
float B = (Light.Color & 0x0000FF00) >> 8; float B = (Light.Color & 0x0000FF00) >> 8;
float3 Color = float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 float3 LightColor = LightAmp * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
float3 Diffuse = DiffuseFactor * Color; float Attenuation = 1.0f / (LightDistance * LightDistance); // TODO: Controlled Attenuation
float3 Halfway = normalize(ViewDir + LightDir);
Contrib += (Light.Range < 0 ? float3(0.0f, 0.0f, 0.0f) : Diffuse); float CosineFactor = max(dot(Halfway, ViewDir), 0.0f);
float NdotV = max(dot(Normal, ViewDir), 0.0f);
float NdotL = max(dot(Normal, LightDir), 0.0f);
float3 Radiance = LightColor * Attenuation;
float NormalDistribution = TrowbridgeReitzGGX(Normal, Halfway, Roughness);
float Geometry = GeometrySmith(NdotV, NdotL, Roughness);
float3 Fresnel = FresnelSchlick(CosineFactor, F_0);
float3 Numerator = (NormalDistribution * Geometry) * Fresnel;
float Denominator = 4.0f * NdotV * NdotL;
float3 Specular = Numerator / max(Denominator, 0.00001);
float3 K_Specular = Fresnel;
float3 K_Diffuse = 1.0f.xxx - K_Specular;
K_Diffuse *= 1.0f - Metallic;
Contrib += NdotL * Radiance * ((K_Diffuse * Albedo / PI) + Specular);
} }
return Contrib; return Contrib;
} }
float3 Uncharted2Tonemap(float3 color)
{
float A = 0.15f;
float B = 0.50f;
float C = 0.10f;
float D = 0.20f;
float E = 0.02f;
float F = 0.30f;
float W = 11.2f;
return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
}
FS_Output main(FS_Input StageInput) FS_Output main(FS_Input StageInput)
{ {
FS_Output Output;
float3 Ambient = float3(0.02f, 0.02f, 0.02f); float3 Ambient = float3(0.02f, 0.02f, 0.02f);
float3 Normal = normalize(StageInput.InNormal.xyz); // TODO: This should be invalid on the CPU side.
if (PushConstant.MaterialIdx < 0)
{
Output.ColorTarget = float4(1.0f, 0.0f, 1.0f, 1.0f);
return Output;
}
float3 Position = StageInput.InPosition.xyz; float3 Position = StageInput.InPosition.xyz;
float3 Normal = GetNormal(Position, StageInput.InNormal.xyz, StageInput.InUV0);
float4 ObjColor = PushConstant.MaterialIdx < 0 ? StageInput.InColor : GetAlbedo(PushConstant.MaterialIdx, StageInput.InUV0); float4 AlbedoAlpha = GetAlbedo(StageInput.InUV0, StageInput.InColor);
float2 MetalRough = PushConstant.MaterialIdx < 0 ? float2(0.0f, 0.0f) : GetMetalRough(PushConstant.MaterialIdx, StageInput.InUV0); float3 Albedo = AlbedoAlpha.rgb;
float Alpha = AlbedoAlpha.a;
float2 MetalRough = GetMetalRough(StageInput.InUV0);
float3 Emission = GetEmissive(StageInput.InUV0);
float3 Color = /*GetDirectionalLightInfluence(Albedo, MetalRough, Position, Normal) +*/ GetPointLightInfluence(Albedo, MetalRough, Position, Normal);
float3 Diffuse = GetDirectionalLightInfluence(Normal) + GetPointLightInfluence(Position, Normal); Output.ColorTarget = float4(Uncharted2Tonemap(Color + Emission), Alpha);
FS_Output Output;
Output.ColorTarget = float4(ObjColor.rgb * (Diffuse + Ambient), ObjColor.a);
return Output; return Output;
} }