diff --git a/Cargo.toml b/Cargo.toml index f8a53af..77ce96d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,7 @@ gltf = "1.3" egui = "0.27" egui-winit = "0.27" egui-ash-renderer = { version = "0.3.0", features = ["dynamic-rendering"]} + +[patch.crates-io.gltf] +git = "https://github.com/adrien-ben/gltf" +branch = "missing_extensions" diff --git a/crates/libs/model/Cargo.toml b/crates/libs/model/Cargo.toml index f0f9f8c..3a06ea3 100644 --- a/crates/libs/model/Cargo.toml +++ b/crates/libs/model/Cargo.toml @@ -18,5 +18,6 @@ features = [ "KHR_lights_punctual", "KHR_materials_unlit", "KHR_materials_pbrSpecularGlossiness", - "KHR_materials_emissive_strength" + "KHR_materials_emissive_strength", + "KHR_materials_clearcoat", ] diff --git a/crates/libs/model/src/material.rs b/crates/libs/model/src/material.rs index 487825e..360baf5 100644 --- a/crates/libs/model/src/material.rs +++ b/crates/libs/model/src/material.rs @@ -21,6 +21,7 @@ pub struct Material { alpha_cutoff: f32, double_sided: bool, is_unlit: bool, + clearcoat: Option, } #[derive(Clone, Copy, Debug)] @@ -85,6 +86,49 @@ impl SpecularGlossinessWorkflow { } } +#[derive(Clone, Copy, Debug, Default)] +pub struct Clearcoat { + factor: f32, + roughness: f32, + factor_texture: Option, + roughness_texture: Option, + normal_texture: Option, +} + +impl Clearcoat { + pub fn factor(&self) -> f32 { + self.factor + } + + pub fn roughness(&self) -> f32 { + self.roughness + } + + pub fn factor_texture(&self) -> Option { + self.factor_texture + } + + pub fn factor_texture_index(&self) -> Option { + self.factor_texture.map(|info| info.index) + } + + pub fn roughness_texture(&self) -> Option { + self.roughness_texture + } + + pub fn roughness_texture_index(&self) -> Option { + self.roughness_texture.map(|info| info.index) + } + + pub fn normal_texture(&self) -> Option { + self.normal_texture + } + + pub fn normal_texture_index(&self) -> Option { + self.normal_texture.map(|info| info.index) + } +} + impl Material { pub fn get_color(&self) -> [f32; 4] { self.color @@ -150,6 +194,10 @@ impl Material { self.is_unlit } + pub fn get_clearcoat(&self) -> Option { + self.clearcoat + } + pub fn get_workflow(&self) -> Workflow { self.workflow } @@ -212,6 +260,14 @@ impl<'a> From> for Material { let is_unlit = material.unlit(); + let clearcoat = material.clearcoat().map(|m| Clearcoat { + factor: m.clearcoat_factor(), + roughness: m.clearcoat_roughness_factor(), + factor_texture: get_texture(m.clearcoat_texture()), + roughness_texture: get_texture(m.clearcoat_roughness_texture()), + normal_texture: get_texture(m.clearcoat_normal_texture()), + }); + Material { color, emissive, @@ -225,6 +281,7 @@ impl<'a> From> for Material { alpha_cutoff, double_sided, is_unlit, + clearcoat, } } } diff --git a/crates/viewer/shaders/model.frag b/crates/viewer/shaders/model.frag index 4f5261f..1f95344 100644 --- a/crates/viewer/shaders/model.frag +++ b/crates/viewer/shaders/model.frag @@ -21,12 +21,15 @@ const uint OUTPUT_MODE_ALPHA = 8; const uint OUTPUT_MODE_UVS0 = 9; const uint OUTPUT_MODE_UVS1 = 10; const uint OUTPUT_MODE_SSAO = 11; +const uint OUTPUT_MODE_CLEARCOAT_FACTOR = 12; +const uint OUTPUT_MODE_CLEARCOAT_ROUGHNESS = 13; +const uint OUTPUT_MODE_CLEARCOAT_NORMAL = 14; const vec3 DIELECTRIC_SPECULAR = vec3(0.04); const vec3 BLACK = vec3(0.0); const float PI = 3.14159; -const uint NO_TEXTURE_ID = 255; +const uint NO_TEXTURE_ID = 3; const uint ALPHA_MODE_MASK = 1; const uint ALPHA_MODE_BLEND = 2; @@ -47,6 +50,9 @@ struct TextureChannels { uint emissive; uint normal; uint occlusion; + uint clearcoatFactor; + uint clearcoatRoughness; + uint clearcoatNormal; }; struct Light { @@ -66,6 +72,10 @@ struct PbrInfo { vec3 specular; float roughness; bool metallicRoughnessWorkflow; + vec3 normal; + float clearcoatFactor; + float clearcoatRoughness; + vec3 clearcoatNormal; }; // -- Inputs -- @@ -79,29 +89,35 @@ layout(location = 5) in mat3 oTBN; // -- Push constants layout(push_constant) uniform MaterialUniform { vec4 color; - // Contains the emissive factor and roughness (or glossiness) factor. - // - emissive: emissiveAndRoughnessGlossiness.rgb - // - roughness: emissiveAndRoughnessGlossiness.a (for metallic/roughness workflows) - // - glossiness: emissiveAndRoughnessGlossiness.a (for specular/glossiness workflows) - vec4 emissiveAndRoughnessGlossiness; - // Contains the metallic (or specular) factor and occlusion factor. - // - metallic: metallicSpecularAndOcclusion.r (for metallic/roughness workflows) - // - specular: metallicSpecularAndOcclusion.rgb (for specular/glossiness workflows) - // - occlusion: metallicSpecularAndOcclusion.a - vec4 metallicSpecularAndOcclusion; - // Contains the texture channels for color metallic/roughness emissive and normal - // [0-7] Color texture channel - // [8-15] metallic/roughness texture channel - // [16-23] emissive texture channel - // [24-31] normals texture channel - uint colorMetallicRoughnessEmissiveNormalTextureChannels; - // Contains occlusion texture channel, alpha mode and unlit flag - // [0-7] Occlusion texture channel - // [8-15] Alpha mode - // [16-23] Unlit flag - // [24-31] Workflow (metallic/roughness or specular/glossiness) - uint occlusionTextureChannelAlphaModeUnlitFlagAndWorkflow; + vec3 emissiveFactor; + // - roughness for metallic/roughness workflows + // - glossiness for specular/glossiness workflows + float roughnessGlossiness; + // Contains the metallic (or specular) factor. + // - metallic: r (for metallic/roughness workflows) + // - specular: rgb (for specular/glossiness workflows) + vec3 metallicSpecular; + float occlusion; float alphaCutoff; + float clearcoatFactor; + float clearcoatRoughness; + // Contains the texture channels. Each channel taking 2 bits + // [0-1] Color texture channel + // [2-3] metallic/roughness or specular/glossiness texture channel + // [4-5] emissive texture channel + // [6-7] normals texture channel + // [8-9] Occlusion texture channel + // [10-11] Clearcoat factor texture channel + // [12-13] Clearcoat roughness texture channel + // [14-15] Clearcoat normal texture channel + // [16-31] Reserved + uint texturesChannels; + // Contains alpha mode, unlit flag and workflow flag + // [0-7] Alpha mode + // [8-15] Unlit flag + // [16-23] Workflow (metallic/roughness or specular/glossiness) + // [24-31] Reserved + uint alphaModeUnlitFlagAndWorkflow; uint lightCount; uint outputMode; float emissiveIntensity; @@ -130,18 +146,24 @@ layout(binding = 8, set = 2) uniform sampler2D normalsSampler; layout(binding = 9, set = 2) uniform sampler2D materialSampler; layout(binding = 10, set = 2) uniform sampler2D occlusionSampler; layout(binding = 11, set = 2) uniform sampler2D emissiveSampler; -layout(binding = 12, set = 3) uniform sampler2D aoMapSampler; +layout(binding = 12, set = 2) uniform sampler2D clearcoatFactorSampler; +layout(binding = 13, set = 2) uniform sampler2D clearcoatRoughnessSampler; +layout(binding = 14, set = 2) uniform sampler2D clearcoatNormalSampler; +layout(binding = 15, set = 3) uniform sampler2D aoMapSampler; // Output layout(location = 0) out vec4 outColor; TextureChannels getTextureChannels() { return TextureChannels( - (material.colorMetallicRoughnessEmissiveNormalTextureChannels >> 24) & 255, - (material.colorMetallicRoughnessEmissiveNormalTextureChannels >> 16) & 255, - (material.colorMetallicRoughnessEmissiveNormalTextureChannels >> 8) & 255, - material.colorMetallicRoughnessEmissiveNormalTextureChannels & 255, - (material.occlusionTextureChannelAlphaModeUnlitFlagAndWorkflow >> 24) & 255 + (material.texturesChannels >> 30) & 3, + (material.texturesChannels >> 28) & 3, + (material.texturesChannels >> 26) & 3, + (material.texturesChannels >> 24) & 3, + (material.texturesChannels >> 22) & 3, + (material.texturesChannels >> 20) & 3, + (material.texturesChannels >> 18) & 3, + (material.texturesChannels >> 16) & 3 ); } @@ -162,7 +184,7 @@ vec4 getBaseColor(TextureChannels textureChannels) { } float getMetallic(TextureChannels textureChannels) { - float metallic = material.metallicSpecularAndOcclusion.r; + float metallic = material.metallicSpecular.r; if(textureChannels.material != NO_TEXTURE_ID) { vec2 uv = getUV(textureChannels.material); metallic *= texture(materialSampler, uv).b; @@ -171,7 +193,7 @@ float getMetallic(TextureChannels textureChannels) { } vec3 getSpecular(TextureChannels textureChannels) { - vec3 specular = material.metallicSpecularAndOcclusion.rgb; + vec3 specular = material.metallicSpecular; if(textureChannels.material != NO_TEXTURE_ID) { vec2 uv = getUV(textureChannels.material); specular *= texture(materialSampler, uv).rgb; @@ -194,7 +216,7 @@ float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { } float getRoughness(TextureChannels textureChannels, bool metallicRoughnessWorkflow) { - float roughness = material.emissiveAndRoughnessGlossiness.a; + float roughness = material.roughnessGlossiness; if(textureChannels.material != NO_TEXTURE_ID) { vec2 uv = getUV(textureChannels.material); if (metallicRoughnessWorkflow) { @@ -211,7 +233,7 @@ float getRoughness(TextureChannels textureChannels, bool metallicRoughnessWorkfl } vec3 getEmissiveColor(TextureChannels textureChannels) { - vec3 emissive = material.emissiveAndRoughnessGlossiness.rgb; + vec3 emissive = material.emissiveFactor; if(textureChannels.emissive != NO_TEXTURE_ID) { vec2 uv = getUV(textureChannels.emissive); emissive *= texture(emissiveSampler, uv).rgb; @@ -247,17 +269,18 @@ vec3 occludeAmbientColor(vec3 ambientColor, TextureChannels textureChannels) { vec2 uv = getUV(textureChannels.occlusion); sampledOcclusion = texture(occlusionSampler, uv).r; } - return mix(ambientColor, ambientColor * sampledOcclusion, material.metallicSpecularAndOcclusion.a) * aoMapSample; + return mix(ambientColor, ambientColor * sampledOcclusion, material.occlusion) * aoMapSample; } uint getAlphaMode() { - return (material.occlusionTextureChannelAlphaModeUnlitFlagAndWorkflow >> 16) & 255; + return (material.alphaModeUnlitFlagAndWorkflow >> 24) & 255; } bool isMasked(vec4 baseColor) { // discard masked fragments if (PASS == PASS_OPAQUE) { - return getAlphaMode() == ALPHA_MODE_MASK && baseColor.a + ALPHA_CUTOFF_BIAS < material.alphaCutoff; + float alphaCutoff = material.alphaCutoff; + return getAlphaMode() == ALPHA_MODE_MASK && baseColor.a + ALPHA_CUTOFF_BIAS < alphaCutoff; } // discard non opaque fragment @@ -282,7 +305,7 @@ float getAlpha(vec4 baseColor) { } bool isUnlit() { - uint unlitFlag = (material.occlusionTextureChannelAlphaModeUnlitFlagAndWorkflow >> 8) & 255; + uint unlitFlag = (material.alphaModeUnlitFlagAndWorkflow >> 16) & 255; if (unlitFlag == UNLIT_FLAG_UNLIT) { return true; } @@ -290,13 +313,46 @@ bool isUnlit() { } bool isMetallicRoughnessWorkflow() { - uint workflow = material.occlusionTextureChannelAlphaModeUnlitFlagAndWorkflow & 255; + uint workflow = (material.alphaModeUnlitFlagAndWorkflow >> 8) & 255; if (workflow == METALLIC_ROUGHNESS_WORKFLOW) { return true; } return false; } +float getClearcoatFactor(TextureChannels textureChannels) { + float factor = material.clearcoatFactor; + if(textureChannels.clearcoatFactor != NO_TEXTURE_ID) { + vec2 uv = getUV(textureChannels.clearcoatFactor); + factor *= texture(clearcoatFactorSampler, uv).r; + } + return factor; +} + +float getClearcoatRoughness(TextureChannels textureChannels) { + float roughness = material.clearcoatRoughness; + if(textureChannels.clearcoatRoughness != NO_TEXTURE_ID) { + vec2 uv = getUV(textureChannels.clearcoatRoughness); + roughness *= texture(clearcoatRoughnessSampler, uv).g; + } + return roughness; +} + +vec3 getClearcoatNormal(TextureChannels textureChannels) { + vec3 normal = normalize(oNormals); + if (textureChannels.clearcoatNormal != NO_TEXTURE_ID) { + vec2 uv = getUV(textureChannels.clearcoatNormal); + vec3 normalMap = texture(clearcoatNormalSampler, uv).rgb * 2.0 - 1.0; + normal = normalize(oTBN * normalMap); + } + + if (!gl_FrontFacing) { + normal *= -1.0; + } + + return normal; +} + vec3 f(vec3 f0, vec3 v, vec3 h) { return f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0); } @@ -334,13 +390,14 @@ float computeAttenuation(float distance, float range) { vec3 computeColor( PbrInfo pbrInfo, - vec3 n, vec3 l, vec3 v, vec3 h, vec3 lightColor, float lightIntensity ) { + vec3 n = pbrInfo.normal; + vec3 color = vec3(0.0); if (dot(n, l) > 0.0 || dot(n, v) > 0.0) { vec3 cDiffuse; @@ -367,13 +424,13 @@ vec3 computeColor( return color; } -vec3 computeDirectionalLight(Light light, PbrInfo pbrInfo, vec3 n, vec3 v) { +vec3 computeDirectionalLight(Light light, PbrInfo pbrInfo, vec3 v) { vec3 l = -normalize(light.direction.xyz); vec3 h = normalize(l + v); - return computeColor(pbrInfo, n, l, v, h, light.color.rgb, light.intensity); + return computeColor(pbrInfo, l, v, h, light.color.rgb, light.intensity); } -vec3 computePointLight(Light light, PbrInfo pbrInfo, vec3 n, vec3 v) { +vec3 computePointLight(Light light, PbrInfo pbrInfo, vec3 v) { vec3 toLight = light.position.xyz - oPositions; float distance = length(toLight); vec3 l = normalize(toLight); @@ -381,10 +438,10 @@ vec3 computePointLight(Light light, PbrInfo pbrInfo, vec3 n, vec3 v) { float attenuation = computeAttenuation(distance, light.range); - return computeColor(pbrInfo, n, l, v, h, light.color.rgb, light.intensity * attenuation); + return computeColor(pbrInfo, l, v, h, light.color.rgb, light.intensity * attenuation); } -vec3 computeSpotLight(Light light, PbrInfo pbrInfo, vec3 n, vec3 v) { +vec3 computeSpotLight(Light light, PbrInfo pbrInfo, vec3 v) { vec3 invLightDir = -normalize(light.direction.xyz); vec3 toLight = light.position.xyz - oPositions; @@ -398,7 +455,7 @@ vec3 computeSpotLight(Light light, PbrInfo pbrInfo, vec3 n, vec3 v) { float angularAttenuation = max(0.0, cd * light.angleScale + light.angleOffset); angularAttenuation *= angularAttenuation; - return computeColor(pbrInfo, n, l, v, h, light.color.rgb, light.intensity * attenuation * angularAttenuation); + return computeColor(pbrInfo, l, v, h, light.color.rgb, light.intensity * attenuation * angularAttenuation); } vec3 prefilteredReflectionLinear(vec3 R, float roughness) { @@ -415,15 +472,16 @@ vec3 prefilteredReflection(vec3 R, float roughness) { return textureLod(preFilteredSampler, R, lod).rgb; } -vec3 computeIBL(PbrInfo pbrInfo, vec3 v, vec3 n) { +vec3 computeIBL(PbrInfo pbrInfo, vec3 v) { vec3 f0 = pbrInfo.specular; if (pbrInfo.metallicRoughnessWorkflow) { f0 = mix(DIELECTRIC_SPECULAR, pbrInfo.baseColor, pbrInfo.metallic); } - vec3 f = f(f0, v, n, pbrInfo.roughness); - vec3 kD = 1.0 - f; + vec3 n = pbrInfo.normal; + vec3 fBase = f(f0, v, n, pbrInfo.roughness); + vec3 kD = 1.0 - fBase; kD *= 1.0 - pbrInfo.metallic; vec3 irradiance = texture(irradianceMapSampler, n).rgb; @@ -432,9 +490,22 @@ vec3 computeIBL(PbrInfo pbrInfo, vec3 v, vec3 n) { vec3 r = normalize(reflect(-v, n)); vec3 reflection = prefilteredReflection(r, pbrInfo.roughness); vec2 envBRDF = texture(brdfLookupSampler, vec2(max(dot(n, v), 0.0), pbrInfo.roughness)).rg; - vec3 specular = reflection * (f * envBRDF.x + envBRDF.y); + vec3 specular = reflection * (fBase * envBRDF.x + envBRDF.y); + + // clearcoat layer + vec3 cn = pbrInfo.clearcoatNormal; + vec3 fClearcoat = f(DIELECTRIC_SPECULAR, v, cn, pbrInfo.clearcoatRoughness); - return kD * diffuse + specular; + vec3 cr = normalize(reflect(-v, cn)); + vec3 cReflection = prefilteredReflection(cr, pbrInfo.clearcoatRoughness); + vec2 cEnvBRDF = texture(brdfLookupSampler, vec2(max(dot(cn, v), 0.0), pbrInfo.clearcoatRoughness)).rg; + vec3 cSpecular = cReflection * (fClearcoat * cEnvBRDF.x + cEnvBRDF.y); + + // final color + vec3 baseLayer = kD * diffuse + specular; + vec3 clearcoat = cSpecular * pbrInfo.clearcoatFactor; + + return baseLayer * (1.0 - fClearcoat * pbrInfo.clearcoatFactor) + clearcoat; } void main() { @@ -463,13 +534,27 @@ void main() { metallic = convertMetallic(baseColor.rgb, specular, maxSpecular); } - PbrInfo pbrInfo = PbrInfo(baseColor.rgb, metallic, specular, roughness, metallicRoughnessWorkflow); - vec3 emissive = getEmissiveColor(textureChannels); + float clearcoatFactor = getClearcoatFactor(textureChannels); + float clearcoatRoughness = getClearcoatRoughness(textureChannels); + vec3 clearcoatNormal = getClearcoatNormal(textureChannels); + vec3 n = getNormal(textureChannels); vec3 v = normalize(cameraUBO.eye.xyz - oPositions); + PbrInfo pbrInfo = PbrInfo( + baseColor.rgb, + metallic, + specular, + roughness, + metallicRoughnessWorkflow, + n, + clearcoatFactor, + clearcoatRoughness, + clearcoatNormal + ); + vec3 color = vec3(0.0); for (int i = 0; i < material.lightCount; i++) { @@ -478,15 +563,15 @@ void main() { uint lightType = light.type; if (lightType == DIRECTIONAL_LIGHT_TYPE) { - color += computeDirectionalLight(light, pbrInfo, n, v); + color += computeDirectionalLight(light, pbrInfo, v); } else if (lightType == POINT_LIGHT_TYPE) { - color += computePointLight(light, pbrInfo, n, v); + color += computePointLight(light, pbrInfo, v); } else if (lightType == SPOT_LIGHT_TYPE) { - color += computeSpotLight(light, pbrInfo, n, v); + color += computeSpotLight(light, pbrInfo, v); } } - vec3 ambient = computeIBL(pbrInfo, v, n); + vec3 ambient = computeIBL(pbrInfo, v); color += emissive + occludeAmbientColor(ambient, textureChannels); @@ -515,5 +600,11 @@ void main() { } else if (material.outputMode == OUTPUT_MODE_SSAO) { float ao = sampleAOMap(); outColor = vec4(vec3(ao), 1.0); + } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_FACTOR) { + outColor = vec4(vec3(clearcoatFactor), 1.0); + } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_ROUGHNESS) { + outColor = vec4(vec3(clearcoatRoughness), 1.0); + } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_NORMAL) { + outColor = outColor = vec4(clearcoatNormal*0.5 + 0.5, 1.0); } } diff --git a/crates/viewer/shaders/model.frag.spv b/crates/viewer/shaders/model.frag.spv index 9928e3d..9f7f247 100644 Binary files a/crates/viewer/shaders/model.frag.spv and b/crates/viewer/shaders/model.frag.spv differ diff --git a/crates/viewer/src/renderer/model/lightpass.rs b/crates/viewer/src/renderer/model/lightpass.rs index effc2c9..a160d4f 100644 --- a/crates/viewer/src/renderer/model/lightpass.rs +++ b/crates/viewer/src/renderer/model/lightpass.rs @@ -10,6 +10,8 @@ use util::*; use vulkan::ash::{vk, Device}; use vulkan::{Buffer, Context, Texture as VulkanTexture}; +const SAMPLERS_PER_PRIMITIVE: u32 = 8; + const DYNAMIC_DATA_SET_INDEX: u32 = 0; const STATIC_DATA_SET_INDEX: u32 = 1; const PER_PRIMITIVE_DATA_SET_INDEX: u32 = 2; @@ -27,7 +29,10 @@ const NORMALS_SAMPLER_BINDING: u32 = 8; const MATERIAL_SAMPLER_BINDING: u32 = 9; const OCCLUSION_SAMPLER_BINDING: u32 = 10; const EMISSIVE_SAMPLER_BINDING: u32 = 11; -const AO_MAP_SAMPLER_BINDING: u32 = 12; +const CLEARCOAT_FACTOR_SAMPLER_BINDING: u32 = 12; +const CLEARCOAT_ROUGHNESS_SAMPLER_BINDING: u32 = 13; +const CLEARCOAT_NORMAL_SAMPLER_BINDING: u32 = 14; +const AO_MAP_SAMPLER_BINDING: u32 = 15; const MAX_LIGHT_COUNT: u32 = 8; @@ -58,14 +63,30 @@ pub enum OutputMode { TexCoord0, TexCoord1, Ssao, + ClearcoatFactor, + ClearcoatRoughness, + ClearcoatNormal, } impl OutputMode { - pub fn all() -> [OutputMode; 12] { + pub fn all() -> [OutputMode; 15] { use OutputMode::*; [ - Final, Color, Emissive, Metallic, Specular, Roughness, Occlusion, Normal, Alpha, - TexCoord0, TexCoord1, Ssao, + Final, + Color, + Emissive, + Metallic, + Specular, + Roughness, + Occlusion, + Normal, + Alpha, + TexCoord0, + TexCoord1, + Ssao, + ClearcoatFactor, + ClearcoatRoughness, + ClearcoatNormal, ] } @@ -84,6 +105,9 @@ impl OutputMode { 9 => Some(TexCoord0), 10 => Some(TexCoord1), 11 => Some(Ssao), + 12 => Some(ClearcoatFactor), + 13 => Some(ClearcoatRoughness), + 14 => Some(ClearcoatNormal), _ => None, } } @@ -556,7 +580,7 @@ fn create_descriptor_pool( let descriptor_count = descriptors_resources.camera_buffers.len() as u32; let primitive_count = descriptors_resources.model.primitive_count() as u32; - let textures_desc_count = primitive_count * 5; + let textures_desc_count = primitive_count * SAMPLERS_PER_PRIMITIVE; let pool_sizes = [ vk::DescriptorPoolSize { @@ -833,6 +857,24 @@ fn create_per_primitive_descriptor_set_layout(device: &Device) -> vk::Descriptor .descriptor_count(1) .stage_flags(vk::ShaderStageFlags::FRAGMENT) .build(), + vk::DescriptorSetLayoutBinding::builder() + .binding(CLEARCOAT_FACTOR_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .build(), + vk::DescriptorSetLayoutBinding::builder() + .binding(CLEARCOAT_ROUGHNESS_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .build(), + vk::DescriptorSetLayoutBinding::builder() + .binding(CLEARCOAT_NORMAL_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .build(), ]; let layout_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings); @@ -903,6 +945,23 @@ fn create_per_primitive_descriptor_sets( resources.dummy_texture, ); + let clearcoat = material.get_clearcoat().unwrap_or_default(); + let clearcoat_factor_info = create_descriptor_image_info( + clearcoat.factor_texture_index(), + textures, + resources.dummy_texture, + ); + let clearcoat_roughness_info = create_descriptor_image_info( + clearcoat.roughness_texture_index(), + textures, + resources.dummy_texture, + ); + let clearcoat_normal_info = create_descriptor_image_info( + clearcoat.normal_texture_index(), + textures, + resources.dummy_texture, + ); + let set = sets[primitive_index]; primitive_index += 1; @@ -937,6 +996,24 @@ fn create_per_primitive_descriptor_sets( .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) .image_info(&emissive_info) .build(), + vk::WriteDescriptorSet::builder() + .dst_set(set) + .dst_binding(CLEARCOAT_FACTOR_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&clearcoat_factor_info) + .build(), + vk::WriteDescriptorSet::builder() + .dst_set(set) + .dst_binding(CLEARCOAT_ROUGHNESS_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&clearcoat_roughness_info) + .build(), + vk::WriteDescriptorSet::builder() + .dst_set(set) + .dst_binding(CLEARCOAT_NORMAL_SAMPLER_BINDING) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&clearcoat_normal_info) + .build(), ]; unsafe { diff --git a/crates/viewer/src/renderer/model/uniform.rs b/crates/viewer/src/renderer/model/uniform.rs index 10cafe1..be36368 100644 --- a/crates/viewer/src/renderer/model/uniform.rs +++ b/crates/viewer/src/renderer/model/uniform.rs @@ -8,7 +8,7 @@ const DEFAULT_LIGHT_DIRECTION: [f32; 4] = [0.0, 0.0, -1.0, 0.0]; const DIRECTIONAL_LIGHT_TYPE: u32 = 0; const POINT_LIGHT_TYPE: u32 = 1; const SPOT_LIGHT_TYPE: u32 = 2; -const NO_TEXTURE_ID: u32 = std::u8::MAX as u32; +const NO_TEXTURE_ID: u32 = 3; // MAX u2 const UNLIT_FLAG_LIT: u32 = 0; const UNLIT_FLAG_UNLIT: u32 = 1; const METALLIC_ROUGHNESS_WORKFLOW: u32 = 0; @@ -76,33 +76,40 @@ impl From<(Matrix4, Light)> for LightUniform { } } +#[repr(C)] #[derive(Clone, Copy)] #[allow(dead_code)] pub struct MaterialUniform { color: [f32; 4], - // Contains the emissive factor and roughness (or glossiness) factor. - // - emissive: emissive_and_roughness_glossiness[0,1,2] - // - roughness: emissive_and_roughness_glossiness[3] (for metallic/roughness workflows) - // - glossiness: emissive_and_roughness_glossiness[3] (for specular/glossiness workflows) - emissive_and_roughness_glossiness: [f32; 4], - // Contains the metallic (or specular) factor and occlusion factor. + emissive_factor: [f32; 3], + // - roughness for metallic/roughness workflows + // - glossiness for specular/glossiness workflows + roughness_glossiness: f32, + // Contains the metallic (or specular) factor. // - metallic: metallic_specular_and_occlusion[0] (for metallic/roughness workflows) // - specular: metallic_specular_and_occlusion[0,1,2] (for specular/glossiness workflows) - // - occlusion: metallic_specular_and_occlusion[3] - metallic_specular_and_occlusion: [f32; 4], - // Contains the texture channels for color metallic/roughness emissive and normal - // [0-7] Color texture channel - // [8-15] metallic/roughness or specular/glossiness texture channel - // [16-23] emissive texture channel - // [24-31] normals texture channel - color_material_emissive_normal_texture_channels: u32, - // Contains occlusion texture channel, alpha mode and unlit flag - // [0-7] Occlusion texture channel - // [8-15] Alpha mode - // [16-23] Unlit flag - // [24-31] Workflow (metallic/roughness or specular/glossiness) - occlusion_texture_channel_alpha_mode_unlit_flag_and_workflow: u32, + metallic_specular: [f32; 3], + occlusion: f32, alpha_cutoff: f32, + clearcoat_factor: f32, + clearcoat_roughness: f32, + // Contains the texture channels. Each channel taking 2 bits + // [0-1] Color texture channel + // [2-3] metallic/roughness or specular/glossiness texture channel + // [4-5] emissive texture channel + // [6-7] normals texture channel + // [8-9] Occlusion texture channel + // [10-11] Clearcoat factor texture channel + // [12-13] Clearcoat roughness texture channel + // [14-15] Clearcoat normal texture channel + // [16-31] Reserved + textures_channels: u32, + // Contains alpha mode, unlit flag and workflow flag + // [0-7] Alpha mode + // [8-15] Unlit flag + // [16-23] Workflow (metallic/roughness or specular/glossiness) + // [24-31] Reserved + alpha_mode_unlit_flag_and_workflow: u32, } impl From for MaterialUniform { @@ -117,72 +124,87 @@ impl From for MaterialUniform { Workflow::SpecularGlossiness(workflow) => workflow.get_glossiness(), }; - let emissive_and_roughness_glossiness = [ - emissive_factor[0], - emissive_factor[1], - emissive_factor[2], - roughness_glossiness, - ]; - let metallic_specular = match workflow { Workflow::MetallicRoughness(workflow) => [workflow.get_metallic(), 0.0, 0.0], Workflow::SpecularGlossiness(workflow) => workflow.get_specular(), }; let occlusion = material.get_occlusion(); - let metallic_specular_and_occlusion = [ - metallic_specular[0], - metallic_specular[1], - metallic_specular[2], - occlusion, - ]; - let color_texture_id = material - .get_color_texture() - .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let alpha_cutoff = material.get_alpha_cutoff(); + let clearcoat = material.get_clearcoat().unwrap_or_default(); + let clearcoat_factor = clearcoat.factor(); + let clearcoat_roughness = clearcoat.roughness(); - let metallic_roughness_texture_id = match material.get_workflow() { - Workflow::MetallicRoughness(workflow) => workflow.get_metallic_roughness_texture(), - Workflow::SpecularGlossiness(workflow) => workflow.get_specular_glossiness_texture(), - } - .map_or(NO_TEXTURE_ID, |t| t.get_channel()); - let emissive_texture_id = material - .get_emissive_texture() - .map_or(NO_TEXTURE_ID, |info| info.get_channel()); - let normal_texture_id = material - .get_normals_texture() - .map_or(NO_TEXTURE_ID, |info| info.get_channel()); - let color_material_emissive_normal_texture_channels = (color_texture_id << 24) - | (metallic_roughness_texture_id << 16) - | (emissive_texture_id << 8) - | normal_texture_id; - - let occlusion_texture_id = material - .get_occlusion_texture() - .map_or(NO_TEXTURE_ID, |info| info.get_channel()); - let alpha_mode = material.get_alpha_mode(); - let unlit_flag = if material.is_unlit() { - UNLIT_FLAG_UNLIT - } else { - UNLIT_FLAG_LIT - }; - let workflow = if let Workflow::MetallicRoughness { .. } = workflow { - METALLIC_ROUGHNESS_WORKFLOW - } else { - SPECULAR_GLOSSINESS_WORKFLOW + let textures_channels = { + let color = material + .get_color_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let metallic_roughness = match material.get_workflow() { + Workflow::MetallicRoughness(workflow) => workflow.get_metallic_roughness_texture(), + Workflow::SpecularGlossiness(workflow) => { + workflow.get_specular_glossiness_texture() + } + } + .map_or(NO_TEXTURE_ID, |t| t.get_channel()); + let emissive = material + .get_emissive_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let normal = material + .get_normals_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let occlusion = material + .get_occlusion_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let clearcoat_factor = clearcoat + .factor_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + let clearcoat_roughness = clearcoat + .roughness_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + let clearcoat_normal = clearcoat + .normal_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + (color << 30) + | (metallic_roughness << 28) + | (emissive << 26) + | (normal << 24) + | (occlusion << 22) + | (clearcoat_factor << 20) + | (clearcoat_roughness << 18) + | (clearcoat_normal << 16) }; - let occlusion_texture_channel_alpha_mode_unlit_flag_and_workflow = - (occlusion_texture_id << 24) | (alpha_mode << 16) | (unlit_flag << 8) | workflow; - let alpha_cutoff = material.get_alpha_cutoff(); + let alpha_mode_unlit_flag_and_workflow = { + let alpha_mode = material.get_alpha_mode(); + let unlit_flag = if material.is_unlit() { + UNLIT_FLAG_UNLIT + } else { + UNLIT_FLAG_LIT + }; + let workflow = if let Workflow::MetallicRoughness { .. } = workflow { + METALLIC_ROUGHNESS_WORKFLOW + } else { + SPECULAR_GLOSSINESS_WORKFLOW + }; + + (alpha_mode << 24) | (unlit_flag << 16) | (workflow << 8) + }; MaterialUniform { color, - emissive_and_roughness_glossiness, - metallic_specular_and_occlusion, - color_material_emissive_normal_texture_channels, - occlusion_texture_channel_alpha_mode_unlit_flag_and_workflow, + emissive_factor, + roughness_glossiness, + metallic_specular, + occlusion, alpha_cutoff, + clearcoat_factor, + clearcoat_roughness, + textures_channels, + alpha_mode_unlit_flag_and_workflow, } } }