Punctual PBR lighting.
This commit is contained in:
parent
9314b3504e
commit
d51fc375d2
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import Bindless;
|
import Bindless;
|
||||||
|
import Material;
|
||||||
|
import PBR;
|
||||||
|
|
||||||
struct VertexOut {
|
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 outPosition : SV_Position;
|
||||||
float4 worldPosition : WorldPosition;
|
|
||||||
float4 normal : WorldNormal;
|
|
||||||
float2 texCoord0 : TexCoord0;
|
|
||||||
float2 texCoord1 : TexCoord1;
|
|
||||||
float4 vertexColor0 : VertexColor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CameraData {
|
struct CameraData {
|
||||||
|
|
@ -15,13 +18,6 @@ struct CameraData {
|
||||||
float4 position;
|
float4 position;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PointLight {
|
|
||||||
float3 position;
|
|
||||||
float range;
|
|
||||||
float3 color;
|
|
||||||
float attenuation;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DirectionalLight {
|
struct DirectionalLight {
|
||||||
float3 direction;
|
float3 direction;
|
||||||
float _padding0;
|
float _padding0;
|
||||||
|
|
@ -55,11 +51,8 @@ uniform ParameterBlock<PerFrameData> pfd;
|
||||||
|
|
||||||
struct PerInstanceData {
|
struct PerInstanceData {
|
||||||
float4x4 transform;
|
float4x4 transform;
|
||||||
Sampler2D.RID textureID;
|
float4x4 invTransform;
|
||||||
uint _padding;
|
Material material;
|
||||||
float metallic;
|
|
||||||
float roughness;
|
|
||||||
float4 baseColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[vk::push_constant]]
|
[[vk::push_constant]]
|
||||||
|
|
@ -70,6 +63,7 @@ VertexOut VertexMain(
|
||||||
uint vertexId: SV_VertexID,
|
uint vertexId: SV_VertexID,
|
||||||
float3 position,
|
float3 position,
|
||||||
float3 normal,
|
float3 normal,
|
||||||
|
float4 tangent,
|
||||||
float2 texCoord0,
|
float2 texCoord0,
|
||||||
float2 texCoord1,
|
float2 texCoord1,
|
||||||
float4 vertexColor0,
|
float4 vertexColor0,
|
||||||
|
|
@ -79,49 +73,164 @@ VertexOut VertexMain(
|
||||||
VertexOut output;
|
VertexOut output;
|
||||||
output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition));
|
output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition));
|
||||||
output.worldPosition = 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.texCoord0 = texCoord0;
|
||||||
output.texCoord1 = texCoord1;
|
output.texCoord1 = texCoord1;
|
||||||
output.vertexColor0 = vertexColor0;
|
output.vertexColor0 = vertexColor0;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
[shader("fragment")]
|
|
||||||
float4 FragmentMain(
|
|
||||||
float4 worldPosition : WorldPosition,
|
|
||||||
float4 normal : WorldNormal,
|
|
||||||
float2 uv0 : TexCoord0,
|
|
||||||
float2 uv1 : TexCoord1,
|
|
||||||
float4 color : VertexColor,
|
|
||||||
) : SV_Target0 {
|
|
||||||
|
|
||||||
float3 diffuse = 0.0f.xxx;
|
float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness)
|
||||||
float3 specular = 0.0f.xxx;
|
{
|
||||||
|
float Coeff = Roughness * Roughness;
|
||||||
|
float Alpha2 = Coeff * Coeff;
|
||||||
|
float NdotH = max(dot(Normal, Halfway), 0.0f);
|
||||||
|
float NdotH2 = NdotH * NdotH;
|
||||||
|
|
||||||
for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) {
|
float Numerator = Alpha2;
|
||||||
PointLight pointlight = pfd.lightData.pointLights[i];
|
float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f;
|
||||||
|
Denominator = PI * Denominator * Denominator;
|
||||||
|
|
||||||
let lightPosition = pointlight.position;
|
return Numerator / Denominator;
|
||||||
let lightDisplace = worldPosition.xyz - lightPosition;
|
}
|
||||||
let lightDistance = length(lightDisplace);
|
|
||||||
let lightDirection = normalize(lightDisplace);
|
float GeometrySchlickGGX(float NdotV, float Roughness)
|
||||||
let viewDirection = normalize(worldPosition.xyz - pfd.camera.position.xyz);
|
{
|
||||||
let halfWayVector = normalize(-lightDirection + viewDirection);
|
float R = Roughness + 1.0f;
|
||||||
|
float K = (R * R) / 8.0f;
|
||||||
let attenuation = (1.0f / lightDistance);
|
|
||||||
|
float Numerator = NdotV;
|
||||||
let diffuseFactor = pcb.roughness * dot(-lightDirection, normalize(normal.xyz));
|
float Denominator = NdotV * (1.0f - K) + K;
|
||||||
diffuse += pointlight.color * diffuseFactor;
|
|
||||||
|
return Numerator / Denominator;
|
||||||
let specularFactor = (1.0f - pcb.roughness) * pow(max(dot(halfWayVector, viewDirection), 0.0f), 32.0f) * attenuation;
|
}
|
||||||
|
|
||||||
specular += pointlight.color * specularFactor;
|
float GeometrySmith(float NdotV, float NdotL, float Roughness)
|
||||||
}
|
{
|
||||||
|
float GGX1 = GeometrySchlickGGX(NdotV, Roughness);
|
||||||
if (let texture = pcb.textureID) {
|
float GGX2 = GeometrySchlickGGX(NdotL, Roughness);
|
||||||
return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * color * float4((diffuse + specular), 0.0f);
|
|
||||||
} else {
|
return GGX1 * GGX2;
|
||||||
return pcb.baseColor * color * float4((diffuse + specular), 0.0f);
|
}
|
||||||
}
|
|
||||||
|
// 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 position : POSITION,
|
||||||
|
float3 normal : NORMAL,
|
||||||
|
float4 tangent : TANGENT,
|
||||||
|
float2 texCoord0 : TEXCOORD0,
|
||||||
|
float2 texCoord1 : TEXCOORD1,
|
||||||
|
float4 vertexColor0 : COLOR0,
|
||||||
|
) : SV_Target0 {
|
||||||
|
|
||||||
|
float3 N = pcb.material.getNormal(position.xyz, normal.xyz, tangent, texCoord0);
|
||||||
|
float2 metalRough = pcb.material.getMetalRough(texCoord0);
|
||||||
|
|
||||||
|
let albedo = pcb.material.getAlbedo(texCoord0, vertexColor0);
|
||||||
|
let viewDir = normalize(position.xyz - pfd.camera.position.xyz);
|
||||||
|
|
||||||
|
//float3 f_0 = 0.04f.xxx;
|
||||||
|
//f_0 = lerp(f_0, albedo.rgb, metalRough.x);
|
||||||
|
|
||||||
|
|
||||||
|
//float3 contrib = 0.0f.xxx;
|
||||||
|
//for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) {
|
||||||
|
// PointLight pointlight = pfd.lightData.pointLights[i];
|
||||||
|
|
||||||
|
// contrib += pointlight.getInfluence(albedo.rgb, metalRough, viewDir, position.xyz, N, f_0);
|
||||||
|
//}
|
||||||
|
|
||||||
|
let contrib = GetPointLightInfluence(albedo.rgb, metalRough, position.xyz, N);
|
||||||
|
|
||||||
|
return float4(pcb.material.getEmissive(texCoord0) + contrib, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -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">
|
<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"><NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy></NamingElement></s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue"><NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Local_0020variables/@EntryIndexedValue"><NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue"><NamingElement Priority="6"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String></wpf:ResourceDictionary>
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
<Command>slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
|
<Command>slangc %(FullPath) -profile sm_6_6 -target spirv -o %(Filename).spv</Command>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
<CustomBuild>
|
<CustomBuild>
|
||||||
<Message>Compiling %(Filename).slang</Message>
|
<Message>Compiling %(Filename)</Message>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
<CustomBuild>
|
<CustomBuild>
|
||||||
<Outputs>%(Filename).spv</Outputs>
|
<Outputs>%(Filename).spv</Outputs>
|
||||||
|
|
@ -155,6 +155,15 @@
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
</Link>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include=".clang-format" />
|
<None Include=".clang-format" />
|
||||||
|
|
@ -167,7 +176,13 @@
|
||||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling %(Filename).slang</Message>
|
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling %(Filename).slang</Message>
|
||||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).spv</Outputs>
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).spv</Outputs>
|
||||||
</CustomBuild>
|
</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">
|
<None Include="PLAN.md">
|
||||||
<SubType>
|
<SubType>
|
||||||
</SubType>
|
</SubType>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,12 @@
|
||||||
<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>
|
||||||
|
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Blaze\AppState.h">
|
<ClInclude Include="Blaze\AppState.h">
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,9 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
|
||||||
.attenuation = 1.0f,
|
.attenuation = 1.0f,
|
||||||
},
|
},
|
||||||
MiscData::PointLight{
|
MiscData::PointLight{
|
||||||
.position = { 0.0f, 12.0f, 0.0f },
|
.position = { 0.0f, 3.0f, 0.0f },
|
||||||
.range = 12,
|
.range = 12,
|
||||||
.color = { 0.0f, 1.0f, 0.0f },
|
.color = { 12.0f, 12.0f, 12.0f },
|
||||||
.attenuation = 1.0f,
|
.attenuation = 1.0f,
|
||||||
},
|
},
|
||||||
MiscData::PointLight{
|
MiscData::PointLight{
|
||||||
|
|
@ -262,6 +262,16 @@ SDL_AppResult SDL_AppIterate( void* appstate )
|
||||||
vkCmdPushConstants(
|
vkCmdPushConstants(
|
||||||
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
|
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() )
|
if ( not entity.modelMesh.isNull() )
|
||||||
{
|
{
|
||||||
ASSERT( current );
|
ASSERT( current );
|
||||||
|
|
@ -285,7 +295,7 @@ SDL_AppResult SDL_AppIterate( void* appstate )
|
||||||
cmd,
|
cmd,
|
||||||
misc.pipelineLayout,
|
misc.pipelineLayout,
|
||||||
VK_SHADER_STAGE_ALL_GRAPHICS,
|
VK_SHADER_STAGE_ALL_GRAPHICS,
|
||||||
sizeof worldTransform,
|
2 * sizeof worldTransform,
|
||||||
Material::GPU_DATA_SIZE,
|
Material::GPU_DATA_SIZE,
|
||||||
materialData );
|
materialData );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,10 @@ void EntityManager::destroyEntity( Entity* entity )
|
||||||
for ( auto& material : entity->model.materials )
|
for ( auto& material : entity->model.materials )
|
||||||
{
|
{
|
||||||
vkDestroySampler( device, Take( material.sampler ), nullptr );
|
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 ) );
|
pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.vertexBuffer ) );
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
VkPushConstantRange const pushConstantRange = {
|
VkPushConstantRange const pushConstantRange = {
|
||||||
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
|
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE,
|
.size = 2 * sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array const descriptorSetLayouts = {
|
std::array const descriptorSetLayouts = {
|
||||||
|
|
@ -123,18 +123,24 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
VkVertexInputAttributeDescription{
|
VkVertexInputAttributeDescription{
|
||||||
.location = 2,
|
.location = 2,
|
||||||
.binding = 0,
|
.binding = 0,
|
||||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||||
.offset = offsetof( Vertex, texCoord0 ),
|
.offset = offsetof( Vertex, tangent ),
|
||||||
},
|
},
|
||||||
VkVertexInputAttributeDescription{
|
VkVertexInputAttributeDescription{
|
||||||
.location = 3,
|
.location = 3,
|
||||||
.binding = 0,
|
.binding = 0,
|
||||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||||
.offset = offsetof( Vertex, texCoord1 ),
|
.offset = offsetof( Vertex, texCoord0 ),
|
||||||
},
|
},
|
||||||
VkVertexInputAttributeDescription{
|
VkVertexInputAttributeDescription{
|
||||||
.location = 4,
|
.location = 4,
|
||||||
.binding = 0,
|
.binding = 0,
|
||||||
|
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||||
|
.offset = offsetof( Vertex, texCoord1 ),
|
||||||
|
},
|
||||||
|
VkVertexInputAttributeDescription{
|
||||||
|
.location = 5,
|
||||||
|
.binding = 0,
|
||||||
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||||
.offset = offsetof( Vertex, color0 ),
|
.offset = offsetof( Vertex, color0 ),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -16,188 +16,154 @@
|
||||||
#include "MacroUtils.h"
|
#include "MacroUtils.h"
|
||||||
#include "MathUtil.h"
|
#include "MathUtil.h"
|
||||||
|
|
||||||
// TODO: Cache materials while loading.
|
std::optional<TextureID> LoadTexture(
|
||||||
uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_material const& material )
|
RenderDevice* renderDevice, VkSampler sampler, cgltf_image const& baseColorImage, bool const linear )
|
||||||
{
|
{
|
||||||
ASSERT( material.has_pbr_metallic_roughness );
|
byte* data;
|
||||||
|
if ( baseColorImage.buffer_view->data )
|
||||||
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;
|
data = static_cast<byte*>( baseColorImage.buffer_view->data );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = static_cast<byte*>( baseColorImage.buffer_view->buffer->data ) + baseColorImage.buffer_view->offset;
|
||||||
|
}
|
||||||
|
size_t size = baseColorImage.buffer_view->size;
|
||||||
|
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t numChannels = 4;
|
||||||
|
stbi_uc* textureData;
|
||||||
|
{
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
int nc;
|
||||||
|
int requestedChannels = static_cast<int>( numChannels );
|
||||||
|
|
||||||
|
textureData = stbi_load_from_memory(
|
||||||
|
reinterpret_cast<stbi_uc const*>( data ), static_cast<int>( size ), &w, &h, &nc, requestedChannels );
|
||||||
|
ASSERT( nc <= requestedChannels );
|
||||||
|
|
||||||
|
if ( not textureData )
|
||||||
{
|
{
|
||||||
byte* data;
|
return std::nullopt;
|
||||||
if ( 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;
|
|
||||||
}
|
|
||||||
size_t size = baseColorImage->buffer_view->size;
|
|
||||||
|
|
||||||
uint32_t width;
|
width = static_cast<uint32_t>( w );
|
||||||
uint32_t height;
|
height = static_cast<uint32_t>( h );
|
||||||
uint32_t numChannels = 4;
|
}
|
||||||
stbi_uc* textureData;
|
|
||||||
{
|
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
int nc;
|
|
||||||
int requestedChannels = static_cast<int>( numChannels );
|
|
||||||
|
|
||||||
textureData = stbi_load_from_memory(
|
auto textureOpt = renderDevice->textureManager->createTexture(
|
||||||
reinterpret_cast<stbi_uc const*>( data ), static_cast<int>( size ), &w, &h, &nc, requestedChannels );
|
{ width, height, 1 }, sampler, linear ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_SRGB );
|
||||||
ASSERT( nc <= requestedChannels );
|
if ( not textureOpt )
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
if ( not textureData )
|
TextureID texture = std::move( textureOpt.value() );
|
||||||
{
|
VkImage textureImage = renderDevice->textureManager->fetchImage( texture ).value();
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
width = static_cast<uint32_t>( w );
|
// Staging Buffer Create
|
||||||
height = static_cast<uint32_t>( h );
|
VkBuffer stagingBuffer;
|
||||||
}
|
VmaAllocation stagingAllocation;
|
||||||
|
{
|
||||||
|
VkBufferCreateInfo const stagingBufferCreateInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.size = static_cast<VkDeviceSize>( width ) * height * numChannels * sizeof( textureData[0] ),
|
||||||
|
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
.queueFamilyIndexCount = 0,
|
||||||
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
VkSamplerCreateInfo constexpr samplerCreateInfo = {
|
VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||||
.pNext = nullptr,
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
.flags = 0,
|
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
.magFilter = VK_FILTER_LINEAR,
|
.preferredFlags = 0,
|
||||||
.minFilter = VK_FILTER_LINEAR,
|
.memoryTypeBits = 0,
|
||||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
.pool = nullptr,
|
||||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
.pUserData = nullptr,
|
||||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
.priority = 1.0f,
|
||||||
.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 ) );
|
VmaAllocationInfo allocationInfo;
|
||||||
|
|
||||||
auto textureOpt = renderDevice->textureManager->createTexture( { width, height, 1 }, sampler );
|
VK_CHECK( vmaCreateBuffer(
|
||||||
if ( not textureOpt )
|
renderDevice->gpuAllocator,
|
||||||
{
|
&stagingBufferCreateInfo,
|
||||||
return UINT32_MAX;
|
&stagingAllocationCreateInfo,
|
||||||
}
|
&stagingBuffer,
|
||||||
|
&stagingAllocation,
|
||||||
|
&allocationInfo ) );
|
||||||
|
|
||||||
baseColorTexture = std::move( textureOpt.value() );
|
if ( allocationInfo.pMappedData )
|
||||||
VkImage textureImage = renderDevice->textureManager->fetchImage( baseColorTexture ).value();
|
{
|
||||||
|
memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Staging Buffer Create
|
// All data is copied to stagingBuffer, don't need this.
|
||||||
VkBuffer stagingBuffer;
|
stbi_image_free( textureData );
|
||||||
VmaAllocation stagingAllocation;
|
|
||||||
{
|
|
||||||
VkBufferCreateInfo const stagingBufferCreateInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.size = static_cast<VkDeviceSize>( width ) * height * numChannels * sizeof( textureData[0] ),
|
|
||||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = {
|
// Staging -> Texture transfer
|
||||||
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
{
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
Frame& frameInUse = renderDevice->frames[renderDevice->frameIndex];
|
||||||
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
||||||
.preferredFlags = 0,
|
|
||||||
.memoryTypeBits = 0,
|
|
||||||
.pool = nullptr,
|
|
||||||
.pUserData = nullptr,
|
|
||||||
.priority = 1.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
VmaAllocationInfo allocationInfo;
|
// This should just pass.
|
||||||
|
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) );
|
||||||
|
|
||||||
VK_CHECK( vmaCreateBuffer(
|
// Reset Frame
|
||||||
renderDevice->gpuAllocator,
|
VK_CHECK( vkResetFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse ) );
|
||||||
&stagingBufferCreateInfo,
|
VK_CHECK( vkResetCommandPool( renderDevice->device, frameInUse.commandPool, 0 ) );
|
||||||
&stagingAllocationCreateInfo,
|
|
||||||
&stagingBuffer,
|
|
||||||
&stagingAllocation,
|
|
||||||
&allocationInfo ) );
|
|
||||||
|
|
||||||
if ( allocationInfo.pMappedData )
|
VkCommandBufferBeginInfo constexpr beginInfo = {
|
||||||
{
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size );
|
.pNext = nullptr,
|
||||||
}
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||||
}
|
.pInheritanceInfo = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
// All data is copied to stagingBuffer, don't need this.
|
uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 );
|
||||||
stbi_image_free( textureData );
|
|
||||||
|
|
||||||
// Staging -> Texture transfer
|
VkImageSubresourceRange const subresourceRange = {
|
||||||
{
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
Frame& frameInUse = renderDevice->frames[renderDevice->frameIndex];
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = mipLevels,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
// This should just pass.
|
VkImageMemoryBarrier2 const creationToTransferImageBarrier = {
|
||||||
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) );
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT,
|
||||||
|
.srcAccessMask = VK_ACCESS_2_NONE,
|
||||||
|
.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = renderDevice->textureManager->fetchImage( texture ).value(),
|
||||||
|
.subresourceRange = subresourceRange,
|
||||||
|
};
|
||||||
|
|
||||||
// Reset Frame
|
VkDependencyInfo const creationToTransferDependency = {
|
||||||
VK_CHECK( vkResetFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse ) );
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
VK_CHECK( vkResetCommandPool( renderDevice->device, frameInUse.commandPool, 0 ) );
|
.pNext = nullptr,
|
||||||
|
.dependencyFlags = 0,
|
||||||
|
.memoryBarrierCount = 0,
|
||||||
|
.pMemoryBarriers = nullptr,
|
||||||
|
.bufferMemoryBarrierCount = 0,
|
||||||
|
.pBufferMemoryBarriers = nullptr,
|
||||||
|
.imageMemoryBarrierCount = 1,
|
||||||
|
.pImageMemoryBarriers = &creationToTransferImageBarrier,
|
||||||
|
};
|
||||||
|
|
||||||
VkCommandBufferBeginInfo constexpr beginInfo = {
|
std::array transferToReadyImageBarriers{
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
||||||
.pInheritanceInfo = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 );
|
|
||||||
|
|
||||||
VkImageSubresourceRange const subresourceRange = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImageMemoryBarrier2 const creationToTransferImageBarrier = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_2_NONE,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = renderDevice->textureManager->fetchImage( baseColorTexture ).value(),
|
|
||||||
.subresourceRange = subresourceRange,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDependencyInfo const creationToTransferDependency = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.dependencyFlags = 0,
|
|
||||||
.memoryBarrierCount = 0,
|
|
||||||
.pMemoryBarriers = nullptr,
|
|
||||||
.bufferMemoryBarrierCount = 0,
|
|
||||||
.pBufferMemoryBarriers = nullptr,
|
|
||||||
.imageMemoryBarrierCount = 1,
|
|
||||||
.pImageMemoryBarriers = &creationToTransferImageBarrier,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array transferToReadyImageBarriers{
|
|
||||||
// transferToReadyImageBarrier
|
// transferToReadyImageBarrier
|
||||||
VkImageMemoryBarrier2{
|
VkImageMemoryBarrier2{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||||
|
|
@ -241,204 +207,309 @@ uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_materi
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VkDependencyInfo const transferToReadyDependency = {
|
VkDependencyInfo const transferToReadyDependency = {
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.dependencyFlags = 0,
|
.dependencyFlags = 0,
|
||||||
.memoryBarrierCount = 0,
|
.memoryBarrierCount = 0,
|
||||||
.pMemoryBarriers = nullptr,
|
.pMemoryBarriers = nullptr,
|
||||||
.bufferMemoryBarrierCount = 0,
|
.bufferMemoryBarrierCount = 0,
|
||||||
.pBufferMemoryBarriers = nullptr,
|
.pBufferMemoryBarriers = nullptr,
|
||||||
.imageMemoryBarrierCount = static_cast<uint32_t>( transferToReadyImageBarriers.size() ),
|
.imageMemoryBarrierCount = static_cast<uint32_t>( transferToReadyImageBarriers.size() ),
|
||||||
.pImageMemoryBarriers = transferToReadyImageBarriers.data(),
|
.pImageMemoryBarriers = transferToReadyImageBarriers.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
VkImageSubresourceRange const mipLevelSubresource = {
|
VkImageSubresourceRange const mipLevelSubresource = {
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
.baseMipLevel = 0,
|
.baseMipLevel = 0,
|
||||||
.levelCount = 1,
|
.levelCount = 1,
|
||||||
.baseArrayLayer = 0,
|
.baseArrayLayer = 0,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array prepareNextMipLevelBarriers{
|
std::array prepareNextMipLevelBarriers{
|
||||||
// prepareNextMipLevelSrcImageBarrier
|
// prepareNextMipLevelSrcImageBarrier
|
||||||
VkImageMemoryBarrier2{
|
VkImageMemoryBarrier2{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||||
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
||||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
|
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.image = textureImage,
|
.image = textureImage,
|
||||||
.subresourceRange = mipLevelSubresource,
|
.subresourceRange = mipLevelSubresource,
|
||||||
},
|
},
|
||||||
// prepareNextMipLevelDstImageBarrier
|
// prepareNextMipLevelDstImageBarrier
|
||||||
VkImageMemoryBarrier2{
|
VkImageMemoryBarrier2{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
|
.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT,
|
||||||
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT,
|
.dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT,
|
||||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
.newLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.image = textureImage,
|
.image = textureImage,
|
||||||
.subresourceRange = mipLevelSubresource,
|
.subresourceRange = mipLevelSubresource,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VkDependencyInfo const prepareNextMipLevelDependency = {
|
VkDependencyInfo const prepareNextMipLevelDependency = {
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.dependencyFlags = 0,
|
.dependencyFlags = 0,
|
||||||
.memoryBarrierCount = 0,
|
.memoryBarrierCount = 0,
|
||||||
.pMemoryBarriers = nullptr,
|
.pMemoryBarriers = nullptr,
|
||||||
.bufferMemoryBarrierCount = 0,
|
.bufferMemoryBarrierCount = 0,
|
||||||
.pBufferMemoryBarriers = nullptr,
|
.pBufferMemoryBarriers = nullptr,
|
||||||
.imageMemoryBarrierCount = static_cast<uint32_t>( prepareNextMipLevelBarriers.size() ),
|
.imageMemoryBarrierCount = static_cast<uint32_t>( prepareNextMipLevelBarriers.size() ),
|
||||||
.pImageMemoryBarriers = prepareNextMipLevelBarriers.data(),
|
.pImageMemoryBarriers = prepareNextMipLevelBarriers.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo );
|
vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo );
|
||||||
{
|
{
|
||||||
VkImageSubresourceLayers imageSubresourceLayers = {
|
VkImageSubresourceLayers imageSubresourceLayers = {
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
.mipLevel = 0,
|
.mipLevel = 0,
|
||||||
.baseArrayLayer = 0,
|
.baseArrayLayer = 0,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required.
|
// TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required.
|
||||||
VkBufferImageCopy copyRegion = {
|
VkBufferImageCopy copyRegion = {
|
||||||
.bufferOffset = 0,
|
.bufferOffset = 0,
|
||||||
.bufferRowLength = 0,
|
.bufferRowLength = 0,
|
||||||
.bufferImageHeight = 0,
|
.bufferImageHeight = 0,
|
||||||
.imageSubresource = imageSubresourceLayers,
|
.imageSubresource = imageSubresourceLayers,
|
||||||
.imageOffset = { 0, 0, 0 },
|
.imageOffset = { 0, 0, 0 },
|
||||||
.imageExtent = { width, height, 1 }
|
.imageExtent = { width, height, 1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency );
|
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency );
|
||||||
|
|
||||||
// Staging -> Image L0
|
// Staging -> Image L0
|
||||||
vkCmdCopyBufferToImage(
|
vkCmdCopyBufferToImage(
|
||||||
frameInUse.commandBuffer,
|
frameInUse.commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region );
|
||||||
stagingBuffer,
|
|
||||||
textureImage,
|
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
1,
|
|
||||||
©Region );
|
|
||||||
|
|
||||||
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0;
|
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0;
|
||||||
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1;
|
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1;
|
||||||
|
|
||||||
int32_t mipSrcWidth = static_cast<int32_t>( width );
|
int32_t mipSrcWidth = static_cast<int32_t>( width );
|
||||||
int32_t mipSrcHeight = static_cast<int32_t>( height );
|
int32_t mipSrcHeight = static_cast<int32_t>( height );
|
||||||
int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 );
|
int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 );
|
||||||
int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 );
|
int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 );
|
||||||
|
|
||||||
VkImageSubresourceLayers constexpr mipSubresourceLayers = {
|
VkImageSubresourceLayers constexpr mipSubresourceLayers = {
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
.mipLevel = 0,
|
.mipLevel = 0,
|
||||||
.baseArrayLayer = 0,
|
.baseArrayLayer = 0,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
VkImageBlit2 imageBlit = {
|
VkImageBlit2 imageBlit = {
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.srcSubresource = mipSubresourceLayers,
|
.srcSubresource = mipSubresourceLayers,
|
||||||
.srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } },
|
.srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } },
|
||||||
.dstSubresource = mipSubresourceLayers,
|
.dstSubresource = mipSubresourceLayers,
|
||||||
.dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } },
|
.dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
imageBlit.srcSubresource.mipLevel = 0;
|
imageBlit.srcSubresource.mipLevel = 0;
|
||||||
imageBlit.dstSubresource.mipLevel = 1;
|
imageBlit.dstSubresource.mipLevel = 1;
|
||||||
imageBlit.srcOffsets[1].x = mipSrcWidth;
|
imageBlit.srcOffsets[1].x = mipSrcWidth;
|
||||||
imageBlit.srcOffsets[1].y = mipSrcHeight;
|
imageBlit.srcOffsets[1].y = mipSrcHeight;
|
||||||
imageBlit.dstOffsets[1].x = mipDstWidth;
|
imageBlit.dstOffsets[1].x = mipDstWidth;
|
||||||
imageBlit.dstOffsets[1].y = mipDstHeight;
|
imageBlit.dstOffsets[1].y = mipDstHeight;
|
||||||
|
|
||||||
VkBlitImageInfo2 blitInfo = {
|
VkBlitImageInfo2 blitInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
|
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.srcImage = textureImage,
|
.srcImage = textureImage,
|
||||||
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
.dstImage = textureImage,
|
.dstImage = textureImage,
|
||||||
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
.regionCount = 1,
|
.regionCount = 1,
|
||||||
.pRegions = &imageBlit,
|
.pRegions = &imageBlit,
|
||||||
.filter = VK_FILTER_LINEAR,
|
.filter = VK_FILTER_LINEAR,
|
||||||
};
|
};
|
||||||
|
|
||||||
// MipMapping
|
// MipMapping
|
||||||
for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel )
|
for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel )
|
||||||
{
|
{
|
||||||
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency );
|
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency );
|
||||||
vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo );
|
vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo );
|
||||||
|
|
||||||
// Prep for NEXT iteration
|
// Prep for NEXT iteration
|
||||||
|
|
||||||
mipSrcWidth = mipDstWidth;
|
mipSrcWidth = mipDstWidth;
|
||||||
mipSrcHeight = mipDstHeight;
|
mipSrcHeight = mipDstHeight;
|
||||||
mipDstWidth = std::max( mipSrcWidth / 2, 1 );
|
mipDstWidth = std::max( mipSrcWidth / 2, 1 );
|
||||||
mipDstHeight = std::max( mipSrcHeight / 2, 1 );
|
mipDstHeight = std::max( mipSrcHeight / 2, 1 );
|
||||||
|
|
||||||
imageBlit.srcSubresource.mipLevel = dstMipLevel;
|
imageBlit.srcSubresource.mipLevel = dstMipLevel;
|
||||||
imageBlit.dstSubresource.mipLevel = dstMipLevel + 1;
|
imageBlit.dstSubresource.mipLevel = dstMipLevel + 1;
|
||||||
imageBlit.srcOffsets[1].x = mipSrcWidth;
|
imageBlit.srcOffsets[1].x = mipSrcWidth;
|
||||||
imageBlit.srcOffsets[1].y = mipSrcHeight;
|
imageBlit.srcOffsets[1].y = mipSrcHeight;
|
||||||
imageBlit.dstOffsets[1].x = mipDstWidth;
|
imageBlit.dstOffsets[1].x = mipDstWidth;
|
||||||
imageBlit.dstOffsets[1].y = mipDstHeight;
|
imageBlit.dstOffsets[1].y = mipDstHeight;
|
||||||
|
|
||||||
// Prep current mip level as source
|
// Prep current mip level as source
|
||||||
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel;
|
prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel;
|
||||||
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1;
|
prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1;
|
||||||
}
|
|
||||||
|
|
||||||
// End
|
|
||||||
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency );
|
|
||||||
}
|
|
||||||
vkEndCommandBuffer( frameInUse.commandBuffer );
|
|
||||||
|
|
||||||
VkSubmitInfo submitInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.waitSemaphoreCount = 0,
|
|
||||||
.pWaitSemaphores = nullptr,
|
|
||||||
.pWaitDstStageMask = nullptr,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &frameInUse.commandBuffer,
|
|
||||||
.signalSemaphoreCount = 0,
|
|
||||||
.pSignalSemaphores = nullptr,
|
|
||||||
};
|
|
||||||
VK_CHECK( vkQueueSubmit( renderDevice->directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) );
|
|
||||||
|
|
||||||
// Do not reset this. Else, the frame will never be available to the main loop.
|
|
||||||
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) );
|
|
||||||
|
|
||||||
renderDevice->frameIndex = ( renderDevice->frameIndex + 1 ) % renderDevice->getNumFrames();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vmaDestroyBuffer( renderDevice->gpuAllocator, stagingBuffer, stagingAllocation );
|
// End
|
||||||
|
vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency );
|
||||||
}
|
}
|
||||||
|
vkEndCommandBuffer( frameInUse.commandBuffer );
|
||||||
|
|
||||||
|
VkSubmitInfo submitInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.waitSemaphoreCount = 0,
|
||||||
|
.pWaitSemaphores = nullptr,
|
||||||
|
.pWaitDstStageMask = nullptr,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &frameInUse.commandBuffer,
|
||||||
|
.signalSemaphoreCount = 0,
|
||||||
|
.pSignalSemaphores = nullptr,
|
||||||
|
};
|
||||||
|
VK_CHECK( vkQueueSubmit( renderDevice->directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) );
|
||||||
|
|
||||||
|
// Do not reset this. Else, the frame will never be available to the main loop.
|
||||||
|
VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) );
|
||||||
|
|
||||||
|
renderDevice->frameIndex = ( renderDevice->frameIndex + 1 ) % renderDevice->getNumFrames();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 metallic = material.pbr_metallic_roughness.metallic_factor;
|
||||||
float const roughness = material.pbr_metallic_roughness.roughness_factor;
|
float const roughness = material.pbr_metallic_roughness.roughness_factor;
|
||||||
|
|
||||||
uint32_t const materialIdx = static_cast<uint32_t>( model->materials.size() );
|
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;
|
return materialIdx;
|
||||||
}
|
}
|
||||||
|
|
@ -453,15 +524,15 @@ void LoadAttribute(
|
||||||
size_t const components )
|
size_t const components )
|
||||||
{
|
{
|
||||||
size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 );
|
size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 );
|
||||||
ASSERT( floatCount % 3 == 0 );
|
ASSERT( floatCount % components == 0 );
|
||||||
scratch->resize( floatCount );
|
scratch->resize( floatCount );
|
||||||
cgltf_accessor_unpack_floats( positionAttr.data, scratch->data(), scratch->size() );
|
cgltf_accessor_unpack_floats( positionAttr.data, scratch->data(), scratch->size() );
|
||||||
|
|
||||||
// Guaranteed to have space for these vertices.
|
// Guaranteed to have space for these vertices.
|
||||||
pVertices->resize( vertexStart + floatCount / components );
|
pVertices->resize( vertexStart + floatCount / components );
|
||||||
|
|
||||||
byte* writePtr = reinterpret_cast<byte*>( pVertices->data() + vertexStart ) + offset;
|
byte* writePtr = reinterpret_cast<byte*>( pVertices->data() + vertexStart ) + offset;
|
||||||
float* readPtr = scratch->data();
|
float const* readPtr = scratch->data();
|
||||||
for ( size_t i = vertexStart; i < pVertices->size(); ++i )
|
for ( size_t i = vertexStart; i < pVertices->size(); ++i )
|
||||||
{
|
{
|
||||||
memcpy( writePtr, readPtr, components * sizeof( float ) );
|
memcpy( writePtr, readPtr, components * sizeof( float ) );
|
||||||
|
|
@ -547,6 +618,18 @@ ModelMesh ProcessMesh(
|
||||||
|
|
||||||
LoadAttribute( pVertices, vertexStart, &scratch, normalAttr, stride, offset, components );
|
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 )
|
if ( "TEXCOORD_0"sv == attributes[attribIndex].name )
|
||||||
{
|
{
|
||||||
cgltf_attribute const& texCoordAttr = attributes[attribIndex];
|
cgltf_attribute const& texCoordAttr = attributes[attribIndex];
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ struct GlobalMemory;
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
DirectX::XMFLOAT4 position = { 0.0f, 0.0f, 0.0f, 1.0f };
|
DirectX::XMFLOAT4 position = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
DirectX::XMFLOAT4 normal = { 1.0f, 1.0f, 1.0f, 0.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 texCoord0 = { 0.0f, 0.0f };
|
||||||
DirectX::XMFLOAT2 texCoord1 = { 0.0f, 0.0f };
|
DirectX::XMFLOAT2 texCoord1 = { 0.0f, 0.0f };
|
||||||
DirectX::XMFLOAT4 color0 = { 1.0f, 1.0f, 1.0f, 1.0f };
|
DirectX::XMFLOAT4 color0 = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
|
|
@ -42,21 +43,23 @@ struct ModelMesh
|
||||||
|
|
||||||
struct Material
|
struct Material
|
||||||
{
|
{
|
||||||
constexpr static size_t GPU_DATA_OFFSET = sizeof( VkSampler );
|
size_t constexpr static GPU_DATA_OFFSET = sizeof( VkSampler );
|
||||||
constexpr static size_t GPU_DATA_SIZE =
|
size_t constexpr static GPU_DATA_SIZE = 56;
|
||||||
sizeof( TextureID ) + sizeof( uint32_t ) + 2 * sizeof( float ) + sizeof( DirectX::XMFLOAT4 );
|
|
||||||
|
|
||||||
VkSampler sampler; // TODO: Reuse
|
VkSampler sampler; // TODO: Reuse
|
||||||
// To copy directly.
|
// To copy directly.
|
||||||
TextureID texture;
|
DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
uint32_t padding0; // FIXME: Wasting space.
|
DirectX::XMFLOAT4 emission = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
TextureID albedoTextureID;
|
||||||
|
TextureID normalTextureID;
|
||||||
|
TextureID metalRoughTextureID;
|
||||||
|
TextureID emissiveTextureID;
|
||||||
float roughness = 1.0f;
|
float roughness = 1.0f;
|
||||||
float metallic = 1.0f;
|
float metallic = 1.0f;
|
||||||
DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
||||||
|
|
||||||
[[nodiscard]] bool isNull() const
|
[[nodiscard]] bool isNull() const
|
||||||
{
|
{
|
||||||
return texture.isNull() or sampler;
|
return not( albedoTextureID and normalTextureID and metalRoughTextureID and emissiveTextureID and sampler );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
template struct RID<Texture>;
|
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() )
|
if ( m_freeList.empty() )
|
||||||
{
|
{
|
||||||
|
|
@ -20,8 +20,6 @@ std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent,
|
||||||
ASSERT( m_pRenderDevice );
|
ASSERT( m_pRenderDevice );
|
||||||
RenderDevice const& renderDevice = *m_pRenderDevice;
|
RenderDevice const& renderDevice = *m_pRenderDevice;
|
||||||
|
|
||||||
VkFormat const format = VK_FORMAT_R8G8B8A8_SRGB;
|
|
||||||
|
|
||||||
VkImage texture;
|
VkImage texture;
|
||||||
VmaAllocation textureAllocation;
|
VmaAllocation textureAllocation;
|
||||||
VkImageView textureView;
|
VkImageView textureView;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ public:
|
||||||
void freeTexture( TextureID&& rid );
|
void freeTexture( TextureID&& rid );
|
||||||
|
|
||||||
DEPRECATE_JULY_2025
|
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
|
DEPRECATE_JULY_2025
|
||||||
std::optional<VkImage> fetchImage( TextureID const& rid );
|
std::optional<VkImage> fetchImage( TextureID const& rid );
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue