From 5502e51d714e6693e15cc9ffecde30d284d82bd1 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 5 Nov 2022 00:26:56 +0100 Subject: [PATCH] Graphics: Move skinning to a separate module --- src/Nazara/Graphics/Graphics.cpp | 10 ++++ .../Resources/Shaders/BasicMaterial.nzsl | 22 +++++---- .../Shaders/Modules/Engine/SkeletalData.nzsl | 2 +- .../Shaders/Modules/Engine/SkinningData.nzsl | 23 +++++++++ .../Modules/Engine/SkinningLinear.nzsl | 35 ++++++++++++++ .../Resources/Shaders/PhongMaterial.nzsl | 48 +++++++++++++++++-- .../Shaders/PhysicallyBasedMaterial.nzsl | 45 ++++++++++++++++- 7 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningData.nzsl create mode 100644 src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningLinear.nzsl diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 5f5344b6c..199522fb7 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -53,6 +53,14 @@ namespace Nz #include }; + const UInt8 r_skinningDataModule[] = { + #include + }; + + const UInt8 r_skinningLinearModule[] = { + #include + }; + const UInt8 r_viewerDataModule[] = { #include }; @@ -321,6 +329,8 @@ namespace Nz RegisterEmbedShaderModule(r_mathCookTorrancePBRModule); RegisterEmbedShaderModule(r_phongMaterialShader); RegisterEmbedShaderModule(r_physicallyBasedMaterialShader); + RegisterEmbedShaderModule(r_skinningDataModule); + RegisterEmbedShaderModule(r_skinningLinearModule); RegisterEmbedShaderModule(r_skeletalDataModule); RegisterEmbedShaderModule(r_textureBlitShader); RegisterEmbedShaderModule(r_viewerDataModule); diff --git a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl index 05ac30374..c888af92c 100644 --- a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl @@ -4,6 +4,7 @@ module BasicMaterial; import InstanceData from Engine.InstanceData; import SkeletalData from Engine.SkeletalData; import ViewerData from Engine.ViewerData; +import SkinLinearPosition from Engine.SkinningLinear; // Pass-specific options option DepthPass: bool = false; @@ -175,23 +176,24 @@ fn billboardMain(input: VertIn) -> VertOut fn main(input: VertIn) -> VertOut { let pos: vec3[f32]; + const if (HasSkinning) { - pos = vec3[f32](0.0, 0.0, 0.0); + let jointMatrices = array[mat4[f32]]( + skeletalData.jointMatrices[input.jointIndices[0]], + skeletalData.jointMatrices[input.jointIndices[1]], + skeletalData.jointMatrices[input.jointIndices[2]], + skeletalData.jointMatrices[input.jointIndices[3]] + ); - [unroll] - for i in 0 -> 4 - { - let jointIndex = input.jointIndices[i]; - let jointWeight = input.jointWeights[i]; - - let jointMatrix = skeletalData.JointMatrices[jointIndex]; - pos += (jointMatrix * vec4[f32](input.pos, 1.0)).xyz * jointWeight; - } + let skinningOutput = SkinLinearPosition(jointMatrices, input.jointWeights, input.pos); + pos = skinningOutput.position; } else pos = input.pos; + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); + let output: VertOut; output.position = viewerData.viewProjMatrix * instanceData.worldMatrix * vec4[f32](pos, 1.0); diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkeletalData.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkeletalData.nzsl index 84c9d9c95..81ec97686 100644 --- a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkeletalData.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkeletalData.nzsl @@ -9,5 +9,5 @@ const MaxJointCount: u32 = u32(256); //< FIXME: Fix integral value types [layout(std140)] struct SkeletalData { - JointMatrices: array[mat4[f32], MaxJointCount] + jointMatrices: array[mat4[f32], MaxJointCount] } diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningData.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningData.nzsl new file mode 100644 index 000000000..5cc036674 --- /dev/null +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningData.nzsl @@ -0,0 +1,23 @@ +[nzsl_version("1.0")] +module Engine.SkinningData; + +[export] +struct SkinPositionOutput +{ + position: vec3[f32] +} + +[export] +struct SkinPositionNormalOutput +{ + position: vec3[f32], + normal: vec3[f32] +} + +[export] +struct SkinPositionNormalTangentOutput +{ + position: vec3[f32], + normal: vec3[f32], + tangent: vec3[f32] +} diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningLinear.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningLinear.nzsl new file mode 100644 index 000000000..5792f1acc --- /dev/null +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/SkinningLinear.nzsl @@ -0,0 +1,35 @@ +[nzsl_version("1.0")] +module Engine.SkinningLinear; + +import * from Engine.SkinningData; + +[export] +fn SkinLinearPosition(jointMatrices: array[mat4[f32], 4], jointWeights: vec4[f32], position: vec3[f32]) -> SkinPositionOutput +{ + let skinMatrix = mat4[f32](0.0); + + [unroll] + for i in 0 -> 4 + skinMatrix += jointMatrices[i] * jointWeights[i]; + + let output: SkinPositionOutput; + output.position = (skinMatrix * vec4[f32](position, 1.0)).xyz; + return output; +} + +[export] +fn SkinLinearPositionNormal(jointMatrices: array[mat4[f32], 4], jointWeights: vec4[f32], position: vec3[f32], normal: vec3[f32]) -> SkinPositionNormalOutput +{ + let skinMatrix = mat4[f32](0.0); + + [unroll] + for i in 0 -> 4 + skinMatrix += jointMatrices[i] * jointWeights[i]; + + let inverseTransposeSkinMatrix = transpose(inverse(mat3[f32](skinMatrix))); + + let output: SkinPositionNormalOutput; + output.position = (skinMatrix * vec4[f32](position, 1.0)).xyz; + output.normal = inverseTransposeSkinMatrix * normal; + return output; +} diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index 1697e0831..ab2589167 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -6,6 +6,8 @@ import LightData from Engine.LightData; import SkeletalData from Engine.SkeletalData; import ViewerData from Engine.ViewerData; +import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear; + // Pass-specific options option DepthPass: bool = false; @@ -33,13 +35,16 @@ option VertexPositionLoc: i32; option VertexTangentLoc: i32 = -1; option VertexUvLoc: i32 = -1; +option VertexJointIndicesLoc: i32 = -1; +option VertexJointWeightsLoc: i32 = -1; + const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); const HasTangent = (VertexTangentLoc >= 0); const HasUV = (VertexUvLoc >= 0); const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; - +const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); [layout(std140)] struct MaterialSettings @@ -272,6 +277,12 @@ struct VertIn [cond(HasTangent), location(VertexTangentLoc)] tangent: vec3[f32], + [cond(HasSkinning), location(VertexJointIndicesLoc)] + jointIndices: vec4[i32], + + [cond(HasSkinning), location(VertexJointWeightsLoc)] + jointWeights: vec4[f32], + [cond(Billboard), location(BillboardCenterLocation)] billboardCenter: vec3[f32], @@ -316,7 +327,38 @@ fn billboardMain(input: VertIn) -> VertToFrag [entry(vert), cond(!Billboard)] fn main(input: VertIn) -> VertToFrag { - let worldPosition = instanceData.worldMatrix * vec4[f32](input.pos, 1.0); + let pos: vec3[f32]; + const if (HasNormal) let normal: vec3[f32]; + + const if (HasSkinning) + { + let jointMatrices = array[mat4[f32]]( + skeletalData.jointMatrices[input.jointIndices[0]], + skeletalData.jointMatrices[input.jointIndices[1]], + skeletalData.jointMatrices[input.jointIndices[2]], + skeletalData.jointMatrices[input.jointIndices[3]] + ); + + const if (HasNormal) + { + let skinningOutput = SkinLinearPositionNormal(jointMatrices, input.jointWeights, input.pos, input.normal); + pos = skinningOutput.position; + normal = skinningOutput.normal; + } + else + { + let skinningOutput = SkinLinearPosition(jointMatrices, input.jointWeights, input.pos); + pos = skinningOutput.position; + } + } + else + { + pos = input.pos; + const if (HasNormal) + normal = input.normal; + } + + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); let output: VertToFrag; output.worldPos = worldPosition.xyz; @@ -328,7 +370,7 @@ fn main(input: VertIn) -> VertToFrag output.color = input.color; const if (HasNormal) - output.normal = rotationMatrix * input.normal; + output.normal = rotationMatrix * normal; const if (HasUV) output.uv = input.uv; diff --git a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl index 7f93a710a..1ea78fbf6 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl @@ -6,6 +6,8 @@ import LightData from Engine.LightData; import SkeletalData from Engine.SkeletalData; import ViewerData from Engine.ViewerData; +import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear; + // Pass-specific options option DepthPass: bool = false; @@ -35,12 +37,16 @@ option VertexPositionLoc: i32; option VertexTangentLoc: i32 = -1; option VertexUvLoc: i32 = -1; +option VertexJointIndicesLoc: i32 = -1; +option VertexJointWeightsLoc: i32 = -1; + const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); const HasTangent = (VertexTangentLoc >= 0); const HasUV = (VertexUvLoc >= 0); const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass; +const HasSkinning = (VertexJointIndicesLoc >= 0 && VertexJointWeightsLoc >= 0); [layout(std140)] struct MaterialSettings @@ -260,6 +266,12 @@ struct VertIn [cond(HasTangent), location(VertexTangentLoc)] tangent: vec3[f32], + [cond(HasSkinning), location(VertexJointIndicesLoc)] + jointIndices: vec4[i32], + + [cond(HasSkinning), location(VertexJointWeightsLoc)] + jointWeights: vec4[f32], + [cond(Billboard), location(BillboardCenterLocation)] billboardCenter: vec3[f32], @@ -304,7 +316,38 @@ fn billboardMain(input: VertIn) -> VertToFrag [entry(vert), cond(!Billboard)] fn main(input: VertIn) -> VertToFrag { - let worldPosition = instanceData.worldMatrix * vec4[f32](input.pos, 1.0); + let pos: vec3[f32]; + const if (HasNormal) let normal: vec3[f32]; + + const if (HasSkinning) + { + let jointMatrices = array[mat4[f32]]( + skeletalData.jointMatrices[input.jointIndices[0]], + skeletalData.jointMatrices[input.jointIndices[1]], + skeletalData.jointMatrices[input.jointIndices[2]], + skeletalData.jointMatrices[input.jointIndices[3]] + ); + + const if (HasNormal) + { + let skinningOutput = SkinLinearPositionNormal(jointMatrices, input.jointWeights, input.pos, input.normal); + pos = skinningOutput.position; + normal = skinningOutput.normal; + } + else + { + let skinningOutput = SkinLinearPosition(jointMatrices, input.jointWeights, input.pos); + pos = skinningOutput.position; + } + } + else + { + pos = input.pos; + const if (HasNormal) + normal = input.normal; + } + + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); let output: VertToFrag; output.worldPos = worldPosition.xyz;