Compare commits

..

3 Commits

Author SHA1 Message Date
Anish Bhobe d51fc375d2 Punctual PBR lighting. 2025-07-02 03:28:19 +02:00
Anish Bhobe 9314b3504e cleanup: Attribute Loading 2025-06-30 19:57:44 +02:00
Anish Bhobe afec17cb0f Updated plan + config. 2025-06-28 22:21:51 +02:00
15 changed files with 905 additions and 519 deletions

View File

@ -0,0 +1,63 @@
import Bindless;
public struct Material {
float4 baseColor;
float4 emissiveColor;
Sampler2D.RID albedoTextureID;
Sampler2D.RID normalTextureID;
Sampler2D.RID metalRoughTextureID;
Sampler2D.RID emissiveTextureID;
float metallic;
float roughness;
public float4 getAlbedo(float2 uv, float4 inColor) {
if (let albedoTex = albedoTextureID) {
return baseColor * albedoTex.Sample(uv).rgba;
}
return baseColor;
}
public float3 getEmissive(float2 uv) {
if (let emissionTex = emissiveTextureID) {
return emissionTex.Sample(uv).rgb * emissiveColor.rgb * emissiveColor.a;
}
return emissiveColor.rgb * emissiveColor.a;
}
public float3 getNormal(float3 position, float3 normal, float4 tangent, float2 uv) {
float3 N = normalize(normal.xyz);
if (let normalTex = normalTextureID) {
let vNt = normalize(2.0f * normalTex.Sample(uv).rgb - 1.0f);
float3 T;
float3 B;
if (tangent.w == 0.0f) {
float3 q1 = ddx(position);
float3 q2 = ddy(position);
float2 st1 = ddx(uv);
float2 st2 = ddy(uv);
float det = (st1.x * st2.y - st2.x * st1.y);
T = -(q1 * st2.y - q2 * st1.y) / det;
T = T - N * dot(N, T);
B = normalize(cross(N, T));
} else {
T = normalize(tangent.xyz);
B = tangent.w * cross(N, T);
}
N = normalize(T * vNt.x + B * vNt.y + N * vNt.z);
}
return N;
}
public float2 getMetalRough(float2 uv) {
if (let metalRoughTex = metalRoughTextureID) {
return metalRoughTex.Sample(uv).bg * float2(metallic, roughness);
}
return float2(metallic, roughness);
}
}

View File

@ -1,12 +1,15 @@
import Bindless;
import Material;
import PBR;
struct VertexOut {
float4 worldPosition : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texCoord0 : TEXCOORD0;
float2 texCoord1 : TEXCOORD1;
float4 vertexColor0 : COLOR0;
float4 outPosition : SV_Position;
float4 worldPosition : WorldPosition;
float4 normal : WorldNormal;
float2 texCoord0 : TexCoord0;
float2 texCoord1 : TexCoord1;
float4 vertexColor0 : VertexColor;
};
struct CameraData {
@ -15,13 +18,6 @@ struct CameraData {
float4 position;
};
struct PointLight {
float3 position;
float range;
float3 color;
float attenuation;
};
struct DirectionalLight {
float3 direction;
float _padding0;
@ -55,11 +51,8 @@ uniform ParameterBlock<PerFrameData> pfd;
struct PerInstanceData {
float4x4 transform;
Sampler2D.RID textureID;
uint _padding;
float metallic;
float roughness;
float4 baseColor;
float4x4 invTransform;
Material material;
}
[[vk::push_constant]]
@ -70,6 +63,7 @@ VertexOut VertexMain(
uint vertexId: SV_VertexID,
float3 position,
float3 normal,
float4 tangent,
float2 texCoord0,
float2 texCoord1,
float4 vertexColor0,
@ -79,49 +73,164 @@ VertexOut VertexMain(
VertexOut output;
output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition));
output.worldPosition = worldPosition;
output.normal = mul(pcb.transform, float4(normalize(normal.rgb), 0.0f));
output.normal = normalize(mul(float4(normal.xyz, 0.0f), pcb.invTransform).xyz);
if (tangent.w == 0.0f) {
output.tangent = 0.0f.xxxx;
} else {
output.tangent = float4(normalize(mul(float4(tangent.xyz, 0.0f), pcb.invTransform).xyz), tangent.w);
}
output.texCoord0 = texCoord0;
output.texCoord1 = texCoord1;
output.vertexColor0 = vertexColor0;
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;
}
[shader("fragment")]
float4 FragmentMain(
float4 worldPosition : WorldPosition,
float4 normal : WorldNormal,
float2 uv0 : TexCoord0,
float2 uv1 : TexCoord1,
float4 color : VertexColor,
float4 position : POSITION,
float3 normal : NORMAL,
float4 tangent : TANGENT,
float2 texCoord0 : TEXCOORD0,
float2 texCoord1 : TEXCOORD1,
float4 vertexColor0 : COLOR0,
) : SV_Target0 {
float3 diffuse = 0.0f.xxx;
float3 specular = 0.0f.xxx;
float3 N = pcb.material.getNormal(position.xyz, normal.xyz, tangent, texCoord0);
float2 metalRough = pcb.material.getMetalRough(texCoord0);
for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) {
PointLight pointlight = pfd.lightData.pointLights[i];
let albedo = pcb.material.getAlbedo(texCoord0, vertexColor0);
let viewDir = normalize(position.xyz - pfd.camera.position.xyz);
let lightPosition = pointlight.position;
let lightDisplace = worldPosition.xyz - lightPosition;
let lightDistance = length(lightDisplace);
let lightDirection = normalize(lightDisplace);
let viewDirection = normalize(worldPosition.xyz - pfd.camera.position.xyz);
let halfWayVector = normalize(-lightDirection + viewDirection);
//float3 f_0 = 0.04f.xxx;
//f_0 = lerp(f_0, albedo.rgb, metalRough.x);
let attenuation = (1.0f / lightDistance);
let diffuseFactor = pcb.roughness * dot(-lightDirection, normalize(normal.xyz));
diffuse += pointlight.color * diffuseFactor;
//float3 contrib = 0.0f.xxx;
//for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) {
// PointLight pointlight = pfd.lightData.pointLights[i];
let specularFactor = (1.0f - pcb.roughness) * pow(max(dot(halfWayVector, viewDirection), 0.0f), 32.0f) * attenuation;
// contrib += pointlight.getInfluence(albedo.rgb, metalRough, viewDir, position.xyz, N, f_0);
//}
specular += pointlight.color * specularFactor;
}
if (let texture = pcb.textureID) {
return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * color * float4((diffuse + specular), 0.0f);
} else {
return pcb.baseColor * color * float4((diffuse + specular), 0.0f);
}
let contrib = GetPointLightInfluence(albedo.rgb, metalRough, position.xyz, N);
return float4(pcb.material.getEmissive(texCoord0) + contrib, 1.0f);
}

107
Assets/Shaders/PBR.slang Normal file
View File

@ -0,0 +1,107 @@
public static const float PI = 3.14159265f;
float TrowbridgeReitzGGX(float3 normal, float3 halfway, float roughness)
{
float alpha = roughness * roughness;
float alpha2 = alpha * alpha;
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 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 float3 position;
public float range;
public float3 color;
float attenuation;
public float3 getInfluence(float3 albedo, float2 metalRough, float3 viewDir, float3 worldPosition, float3 normal, float3 f_0) {
float3 lightDir = float3(position) - worldPosition;
float lightDistance = length(lightDir);
if (lightDistance > range)
return 0.0f.xxx;
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
let attenuation_ = attenuation / (lightDistance * lightDistance);
let radiance = color * attenuation_;
return GetPBRContrib(albedo, radiance, viewDir, normal, lightDir, metalRough, f_0);
}
};

View File

@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue">&lt;NamingElement Priority="10"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="member function" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;&lt;/NamingElement&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue">&lt;NamingElement Priority="10"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="member function" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Local_0020variables/@EntryIndexedValue">&lt;NamingElement Priority="7"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="local variable" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue">&lt;NamingElement Priority="6"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="function parameter" /&gt;&lt;type Name="lambda parameter" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/NamingElement&gt;</s:String></wpf:ResourceDictionary>

View File

@ -121,6 +121,15 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<CustomBuild>
<Command>slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
</CustomBuild>
<CustomBuild>
<Message>Compiling %(Filename)</Message>
</CustomBuild>
<CustomBuild>
<Outputs>%(Filename).spv</Outputs>
</CustomBuild>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
@ -146,6 +155,15 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<CustomBuild>
<Command>slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
</CustomBuild>
<CustomBuild>
<Message>Compiling %(Filename)</Message>
</CustomBuild>
<CustomBuild>
<Outputs>%(Filename).spv</Outputs>
</CustomBuild>
</ItemDefinitionGroup>
<ItemGroup>
<None Include=".clang-format" />
@ -154,14 +172,17 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Compiling %(Filename).slang</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).spv</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling %(Filename).slang</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).spv</Outputs>
</CustomBuild>
<None Include="Assets\Shaders\Bindless.slang" />
<None Include="Assets\Shaders\Bindless.slang">
<FileType>Document</FileType>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">slangc %(FullPath) -profile sm_6_6 -target module -o %(Filename).slang-module</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).slang-module</Outputs>
</None>
<None Include="Assets\Shaders\Material.slang" />
<None Include="Assets\Shaders\PBR.slang" />
<None Include="PLAN.md">
<SubType>
</SubType>

View File

@ -48,6 +48,12 @@
<None Include="Assets\Shaders\Bindless.slang">
<Filter>Resource Files\Shader Files</Filter>
</None>
<None Include="Assets\Shaders\PBR.slang">
<Filter>Resource Files\Shader Files</Filter>
</None>
<None Include="Assets\Shaders\Material.slang">
<Filter>Resource Files\Shader Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Blaze\AppState.h">

View File

@ -46,8 +46,7 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
AppState& appState = *static_cast<AppState*>( *appstate );
Entity const* entity =
LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/OrientationTest.glb" );
Entity const* entity = LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/DamagedHelmet.glb" );
ASSERT( entity );
std::array pointLight = {
@ -58,9 +57,9 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
.attenuation = 1.0f,
},
MiscData::PointLight{
.position = { 0.0f, 12.0f, 0.0f },
.position = { 0.0f, 3.0f, 0.0f },
.range = 12,
.color = { 0.0f, 1.0f, 0.0f },
.color = { 12.0f, 12.0f, 12.0f },
.attenuation = 1.0f,
},
MiscData::PointLight{
@ -263,6 +262,16 @@ SDL_AppResult SDL_AppIterate( void* appstate )
vkCmdPushConstants(
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
DirectX::XMMATRIX const inverseTransform = XMMatrixInverse( nullptr, worldTransform );
vkCmdPushConstants(
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
sizeof worldTransform,
sizeof inverseTransform,
&inverseTransform );
if ( not entity.modelMesh.isNull() )
{
ASSERT( current );
@ -286,7 +295,7 @@ SDL_AppResult SDL_AppIterate( void* appstate )
cmd,
misc.pipelineLayout,
VK_SHADER_STAGE_ALL_GRAPHICS,
sizeof worldTransform,
2 * sizeof worldTransform,
Material::GPU_DATA_SIZE,
materialData );

View File

@ -132,7 +132,10 @@ void EntityManager::destroyEntity( Entity* entity )
for ( auto& material : entity->model.materials )
{
vkDestroySampler( device, Take( material.sampler ), nullptr );
pRenderDevice->textureManager->freeTexture( std::move( material.texture ) );
pRenderDevice->textureManager->freeTexture( std::move( material.albedoTextureID ) );
pRenderDevice->textureManager->freeTexture( std::move( material.normalTextureID ) );
pRenderDevice->textureManager->freeTexture( std::move( material.metalRoughTextureID ) );
pRenderDevice->textureManager->freeTexture( std::move( material.emissiveTextureID ) );
}
pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.vertexBuffer ) );

View File

@ -36,3 +36,5 @@
while ( false )
#define Take( OBJ ) std::exchange( OBJ, {} )
#define UNREACHABLE G_ASSERT( false )

View File

@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
VkPushConstantRange const pushConstantRange = {
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
.offset = 0,
.size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE,
.size = 2 * sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE,
};
std::array const descriptorSetLayouts = {
@ -123,18 +123,24 @@ bool MiscData::init( RenderDevice const& renderDevice )
VkVertexInputAttributeDescription{
.location = 2,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof( Vertex, texCoord0 ),
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = offsetof( Vertex, tangent ),
},
VkVertexInputAttributeDescription{
.location = 3,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof( Vertex, texCoord1 ),
.offset = offsetof( Vertex, texCoord0 ),
},
VkVertexInputAttributeDescription{
.location = 4,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof( Vertex, texCoord1 ),
},
VkVertexInputAttributeDescription{
.location = 5,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = offsetof( Vertex, color0 ),
},
@ -289,7 +295,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
// Camera
{
cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 20.0f, -20.0f, 1.0f );
cameraData.cameraPosition = DirectX::XMVectorSet( 0.0f, 2.0f, -2.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 );
cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraData.cameraPosition, cameraTarget, cameraUp );

View File

@ -16,31 +16,19 @@
#include "MacroUtils.h"
#include "MathUtil.h"
// TODO: Cache materials while loading.
uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_material const& material )
{
ASSERT( material.has_pbr_metallic_roughness );
DirectX::XMFLOAT4 const baseColorFactor = DirectX::XMFLOAT4{ material.pbr_metallic_roughness.base_color_factor };
VkSampler sampler = nullptr;
TextureID baseColorTexture;
if ( material.pbr_metallic_roughness.base_color_texture.texture )
{
cgltf_image* baseColorImage = material.pbr_metallic_roughness.base_color_texture.texture->image;
std::optional<TextureID> LoadTexture(
RenderDevice* renderDevice, VkSampler sampler, cgltf_image const& baseColorImage, bool const linear )
{
byte* data;
if ( baseColorImage->buffer_view->data )
if ( baseColorImage.buffer_view->data )
{
data = static_cast<byte*>( baseColorImage->buffer_view->data );
data = static_cast<byte*>( baseColorImage.buffer_view->data );
}
else
{
data = static_cast<byte*>( baseColorImage->buffer_view->buffer->data ) + baseColorImage->buffer_view->offset;
data = static_cast<byte*>( baseColorImage.buffer_view->buffer->data ) + baseColorImage.buffer_view->offset;
}
size_t size = baseColorImage->buffer_view->size;
size_t size = baseColorImage.buffer_view->size;
uint32_t width;
uint32_t height;
@ -58,44 +46,22 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi
if ( not textureData )
{
return UINT32_MAX;
return std::nullopt;
}
width = static_cast<uint32_t>( w );
height = static_cast<uint32_t>( h );
}
VkSamplerCreateInfo constexpr samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipLodBias = 0.0,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = false,
};
VK_CHECK( vkCreateSampler( renderDevice->device, &samplerCreateInfo, nullptr, &sampler ) );
auto textureOpt = renderDevice->textureManager->createTexture( { width, height, 1 }, sampler );
auto textureOpt = renderDevice->textureManager->createTexture(
{ width, height, 1 }, sampler, linear ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_SRGB );
if ( not textureOpt )
{
return UINT32_MAX;
return std::nullopt;
}
baseColorTexture = std::move( textureOpt.value() );
VkImage textureImage = renderDevice->textureManager->fetchImage( baseColorTexture ).value();
TextureID texture = std::move( textureOpt.value() );
VkImage textureImage = renderDevice->textureManager->fetchImage( texture ).value();
// Staging Buffer Create
VkBuffer stagingBuffer;
@ -181,7 +147,7 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = renderDevice->textureManager->fetchImage( baseColorTexture ).value(),
.image = renderDevice->textureManager->fetchImage( texture ).value(),
.subresourceRange = subresourceRange,
};
@ -331,12 +297,7 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi
// Staging -> Image L0
vkCmdCopyBufferToImage(
frameInUse.commandBuffer,
stagingBuffer,
textureImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copyRegion );
frameInUse.commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion );
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0;
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1;
@ -431,23 +392,163 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi
}
vmaDestroyBuffer( renderDevice->gpuAllocator, stagingBuffer, stagingAllocation );
return texture;
}
// TODO: Cache materials while loading.
uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_material const& material )
{
ASSERT( material.has_pbr_metallic_roughness );
auto const baseColorFactor = DirectX::XMFLOAT4{ material.pbr_metallic_roughness.base_color_factor };
auto const emissiveFactor = DirectX::XMFLOAT4{
material.emissive_factor[0],
material.emissive_factor[1],
material.emissive_factor[2],
std::max( material.emissive_strength.emissive_strength, 1.0f ),
};
VkSampler sampler = nullptr;
TextureID baseColorTexture;
TextureID normalTexture;
TextureID metalRoughTexture;
TextureID emissiveTexture;
VkSamplerCreateInfo constexpr samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mipLodBias = 0.0,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = false,
};
VK_CHECK( vkCreateSampler( renderDevice->device, &samplerCreateInfo, nullptr, &sampler ) );
if ( material.pbr_metallic_roughness.base_color_texture.texture )
{
cgltf_image const* baseColorImage = material.pbr_metallic_roughness.base_color_texture.texture->image;
auto baseColorTextureOpt = LoadTexture( renderDevice, sampler, *baseColorImage, false );
if ( not baseColorTextureOpt )
{
vkDestroySampler( renderDevice->device, Take( sampler ), nullptr );
return UINT32_MAX;
}
baseColorTexture = std::move( baseColorTextureOpt.value() );
}
if ( material.pbr_metallic_roughness.metallic_roughness_texture.texture )
{
cgltf_image const* metalRoughImage = material.pbr_metallic_roughness.metallic_roughness_texture.texture->image;
auto metalRoughTextureOpt = LoadTexture( renderDevice, sampler, *metalRoughImage, true );
if ( not metalRoughTextureOpt )
{
vkDestroySampler( renderDevice->device, Take( sampler ), nullptr );
renderDevice->textureManager->freeTexture( std::move( baseColorTexture ) );
return UINT32_MAX;
}
metalRoughTexture = std::move( metalRoughTextureOpt.value() );
}
if ( material.normal_texture.texture )
{
cgltf_image const* normalImage = material.normal_texture.texture->image;
auto normalTextureOpt = LoadTexture( renderDevice, sampler, *normalImage, true );
if ( not normalTextureOpt )
{
vkDestroySampler( renderDevice->device, Take( sampler ), nullptr );
renderDevice->textureManager->freeTexture( std::move( metalRoughTexture ) );
renderDevice->textureManager->freeTexture( std::move( baseColorTexture ) );
return UINT32_MAX;
}
normalTexture = std::move( normalTextureOpt.value() );
}
if ( material.emissive_texture.texture )
{
cgltf_image const* emissiveImage = material.emissive_texture.texture->image;
auto emissiveTextureOpt = LoadTexture( renderDevice, sampler, *emissiveImage, true );
if ( not emissiveTextureOpt )
{
vkDestroySampler( renderDevice->device, Take( sampler ), nullptr );
renderDevice->textureManager->freeTexture( std::move( baseColorTexture ) );
renderDevice->textureManager->freeTexture( std::move( normalTexture ) );
renderDevice->textureManager->freeTexture( std::move( metalRoughTexture ) );
return UINT32_MAX;
}
emissiveTexture = std::move( emissiveTextureOpt.value() );
}
float const metallic = material.pbr_metallic_roughness.metallic_factor;
float const roughness = material.pbr_metallic_roughness.roughness_factor;
uint32_t const materialIdx = static_cast<uint32_t>( model->materials.size() );
model->materials.push_back( { sampler, std::move( baseColorTexture ), {}, roughness, metallic, baseColorFactor } );
model->materials.push_back( {
sampler,
baseColorFactor,
emissiveFactor,
std::move( baseColorTexture ),
std::move( normalTexture ),
std::move( metalRoughTexture ),
std::move( emissiveTexture ),
roughness,
metallic,
} );
return materialIdx;
}
void LoadAttribute(
std::vector<Vertex>* pVertices,
int32_t const vertexStart,
std::vector<float>* scratch,
cgltf_attribute const& positionAttr,
size_t const stride,
size_t const offset,
size_t const components )
{
size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 );
ASSERT( floatCount % components == 0 );
scratch->resize( floatCount );
cgltf_accessor_unpack_floats( positionAttr.data, scratch->data(), scratch->size() );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + floatCount / components );
byte* writePtr = reinterpret_cast<byte*>( pVertices->data() + vertexStart ) + offset;
float const* readPtr = scratch->data();
for ( size_t i = vertexStart; i < pVertices->size(); ++i )
{
memcpy( writePtr, readPtr, components * sizeof( float ) );
readPtr += components;
writePtr += stride;
}
scratch->clear();
}
ModelMesh ProcessMesh(
RenderDevice* renderDevice,
Model* model,
std::pmr::vector<Vertex>* pVertices,
std::pmr::vector<uint32_t>* pIndices,
std::vector<Vertex>* pVertices,
std::vector<uint32_t>* pIndices,
cgltf_mesh const& mesh )
{
using namespace std::string_view_literals;
@ -488,6 +589,8 @@ ModelMesh ProcessMesh(
.vertexOffset = vertexStart,
} );
std::vector<float> scratch;
cgltf_attribute const* attributes = primitive.attributes;
for ( uint32_t attribIndex = 0; attribIndex < primitive.attributes_count; ++attribIndex )
{
@ -497,22 +600,11 @@ ModelMesh ProcessMesh(
ASSERT( positionAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( positionAttr.data->type == cgltf_type_vec3 );
std::pmr::vector<DirectX::XMFLOAT3> positions{ pVertices->get_allocator() };
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, position );
size_t constexpr components = 3;
size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 );
positions.resize( floatCount / 3 );
cgltf_accessor_unpack_floats(
positionAttr.data, reinterpret_cast<cgltf_float*>( positions.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + positions.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& position : positions )
{
vertexIter->position = position;
++vertexIter;
}
LoadAttribute( pVertices, vertexStart, &scratch, positionAttr, stride, offset, components );
}
if ( "NORMAL"sv == attributes[attribIndex].name )
{
@ -520,21 +612,23 @@ ModelMesh ProcessMesh(
ASSERT( normalAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( normalAttr.data->type == cgltf_type_vec3 );
std::pmr::vector<DirectX::XMFLOAT3> normals{ pVertices->get_allocator() };
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, normal );
size_t constexpr components = 3;
size_t const floatCount = cgltf_accessor_unpack_floats( normalAttr.data, nullptr, 0 );
normals.resize( floatCount / 3 );
cgltf_accessor_unpack_floats( normalAttr.data, reinterpret_cast<cgltf_float*>( normals.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + normals.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& normal : normals )
{
vertexIter->normal = normal;
++vertexIter;
LoadAttribute( pVertices, vertexStart, &scratch, normalAttr, stride, offset, components );
}
if ( "TANGENT"sv == attributes[attribIndex].name )
{
cgltf_attribute const& tangentAttr = attributes[attribIndex];
ASSERT( tangentAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( tangentAttr.data->type == cgltf_type_vec4 );
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, tangent );
size_t constexpr components = 4;
LoadAttribute( pVertices, vertexStart, &scratch, tangentAttr, stride, offset, components );
}
if ( "TEXCOORD_0"sv == attributes[attribIndex].name )
{
@ -542,22 +636,11 @@ ModelMesh ProcessMesh(
ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( texCoordAttr.data->type == cgltf_type_vec2 );
std::pmr::vector<DirectX::XMFLOAT2> texCoords{ pVertices->get_allocator() };
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, texCoord0 );
size_t constexpr components = 2;
size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 );
texCoords.resize( floatCount / 2 );
cgltf_accessor_unpack_floats(
texCoordAttr.data, reinterpret_cast<cgltf_float*>( texCoords.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + texCoords.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT2 const& texCoord : texCoords )
{
vertexIter->texCoord0 = texCoord;
++vertexIter;
}
LoadAttribute( pVertices, vertexStart, &scratch, texCoordAttr, stride, offset, components );
}
if ( "TEXCOORD_1"sv == attributes[attribIndex].name )
{
@ -565,65 +648,33 @@ ModelMesh ProcessMesh(
ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( texCoordAttr.data->type == cgltf_type_vec2 );
std::pmr::vector<DirectX::XMFLOAT2> texCoords{ pVertices->get_allocator() };
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, texCoord1 );
size_t constexpr components = 2;
size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 );
texCoords.resize( floatCount / 2 );
cgltf_accessor_unpack_floats(
texCoordAttr.data, reinterpret_cast<cgltf_float*>( texCoords.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + texCoords.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT2 const& texCoord : texCoords )
{
vertexIter->texCoord1 = texCoord;
++vertexIter;
}
LoadAttribute( pVertices, vertexStart, &scratch, texCoordAttr, stride, offset, components );
}
if ( "COLOR_0"sv == attributes[attribIndex].name )
{
cgltf_attribute const& colorAttr = attributes[attribIndex];
ASSERT( colorAttr.data->component_type == cgltf_component_type_r_32f );
ASSERT( colorAttr.data->type == cgltf_type_vec3 or colorAttr.data->type == cgltf_type_vec4 );
if ( colorAttr.data->type == cgltf_type_vec3 )
size_t constexpr stride = sizeof( Vertex );
size_t constexpr offset = offsetof( Vertex, texCoord1 );
size_t components = 3;
switch ( colorAttr.data->type )
{
std::pmr::vector<DirectX::XMFLOAT3> colors{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 );
colors.resize( floatCount / 3 );
cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast<cgltf_float*>( colors.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + colors.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT3 const& color : colors )
{
vertexIter->color0 = { color.x, color.y, color.z, 1.0f };
++vertexIter;
case cgltf_type_vec3:
components = 3;
break;
case cgltf_type_vec4:
components = 4;
break;
default:
UNREACHABLE;
}
}
else // Since only two options
{
std::pmr::vector<DirectX::XMFLOAT4> colors{ pVertices->get_allocator() };
size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 );
colors.resize( floatCount / 4 );
cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast<cgltf_float*>( colors.data() ), floatCount );
// Guaranteed to have space for these vertices.
pVertices->resize( vertexStart + colors.size() );
auto vertexIter = pVertices->begin() + vertexStart;
for ( DirectX::XMFLOAT4 const& color : colors )
{
vertexIter->color0 = color;
++vertexIter;
}
}
LoadAttribute( pVertices, vertexStart, &scratch, colorAttr, stride, offset, components );
}
// TODO: Grab other attributes.
}
@ -636,8 +687,8 @@ Entity* ProcessNode(
RenderDevice* renderDevice,
EntityManager* entityManager,
Model* model,
std::pmr::vector<Vertex>* vertices,
std::pmr::vector<uint32_t>* indices,
std::vector<Vertex>* vertices,
std::vector<uint32_t>* indices,
cgltf_node const& node )
{
DirectX::XMVECTOR vTranslation;
@ -722,8 +773,8 @@ Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, con
} );
// Output data
std::pmr::vector<Vertex> vertices;
std::pmr::vector<uint32_t> indices;
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
cgltf_scene const* currentScene = gltfModel->scene;
for ( uint32_t nodeIdx = 0; nodeIdx < currentScene->nodes_count; ++nodeIdx )

View File

@ -14,8 +14,9 @@ struct GlobalMemory;
struct Vertex
{
DirectX::XMFLOAT3 position = { 0.0f, 0.0f, 0.0f };
DirectX::XMFLOAT3 normal = { 1.0f, 1.0f, 1.0f };
DirectX::XMFLOAT4 position = { 0.0f, 0.0f, 0.0f, 1.0f };
DirectX::XMFLOAT4 normal = { 0.0f, 0.0f, 1.0f, 0.0f };
DirectX::XMFLOAT4 tangent = { 1.0f, 0.0f, 0.0f, 0.0f };
DirectX::XMFLOAT2 texCoord0 = { 0.0f, 0.0f };
DirectX::XMFLOAT2 texCoord1 = { 0.0f, 0.0f };
DirectX::XMFLOAT4 color0 = { 1.0f, 1.0f, 1.0f, 1.0f };
@ -42,21 +43,23 @@ struct ModelMesh
struct Material
{
constexpr static size_t GPU_DATA_OFFSET = sizeof( VkSampler );
constexpr static size_t GPU_DATA_SIZE =
sizeof( TextureID ) + sizeof( uint32_t ) + 2 * sizeof( float ) + sizeof( DirectX::XMFLOAT4 );
size_t constexpr static GPU_DATA_OFFSET = sizeof( VkSampler );
size_t constexpr static GPU_DATA_SIZE = 56;
VkSampler sampler; // TODO: Reuse
// To copy directly.
TextureID texture;
uint32_t padding0; // FIXME: Wasting space.
DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f };
DirectX::XMFLOAT4 emission = { 0.0f, 0.0f, 0.0f, 1.0f };
TextureID albedoTextureID;
TextureID normalTextureID;
TextureID metalRoughTextureID;
TextureID emissiveTextureID;
float roughness = 1.0f;
float metallic = 1.0f;
DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f };
[[nodiscard]] bool isNull() const
{
return texture.isNull() or sampler;
return not( albedoTextureID and normalTextureID and metalRoughTextureID and emissiveTextureID and sampler );
}
};

View File

@ -7,7 +7,7 @@
template struct RID<Texture>;
std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler )
std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent, VkSampler const sampler, VkFormat const format )
{
if ( m_freeList.empty() )
{
@ -20,8 +20,6 @@ std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent,
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
VkFormat const format = VK_FORMAT_R8G8B8A8_SRGB;
VkImage texture;
VmaAllocation textureAllocation;
VkImageView textureView;

View File

@ -67,7 +67,8 @@ public:
void freeTexture( TextureID&& rid );
DEPRECATE_JULY_2025
[[nodiscard]] std::optional<TextureID> createTexture( VkExtent3D extent, VkSampler sampler );
[[nodiscard]] std::optional<TextureID> createTexture(
VkExtent3D extent, VkSampler sampler, VkFormat format = VK_FORMAT_R8G8B8A8_SRGB );
DEPRECATE_JULY_2025
std::optional<VkImage> fetchImage( TextureID const& rid );

View File

@ -21,7 +21,12 @@
- [X] Create Vertex buffer
- [X] Load texture
- [X] Draw
- [ ] Refactor
- [ ] Render Sponza
- [X] Load GLTF Scene
- [ ] Support Albedo
- [ ] Support Normal
- [ ] Support Metal/Rough
- [ ] Support Emission
## Features
- [ ] Scene Rendering