diff --git a/crates/libs/model/src/lib.rs b/crates/libs/model/src/lib.rs index 10dfc30..5f94494 100644 --- a/crates/libs/model/src/lib.rs +++ b/crates/libs/model/src/lib.rs @@ -35,6 +35,7 @@ pub struct Model { animations: Option, skins: Vec, textures: Textures, + materials: Vec, lights: Vec, } @@ -99,6 +100,8 @@ impl Model { &images, ); + let materials = create_materials_from_gltf(&document); + let lights = create_lights_from_gltf(&document); let model = Model { @@ -109,6 +112,7 @@ impl Model { animations, skins, textures, + materials, lights, }; @@ -220,6 +224,10 @@ impl Model { &self.textures.textures } + pub fn materials(&self) -> &[Material] { + &self.materials + } + pub fn lights(&self) -> &[Light] { &self.lights } diff --git a/crates/libs/model/src/material.rs b/crates/libs/model/src/material.rs index 360baf5..97f35fe 100644 --- a/crates/libs/model/src/material.rs +++ b/crates/libs/model/src/material.rs @@ -1,6 +1,7 @@ use gltf::{ material::{AlphaMode, Material as GltfMaterial, NormalTexture, OcclusionTexture}, texture::Info, + Document, }; const ALPHA_MODE_OPAQUE: u32 = 0; @@ -24,6 +25,30 @@ pub struct Material { clearcoat: Option, } +impl Default for Material { + fn default() -> Self { + Self { + color: [1.0, 1.0, 1.0, 1.0], + emissive: [0.0, 0.0, 0.0], + occlusion: 0.0, + color_texture: None, + emissive_texture: None, + normals_texture: None, + occlusion_texture: None, + workflow: Workflow::MetallicRoughness(MetallicRoughnessWorkflow { + metallic: 1.0, + roughness: 1.0, + metallic_roughness_texture: None, + }), + alpha_mode: ALPHA_MODE_OPAQUE, + alpha_cutoff: 0.5, + double_sided: false, + is_unlit: false, + clearcoat: None, + } + } +} + #[derive(Clone, Copy, Debug)] pub struct TextureInfo { index: usize, @@ -213,6 +238,10 @@ impl TextureInfo { } } +pub(crate) fn create_materials_from_gltf(document: &Document) -> Vec { + document.materials().map(Material::from).collect() +} + impl<'a> From> for Material { fn from(material: GltfMaterial) -> Material { let color = match material.pbr_specular_glossiness() { diff --git a/crates/libs/model/src/mesh.rs b/crates/libs/model/src/mesh.rs index 9fbd6b9..8f57274 100644 --- a/crates/libs/model/src/mesh.rs +++ b/crates/libs/model/src/mesh.rs @@ -42,6 +42,7 @@ pub struct Primitive { vertices: VertexBuffer, indices: Option, material: Material, + material_index: Option, aabb: Aabb, } @@ -62,6 +63,10 @@ impl Primitive { self.material } + pub fn material_index(&self) -> Option { + self.material_index + } + pub fn aabb(&self) -> Aabb { self.aabb } @@ -78,6 +83,7 @@ struct PrimitiveData { indices: Option, vertices: VertexBufferPart, material: Material, + material_index: Option, aabb: Aabb, } @@ -172,6 +178,7 @@ pub fn create_meshes_from_gltf( indices, vertices: (offset, accessor.count()), material, + material_index: primitive.material().index(), aabb, }); } @@ -227,6 +234,7 @@ pub fn create_meshes_from_gltf( vertices: vertex_buffer, indices: index_buffer, material: buffers.material, + material_index: buffers.material_index, aabb: buffers.aabb, } }) diff --git a/crates/viewer/shaders/gbuffer.vert b/crates/viewer/shaders/gbuffer.vert index 1142fd6..6885396 100644 --- a/crates/viewer/shaders/gbuffer.vert +++ b/crates/viewer/shaders/gbuffer.vert @@ -1,4 +1,7 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" layout(location = 0) in vec3 vPositions; layout(location = 1) in vec3 vNormals; @@ -9,14 +12,9 @@ layout(location = 5) in vec4 vWeights; layout(location = 6) in uvec4 vJoints; layout(location = 7) in vec4 vColors; -layout(binding = 0, set = 0) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; +layout(binding = 0, set = 0) uniform Frame { + Camera camera; +}; layout(binding = 1, set = 0) uniform TransformUBO { mat4 matrix; @@ -40,10 +38,10 @@ void main() { + vWeights.w * skin.jointMatrices[vJoints.w]; } - oViewSpaceNormal = normalize((cameraUBO.view * world * vec4(vNormals, 0.0)).xyz); + oViewSpaceNormal = normalize((camera.view * world * vec4(vNormals, 0.0)).xyz); oTexcoords0 = vTexcoords0; oTexcoords1 = vTexcoords1; oAlpha = vColors.a; - gl_Position = cameraUBO.proj * cameraUBO.view * world * vec4(vPositions, 1.0); + gl_Position = camera.proj * camera.view * world * vec4(vPositions, 1.0); } diff --git a/crates/viewer/shaders/gbuffer.vert.spv b/crates/viewer/shaders/gbuffer.vert.spv index 2ed462e..378f685 100644 Binary files a/crates/viewer/shaders/gbuffer.vert.spv and b/crates/viewer/shaders/gbuffer.vert.spv differ diff --git a/crates/viewer/shaders/libs/camera.glsl b/crates/viewer/shaders/libs/camera.glsl new file mode 100644 index 0000000..c90774b --- /dev/null +++ b/crates/viewer/shaders/libs/camera.glsl @@ -0,0 +1,8 @@ +struct Camera { + mat4 view; + mat4 proj; + mat4 invertedProj; + vec4 eye; + float zNear; + float zFar; +}; diff --git a/crates/viewer/shaders/libs/material.glsl b/crates/viewer/shaders/libs/material.glsl new file mode 100644 index 0000000..b298b6a --- /dev/null +++ b/crates/viewer/shaders/libs/material.glsl @@ -0,0 +1,58 @@ +const uint NO_TEXTURE_ID = 255; + +const uint ALPHA_MODE_MASK = 1; +const uint ALPHA_MODE_BLEND = 2; +const float ALPHA_CUTOFF_BIAS = 0.0000001; + +const uint METALLIC_ROUGHNESS_WORKFLOW = 0; + +struct Material { + vec4 color; + 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; + uint colorTextureChannel; + uint materialTextureChannel; + uint emissiveTextureChannel; + uint normalsTextureChannel; + uint occlusionTextureChannel; + uint clearcoatFactorTextureChannel; + uint clearcoatRoughnessTextureChannel; + uint clearcoatNormalTextureChannel; + uint alphaMode; + bool isUnlit; + uint workflow; +}; + +struct TextureChannels { + uint color; + uint material; + uint emissive; + uint normal; + uint occlusion; + uint clearcoatFactor; + uint clearcoatRoughness; + uint clearcoatNormal; +}; + +TextureChannels getTextureChannels(Material m) { + return TextureChannels( + m.colorTextureChannel, + m.materialTextureChannel, + m.emissiveTextureChannel, + m.normalsTextureChannel, + m.occlusionTextureChannel, + m.clearcoatFactorTextureChannel, + m.clearcoatRoughnessTextureChannel, + m.clearcoatNormalTextureChannel + ); +} diff --git a/crates/viewer/shaders/model.frag b/crates/viewer/shaders/model.frag index ffbb99e..2bf331e 100644 --- a/crates/viewer/shaders/model.frag +++ b/crates/viewer/shaders/model.frag @@ -1,4 +1,8 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" +#include "libs/material.glsl" // -- Constants -- layout(constant_id = 0) const uint MAX_LIGHT_COUNT = 1; @@ -29,32 +33,11 @@ const vec3 DIELECTRIC_SPECULAR = vec3(0.04); const vec3 BLACK = vec3(0.0); const float PI = 3.14159; -const uint NO_TEXTURE_ID = 3; - -const uint ALPHA_MODE_MASK = 1; -const uint ALPHA_MODE_BLEND = 2; -const float ALPHA_CUTOFF_BIAS = 0.0000001; - const uint DIRECTIONAL_LIGHT_TYPE = 0; const uint POINT_LIGHT_TYPE = 1; const uint SPOT_LIGHT_TYPE = 2; -const uint UNLIT_FLAG_UNLIT = 1; - -const uint METALLIC_ROUGHNESS_WORKFLOW = 0; - // -- Structures -- -struct TextureChannels { - uint color; - uint material; - uint emissive; - uint normal; - uint occlusion; - uint clearcoatFactor; - uint clearcoatRoughness; - uint clearcoatNormal; -}; - struct Light { vec4 position; vec4 direction; @@ -78,6 +61,11 @@ struct PbrInfo { vec3 clearcoatNormal; }; +struct Config { + uint outputMode; + float emissiveIntensity; +}; + // -- Inputs -- layout(location = 0) in vec3 oNormals; layout(location = 1) in vec2 oTexcoords0; @@ -86,87 +74,43 @@ layout(location = 3) in vec3 oPositions; layout(location = 4) in vec4 oColors; layout(location = 5) in mat3 oTBN; -// -- Push constants -layout(push_constant) uniform MaterialUniform { - vec4 color; - 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; -} material; - // -- Descriptors -- -layout(binding = 0, set = 0) uniform Camera { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; -layout(binding = 1, set = 0) uniform Lights { +layout(binding = 0, set = 0) uniform CameraUBO { + Camera camera; +}; + +layout(binding = 1, set = 0) uniform ConfigUBO { + Config config; +}; + +layout(binding = 2, set = 0) uniform LightsUBO { + uint count; Light lights[MAX_LIGHT_COUNT]; } lights; -layout(binding = 4, set = 1) uniform samplerCube irradianceMapSampler; -layout(binding = 5, set = 1) uniform samplerCube preFilteredSampler; -layout(binding = 6, set = 1) uniform sampler2D brdfLookupSampler; -layout(binding = 7, set = 2) uniform sampler2D colorSampler; -layout(binding = 8, set = 2) uniform sampler2D normalsSampler; + +layout(binding = 5, set = 1) uniform samplerCube irradianceMapSampler; +layout(binding = 6, set = 1) uniform samplerCube preFilteredSampler; +layout(binding = 7, set = 1) uniform sampler2D brdfLookupSampler; +layout(binding = 8, set = 2) uniform sampler2D colorSampler; +layout(binding = 9, set = 2) uniform sampler2D normalsSampler; // This sampler contains either: // - metallic (b) + glossiness (g) for metallic/roughness workflow // - specular (rgb) + glossiness (a) for specular/glossiness workflow -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 = 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; +layout(binding = 10, set = 2) uniform sampler2D materialSampler; +layout(binding = 11, set = 2) uniform sampler2D occlusionSampler; +layout(binding = 12, set = 2) uniform sampler2D emissiveSampler; +layout(binding = 13, set = 2) uniform sampler2D clearcoatFactorSampler; +layout(binding = 14, set = 2) uniform sampler2D clearcoatRoughnessSampler; +layout(binding = 15, set = 2) uniform sampler2D clearcoatNormalSampler; +layout(binding = 16, set = 3) uniform sampler2D aoMapSampler; + +layout(binding = 17, set = 4) uniform MaterialUBO { + Material material; +}; // Output layout(location = 0) out vec4 outColor; -TextureChannels getTextureChannels() { - return TextureChannels( - (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 - ); -} - vec2 getUV(uint texChannel) { if (texChannel == 0) { return oTexcoords0; @@ -238,7 +182,7 @@ vec3 getEmissiveColor(TextureChannels textureChannels) { vec2 uv = getUV(textureChannels.emissive); emissive *= texture(emissiveSampler, uv).rgb; } - return emissive * material.emissiveIntensity; + return emissive * config.emissiveIntensity; } vec3 getNormal(TextureChannels textureChannels) { @@ -273,7 +217,7 @@ vec3 occludeAmbientColor(vec3 ambientColor, TextureChannels textureChannels) { } uint getAlphaMode() { - return (material.alphaModeUnlitFlagAndWorkflow >> 24) & 255; + return material.alphaMode; } bool isMasked(vec4 baseColor) { @@ -305,15 +249,11 @@ float getAlpha(vec4 baseColor) { } bool isUnlit() { - uint unlitFlag = (material.alphaModeUnlitFlagAndWorkflow >> 16) & 255; - if (unlitFlag == UNLIT_FLAG_UNLIT) { - return true; - } - return false; + return material.isUnlit; } bool isMetallicRoughnessWorkflow() { - uint workflow = (material.alphaModeUnlitFlagAndWorkflow >> 8) & 255; + uint workflow = material.workflow; if (workflow == METALLIC_ROUGHNESS_WORKFLOW) { return true; } @@ -523,7 +463,7 @@ vec3 computeIBL(PbrInfo pbrInfo, vec3 v) { } void main() { - TextureChannels textureChannels = getTextureChannels(); + TextureChannels textureChannels = getTextureChannels(material); vec4 baseColor = getBaseColor(textureChannels); if (isMasked(baseColor)) { @@ -555,7 +495,7 @@ void main() { vec3 clearcoatNormal = getClearcoatNormal(textureChannels); vec3 n = getNormal(textureChannels); - vec3 v = normalize(cameraUBO.eye.xyz - oPositions); + vec3 v = normalize(camera.eye.xyz - oPositions); PbrInfo pbrInfo = PbrInfo( baseColor.rgb, @@ -571,7 +511,7 @@ void main() { vec3 color = vec3(0.0); - for (int i = 0; i < material.lightCount; i++) { + for (int i = 0; i < lights.count; i++) { Light light = lights.lights[i]; uint lightType = light.type; @@ -589,36 +529,37 @@ void main() { color += emissive + occludeAmbientColor(ambient, textureChannels); - if (material.outputMode == OUTPUT_MODE_FINAL) { + uint outputMode = config.outputMode; + if (outputMode == OUTPUT_MODE_FINAL) { outColor = vec4(color, alpha); - } else if (material.outputMode == OUTPUT_MODE_COLOR) { + } else if (outputMode == OUTPUT_MODE_COLOR) { outColor = vec4(baseColor.rgb, 1.0); - } else if (material.outputMode == OUTPUT_MODE_EMISSIVE) { + } else if (outputMode == OUTPUT_MODE_EMISSIVE) { outColor = vec4(emissive, 1.0); - } else if (material.outputMode == OUTPUT_MODE_METALLIC) { + } else if (outputMode == OUTPUT_MODE_METALLIC) { outColor = vec4(vec3(metallic), 1.0); - } else if (material.outputMode == OUTPUT_MODE_SPECULAR) { + } else if (outputMode == OUTPUT_MODE_SPECULAR) { outColor = vec4(specular, 1.0); - } else if (material.outputMode == OUTPUT_MODE_ROUGHNESS) { + } else if (outputMode == OUTPUT_MODE_ROUGHNESS) { outColor = vec4(vec3(roughness), 1.0); - } else if (material.outputMode == OUTPUT_MODE_OCCLUSION) { + } else if (outputMode == OUTPUT_MODE_OCCLUSION) { outColor = vec4(occludeAmbientColor(vec3(1.0), textureChannels), 1.0); - } else if (material.outputMode == OUTPUT_MODE_NORMAL) { + } else if (outputMode == OUTPUT_MODE_NORMAL) { outColor = vec4(n*0.5 + 0.5, 1.0); - } else if (material.outputMode == OUTPUT_MODE_ALPHA) { + } else if (outputMode == OUTPUT_MODE_ALPHA) { outColor = vec4(vec3(baseColor.a), 1.0); - } else if (material.outputMode == OUTPUT_MODE_UVS0) { + } else if (outputMode == OUTPUT_MODE_UVS0) { outColor = vec4(vec2(oTexcoords0), 0.0, 1.0); - } else if (material.outputMode == OUTPUT_MODE_UVS1) { + } else if (outputMode == OUTPUT_MODE_UVS1) { outColor = vec4(vec2(oTexcoords1), 0.0, 1.0); - } else if (material.outputMode == OUTPUT_MODE_SSAO) { + } else if (outputMode == OUTPUT_MODE_SSAO) { float ao = sampleAOMap(); outColor = vec4(vec3(ao), 1.0); - } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_FACTOR) { + } else if (outputMode == OUTPUT_MODE_CLEARCOAT_FACTOR) { outColor = vec4(vec3(clearcoatFactor), 1.0); - } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_ROUGHNESS) { + } else if (outputMode == OUTPUT_MODE_CLEARCOAT_ROUGHNESS) { outColor = vec4(vec3(clearcoatRoughness), 1.0); - } else if (material.outputMode == OUTPUT_MODE_CLEARCOAT_NORMAL) { + } else if (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 cf50fd8..060edb7 100644 Binary files a/crates/viewer/shaders/model.frag.spv and b/crates/viewer/shaders/model.frag.spv differ diff --git a/crates/viewer/shaders/model.vert b/crates/viewer/shaders/model.vert index 582cb2d..a77cf5b 100644 --- a/crates/viewer/shaders/model.vert +++ b/crates/viewer/shaders/model.vert @@ -1,4 +1,7 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" layout(location = 0) in vec3 vPositions; layout(location = 1) in vec3 vNormals; @@ -9,20 +12,15 @@ layout(location = 5) in vec4 vWeights; layout(location = 6) in uvec4 vJoints; layout(location = 7) in vec4 vColors; -layout(binding = 0, set = 0) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; +layout(binding = 0, set = 0) uniform Frame { + Camera camera; +}; -layout(binding = 2, set = 0) uniform TransformUBO { +layout(binding = 3, set = 0) uniform TransformUBO { mat4 matrix; } transform; -layout(binding = 3, set = 0) uniform SkinUBO { +layout(binding = 4, set = 0) uniform SkinUBO { mat4 jointMatrices[512]; } skin; @@ -53,5 +51,5 @@ void main() { oPositions = (world * vec4(vPositions, 1.0)).xyz; oTBN = mat3(tangent, bitangent, normal); oColors = vColors; - gl_Position = cameraUBO.proj * cameraUBO.view * world * vec4(vPositions, 1.0); + gl_Position = camera.proj * camera.view * world * vec4(vPositions, 1.0); } diff --git a/crates/viewer/shaders/model.vert.spv b/crates/viewer/shaders/model.vert.spv index 2dffcb6..3099cb1 100644 Binary files a/crates/viewer/shaders/model.vert.spv and b/crates/viewer/shaders/model.vert.spv differ diff --git a/crates/viewer/shaders/skybox.frag b/crates/viewer/shaders/skybox.frag index f87235f..4a00036 100644 --- a/crates/viewer/shaders/skybox.frag +++ b/crates/viewer/shaders/skybox.frag @@ -2,15 +2,6 @@ layout(location = 0) in vec3 oPositions; -layout(binding = 0) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; - layout(binding = 1) uniform samplerCube cubemapSampler; layout(location = 0) out vec4 outColor; diff --git a/crates/viewer/shaders/skybox.frag.spv b/crates/viewer/shaders/skybox.frag.spv index e41d673..84e29f3 100644 Binary files a/crates/viewer/shaders/skybox.frag.spv and b/crates/viewer/shaders/skybox.frag.spv differ diff --git a/crates/viewer/shaders/skybox.vert b/crates/viewer/shaders/skybox.vert index ee992cf..538afae 100644 --- a/crates/viewer/shaders/skybox.vert +++ b/crates/viewer/shaders/skybox.vert @@ -1,20 +1,18 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" layout(location = 0) in vec3 vPositions; -layout(binding = 0) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; +layout(binding = 0) uniform Frame { + Camera camera; +}; layout(location = 0) out vec3 oPositions; mat4 getViewAtOrigin() { - mat4 view = mat4(cameraUBO.view); + mat4 view = mat4(camera.view); view[3][0] = 0; view[3][1] = 0; view[3][2] = 0; @@ -26,5 +24,5 @@ void main() { mat4 view = getViewAtOrigin(); - gl_Position = cameraUBO.proj * view * vec4(vPositions, 1.0); + gl_Position = camera.proj * view * vec4(vPositions, 1.0); } diff --git a/crates/viewer/shaders/skybox.vert.spv b/crates/viewer/shaders/skybox.vert.spv index 1263285..f494608 100644 Binary files a/crates/viewer/shaders/skybox.vert.spv and b/crates/viewer/shaders/skybox.vert.spv differ diff --git a/crates/viewer/shaders/ssao.frag b/crates/viewer/shaders/ssao.frag index 4e92be4..eda808a 100644 --- a/crates/viewer/shaders/ssao.frag +++ b/crates/viewer/shaders/ssao.frag @@ -1,4 +1,7 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" layout (constant_id = 0) const uint SSAO_KERNEL_SIZE = 32; @@ -18,20 +21,15 @@ layout(binding = 3, set = 1) uniform SSAOKernel { vec4 samples[SSAO_KERNEL_SIZE]; } ssaoKernel; -layout(binding = 4, set = 2) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; +layout(binding = 4, set = 2) uniform Frame { + Camera camera; +}; layout(location = 0) out float finalColor; float linearDepth(vec2 uv) { - float near = cameraUBO.zNear; - float far = cameraUBO.zFar; + float near = camera.zNear; + float far = camera.zFar; float depth = texture(depthSampler, uv).r; return (near * far) / (far + depth * (near - far)); } @@ -64,7 +62,7 @@ void main() { // project sample position: vec4 offset = vec4(kSample, 1.0); - offset = cameraUBO.proj * offset; + offset = camera.proj * offset; offset.xy /= offset.w; offset.xy = offset.xy * 0.5 + 0.5; diff --git a/crates/viewer/shaders/ssao.frag.spv b/crates/viewer/shaders/ssao.frag.spv index ee4d6db..dcabc96 100644 Binary files a/crates/viewer/shaders/ssao.frag.spv and b/crates/viewer/shaders/ssao.frag.spv differ diff --git a/crates/viewer/shaders/ssao.vert b/crates/viewer/shaders/ssao.vert index 44de874..8022c53 100644 --- a/crates/viewer/shaders/ssao.vert +++ b/crates/viewer/shaders/ssao.vert @@ -1,22 +1,20 @@ #version 450 +#extension GL_GOOGLE_include_directive : require + +#include "libs/camera.glsl" layout(location = 0) in vec2 vPos; layout(location = 1) in vec2 vCoords; -layout(binding = 4, set = 2) uniform CameraUBO { - mat4 view; - mat4 proj; - mat4 invertedProj; - vec4 eye; - float zNear; - float zFar; -} cameraUBO; +layout(binding = 4, set = 2) uniform Frame { + Camera camera; +}; layout(location = 0) out vec2 oCoords; layout(location = 1) out vec3 oViewRay; void main() { oCoords = vCoords; - oViewRay = (cameraUBO.invertedProj * vec4(vPos.x, vPos.y, 0.0, 1.0)).xyz; + oViewRay = (camera.invertedProj * vec4(vPos.x, vPos.y, 0.0, 1.0)).xyz; gl_Position = vec4(vPos.x, vPos.y, 0.0, 1.0); } diff --git a/crates/viewer/shaders/ssao.vert.spv b/crates/viewer/shaders/ssao.vert.spv index 39bd9ca..5575db1 100644 Binary files a/crates/viewer/shaders/ssao.vert.spv and b/crates/viewer/shaders/ssao.vert.spv differ diff --git a/crates/viewer/src/renderer/mod.rs b/crates/viewer/src/renderer/mod.rs index 63089ad..9c35fe0 100644 --- a/crates/viewer/src/renderer/mod.rs +++ b/crates/viewer/src/renderer/mod.rs @@ -81,7 +81,8 @@ pub struct Renderer { command_buffers: Vec, in_flight_frames: InFlightFrames, environment: Environment, - camera_uniform_buffers: Vec, + camera_ubos: Vec, + config_ubos: Vec, attachments: Attachments, skybox_renderer: SkyboxRenderer, model_renderer: Option, @@ -131,8 +132,8 @@ impl Renderer { let in_flight_frames = create_sync_objects(&context); - let camera_uniform_buffers = - create_camera_uniform_buffers(&context, swapchain.image_count() as u32); + let camera_ubos = create_camera_ubos(&context, swapchain.image_count() as u32); + let config_ubos = create_config_ubos(&context, swapchain.image_count() as u32); let attachments = Attachments::new( &context, @@ -143,7 +144,7 @@ impl Renderer { let skybox_renderer = SkyboxRenderer::create( Arc::clone(&context), - &camera_uniform_buffers, + &camera_ubos, &environment, msaa_samples, depth_format, @@ -153,7 +154,7 @@ impl Renderer { Arc::clone(&context), &attachments.gbuffer_normals, &attachments.gbuffer_depth, - &camera_uniform_buffers, + &camera_ubos, settings, ); @@ -195,7 +196,8 @@ impl Renderer { command_buffers, in_flight_frames, environment, - camera_uniform_buffers, + camera_ubos, + config_ubos, attachments, skybox_renderer, model_renderer: None, @@ -268,17 +270,28 @@ fn create_sync_objects(context: &Arc) -> InFlightFrames { InFlightFrames::new(Arc::clone(context), sync_objects_vec) } -fn create_camera_uniform_buffers(context: &Arc, count: u32) -> Vec { +fn create_camera_ubos(context: &Arc, count: u32) -> Vec { (0..count) .map(|_| { - let mut buffer = Buffer::create( + Buffer::create( Arc::clone(context), size_of::() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, - ); - buffer.map_memory(); - buffer + ) + }) + .collect::>() +} + +fn create_config_ubos(context: &Arc, count: u32) -> Vec { + (0..count) + .map(|_| { + Buffer::create( + Arc::clone(context), + size_of::() as _, + vk::BufferUsageFlags::UNIFORM_BUFFER, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + ) }) .collect::>() } @@ -815,11 +828,12 @@ impl Renderer { if let Some(model_renderer) = self.model_renderer.as_mut() { model_renderer .gbuffer_pass - .set_model(&model_data, &self.camera_uniform_buffers); + .set_model(&model_data, &self.camera_ubos); model_renderer.light_pass.set_model( &model_data, - &self.camera_uniform_buffers, + &self.camera_ubos, + &self.config_ubos, &self.environment, ao_map, ); @@ -829,14 +843,15 @@ impl Renderer { let gbuffer_pass = GBufferPass::create( Arc::clone(&self.context), &model_data, - &self.camera_uniform_buffers, + &self.camera_ubos, self.depth_format, ); let light_pass = LightPass::create( Arc::clone(&self.context), &model_data, - &self.camera_uniform_buffers, + &self.camera_ubos, + &self.config_ubos, &self.environment, ao_map, self.msaa_samples, @@ -1037,7 +1052,20 @@ impl Renderer { let inverted_proj = proj.invert().unwrap(); let ubo = CameraUBO::new(view, proj, inverted_proj, camera.position(), z_near, z_far); - let buffer = &mut self.camera_uniform_buffers[frame_index]; + let buffer = &mut self.camera_ubos[frame_index]; + unsafe { + let data_ptr = buffer.map_memory(); + mem_copy(data_ptr, &[ubo]); + } + } + + // Config + { + let ubo = ConfigUBO { + emissive_intensity: self.settings.emissive_intensity, + output_mode: self.settings.output_mode as u32, + }; + let buffer = &mut self.config_ubos[frame_index]; unsafe { let data_ptr = buffer.map_memory(); mem_copy(data_ptr, &[ubo]); @@ -1195,3 +1223,11 @@ impl SyncObjects { } } } + +#[repr(C, align(4))] +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +struct ConfigUBO { + output_mode: u32, + emissive_intensity: f32, +} diff --git a/crates/viewer/src/renderer/model/lightpass.rs b/crates/viewer/src/renderer/model/lightpass.rs index a160d4f..a694925 100644 --- a/crates/viewer/src/renderer/model/lightpass.rs +++ b/crates/viewer/src/renderer/model/lightpass.rs @@ -12,29 +12,30 @@ use vulkan::{Buffer, Context, Texture as VulkanTexture}; const SAMPLERS_PER_PRIMITIVE: u32 = 8; -const DYNAMIC_DATA_SET_INDEX: u32 = 0; +const PER_NODE_DYNAMIC_DATA_SET_INDEX: u32 = 0; const STATIC_DATA_SET_INDEX: u32 = 1; const PER_PRIMITIVE_DATA_SET_INDEX: u32 = 2; const INPUT_SET_INDEX: u32 = 3; +const PER_PRIMITIVE_DYNAMIC_DATA_SET_INDEX: u32 = 4; const CAMERA_UBO_BINDING: u32 = 0; -const LIGHT_UBO_BINDING: u32 = 1; -const TRANSFORMS_UBO_BINDING: u32 = 2; -const SKINS_UBO_BINDING: u32 = 3; -const IRRADIANCE_SAMPLER_BINDING: u32 = 4; -const PRE_FILTERED_SAMPLER_BINDING: u32 = 5; -const BRDF_SAMPLER_BINDING: u32 = 6; -const COLOR_SAMPLER_BINDING: u32 = 7; -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 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; +const CONFIG_UBO_BINDING: u32 = 1; +const LIGHT_UBO_BINDING: u32 = 2; +const TRANSFORMS_UBO_BINDING: u32 = 3; +const SKINS_UBO_BINDING: u32 = 4; +const IRRADIANCE_SAMPLER_BINDING: u32 = 5; +const PRE_FILTERED_SAMPLER_BINDING: u32 = 6; +const BRDF_SAMPLER_BINDING: u32 = 7; +const COLOR_SAMPLER_BINDING: u32 = 8; +const NORMALS_SAMPLER_BINDING: u32 = 9; +const MATERIAL_SAMPLER_BINDING: u32 = 10; +const OCCLUSION_SAMPLER_BINDING: u32 = 11; +const EMISSIVE_SAMPLER_BINDING: u32 = 12; +const CLEARCOAT_FACTOR_SAMPLER_BINDING: u32 = 13; +const CLEARCOAT_ROUGHNESS_SAMPLER_BINDING: u32 = 14; +const CLEARCOAT_NORMAL_SAMPLER_BINDING: u32 = 15; +const AO_MAP_SAMPLER_BINDING: u32 = 16; +const MATERIAL_UBO_BINDING: u32 = 17; pub struct LightPass { context: Arc, @@ -113,13 +114,6 @@ impl OutputMode { } } -#[allow(dead_code)] -struct ConfigUniform { - light_count: u32, - output_mode: u32, - emissive_intensity: f32, -} - #[derive(Debug, Clone, Copy)] enum Pass { /// Opaque geometry only, will discard masked fragments. @@ -136,7 +130,8 @@ impl LightPass { pub fn create( context: Arc, model_data: &ModelData, - camera_buffers: &[Buffer], + camera_ubos: &[Buffer], + config_ubos: &[Buffer], environment: &Environment, ao_map: Option<&VulkanTexture>, msaa_samples: vk::SampleCountFlags, @@ -153,16 +148,17 @@ impl LightPass { let descriptors = create_descriptors( &context, DescriptorsResources { - camera_buffers, + camera_ubos, + config_ubos, model_transform_buffers: &model_data.transform_ubos, model_skin_buffers: &model_data.skin_ubos, - light_buffers: &model_data.light_buffers, + model_materials_buffer: &model_data.materials_ubo, + light_buffers: &model_data.light_ubos, dummy_texture: &dummy_texture, environment, - model: &model_rc.borrow(), + ao_map: ao_map.unwrap_or(&dummy_texture), }, - ao_map.unwrap_or(&dummy_texture), ); let pipeline_layout = create_pipeline_layout(context.device(), &descriptors); @@ -236,7 +232,8 @@ impl LightPass { pub fn set_model( &mut self, model_data: &ModelData, - camera_buffers: &[Buffer], + camera_ubos: &[Buffer], + config_ubos: &[Buffer], environment: &Environment, ao_map: Option<&VulkanTexture>, ) { @@ -248,16 +245,18 @@ impl LightPass { self.descriptors = create_descriptors( &self.context, DescriptorsResources { - camera_buffers, + camera_ubos, + config_ubos, model_transform_buffers: &model_data.transform_ubos, model_skin_buffers: &model_data.skin_ubos, - light_buffers: &model_data.light_buffers, + model_materials_buffer: &model_data.materials_ubo, + light_buffers: &model_data.light_ubos, dummy_texture: &self.dummy_texture, environment, model: &model_rc.borrow(), + ao_map: ao_map.unwrap_or(&self.dummy_texture), }, - ao_map.unwrap_or(&self.dummy_texture), ); } @@ -380,8 +379,8 @@ impl LightPass { command_buffer, vk::PipelineBindPoint::GRAPHICS, self.pipeline_layout, - DYNAMIC_DATA_SET_INDEX, - &self.descriptors.dynamic_data_sets[frame_index..=frame_index], + PER_NODE_DYNAMIC_DATA_SET_INDEX, + &self.descriptors.per_node_dynamic_data_sets[frame_index..=frame_index], &[ model_transform_ubo_offset * index as u32, model_skin_ubo_offset * skin_index as u32, @@ -391,6 +390,11 @@ impl LightPass { for primitive in mesh.primitives().iter().filter(primitive_filter) { let primitive_index = primitive.index(); + let material_index = primitive + .material_index() + .map(|i| i + 1) + .unwrap_or_default(); + let material_ubo_offset = self.context.get_ubo_alignment::(); // Bind descriptor sets unsafe { @@ -401,8 +405,17 @@ impl LightPass { PER_PRIMITIVE_DATA_SET_INDEX, &self.descriptors.per_primitive_sets[primitive_index..=primitive_index], &[], - ) - }; + ); + + device.cmd_bind_descriptor_sets( + command_buffer, + vk::PipelineBindPoint::GRAPHICS, + self.pipeline_layout, + PER_PRIMITIVE_DYNAMIC_DATA_SET_INDEX, + &[self.descriptors.per_primitive_dynamic_data_set], + &[material_index as u32 * material_ubo_offset], + ); + } unsafe { device.cmd_bind_vertex_buffers( @@ -424,34 +437,6 @@ impl LightPass { } } - // Push material constants - unsafe { - let material: MaterialUniform = primitive.material().into(); - let mut data = any_as_u8_slice(&material).to_vec(); - - let light_count = model - .nodes() - .nodes() - .iter() - .filter(|n| n.light_index().is_some()) - .count() as u32; - - let config = ConfigUniform { - light_count, - output_mode: self.output_mode as _, - emissive_intensity: self.emissive_intensity, - }; - data.extend_from_slice(any_as_u8_slice(&config)); - - device.cmd_push_constants( - command_buffer, - self.pipeline_layout, - vk::ShaderStageFlags::FRAGMENT, - 0, - data.as_slice(), - ); - }; - // Draw geometry match primitive.indices() { Some(index_buffer) => { @@ -499,26 +484,31 @@ impl Drop for LightPass { #[derive(Copy, Clone)] struct DescriptorsResources<'a> { - camera_buffers: &'a [Buffer], + camera_ubos: &'a [Buffer], + config_ubos: &'a [Buffer], model_transform_buffers: &'a [Buffer], model_skin_buffers: &'a [Buffer], + model_materials_buffer: &'a Buffer, light_buffers: &'a [Buffer], dummy_texture: &'a VulkanTexture, environment: &'a Environment, model: &'a Model, + ao_map: &'a VulkanTexture, } pub struct Descriptors { context: Arc, pool: vk::DescriptorPool, - dynamic_data_layout: vk::DescriptorSetLayout, - dynamic_data_sets: Vec, + per_node_dynamic_data_layout: vk::DescriptorSetLayout, + per_node_dynamic_data_sets: Vec, static_data_layout: vk::DescriptorSetLayout, static_data_set: vk::DescriptorSet, per_primitive_layout: vk::DescriptorSetLayout, per_primitive_sets: Vec, input_layout: vk::DescriptorSetLayout, input_set: vk::DescriptorSet, + per_primitive_dynamic_data_layout: vk::DescriptorSetLayout, + per_primitive_dynamic_data_set: vk::DescriptorSet, } impl Drop for Descriptors { @@ -526,24 +516,26 @@ impl Drop for Descriptors { let device = self.context.device(); unsafe { device.destroy_descriptor_pool(self.pool, None); + device.destroy_descriptor_set_layout(self.per_primitive_dynamic_data_layout, None); device.destroy_descriptor_set_layout(self.input_layout, None); - device.destroy_descriptor_set_layout(self.dynamic_data_layout, None); + device.destroy_descriptor_set_layout(self.per_node_dynamic_data_layout, None); device.destroy_descriptor_set_layout(self.static_data_layout, None); device.destroy_descriptor_set_layout(self.per_primitive_layout, None); } } } -fn create_descriptors( - context: &Arc, - resources: DescriptorsResources, - ao_map: &VulkanTexture, -) -> Descriptors { +fn create_descriptors(context: &Arc, resources: DescriptorsResources) -> Descriptors { let pool = create_descriptor_pool(context.device(), resources); - let dynamic_data_layout = create_dynamic_data_descriptor_set_layout(context.device()); - let dynamic_data_sets = - create_dynamic_data_descriptor_sets(context, pool, dynamic_data_layout, resources); + let per_node_dynamic_data_layout = + create_per_node_dynamic_data_descriptor_set_layout(context.device()); + let per_node_dynamic_data_sets = create_per_node_dynamic_data_descriptor_sets( + context, + pool, + per_node_dynamic_data_layout, + resources, + ); let static_data_layout = create_static_data_descriptor_set_layout(context.device()); let static_data_set = @@ -554,19 +546,30 @@ fn create_descriptors( create_per_primitive_descriptor_sets(context, pool, per_primitive_layout, resources); let input_layout = create_input_descriptor_set_layout(context.device()); - let input_set = create_input_descriptor_set(context, pool, input_layout, ao_map); + let input_set = create_input_descriptor_set(context, pool, input_layout, resources.ao_map); + + let per_primitive_dynamic_data_layout = + create_per_primitive_dynamic_data_descriptor_set_layout(context.device()); + let per_primitive_dynamic_data_set = create_per_primitive_dynamic_data_descriptor_set( + context, + pool, + per_primitive_dynamic_data_layout, + resources, + ); Descriptors { context: Arc::clone(context), pool, - dynamic_data_layout, - dynamic_data_sets, + per_node_dynamic_data_layout, + per_node_dynamic_data_sets, static_data_layout, static_data_set, per_primitive_layout, per_primitive_sets, input_layout, input_set, + per_primitive_dynamic_data_layout, + per_primitive_dynamic_data_set, } } @@ -575,21 +578,24 @@ fn create_descriptor_pool( descriptors_resources: DescriptorsResources, ) -> vk::DescriptorPool { const GLOBAL_TEXTURES_COUNT: u32 = 4; // irradiance, prefiltered, brdf lut, ao + const PER_FRAME_SETS_COUNT: u32 = 3; // camera, config, lights const STATIC_SETS_COUNT: u32 = 1; const INPUT_SETS_COUNT: u32 = 1; + const PER_NODE_SETS_COUNT: u32 = 2; + const MATERIAL_SETS_COUNT: u32 = 1; - let descriptor_count = descriptors_resources.camera_buffers.len() as u32; + let descriptor_count = descriptors_resources.camera_ubos.len() as u32; let primitive_count = descriptors_resources.model.primitive_count() as u32; let textures_desc_count = primitive_count * SAMPLERS_PER_PRIMITIVE; let pool_sizes = [ vk::DescriptorPoolSize { ty: vk::DescriptorType::UNIFORM_BUFFER, - descriptor_count: descriptor_count * 2, + descriptor_count: descriptor_count * PER_FRAME_SETS_COUNT, }, vk::DescriptorPoolSize { ty: vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC, - descriptor_count: descriptor_count * 2, + descriptor_count: descriptor_count * PER_NODE_SETS_COUNT + MATERIAL_SETS_COUNT, }, vk::DescriptorPoolSize { ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, @@ -599,13 +605,19 @@ fn create_descriptor_pool( let create_info = vk::DescriptorPoolCreateInfo::builder() .pool_sizes(&pool_sizes) - .max_sets(descriptor_count + STATIC_SETS_COUNT + INPUT_SETS_COUNT + primitive_count) + .max_sets( + descriptor_count + + STATIC_SETS_COUNT + + INPUT_SETS_COUNT + + primitive_count + + MATERIAL_SETS_COUNT, + ) .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET); unsafe { device.create_descriptor_pool(&create_info, None).unwrap() } } -fn create_dynamic_data_descriptor_set_layout(device: &Device) -> vk::DescriptorSetLayout { +fn create_per_node_dynamic_data_descriptor_set_layout(device: &Device) -> vk::DescriptorSetLayout { let bindings = [ vk::DescriptorSetLayoutBinding::builder() .binding(CAMERA_UBO_BINDING) @@ -613,6 +625,12 @@ fn create_dynamic_data_descriptor_set_layout(device: &Device) -> vk::DescriptorS .descriptor_count(1) .stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT) .build(), + vk::DescriptorSetLayoutBinding::builder() + .binding(CONFIG_UBO_BINDING) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .build(), vk::DescriptorSetLayoutBinding::builder() .binding(LIGHT_UBO_BINDING) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) @@ -642,13 +660,13 @@ fn create_dynamic_data_descriptor_set_layout(device: &Device) -> vk::DescriptorS } } -fn create_dynamic_data_descriptor_sets( +fn create_per_node_dynamic_data_descriptor_sets( context: &Arc, pool: vk::DescriptorPool, layout: vk::DescriptorSetLayout, resources: DescriptorsResources, ) -> Vec { - let layouts = (0..resources.camera_buffers.len()) + let layouts = (0..resources.camera_ubos.len()) .map(|_| layout) .collect::>(); @@ -663,7 +681,8 @@ fn create_dynamic_data_descriptor_sets( }; sets.iter().enumerate().for_each(|(i, set)| { - let camera_ubo = &resources.camera_buffers[i]; + let camera_ubo = &resources.camera_ubos[i]; + let config_ubo = &resources.config_ubos[i]; let light_buffer = &resources.light_buffers[i]; let model_transform_ubo = &resources.model_transform_buffers[i]; let model_skin_ubo = &resources.model_skin_buffers[i]; @@ -674,6 +693,12 @@ fn create_dynamic_data_descriptor_sets( .range(vk::WHOLE_SIZE) .build()]; + let config_buffer_info = [vk::DescriptorBufferInfo::builder() + .buffer(config_ubo.buffer) + .offset(0) + .range(vk::WHOLE_SIZE) + .build()]; + let light_buffer_info = [vk::DescriptorBufferInfo::builder() .buffer(light_buffer.buffer) .offset(0) @@ -699,6 +724,12 @@ fn create_dynamic_data_descriptor_sets( .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(&camera_buffer_info) .build(), + vk::WriteDescriptorSet::builder() + .dst_set(*set) + .dst_binding(CONFIG_UBO_BINDING) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .buffer_info(&config_buffer_info) + .build(), vk::WriteDescriptorSet::builder() .dst_set(*set) .dst_binding(LIGHT_UBO_BINDING) @@ -912,6 +943,7 @@ fn create_per_primitive_descriptor_sets( for mesh in model.meshes() { for primitive in mesh.primitives() { let material = primitive.material(); + let albedo_info = create_descriptor_image_info( material.get_color_texture_index(), textures, @@ -1066,6 +1098,64 @@ fn create_input_descriptor_set( set } +fn create_per_primitive_dynamic_data_descriptor_set_layout( + device: &Device, +) -> vk::DescriptorSetLayout { + let bindings = [vk::DescriptorSetLayoutBinding::builder() + .binding(MATERIAL_UBO_BINDING) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .build()]; + + let layout_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings); + + unsafe { + device + .create_descriptor_set_layout(&layout_info, None) + .unwrap() + } +} + +fn create_per_primitive_dynamic_data_descriptor_set( + context: &Arc, + pool: vk::DescriptorPool, + layout: vk::DescriptorSetLayout, + resources: DescriptorsResources, +) -> vk::DescriptorSet { + let layouts = [layout]; + let allocate_info = vk::DescriptorSetAllocateInfo::builder() + .descriptor_pool(pool) + .set_layouts(&layouts); + let set = unsafe { + context + .device() + .allocate_descriptor_sets(&allocate_info) + .unwrap()[0] + }; + + let material_buffer_info = [vk::DescriptorBufferInfo::builder() + .buffer(resources.model_materials_buffer.buffer) + .offset(0) + .range(size_of::() as _) + .build()]; + + let descriptor_writes = [vk::WriteDescriptorSet::builder() + .dst_set(set) + .dst_binding(MATERIAL_UBO_BINDING) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC) + .buffer_info(&material_buffer_info) + .build()]; + + unsafe { + context + .device() + .update_descriptor_sets(&descriptor_writes, &[]) + } + + set +} + fn update_input_descriptor_set( context: &Arc, set: vk::DescriptorSet, @@ -1111,21 +1201,14 @@ fn create_descriptor_image_info( fn create_pipeline_layout(device: &Device, descriptors: &Descriptors) -> vk::PipelineLayout { let layouts = [ - descriptors.dynamic_data_layout, + descriptors.per_node_dynamic_data_layout, descriptors.static_data_layout, descriptors.per_primitive_layout, descriptors.input_layout, + descriptors.per_primitive_dynamic_data_layout, ]; - let size = size_of::() + size_of::(); - let push_constant_range = [vk::PushConstantRange { - stage_flags: vk::ShaderStageFlags::FRAGMENT, - offset: 0, - size: size as _, - }]; - let layout_info = vk::PipelineLayoutCreateInfo::builder() - .set_layouts(&layouts) - .push_constant_ranges(&push_constant_range); + let layout_info = vk::PipelineLayoutCreateInfo::builder().set_layouts(&layouts); unsafe { device.create_pipeline_layout(&layout_info, None).unwrap() } } @@ -1269,7 +1352,7 @@ fn create_model_frag_shader_specialization( let max_reflection_lod = (PRE_FILTERED_MAP_SIZE as f32).log2().floor() as u32; let constants = ModelShaderConstants { - max_light_count: MAX_LIGHT_COUNT, + max_light_count: MAX_LIGHT_COUNT as _, max_reflection_lod, pass: pass as _, }; diff --git a/crates/viewer/src/renderer/model/mod.rs b/crates/viewer/src/renderer/model/mod.rs index 66cc470..4ae579b 100644 --- a/crates/viewer/src/renderer/model/mod.rs +++ b/crates/viewer/src/renderer/model/mod.rs @@ -6,12 +6,14 @@ mod uniform; use gbufferpass::GBufferPass; use lightpass::LightPass; use math::cgmath::Matrix4; +use model::Material; use model::Model; use model::MAX_JOINTS_PER_MESH; use std::cell::RefCell; use std::rc::Weak; use std::sync::Arc; use uniform::*; +use vulkan::ash::vk; use vulkan::{mem_copy, mem_copy_aligned, Buffer, Context}; type JointsBuffer = [Matrix4; MAX_JOINTS_PER_MESH]; @@ -22,7 +24,8 @@ pub struct ModelData { transform_ubos: Vec, skin_ubos: Vec, skin_matrices: Vec>, - light_buffers: Vec, + materials_ubo: Buffer, + light_ubos: Vec, } pub struct ModelRenderer { @@ -40,7 +43,8 @@ impl ModelData { let transform_ubos = create_transform_ubos(&context, &model_rc.borrow(), image_count); let (skin_ubos, skin_matrices) = create_skin_ubos(&context, &model_rc.borrow(), image_count); - let light_buffers = create_lights_ubos(&context, &model_rc.borrow(), image_count); + let materials_ubo = create_materials_ubo(&context, &model_rc.borrow()); + let light_ubos = create_lights_ubos(&context, image_count); Self { context, @@ -48,7 +52,8 @@ impl ModelData { transform_ubos, skin_ubos, skin_matrices, - light_buffers, + materials_ubo, + light_ubos, } } @@ -90,29 +95,52 @@ impl ModelData { } } - let elem_size = &self.context.get_ubo_alignment::(); + let elem_size = self.context.get_ubo_alignment::(); let buffer = &mut self.skin_ubos[frame_index]; unsafe { let data_ptr = buffer.map_memory(); - mem_copy_aligned(data_ptr, u64::from(*elem_size), skin_matrices); + mem_copy_aligned(data_ptr, u64::from(elem_size), skin_matrices); } } // Update light buffers { - let uniforms = model + let mut lights_ubo = LightsUBO::default(); + + for (i, ln) in model .nodes() .nodes() .iter() .filter(|n| n.light_index().is_some()) .map(|n| (n.transform(), n.light_index().unwrap())) .map(|(t, i)| (t, model.lights()[i]).into()) - .collect::>(); + .enumerate() + .take(MAX_LIGHT_COUNT) + { + lights_ubo.count += 1; + lights_ubo.lights[i] = ln; + } + + let buffer = &mut self.light_ubos[frame_index]; + let data_ptr = buffer.map_memory(); + unsafe { mem_copy(data_ptr, &[lights_ubo]) }; + } - if !uniforms.is_empty() { - let buffer = &mut self.light_buffers[frame_index]; + // Update materials buffer + { + let mut ubos: Vec = vec![Material::default().into()]; + model + .materials() + .iter() + .copied() + .map(|m| m.into()) + .for_each(|m| ubos.push(m)); + + let elem_size = self.context.get_ubo_alignment::() as vk::DeviceSize; + let buffer = &mut self.materials_ubo; + unsafe { let data_ptr = buffer.map_memory(); - unsafe { mem_copy(data_ptr, &uniforms) }; + mem_copy_aligned(data_ptr, elem_size, &ubos); } } } diff --git a/crates/viewer/src/renderer/model/uniform.rs b/crates/viewer/src/renderer/model/uniform.rs index be36368..5c64987 100644 --- a/crates/viewer/src/renderer/model/uniform.rs +++ b/crates/viewer/src/renderer/model/uniform.rs @@ -4,18 +4,24 @@ use model::{Light, Material, Model, Type, Workflow, MAX_JOINTS_PER_MESH}; use std::{mem::size_of, sync::Arc}; use vulkan::{ash::vk, Buffer, Context}; +pub const MAX_LIGHT_COUNT: usize = 8; 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 = 3; // MAX u2 -const UNLIT_FLAG_LIT: u32 = 0; -const UNLIT_FLAG_UNLIT: u32 = 1; +const NO_TEXTURE_ID: u32 = u8::MAX as _; const METALLIC_ROUGHNESS_WORKFLOW: u32 = 0; const SPECULAR_GLOSSINESS_WORKFLOW: u32 = 1; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] #[repr(C)] +pub struct LightsUBO { + pub count: u32, + pub lights: [LightUniform; MAX_LIGHT_COUNT], +} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(C, align(16))] pub struct LightUniform { position: [f32; 4], direction: [f32; 4], @@ -77,7 +83,7 @@ impl From<(Matrix4, Light)> for LightUniform { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[allow(dead_code)] pub struct MaterialUniform { color: [f32; 4], @@ -93,23 +99,17 @@ pub struct MaterialUniform { 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, + color_texture_channel: u32, + material_texture_channel: u32, + emissive_texture_channel: u32, + normals_texture_channel: u32, + occlusion_texture_channel: u32, + clearcoat_factor_texture_channel: u32, + clearcoat_roughness_texture_channel: u32, + clearcoat_normal_texture_channel: u32, + alpha_mode: u32, + is_unlit: vk::Bool32, + workflow: u32, } impl From for MaterialUniform { @@ -136,62 +136,41 @@ impl From for MaterialUniform { let clearcoat_factor = clearcoat.factor(); let clearcoat_roughness = clearcoat.roughness(); - 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 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) + let color_texture_channel = material + .get_color_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let material_texture_channel = 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_channel = material + .get_emissive_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let normals_texture_channel = material + .get_normals_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let occlusion_texture_channel = material + .get_occlusion_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + let clearcoat_factor_texture_channel = clearcoat + .factor_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + let clearcoat_roughness_texture_channel = clearcoat + .roughness_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + let clearcoat_normal_texture_channel = clearcoat + .normal_texture() + .map_or(NO_TEXTURE_ID, |info| info.get_channel()); + + let alpha_mode = material.get_alpha_mode(); + let is_unlit = material.is_unlit().into(); + let workflow = if let Workflow::MetallicRoughness { .. } = workflow { + METALLIC_ROUGHNESS_WORKFLOW + } else { + SPECULAR_GLOSSINESS_WORKFLOW }; MaterialUniform { @@ -203,8 +182,17 @@ impl From for MaterialUniform { alpha_cutoff, clearcoat_factor, clearcoat_roughness, - textures_channels, - alpha_mode_unlit_flag_and_workflow, + color_texture_channel, + material_texture_channel, + emissive_texture_channel, + normals_texture_channel, + occlusion_texture_channel, + clearcoat_factor_texture_channel, + clearcoat_roughness_texture_channel, + clearcoat_normal_texture_channel, + alpha_mode, + is_unlit, + workflow, } } } @@ -266,23 +254,28 @@ pub fn create_skin_ubos( (buffers, matrices) } -pub fn create_lights_ubos(context: &Arc, model: &Model, count: u32) -> Vec { - let light_count = model - .nodes() - .nodes() - .iter() - .filter(|n| n.light_index().is_some()) - .count(); - - // Buffer size cannot be 0 so we allocate at least anough space for one light - // Probably a bad idea but I'd rather avoid creating a specific shader - let buffer_size = std::cmp::max(1, light_count) * size_of::(); +/// create a ubo containing model's materials +/// first is a default material (used for primitives that do no reference a material) +/// then the materials actually defined by the model +pub fn create_materials_ubo(context: &Arc, model: &Model) -> Buffer { + let material_count = 1 + model.materials().len() as vk::DeviceSize; + let elem_size = context.get_ubo_alignment::() as vk::DeviceSize; + let size = elem_size * material_count; + Buffer::create( + Arc::clone(context), + size, + vk::BufferUsageFlags::UNIFORM_BUFFER, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + ) +} +pub fn create_lights_ubos(context: &Arc, count: u32) -> Vec { + let size = size_of::(); (0..count) .map(|_| { Buffer::create( Arc::clone(context), - buffer_size as vk::DeviceSize, + size as _, vk::BufferUsageFlags::UNIFORM_BUFFER, vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, )