diff --git a/Blaze/Assets/Shaders/Bindless.slang b/Blaze/Assets/Shaders/Bindless.slang index 6489acb..b4ebd0b 100644 --- a/Blaze/Assets/Shaders/Bindless.slang +++ b/Blaze/Assets/Shaders/Bindless.slang @@ -1,21 +1,21 @@ -[vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> gTextures[]; +[vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> g_Textures[]; public struct RID where T : IOpaqueDescriptor { - private uint internal; + private uint m_internal; private const static uint INDEX_MASK = 0x0007FFFF; private const static uint GENERATION_MASK = ~INDEX_MASK; public property bool hasValue { get { - return (internal & GENERATION_MASK) > 0; + return (m_internal & GENERATION_MASK) > 0; } } public property T value { get { // TODO: Check if has value else placeholder. - return gTextures[internal & INDEX_MASK].asOpaqueDescriptor(); + return g_Textures[m_internal & INDEX_MASK].asOpaqueDescriptor(); } } } diff --git a/Blaze/Assets/Shaders/Material.slang b/Blaze/Assets/Shaders/Material.slang index 3a4d7f7..fa97384 100644 --- a/Blaze/Assets/Shaders/Material.slang +++ b/Blaze/Assets/Shaders/Material.slang @@ -1,4 +1,5 @@ import Bindless; +import PBR; public struct Material { float4 baseColor; @@ -10,54 +11,70 @@ public struct Material { float metallic; float roughness; - public float4 getAlbedo(float2 uv, float4 inColor) { - if (let albedoTex = albedoTextureID) { - return baseColor * albedoTex.Sample(uv).rgba; + float4 GetAlbedo(float2 uv, float4 in_color) { + if (let albedo_tex = albedoTextureID) { + return in_color * baseColor * albedo_tex.Sample(uv).rgba; } - return baseColor; + return baseColor * in_color; } - public float3 getEmissive(float2 uv) { - if (let emissionTex = emissiveTextureID) { - return emissionTex.Sample(uv).rgb * emissiveColor.rgb * emissiveColor.a; + public float3 GetEmissive(float2 uv) { + if (let emission_tex = emissiveTextureID) { + return emission_tex.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); + float3 GetNormal(float3 position, float3 vert_normal, float4 vert_tangent, float2 uv) { + float3 normal = normalize(vert_normal.xyz); - if (let normalTex = normalTextureID) { - let vNt = normalize(2.0f * normalTex.Sample(uv).rgb - 1.0f); + if (let normal_tex = normalTextureID) { + let normal_tanspace = normalize(2.0f * normal_tex.Sample(uv).rgb - 1.0f); - float3 T; - float3 B; + float3 tangent; + float3 bitangent; - if (tangent.w == 0.0f) { - float3 q1 = ddx(position); - float3 q2 = ddy(position); + if (vert_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); + 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)); + tangent = -(q1 * st2.y - q2 * st1.y) / det; + tangent = tangent - normal * dot(normal, tangent); + bitangent = normalize(cross(normal, tangent)); } else { - T = normalize(tangent.xyz); - B = tangent.w * cross(N, T); + tangent = normalize(vert_tangent.xyz); + bitangent = vert_tangent.w * cross(normal, tangent); } - N = normalize(T * vNt.x + B * vNt.y + N * vNt.z); + normal = normalize(tangent * normal_tanspace.x + bitangent * normal_tanspace.y + normal * normal_tanspace.z); } - return N; + return normal; } - public float2 getMetalRough(float2 uv) { - if (let metalRoughTex = metalRoughTextureID) { - return metalRoughTex.Sample(uv).bg * float2(metallic, roughness); + float2 GetMetalRough(float2 uv) { + if (let metal_rough_tex = metalRoughTextureID) { + return metal_rough_tex.Sample(uv).bg * float2(metallic, roughness); } return float2(metallic, roughness); } + + public IBRDF GetBRDF( + float2 uv, + float3 position, + float3 vertex_normal, + float4 vertex_tangent, + float4 vertex_color) + { + float3 normal = GetNormal(position, vertex_normal, vertex_tangent, uv); + float2 metal_rough = GetMetalRough(uv); + float3 albedo = GetAlbedo(uv, vertex_color).xyz; + float3 f0 = 0.04f.xxx; + f0 = lerp(f0, albedo, metal_rough.x); + + return BRDFCookTorranceGGX(albedo, metal_rough.x, normal, metal_rough.y, f0); + } } \ No newline at end of file diff --git a/Blaze/Assets/Shaders/Mesh.slang b/Blaze/Assets/Shaders/Mesh.slang index f20dd64..b179ce3 100644 --- a/Blaze/Assets/Shaders/Mesh.slang +++ b/Blaze/Assets/Shaders/Mesh.slang @@ -15,7 +15,14 @@ struct VertexOut { struct CameraData { float4x4 view; float4x4 proj; - float4 position; + float4 position; +}; + +struct PointLight { + float3 position; + float range; + float3 color; + float attenuation; }; struct DirectionalLight { @@ -26,25 +33,15 @@ struct DirectionalLight { }; struct LightData { - PointLight* pointLights; + PointLight* pointLights; DirectionalLight* dirLights; - uint pointLightCount; - uint dirLightCount; - - PointLight getPointLight(uint idx) { - if (idx >= pointLightCount) return pointLights[0]; - return pointLights[idx]; - } - - DirectionalLight getDirectionalLight(uint idx) { - if (idx >= dirLightCount) return dirLights[0]; - return dirLights[idx]; - } + uint pointLightCount; + uint dirLightCount; }; struct PerFrameData { - CameraData camera; - LightData lightData; + CameraData camera; + LightData lightData; }; uniform ParameterBlock pfd; @@ -64,203 +61,64 @@ VertexOut VertexMain( float3 position, float3 normal, float4 tangent, - float2 texCoord0, - float2 texCoord1, - float4 vertexColor0, + float2 uv0, + float2 uv1, + float4 vertex_color0, ) { - float4 worldPosition = mul(pcb.transform, float4(position, 1.0f)); - VertexOut output; - output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition)); - output.worldPosition = worldPosition; - output.normal = normalize(mul(float4(normal.xyz, 0.0f), pcb.invTransform).xyz); + float4 worldPosition = mul(pcb.transform, float4(position, 1.0f)); + + output.outPosition = mul(pfd.camera.proj, mul(pfd.camera.view, worldPosition)); + output.worldPosition = worldPosition; + output.normal = normalize(mul(float4(normal.xyz, 0.0f), pcb.invTransform).xyz); if (tangent.w == 0.0f) { 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; + output.texCoord0 = uv0; + output.texCoord1 = uv1; + output.vertexColor0 = vertex_color0; return output; } - -float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness) -{ - float Coeff = Roughness * Roughness; - float Alpha2 = Coeff * Coeff; - float NdotH = max(dot(Normal, Halfway), 0.0f); - float NdotH2 = NdotH * NdotH; - - float Numerator = Alpha2; - float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f; - Denominator = PI * Denominator * Denominator; - - return Numerator / Denominator; -} - -float GeometrySchlickGGX(float NdotV, float Roughness) -{ - float R = Roughness + 1.0f; - float K = (R * R) / 8.0f; - - float Numerator = NdotV; - float Denominator = NdotV * (1.0f - K) + K; - - return Numerator / Denominator; -} - -float GeometrySmith(float NdotV, float NdotL, float Roughness) -{ - float GGX1 = GeometrySchlickGGX(NdotV, Roughness); - float GGX2 = GeometrySchlickGGX(NdotL, Roughness); - - return GGX1 * GGX2; -} - -// https://en.wikipedia.org/wiki/Schlick%27s_approximation -float3 FresnelSchlick(float cosine, float3 F_0) -{ - return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. -} - -// Sebastian Lagarde -float3 FresnelSchlickRoughness(float cosine, float3 F_0, float Roughness) -{ - return F_0 + (max((1.0f - Roughness).xxx, F_0) - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. -} - -float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 Normal, float Metallic, float Roughness, float3 F_0, float3 LightDir, float LightDistance) -{ - float Attenuation = 1.0f / (LightDistance * LightDistance); // TODO: Controlled Attenuation - float3 Halfway = normalize(ViewDir + LightDir); - - float CosineFactor = max(dot(Halfway, ViewDir), 0.0f); - float NdotV = max(dot(Normal, ViewDir), 0.0f); - float NdotL = max(dot(Normal, LightDir), 0.0f); - - float3 Radiance = LightColor * Attenuation; - - float NormalDistribution = TrowbridgeReitzGGX(Normal, Halfway, Roughness); - float Geometry = GeometrySmith(NdotV, NdotL, Roughness); - float3 Fresnel = FresnelSchlickRoughness(CosineFactor, F_0, Roughness); - - float3 Numerator = (NormalDistribution * Geometry) * Fresnel; - float Denominator = 4.0f * NdotV * NdotL; - float3 Specular = Numerator / (Denominator + 0.00001f); - - float3 K_Specular = Fresnel; - float3 K_Diffuse = 1.0f.xxx - K_Specular; - - K_Diffuse *= 1.0f - Metallic; - - return NdotL * Radiance * (K_Diffuse * Albedo / PI + Specular); -} - -float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) -{ - if (pfd.lightData.pointLightCount == 0) - return 0.0f.xxx; - - float3 ViewDir = normalize(pfd.camera.position.xyz - Position); - - float Metallic = MetalRough.r; - float Roughness = MetalRough.g; - - // Dielectric F_0 based on LearnOpenGL. - // TODO: Cite - float3 F_0 = 0.04f.xxx; - F_0 = lerp(F_0, Albedo, Metallic); - - float3 Contrib = 0.0f; - for (uint i = 0; i < pfd.lightData.pointLightCount; ++i) - { - PointLight Light = pfd.lightData.pointLights[i]; - - if (Light.range < 0.0f) - continue; - - float3 LightDir = float3(Light.position) - Position; - float LightDistance = length(LightDir); - - if (LightDistance > Light.range) - continue; - - LightDir /= LightDistance; // Normalization - - // Color Unpack - //float R = (Light.Color & 0xFF000000) >> 24; - //float G = (Light.Color & 0x00FF0000) >> 16; - //float B = (Light.Color & 0x0000FF00) >> 8; - - //float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 - - Contrib += GetPBRContrib(Albedo, Light.color, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance); - } - - return Contrib; -} - -float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) -{ - if (pfd.lightData.dirLightCount == 0) - return 0.0f.xxx; - - float3 ViewDir = normalize(pfd.camera.position.xyz - Position); - - float Metallic = MetalRough.r; - float Roughness = MetalRough.g; - - // Dielectric F_0 based on LearnOpenGL. - // TODO: Cite - float3 F_0 = 0.04f.xxx; - F_0 = lerp(F_0, Albedo, Metallic); - - float3 Contrib = 0.0f; - for (uint i = 0; i < pfd.lightData.dirLightCount; ++i) - { - DirectionalLight Light = pfd.lightData.dirLights[i]; - - if (Light._padding0 < 0.0f) - continue; - - float3 LightDir = -normalize(Light.direction); - - // Color Unpack - //float R = (Light.Color & 0xFF000000) >> 24; - //float G = (Light.Color & 0x00FF0000) >> 16; - //float B = (Light.Color & 0x0000FF00) >> 8; - - //float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 - - float3 LightColor = Light.color; - - Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f); - } - - return Contrib; -} - [shader("fragment")] float4 FragmentMain( - float4 position : POSITION, - float3 normal : NORMAL, - float4 tangent : TANGENT, - float2 texCoord0 : TEXCOORD0, - float2 texCoord1 : TEXCOORD1, - float4 vertexColor0 : COLOR0, + float4 position : POSITION, + float3 normal : NORMAL, + float4 tangent : TANGENT, + float2 uv0 : TEXCOORD0, + float2 uv1 : TEXCOORD1, + float4 color0 : COLOR0, ) : SV_Target0 { + IBRDF brdf = pcb.material.GetBRDF(uv0, position.xyz, normal.xyz, tangent, color0); - float3 N = pcb.material.getNormal(position.xyz, normal.xyz, tangent, texCoord0); - float2 metalRough = pcb.material.getMetalRough(texCoord0); + float3 view_dir = normalize(pfd.camera.position.xyz - position.xyz); - float4 albedo = pcb.material.getAlbedo(texCoord0, vertexColor0); - float3 viewDir = normalize(position.xyz - pfd.camera.position.xyz); + float3 point_contrib = 0.0f.xxx; + for (int i = 0; i < pfd.lightData.pointLightCount; ++i) { + PointLight point_light = pfd.lightData.pointLights[i]; + + float3 light_dir = float3(point_light.position) - position.xyz; + float light_dist = length(light_dir); + + if (light_dist > point_light.range) + continue; + + light_dir /= light_dist; // Normalization + + float attenuation = 1.0f / (light_dist * light_dist); // TODO: Controlled Attenuation + float3 radiance = point_light.color * attenuation; - float3 pointContrib = GetPointLightInfluence(albedo.rgb, metalRough, position.xyz, N); - float3 dirContrib = GetDirectionalLightInfluence(albedo.rgb, metalRough, position.xyz, N); + point_contrib += brdf.Evaluate(radiance, view_dir, light_dir); + } - return float4(pcb.material.getEmissive(texCoord0) + pointContrib + dirContrib, 1.0f); + float3 dir_contrib = 0.0f.xxx; + for (int i = 0; i < pfd.lightData.dirLightCount; ++i) { + DirectionalLight dir_light = pfd.lightData.dirLights[i]; + dir_contrib += brdf.Evaluate(dir_light.color, view_dir, -normalize(dir_light.direction)); + } + + return float4(pcb.material.GetEmissive(uv0) + point_contrib + dir_contrib, 1.0f); } diff --git a/Blaze/Assets/Shaders/PBR.slang b/Blaze/Assets/Shaders/PBR.slang index daea05f..9dbb572 100644 --- a/Blaze/Assets/Shaders/PBR.slang +++ b/Blaze/Assets/Shaders/PBR.slang @@ -1,107 +1,93 @@ public static const float PI = 3.14159265f; -float TrowbridgeReitzGGX(float3 normal, float3 halfway, float roughness) +public 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 alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float n_dot_h = max(dot(normal, halfway), 0.0f); + float n_dot_h_2 = n_dot_h * n_dot_h; - float numerator = alpha2; - float denominator = nDotH2 * (alpha2 - 1.0f) + 1.0f; - denominator = PI * denominator * denominator; + float numerator = alpha2; + float denominator = n_dot_h_2 * (alpha2 - 1.0f) + 1.0f; + denominator = PI * denominator * denominator; return numerator / denominator; } -float GeometrySchlickGGX(float nDotV, float roughness) +public float GeometrySchlickGGX(float n_dot_v, float roughness) { - float r = roughness + 1.0f; - float k = (r * r) / 8.0f; + float r = roughness + 1.0f; + float k = (r * r) / 8.0f; - float numerator = nDotV; - float denominator = nDotV * (1.0f - k) + k; + float numerator = n_dot_v; + float denominator = n_dot_v * (1.0f - k) + k; return numerator / denominator; } -float GeometrySmith(float nDotV, float nDotL, float roughness) +public float GeometrySmith(float n_dot_v, float n_dot_l, float roughness) { - float ggx1 = GeometrySchlickGGX(nDotV, roughness); - float ggx2 = GeometrySchlickGGX(nDotL, roughness); + float ggx1 = GeometrySchlickGGX(n_dot_v, roughness); + float ggx2 = GeometrySchlickGGX(n_dot_l, roughness); return ggx1 * ggx2; } // https://en.wikipedia.org/wiki/Schlick%27s_approximation -float3 FresnelSchlick(float cosine, float3 f_0) +public 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) +public 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 interface IBRDF { + public float3 Evaluate(float3 radiance, float3 view_dir, float3 ligth_dir); } -public struct PointLight { - public float3 position; - public float range; - public float3 color; - float attenuation; +public struct BRDFCookTorranceGGX : IBRDF { + float3 m_albedo; + float m_metallic; + float3 m_normal; + float m_roughness; + float3 m_f0; - public float3 getInfluence(float3 albedo, float2 metalRough, float3 viewDir, float3 worldPosition, float3 normal, float3 f_0) { + public __init(float3 albedo, float metallic, float3 normal, float roughness, float3 f0) { + m_albedo = albedo; + m_metallic = metallic; + m_normal = normal; + m_roughness = roughness; + m_f0 = f0; + } - float3 lightDir = float3(position) - worldPosition; - float lightDistance = length(lightDir); + public float3 Evaluate( + float3 radiance, + float3 view_dir, + float3 light_dir) + { + float3 halfway = normalize(view_dir + light_dir); - if (lightDistance > range) - return 0.0f.xxx; + float cosine_factor = max(dot(halfway, view_dir), 0.0f); + float n_dot_v = max(dot(m_normal, view_dir), 0.0f); + float n_dot_l = max(dot(m_normal, light_dir), 0.0f); + + float normal_dist = TrowbridgeReitzGGX(m_normal, halfway, m_roughness); + float geometry = GeometrySmith(n_dot_v, n_dot_l, m_roughness); + float3 fresnel = FresnelSchlickRoughness(cosine_factor, m_f0, m_roughness); - lightDir /= lightDistance; // Normalization + float3 numerator = (normal_dist * geometry) * fresnel; + float denominator = 4.0f * n_dot_v * n_dot_l; + float3 specular = numerator / (denominator + 0.00001f); - // Color Unpack - //float R = (Light.Color & 0xFF000000) >> 24; - //float G = (Light.Color & 0x00FF0000) >> 16; - //float B = (Light.Color & 0x0000FF00) >> 8; + float3 k_specular = fresnel; + float3 k_diffuse = 1.0f.xxx - k_specular; - //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); - } -}; + k_diffuse *= 1.0f - m_metallic; + + return n_dot_l * radiance * (k_diffuse * m_albedo / PI + specular); + } +} \ No newline at end of file diff --git a/Blaze/Source/ModelLoader.cpp b/Blaze/Source/ModelLoader.cpp index 7600266..c8b86f3 100644 --- a/Blaze/Source/ModelLoader.cpp +++ b/Blaze/Source/ModelLoader.cpp @@ -507,15 +507,15 @@ uint32_t ProcessMaterial( RenderDevice* render_device, Model* model, cgltf_mater uint32_t const material_idx = static_cast( model->materials.size() ); model->materials.push_back( { - sampler, - base_color_factor, - emissive_factor, - base_color_texture, - normal_texture, - metal_rough_texture, - emissive_texture, - roughness, - metallic, + .sampler = sampler, + .baseColor = base_color_factor, + .emission = emissive_factor, + .albedoTextureID = base_color_texture, + .normalTextureID = normal_texture, + .metalRoughTextureID = metal_rough_texture, + .emissiveTextureID = emissive_texture, + .metallic = metallic, + .roughness = roughness, } ); return material_idx; diff --git a/Blaze/Source/ModelLoader.h b/Blaze/Source/ModelLoader.h index 20f83ce..227fe7a 100644 --- a/Blaze/Source/ModelLoader.h +++ b/Blaze/Source/ModelLoader.h @@ -57,8 +57,8 @@ struct Material TextureID normalTextureID; TextureID metalRoughTextureID; TextureID emissiveTextureID; - float roughness = 1.0f; float metallic = 1.0f; + float roughness = 1.0f; [[nodiscard]] bool IsNull() const {