Compare commits

...

3 Commits

Author SHA1 Message Date
Anish Bhobe d7c48d28c7 Uniform Buffers. 2025-07-07 19:36:54 +02:00
Anish Bhobe 1a9fac5e21 Fixed handedness of view and proj. 2025-07-04 01:31:26 +02:00
Anish Bhobe e0e4a6a766 Shader refactor and cleanup.
TODO: IBL
2025-07-03 20:05:12 +02:00
13 changed files with 295 additions and 401 deletions

View File

@ -1,21 +1,21 @@
[vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> gTextures[]; [vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> g_Textures[];
public struct RID<T> where T : IOpaqueDescriptor { public struct RID<T> where T : IOpaqueDescriptor {
private uint internal; private uint m_internal;
private const static uint INDEX_MASK = 0x0007FFFF; private const static uint INDEX_MASK = 0x0007FFFF;
private const static uint GENERATION_MASK = ~INDEX_MASK; private const static uint GENERATION_MASK = ~INDEX_MASK;
public property bool hasValue { public property bool hasValue {
get { get {
return (internal & GENERATION_MASK) > 0; return (m_internal & GENERATION_MASK) > 0;
} }
} }
public property T value { public property T value {
get { get {
// TODO: Check if has value else placeholder. // TODO: Check if has value else placeholder.
return gTextures[internal & INDEX_MASK].asOpaqueDescriptor<T>(); return g_Textures[m_internal & INDEX_MASK].asOpaqueDescriptor<T>();
} }
} }
} }

View File

@ -1,4 +1,5 @@
import Bindless; import Bindless;
import PBR;
public struct Material { public struct Material {
float4 baseColor; float4 baseColor;
@ -10,54 +11,70 @@ public struct Material {
float metallic; float metallic;
float roughness; float roughness;
public float4 getAlbedo(float2 uv, float4 inColor) { float4 GetAlbedo(float2 uv, float4 in_color) {
if (let albedoTex = albedoTextureID) { if (let albedo_tex = albedoTextureID) {
return baseColor * albedoTex.Sample(uv).rgba; return in_color * baseColor * albedo_tex.Sample(uv).rgba;
} }
return baseColor; return baseColor * in_color;
} }
public float3 getEmissive(float2 uv) { public float3 GetEmissive(float2 uv) {
if (let emissionTex = emissiveTextureID) { if (let emission_tex = emissiveTextureID) {
return emissionTex.Sample(uv).rgb * emissiveColor.rgb * emissiveColor.a; return emission_tex.Sample(uv).rgb * emissiveColor.rgb * emissiveColor.a;
} }
return emissiveColor.rgb * emissiveColor.a; return emissiveColor.rgb * emissiveColor.a;
} }
public float3 getNormal(float3 position, float3 normal, float4 tangent, float2 uv) { float3 GetNormal(float3 position, float3 vert_normal, float4 vert_tangent, float2 uv) {
float3 N = normalize(normal.xyz); float3 normal = normalize(vert_normal.xyz);
if (let normalTex = normalTextureID) { if (let normal_tex = normalTextureID) {
let vNt = normalize(2.0f * normalTex.Sample(uv).rgb - 1.0f); let normal_tanspace = normalize(2.0f * normal_tex.Sample(uv).rgb - 1.0f);
float3 T; float3 tangent;
float3 B; float3 bitangent;
if (tangent.w == 0.0f) { if (vert_tangent.w == 0.0f) {
float3 q1 = ddx(position); float3 q1 = ddx(position);
float3 q2 = ddy(position); float3 q2 = ddy(position);
float2 st1 = ddx(uv); float2 st1 = ddx(uv);
float2 st2 = ddy(uv); float2 st2 = ddy(uv);
float det = (st1.x * st2.y - st2.x * st1.y); float det = (st1.x * st2.y - st2.x * st1.y);
T = -(q1 * st2.y - q2 * st1.y) / det; tangent = -(q1 * st2.y - q2 * st1.y) / det;
T = T - N * dot(N, T); tangent = tangent - normal * dot(normal, tangent);
B = normalize(cross(N, T)); bitangent = normalize(cross(normal, tangent));
} else { } else {
T = normalize(tangent.xyz); tangent = normalize(vert_tangent.xyz);
B = tangent.w * cross(N, T); bitangent = vert_tangent.w * cross(normal, tangent);
} }
N = normalize(T * vNt.x + B * vNt.y + N * vNt.z); normal = normalize(tangent * normal_tanspace.x + bitangent * normal_tanspace.y + normal * normal_tanspace.z);
} }
return N; return normal;
} }
public float2 getMetalRough(float2 uv) { float2 GetMetalRough(float2 uv) {
if (let metalRoughTex = metalRoughTextureID) { if (let metal_rough_tex = metalRoughTextureID) {
return metalRoughTex.Sample(uv).bg * float2(metallic, roughness); return metal_rough_tex.Sample(uv).bg * float2(metallic, roughness);
} }
return float2(metallic, roughness); return float2(metallic, roughness);
} }
public IBRDF GetBRDF(
float2 uv,
float3 position,
float3 vertex_normal,
float4 vertex_tangent,
float4 vertex_color)
{
float3 normal = GetNormal(position, vertex_normal, vertex_tangent, uv);
float2 metal_rough = GetMetalRough(uv);
float3 albedo = GetAlbedo(uv, vertex_color).xyz;
float3 f0 = 0.04f.xxx;
f0 = lerp(f0, albedo, metal_rough.x);
return BRDFCookTorranceGGX(albedo, metal_rough.x, normal, metal_rough.y, f0);
}
} }

View File

@ -15,7 +15,14 @@ struct VertexOut {
struct CameraData { struct CameraData {
float4x4 view; float4x4 view;
float4x4 proj; float4x4 proj;
float4 position; float4 position;
};
struct PointLight {
float3 position;
float range;
float3 color;
float attenuation;
}; };
struct DirectionalLight { struct DirectionalLight {
@ -26,25 +33,15 @@ struct DirectionalLight {
}; };
struct LightData { struct LightData {
PointLight* pointLights; PointLight* pointLights;
DirectionalLight* dirLights; DirectionalLight* dirLights;
uint pointLightCount; uint pointLightCount;
uint dirLightCount; uint dirLightCount;
PointLight getPointLight(uint idx) {
if (idx >= pointLightCount) return pointLights[0];
return pointLights[idx];
}
DirectionalLight getDirectionalLight(uint idx) {
if (idx >= dirLightCount) return dirLights[0];
return dirLights[idx];
}
}; };
struct PerFrameData { struct PerFrameData {
CameraData camera; CameraData camera;
LightData lightData; LightData lightData;
}; };
uniform ParameterBlock<PerFrameData> pfd; uniform ParameterBlock<PerFrameData> pfd;
@ -64,203 +61,64 @@ VertexOut VertexMain(
float3 position, float3 position,
float3 normal, float3 normal,
float4 tangent, float4 tangent,
float2 texCoord0, float2 uv0,
float2 texCoord1, float2 uv1,
float4 vertexColor0, float4 vertex_color0,
) { ) {
float4 worldPosition = mul(pcb.transform, float4(position, 1.0f));
VertexOut output; VertexOut output;
output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition)); float4 worldPosition = mul(pcb.transform, float4(position, 1.0f));
output.worldPosition = worldPosition;
output.normal = normalize(mul(float4(normal.xyz, 0.0f), pcb.invTransform).xyz); output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition));
output.worldPosition = worldPosition;
output.normal = normalize(mul(float4(normal.xyz, 0.0f), pcb.invTransform).xyz);
if (tangent.w == 0.0f) { if (tangent.w == 0.0f) {
output.tangent = 0.0f.xxxx; output.tangent = 0.0f.xxxx;
} else { } else {
output.tangent = float4(normalize(mul(float4(tangent.xyz, 0.0f), pcb.invTransform).xyz), tangent.w); output.tangent = float4(normalize(mul(float4(tangent.xyz, 0.0f), pcb.invTransform).xyz), tangent.w);
} }
output.texCoord0 = texCoord0; output.texCoord0 = uv0;
output.texCoord1 = texCoord1; output.texCoord1 = uv1;
output.vertexColor0 = vertexColor0; output.vertexColor0 = vertex_color0;
return output; return output;
} }
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.
}
// Sebastian Lagarde
float3 FresnelSchlickRoughness(float cosine, float3 F_0, float Roughness)
{
return F_0 + (max((1.0f - Roughness).xxx, F_0) - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
}
float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 Normal, float Metallic, float Roughness, float3 F_0, float3 LightDir, float LightDistance)
{
float Attenuation = 1.0f / (LightDistance * LightDistance); // TODO: Controlled Attenuation
float3 Halfway = normalize(ViewDir + LightDir);
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 = FresnelSchlickRoughness(CosineFactor, F_0, Roughness);
float3 Numerator = (NormalDistribution * Geometry) * Fresnel;
float Denominator = 4.0f * NdotV * NdotL;
float3 Specular = Numerator / (Denominator + 0.00001f);
float3 K_Specular = Fresnel;
float3 K_Diffuse = 1.0f.xxx - K_Specular;
K_Diffuse *= 1.0f - Metallic;
return NdotL * Radiance * (K_Diffuse * Albedo / PI + Specular);
}
float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
{
if (pfd.lightData.pointLightCount == 0)
return 0.0f.xxx;
float3 ViewDir = normalize(pfd.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 Contrib = 0.0f;
for (uint i = 0; i < pfd.lightData.pointLightCount; ++i)
{
PointLight Light = pfd.lightData.pointLights[i];
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 G = (Light.Color & 0x00FF0000) >> 16;
//float B = (Light.Color & 0x0000FF00) >> 8;
//float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
Contrib += GetPBRContrib(Albedo, Light.color, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance);
}
return Contrib;
}
float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
{
if (pfd.lightData.dirLightCount == 0)
return 0.0f.xxx;
float3 ViewDir = normalize(pfd.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 Contrib = 0.0f;
for (uint i = 0; i < pfd.lightData.dirLightCount; ++i)
{
DirectionalLight Light = pfd.lightData.dirLights[i];
if (Light._padding0 < 0.0f)
continue;
float3 LightDir = -normalize(Light.direction);
// Color Unpack
//float R = (Light.Color & 0xFF000000) >> 24;
//float G = (Light.Color & 0x00FF0000) >> 16;
//float B = (Light.Color & 0x0000FF00) >> 8;
//float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
float3 LightColor = Light.color;
Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f);
}
return Contrib;
}
[shader("fragment")] [shader("fragment")]
float4 FragmentMain( float4 FragmentMain(
float4 position : POSITION, float4 position : POSITION,
float3 normal : NORMAL, float3 normal : NORMAL,
float4 tangent : TANGENT, float4 tangent : TANGENT,
float2 texCoord0 : TEXCOORD0, float2 uv0 : TEXCOORD0,
float2 texCoord1 : TEXCOORD1, float2 uv1 : TEXCOORD1,
float4 vertexColor0 : COLOR0, float4 color0 : COLOR0,
) : SV_Target0 { ) : SV_Target0 {
IBRDF brdf = pcb.material.GetBRDF(uv0, position.xyz, normal.xyz, tangent, color0);
float3 N = pcb.material.getNormal(position.xyz, normal.xyz, tangent, texCoord0); float3 view_dir = normalize(pfd.camera.position.xyz - position.xyz);
float2 metalRough = pcb.material.getMetalRough(texCoord0);
float4 albedo = pcb.material.getAlbedo(texCoord0, vertexColor0); float3 point_contrib = 0.0f.xxx;
float3 viewDir = normalize(position.xyz - pfd.camera.position.xyz); for (int i = 0; i < pfd.lightData.pointLightCount; ++i) {
PointLight point_light = pfd.lightData.pointLights[i];
float3 light_dir = float3(point_light.position) - position.xyz;
float light_dist = length(light_dir);
if (light_dist > point_light.range)
continue;
light_dir /= light_dist; // Normalization
float attenuation = 1.0f / (light_dist * light_dist); // TODO: Controlled Attenuation
float3 radiance = point_light.color * attenuation;
float3 pointContrib = GetPointLightInfluence(albedo.rgb, metalRough, position.xyz, N); point_contrib += brdf.Evaluate(radiance, view_dir, light_dir);
float3 dirContrib = GetDirectionalLightInfluence(albedo.rgb, metalRough, position.xyz, N); }
return float4(pcb.material.getEmissive(texCoord0) + pointContrib + dirContrib, 1.0f); float3 dir_contrib = 0.0f.xxx;
for (int i = 0; i < pfd.lightData.dirLightCount; ++i) {
DirectionalLight dir_light = pfd.lightData.dirLights[i];
dir_contrib += brdf.Evaluate(dir_light.color, view_dir, -normalize(dir_light.direction));
}
return float4(pcb.material.GetEmissive(uv0) + point_contrib + dir_contrib, 1.0f);
} }

View File

@ -2,34 +2,34 @@ public static const float PI = 3.14159265f;
float TrowbridgeReitzGGX(float3 normal, float3 halfway, float roughness) float TrowbridgeReitzGGX(float3 normal, float3 halfway, float roughness)
{ {
float alpha = roughness * roughness; float alpha = roughness * roughness;
float alpha2 = alpha * alpha; float alpha2 = alpha * alpha;
float nDotH = max(dot(normal, halfway), 0.0f); float n_dot_h = max(dot(normal, halfway), 0.0f);
float nDotH2 = nDotH * nDotH; float n_dot_h_2 = n_dot_h * n_dot_h;
float numerator = alpha2; float numerator = alpha2;
float denominator = nDotH2 * (alpha2 - 1.0f) + 1.0f; float denominator = n_dot_h_2 * (alpha2 - 1.0f) + 1.0f;
denominator = PI * denominator * denominator; denominator = PI * denominator * denominator;
return numerator / denominator; return numerator / denominator;
} }
float GeometrySchlickGGX(float nDotV, float roughness) float GeometrySchlickGGX(float n_dot_v, float roughness)
{ {
float r = roughness + 1.0f; float r = roughness + 1.0f;
float k = (r * r) / 8.0f; float k = (r * r) / 8.0f;
float numerator = nDotV; float numerator = n_dot_v;
float denominator = nDotV * (1.0f - k) + k; float denominator = n_dot_v * (1.0f - k) + k;
return numerator / denominator; return numerator / denominator;
} }
float GeometrySmith(float nDotV, float nDotL, float roughness) float GeometrySmith(float n_dot_v, float n_dot_l, float roughness)
{ {
float ggx1 = GeometrySchlickGGX(nDotV, roughness); float ggx1 = GeometrySchlickGGX(n_dot_v, roughness);
float ggx2 = GeometrySchlickGGX(nDotL, roughness); float ggx2 = GeometrySchlickGGX(n_dot_l, roughness);
return ggx1 * ggx2; return ggx1 * ggx2;
} }
@ -45,63 +45,46 @@ float3 FresnelSchlickRoughness(float cosine, float3 f_0, float roughness)
return f_0 + (max((1.0f - roughness).xxx, f_0) - f_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. return f_0 + (max((1.0f - roughness).xxx, f_0) - f_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
} }
float3 GetPBRContrib( public interface IBRDF {
float3 albedo, public float3 Evaluate(float3 radiance, float3 view_dir, float3 ligth_dir);
float3 radiance,
float3 viewDir,
float3 normal,
float3 lightDir,
float2 metalRough,
float3 f_0)
{
float3 halfway = normalize(viewDir + lightDir);
float cosineFactor = max(dot(halfway, viewDir), 0.0f);
float nDotV = max(dot(normal, viewDir), 0.0f);
float nDotL = max(dot(normal, lightDir), 0.0f);
float normalDistribution = TrowbridgeReitzGGX(normal, halfway, metalRough.y);
float geometry = GeometrySmith(nDotV, nDotL, metalRough.y);
float3 fresnel = FresnelSchlickRoughness(cosineFactor, f_0, metalRough.y);
float3 numerator = (normalDistribution * geometry) * fresnel;
float denominator = 4.0f * nDotV * nDotL;
float3 specular = numerator / (denominator + 0.00001f);
float3 kSpecular = fresnel;
float3 kDiffuse = 1.0f - kSpecular;
kDiffuse *= 1.0f - metalRough.x;
return nDotL * radiance * (kDiffuse * albedo / PI + specular);
} }
public struct PointLight { public struct BRDFCookTorranceGGX : IBRDF {
public float3 position; float3 m_albedo;
public float range; float m_metallic;
public float3 color; float3 m_normal;
float attenuation; float m_roughness;
float3 m_f0;
public float3 getInfluence(float3 albedo, float2 metalRough, float3 viewDir, float3 worldPosition, float3 normal, float3 f_0) { public __init(float3 albedo, float metallic, float3 normal, float roughness, float3 f0) {
m_albedo = albedo;
m_metallic = metallic;
m_normal = normal;
m_roughness = roughness;
m_f0 = f0;
}
float3 lightDir = float3(position) - worldPosition; public float3 Evaluate(float3 radiance, float3 view_dir, float3 light_dir)
float lightDistance = length(lightDir); {
float3 halfway = normalize(view_dir + light_dir);
if (lightDistance > range) float cosine_factor = max(dot(halfway, view_dir), 0.0f);
return 0.0f.xxx; float n_dot_v = max(dot(m_normal, view_dir), 0.0f);
float n_dot_l = max(dot(m_normal, light_dir), 0.0f);
float normal_dist = TrowbridgeReitzGGX(m_normal, halfway, m_roughness);
float geometry = GeometrySmith(n_dot_v, n_dot_l, m_roughness);
float3 fresnel = FresnelSchlickRoughness(cosine_factor, m_f0, m_roughness);
lightDir /= lightDistance; // Normalization float3 numerator = (normal_dist * geometry) * fresnel;
float denominator = 4.0f * n_dot_v * n_dot_l;
float3 specular = numerator / (denominator + 0.00001f);
// Color Unpack float3 k_specular = fresnel;
//float R = (Light.Color & 0xFF000000) >> 24; float3 k_diffuse = 1.0f.xxx - k_specular;
//float G = (Light.Color & 0x00FF0000) >> 16;
//float B = (Light.Color & 0x0000FF00) >> 8;
//float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 k_diffuse *= 1.0f - m_metallic;
let attenuation_ = attenuation / (lightDistance * lightDistance); return n_dot_l * radiance * (k_diffuse * m_albedo / PI + specular);
let radiance = color * attenuation_; }
}
return GetPBRContrib(albedo, radiance, viewDir, normal, lightDir, metalRough, f_0);
}
};

View File

@ -122,7 +122,7 @@
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<CustomBuild> <CustomBuild>
<Command>slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command> <Command>slangc %(FullPath) -profile sm_6_6 -target spirv -matrix-layout-column-major -o %(Filename).spv</Command>
</CustomBuild> </CustomBuild>
<CustomBuild> <CustomBuild>
<Message>Compiling %(Filename)</Message> <Message>Compiling %(Filename)</Message>

View File

@ -39,24 +39,6 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="README.md">
<Filter>Resource Files</Filter>
</None>
<None Include="PLAN.md">
<Filter>Resource Files</Filter>
</None>
<None Include=".clang-format">
<Filter>Resource Files\Config</Filter>
</None>
<None Include=".gitignore">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="vcpkg.json">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="vcpkg-configuration.json">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="Assets\Shaders\Bindless.slang"> <None Include="Assets\Shaders\Bindless.slang">
<Filter>Resource Files\Shader Files</Filter> <Filter>Resource Files\Shader Files</Filter>
</None> </None>
@ -66,6 +48,30 @@
<None Include="Assets\Shaders\Material.slang"> <None Include="Assets\Shaders\Material.slang">
<Filter>Resource Files\Shader Files</Filter> <Filter>Resource Files\Shader Files</Filter>
</None> </None>
<None Include="..\vcpkg-configuration.json">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="..\vcpkg.json">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="..\.gitignore">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="..\.clang-format">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="..\.gitattributes">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="..\README.md">
<Filter>Resource Files</Filter>
</None>
<None Include="..\LICENSE">
<Filter>Resource Files</Filter>
</None>
<None Include="..\PLAN.md">
<Filter>Resource Files</Filter>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Source\AppState.cpp"> <ClCompile Include="Source\AppState.cpp">
@ -104,9 +110,6 @@
<ClCompile Include="Source\TextureManager.cpp"> <ClCompile Include="Source\TextureManager.cpp">
<Filter>Source Files\Render</Filter> <Filter>Source Files\Render</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Source\RID.cpp">
<Filter>Source Files\Render</Filter>
</ClCompile>
<ClCompile Include="Source\RenderDevice.cpp"> <ClCompile Include="Source\RenderDevice.cpp">
<Filter>Source Files\Render</Filter> <Filter>Source Files\Render</Filter>
</ClCompile> </ClCompile>
@ -140,9 +143,6 @@
<ClInclude Include="Source\ModelLoader.h"> <ClInclude Include="Source\ModelLoader.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Source\MathUtil.h">
<Filter>Header Files\Util</Filter>
</ClInclude>
<ClInclude Include="Source\MacroUtils.h"> <ClInclude Include="Source\MacroUtils.h">
<Filter>Header Files\Util</Filter> <Filter>Header Files\Util</Filter>
</ClInclude> </ClInclude>

View File

@ -74,23 +74,21 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
app_state.miscData->lightData.pointLightCount = _countof( point_light ); app_state.miscData->lightData.pointLightCount = _countof( point_light );
app_state.renderDevice->bufferManager->WriteToBuffer( app_state.miscData->pointLights, point_light ); app_state.renderDevice->bufferManager->WriteRangeToBuffer( app_state.miscData->pointLights, point_light );
Blaze::MiscData::DirectionalLight dir_light[] = { Blaze::MiscData::DirectionalLight dir_light[] = {
{ {
.direction = { 1.0f, -1.0f, 0.0f }, .direction = { -1.0f, -1.0f, -1.0f },
.color = { 12.0f, 10.0f, 5.0f }, .color = { 12.0f, 10.0f, 5.0f },
}, },
}; };
app_state.miscData->lightData.dirLightCount = _countof( dir_light ); app_state.miscData->lightData.dirLightCount = _countof( dir_light );
app_state.renderDevice->bufferManager->WriteToBuffer( app_state.miscData->directionalLights, dir_light ); app_state.renderDevice->bufferManager->WriteRangeToBuffer( app_state.miscData->directionalLights, dir_light );
memcpy( app_state.renderDevice->bufferManager->WriteObjectToBuffer(
app_state.miscData->cameraUniformBufferPtr + sizeof( Blaze::MiscData::CameraData ), app_state.miscData->uniformBuffer, sizeof( Blaze::MiscData::CameraData ), app_state.miscData->lightData );
&app_state.miscData->lightData,
sizeof app_state.miscData->lightData );
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
@ -143,7 +141,7 @@ SDL_AppResult SDL_AppIterate( void* appstate )
VK_CHECK( vkAcquireNextImageKHR( VK_CHECK( vkAcquireNextImageKHR(
render_device.device, render_device.device,
render_device.swapchain, render_device.swapchain,
std::numeric_limits<uint32_t>::max(), UINT32_MAX,
current_frame.imageAcquiredSemaphore, current_frame.imageAcquiredSemaphore,
nullptr, nullptr,
&current_image_index ) ); &current_image_index ) );

View File

@ -40,7 +40,7 @@ Blaze::Buffer& BufferManager::FetchBufferUnchecked( BufferID const& rid )
return m_buffers[inner_index]; return m_buffers[inner_index];
} }
void BufferManager::WriteToBufferImpl( BufferID const& rid, void const* data, size_t const size ) void BufferManager::WriteToBuffer( BufferID const& rid, void const* data, size_t const size, size_t const offset )
{ {
ASSERT( IsValidID( rid ) ); ASSERT( IsValidID( rid ) );
@ -48,7 +48,7 @@ void BufferManager::WriteToBufferImpl( BufferID const& rid, void const* data, si
ASSERT( size <= buffer.size ); ASSERT( size <= buffer.size );
memcpy( buffer.mappedData, data, size ); memcpy( buffer.mappedData + offset, data, size );
} }
bool BufferManager::IsValidID( BufferID const& rid ) const bool BufferManager::IsValidID( BufferID const& rid ) const
@ -248,6 +248,73 @@ Blaze::BufferID BufferManager::CreateStorageBuffer( size_t const size )
return *reinterpret_cast<BufferID*>( &index ); return *reinterpret_cast<BufferID*>( &index );
} }
Blaze::BufferID BufferManager::CreateUniformBuffer( size_t const size )
{
ASSERT( not m_freeList.Empty() );
Buffer* buffer_slot = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
++m_count;
ASSERT( m_renderDevice );
RenderDevice const& render_device = *m_renderDevice;
VkBufferCreateInfo const buffer_create_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = size,
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr allocation_create_info = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocation_info;
VkBuffer storage_buffer;
VmaAllocation storage_buffer_allocation;
VK_CHECK( vmaCreateBuffer(
render_device.gpuAllocator,
&buffer_create_info,
&allocation_create_info,
&storage_buffer,
&storage_buffer_allocation,
&allocation_info ) );
VkBufferDeviceAddressInfo const device_address_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.pNext = nullptr,
.buffer = storage_buffer,
};
VkDeviceAddress const device_address = vkGetBufferDeviceAddress( render_device.device, &device_address_info );
// NOTE: bufferSlot preserves index between uses.
uint32_t index = buffer_slot->index;
new ( buffer_slot ) Buffer{
.buffer = storage_buffer,
.allocation = storage_buffer_allocation,
.mappedData = static_cast<std::byte*>( allocation_info.pMappedData ),
.deviceAddress = device_address,
.size = size,
.index = index,
};
// NOTE: Memory hackery to create BufferID;
return *reinterpret_cast<BufferID*>( &index );
}
void BufferManager::FreeBuffer( BufferID* rid ) void BufferManager::FreeBuffer( BufferID* rid )
{ {
if ( not IsValidID( *rid ) ) return; if ( not IsValidID( *rid ) ) return;

View File

@ -52,7 +52,6 @@ private:
void DestroyBuffer( Buffer& buf ); void DestroyBuffer( Buffer& buf );
Buffer& FetchBufferUnchecked( BufferID const& rid ); Buffer& FetchBufferUnchecked( BufferID const& rid );
void WriteToBufferImpl( BufferID const& rid, void const* data, size_t size );
public: public:
[[nodiscard]] bool IsValidID( BufferID const& rid ) const; [[nodiscard]] bool IsValidID( BufferID const& rid ) const;
@ -60,6 +59,7 @@ public:
BufferID CreateVertexBuffer( size_t size ); BufferID CreateVertexBuffer( size_t size );
BufferID CreateIndexBuffer( size_t size ); BufferID CreateIndexBuffer( size_t size );
BufferID CreateStorageBuffer( size_t size ); BufferID CreateStorageBuffer( size_t size );
BufferID CreateUniformBuffer( size_t size );
void FreeBuffer( BufferID* rid ); void FreeBuffer( BufferID* rid );
@ -67,13 +67,23 @@ public:
std::optional<VkBuffer> FetchBuffer( BufferID const& rid ); std::optional<VkBuffer> FetchBuffer( BufferID const& rid );
std::optional<VkDeviceAddress> FetchDeviceAddress( BufferID const& rid ); std::optional<VkDeviceAddress> FetchDeviceAddress( BufferID const& rid );
void WriteToBuffer( BufferID const& rid, void const* data, size_t size, size_t offset );
// Utility to directly muck the data // Utility to directly muck the data
void WriteToBuffer( BufferID const& rid, std::ranges::contiguous_range auto const& data ) void WriteRangeToBuffer( BufferID const& rid, std::ranges::contiguous_range auto const& data )
{ {
WriteToBufferImpl( WriteToBuffer(
rid, rid,
std::ranges::data( data ), std::ranges::data( data ),
std::ranges::size( data ) * sizeof( std::ranges::range_value_t<decltype( data )> ) ); std::ranges::size( data ) * sizeof( std::ranges::range_value_t<decltype( data )> ),
0 );
}
// Utility to directly muck the data
void WriteObjectToBuffer( BufferID const& rid, size_t const offset, auto const& data )
requires not std::ranges::range<decltype( data )>
{
WriteToBuffer( rid, &data, sizeof data, offset );
} }
static BufferManager* Create( GlobalMemory* mem, RenderDevice* render_device, uint32_t max_count ); static BufferManager* Create( GlobalMemory* mem, RenderDevice* render_device, uint32_t max_count );

View File

@ -161,7 +161,7 @@ bool MiscData::Init( RenderDevice const& render_device )
.depthClampEnable = VK_TRUE, .depthClampEnable = VK_TRUE,
.rasterizerDiscardEnable = VK_FALSE, .rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL, .polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE, .cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE, .depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f, .depthBiasConstantFactor = 0.0f,
@ -268,14 +268,12 @@ bool MiscData::Init( RenderDevice const& render_device )
// Camera // Camera
{ {
cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 2.0f, -2.0f, 1.0f ); cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 1.0f, 2.0f, 1.0f );
cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f ); cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f ); cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f );
cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraData.cameraPosition, cameraTarget, cameraUp ); cameraData.viewMatrix = DirectX::XMMatrixLookAtRH( cameraData.cameraPosition, cameraTarget, cameraUp );
cameraData.projectionMatrix = cameraData.projectionMatrix =
DirectX::XMMatrixPerspectiveFovLH( DirectX::XMConvertToRadians( 70.0f ), 16.0f / 9.0f, 0.1f, 1000.0f ); DirectX::XMMatrixPerspectiveFovRH( DirectX::XMConvertToRadians( 70.0f ), 16.0f / 9.0f, 0.1f, 1000.0f );
cameraUniformBufferSize = sizeof( CameraData ) + sizeof( LightData );
} }
@ -295,45 +293,10 @@ bool MiscData::Init( RenderDevice const& render_device )
// Uniform Buffer // Uniform Buffer
{ {
uniformBuffer = render_device.bufferManager->CreateUniformBuffer( sizeof( CameraData ) + sizeof( LightData ) );
VkBufferCreateInfo const buffer_create_info = { render_device.bufferManager->WriteObjectToBuffer( uniformBuffer, 0, cameraData );
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, render_device.bufferManager->WriteObjectToBuffer( uniformBuffer, sizeof CameraData, lightData );
.pNext = nullptr,
.flags = 0,
.size = cameraUniformBufferSize,
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
VmaAllocationCreateInfo constexpr allocation_create_info = {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VmaAllocationInfo allocation_info;
VK_CHECK( vmaCreateBuffer(
render_device.gpuAllocator,
&buffer_create_info,
&allocation_create_info,
&cameraUniformBuffer,
&cameraUniformBufferAllocation,
&allocation_info ) );
if ( allocation_info.pMappedData )
{
cameraUniformBufferPtr = static_cast<uint8_t*>( allocation_info.pMappedData );
memcpy( cameraUniformBufferPtr, &cameraData, sizeof cameraData );
memcpy( cameraUniformBufferPtr + sizeof cameraData, &lightData, sizeof lightData );
}
} }
// Descriptors // Descriptors
@ -364,9 +327,9 @@ bool MiscData::Init( RenderDevice const& render_device )
VK_CHECK( vkAllocateDescriptorSets( device, &descriptor_set_allocate_info, &descriptorSet ) ); VK_CHECK( vkAllocateDescriptorSets( device, &descriptor_set_allocate_info, &descriptorSet ) );
VkDescriptorBufferInfo const descriptor_buffer_info = { VkDescriptorBufferInfo const descriptor_buffer_info = {
.buffer = cameraUniformBuffer, .buffer = render_device.bufferManager->FetchBuffer( uniformBuffer ).value(),
.offset = 0, .offset = 0,
.range = cameraUniformBufferSize, .range = sizeof( CameraData ) + sizeof( LightData ),
}; };
VkWriteDescriptorSet write_descriptor_sets[] = { VkWriteDescriptorSet write_descriptor_sets[] = {
@ -464,7 +427,8 @@ void MiscData::Destroy( Blaze::RenderDevice const& render_device )
VkDevice const device = render_device.device; VkDevice const device = render_device.device;
vkDestroyDescriptorPool( device, Take( descriptorPool ), nullptr ); vkDestroyDescriptorPool( device, Take( descriptorPool ), nullptr );
vmaDestroyBuffer( render_device.gpuAllocator, Take( cameraUniformBuffer ), Take( cameraUniformBufferAllocation ) );
render_device.bufferManager->FreeBuffer( &uniformBuffer );
render_device.bufferManager->FreeBuffer( &pointLights ); render_device.bufferManager->FreeBuffer( &pointLights );
render_device.bufferManager->FreeBuffer( &directionalLights ); render_device.bufferManager->FreeBuffer( &directionalLights );

View File

@ -58,10 +58,7 @@ struct MiscData
BufferID directionalLights; BufferID directionalLights;
LightData lightData; LightData lightData;
VkBuffer cameraUniformBuffer; BufferID uniformBuffer;
VmaAllocation cameraUniformBufferAllocation;
size_t cameraUniformBufferSize;
uint8_t* cameraUniformBufferPtr;
VkDescriptorPool descriptorPool; VkDescriptorPool descriptorPool;
VkDescriptorSet descriptorSet; VkDescriptorSet descriptorSet;

View File

@ -507,15 +507,15 @@ uint32_t ProcessMaterial( RenderDevice* render_device, Model* model, cgltf_mater
uint32_t const material_idx = static_cast<uint32_t>( model->materials.size() ); uint32_t const material_idx = static_cast<uint32_t>( model->materials.size() );
model->materials.push_back( { model->materials.push_back( {
sampler, .sampler = sampler,
base_color_factor, .baseColor = base_color_factor,
emissive_factor, .emission = emissive_factor,
base_color_texture, .albedoTextureID = base_color_texture,
normal_texture, .normalTextureID = normal_texture,
metal_rough_texture, .metalRoughTextureID = metal_rough_texture,
emissive_texture, .emissiveTextureID = emissive_texture,
roughness, .metallic = metallic,
metallic, .roughness = roughness,
} ); } );
return material_idx; return material_idx;
@ -793,12 +793,12 @@ Entity* LoadModel( Blaze::RenderDevice* render_device, EntityManager* entity_man
entity->model.vertexBuffer = render_device->bufferManager->CreateVertexBuffer( vertices.size() * sizeof vertices[0] ); entity->model.vertexBuffer = render_device->bufferManager->CreateVertexBuffer( vertices.size() * sizeof vertices[0] );
if ( not entity->model.vertexBuffer ) return nullptr; if ( not entity->model.vertexBuffer ) return nullptr;
render_device->bufferManager->WriteToBuffer( entity->model.vertexBuffer, vertices ); render_device->bufferManager->WriteRangeToBuffer( entity->model.vertexBuffer, vertices );
entity->model.indexBuffer = render_device->bufferManager->CreateIndexBuffer( indices.size() * sizeof indices[0] ); entity->model.indexBuffer = render_device->bufferManager->CreateIndexBuffer( indices.size() * sizeof indices[0] );
if ( not entity->model.indexBuffer ) return nullptr; if ( not entity->model.indexBuffer ) return nullptr;
render_device->bufferManager->WriteToBuffer( entity->model.indexBuffer, std::span{ indices } ); render_device->bufferManager->WriteRangeToBuffer( entity->model.indexBuffer, indices );
cgltf_free( gltf_model ); cgltf_free( gltf_model );
return entity; return entity;

View File

@ -57,8 +57,8 @@ struct Material
TextureID normalTextureID; TextureID normalTextureID;
TextureID metalRoughTextureID; TextureID metalRoughTextureID;
TextureID emissiveTextureID; TextureID emissiveTextureID;
float roughness = 1.0f;
float metallic = 1.0f; float metallic = 1.0f;
float roughness = 1.0f;
[[nodiscard]] bool IsNull() const [[nodiscard]] bool IsNull() const
{ {