Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual Quaternion Skinning #135

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions examples/vertex_skinning/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
add_executable(vertex_skinning
source/vertex_skinning.cpp source/animated_model.cpp source/animated_model.hpp)
add_executable(
vertex_skinning
source/vertex_skinning.cpp
source/animated_model.cpp
source/animated_model.hpp
source/dual_quaternion.cpp
source/dual_quaternion.hpp
source/quaternion.cpp
source/quaternion.hpp
)
target_include_directories(vertex_skinning PRIVATE ${PROJECT_NAME})
target_link_libraries(vertex_skinning PRIVATE ${PROJECT_NAME})

Expand Down
63 changes: 63 additions & 0 deletions examples/vertex_skinning/shaders/dual_quaternion.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// #version 460
#ifndef dual_quaternion_glsl
#define dual_quaternion_glsl
#extension GL_GOOGLE_include_directive : require
#include "quaternion.glsl"

// code from https://github.com/pmbittner/OptimisedCentresOfRotationSkinning/blob/master/shader/quaternion.glsl

// real part + dual part
// = real + e*dual
// = q1 + e*q2
// = (w0 + i*x0 + j*y0 + k*z0) + e*(we + i*xe + j*ye + k*ze)
struct DualQuaternion {
vec4 real, dual;
};

vec3 dualquat_getTranslation(in DualQuaternion dq)
{
return (2 * quat_mult(dq.dual, quat_conj(dq.real))).xyz;
}

DualQuaternion dualquat_conj(in DualQuaternion dq)
{
return DualQuaternion(quat_conj(dq.real), quat_conj(dq.dual));
}

float dualquat_norm(in DualQuaternion dq)
{
return quat_norm(dq.real);
}

DualQuaternion dualquat_add(in DualQuaternion first, in DualQuaternion second)
{
if (dot(first.real, second.real) >= 0) {
return DualQuaternion(first.real + second.real, first.dual + second.dual);
} else {
return DualQuaternion(first.real - second.real, first.dual - second.dual);
}
}

DualQuaternion dualquat_mult(in float scalar, in DualQuaternion dq)
{
return DualQuaternion(scalar * dq.real, scalar * dq.dual);
}

DualQuaternion dualquat_mult(in DualQuaternion first, in DualQuaternion second)
{
return DualQuaternion(
quat_mult(first.real, second.real),
quat_mult(first.real, second.dual) + quat_mult(first.dual, second.real)
);
}

DualQuaternion dualquat_normalize(in DualQuaternion dq)
{
float inv_real_norm = 1.0 / quat_norm(dq.real);
return DualQuaternion(
dq.real * inv_real_norm,
dq.dual * inv_real_norm
);
}

#endif
140 changes: 140 additions & 0 deletions examples/vertex_skinning/shaders/quaternion.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// #version 460
#ifndef quaternion_glsl
#define quaternion_glsl

// code from https://github.com/pmbittner/OptimisedCentresOfRotationSkinning/blob/master/shader/quaternion.glsl

float quat_norm(in vec4 a)
{
return length(a);
}

vec4 quat_conj(vec4 q)
{
return vec4(-q.xyz, q.w);
}

vec4 quat_mult(vec4 q1, vec4 q2)
{
vec4 qr;
qr.x = (q1.w * q2.x) + (q1.x * q2.w) + (q1.y * q2.z) - (q1.z * q2.y);
qr.y = (q1.w * q2.y) - (q1.x * q2.z) + (q1.y * q2.w) + (q1.z * q2.x);
qr.z = (q1.w * q2.z) + (q1.x * q2.y) - (q1.y * q2.x) + (q1.z * q2.w);
qr.w = (q1.w * q2.w) - (q1.x * q2.x) - (q1.y * q2.y) - (q1.z * q2.z);
return qr;
}

// function from https://twistedpairdevelopment.wordpress.com/2013/02/11/rotating-a-vector-by-a-quaternion-in-glsl/
vec4 multQuat(vec4 q1, vec4 q2)
{
return vec4(
q1.w * q2.x + q1.x * q2.w + q1.z * q2.y - q1.y * q2.z,
q1.w * q2.y + q1.y * q2.w + q1.x * q2.z - q1.z * q2.x,
q1.w * q2.z + q1.z * q2.w + q1.y * q2.x - q1.x * q2.y,
q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
);
}

vec4 quat_add_oriented(vec4 q1, vec4 q2)
{
if (dot(q1, q2) >= 0) {
return q1 + q2;
} else {
return q1 - q2;
}
}

mat3 quat_toRotationMatrix(vec4 quat)
{
float twiceXY = 2 * quat.x * quat.y;
float twiceXZ = 2 * quat.x * quat.z;
float twiceYZ = 2 * quat.y * quat.z;
float twiceWX = 2 * quat.w * quat.x;
float twiceWY = 2 * quat.w * quat.y;
float twiceWZ = 2 * quat.w * quat.z;

float wSqrd = quat.w * quat.w;
float xSqrd = quat.x * quat.x;
float ySqrd = quat.y * quat.y;
float zSqrd = quat.z * quat.z;

return mat3(
// first column
vec3(
1.0f - 2*(ySqrd + zSqrd),
twiceXY + twiceWZ,
twiceXZ - twiceWY
),
// second column
vec3(
twiceXY - twiceWZ,
1.0f - 2*(xSqrd + zSqrd),
twiceYZ + twiceWX
),
// third column
vec3(
twiceXZ + twiceWY,
twiceYZ - twiceWX,
1.0f - 2*(xSqrd + ySqrd)
)
);
}

vec4 quat_from_axis_angle(vec3 axis, float angle)
{
vec4 qr;
float half_angle = (angle * 0.5) * 3.14159 / 180.0;
qr.x = axis.x * sin(half_angle);
qr.y = axis.y * sin(half_angle);
qr.z = axis.z * sin(half_angle);
qr.w = cos(half_angle);
return qr;
}

vec4 quat_from_axis_angle_rad(vec3 axis, float angle)
{
vec4 qr;
float half_angle = (angle * 0.5);
qr.x = axis.x * sin(half_angle);
qr.y = axis.y * sin(half_angle);
qr.z = axis.z * sin(half_angle);
qr.w = cos(half_angle);
return qr;
}

vec3 rotate_vertex_position(vec3 position, vec3 axis, float angle)
{
vec4 qr = quat_from_axis_angle(axis, angle);
vec4 qr_conj = quat_conj(qr);
vec4 q_pos = vec4(position.x, position.y, position.z, 0);

vec4 q_tmp = quat_mult(qr, q_pos);
qr = quat_mult(q_tmp, qr_conj);

return vec3(qr.x, qr.y, qr.z);
}

// function from https://twistedpairdevelopment.wordpress.com/2013/02/11/rotating-a-vector-by-a-quaternion-in-glsl/
vec3 rotate_vector( vec4 quat, vec3 vec )
{
vec4 qv = multQuat( quat, vec4(vec, 0.0) );
return multQuat( qv, vec4(-quat.x, -quat.y, -quat.z, quat.w) ).xyz;
}

// function from https://twistedpairdevelopment.wordpress.com/2013/02/11/rotating-a-vector-by-a-quaternion-in-glsl/
vec3 rotate_vector_optimized( vec4 quat, vec3 vec )
{
return vec + 2.0 * cross( cross( vec, quat.xyz ) + quat.w * vec, quat.xyz );
}

vec4 quat_from_two_vectors(vec3 u, vec3 v)
{
vec3 unorm = normalize(u);
vec3 vnorm = normalize(v);
float cos_theta = dot(unorm, vnorm);
float angle = acos(cos_theta);
vec3 w = normalize(cross(u, v));
return quat_from_axis_angle_rad(w, angle);
}

#endif
4 changes: 2 additions & 2 deletions examples/vertex_skinning/shaders/skinning.frag
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#version 460
#extension GL_EXT_nonuniform_qualifier : require

layout(set = 2, binding = 0) uniform sampler2D textures[];
layout(set = 3, binding = 0) uniform sampler2D textures[];

struct MaterialGpuData
{
Expand Down Expand Up @@ -58,7 +58,7 @@ struct MaterialGpuData
vec4 mExtraTexOffsetTiling;
};

layout(set = 3, binding = 0) buffer Material
layout(set = 4, binding = 0) buffer Material
{
MaterialGpuData materials[];
} matSsbo;
Expand Down
44 changes: 38 additions & 6 deletions examples/vertex_skinning/shaders/skinning.vert
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#version 460
#extension GL_EXT_debug_printf : enable
#extension GL_GOOGLE_include_directive : require
#include "dual_quaternion.glsl"

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inTexCoord;
Expand All @@ -12,31 +15,60 @@ layout(push_constant) uniform PushConstants {
mat4 mModelMatrix;
vec4 mCamPos;
int mMaterialIndex;
int mSkinningMode; // 0 = LBS, 1 = DQS, 2 = CORS
} pushConstants;

layout(set = 0, binding = 0) uniform CameraTransform
{
mat4 mViewProjMatrix;
} ct;

layout(set = 1, binding = 0) readonly buffer BoneMatricesBuffer{
layout(set = 1, binding = 0) readonly buffer BoneMatricesBuffer {
mat4 mat[];
} boneMatrices;

layout(set = 2, binding = 0) readonly buffer DualQuaternionsBuffer {
DualQuaternion mDqs[];
} dqb;

layout (location = 0) out vec3 positionWS;
layout (location = 1) out vec3 normalWS;
layout (location = 2) out vec2 texCoord;
layout (location = 3) flat out int materialIndex;

/*void debugMsg(int vertexIndex, int lineNr) {
if(gl_VertexIndex == vertexIndex) {
debugPrintfEXT("gl_VertexIndex = %d, lineNr = %d", gl_VertexIndex, lineNr);
}
}*/

void main() {
mat4 skinning_matrix = boneMatrices.mat[inBoneIndices[0]] * inBoneWeights[0]
+ boneMatrices.mat[inBoneIndices[1]] * inBoneWeights[1]
+ boneMatrices.mat[inBoneIndices[2]] * inBoneWeights[2]
+ boneMatrices.mat[inBoneIndices[3]] * inBoneWeights[3];
mat4 skinning_matrix = mat4(0.0);
if (pushConstants.mSkinningMode == 0) {
// LBS
skinning_matrix = boneMatrices.mat[inBoneIndices.x] * inBoneWeights.x
+ boneMatrices.mat[inBoneIndices.y] * inBoneWeights.y
+ boneMatrices.mat[inBoneIndices.z] * inBoneWeights.z
+ boneMatrices.mat[inBoneIndices.w] * inBoneWeights.w;
} else if (pushConstants.mSkinningMode == 1) {
DualQuaternion dqResult = DualQuaternion(vec4(0,0,0,0), vec4(0));
for (int i = 0; i < 4; ++i) {
dqResult = dualquat_add(dqResult, dualquat_mult(inBoneWeights[i], dqb.mDqs[inBoneIndices[i]]));
}

DualQuaternion c = dualquat_normalize(dqResult);
vec4 translation = vec4(dualquat_getTranslation(c), 1);
skinning_matrix = mat4(quat_toRotationMatrix(c.real));
skinning_matrix[3] = translation;
debugMsg(77);
}

// debugPrintfEXT("debug text vong shader wuhu");

mat4 totalMatrix = pushConstants.mModelMatrix * skinning_matrix;
vec4 posWS = totalMatrix * vec4(inPosition.xyz, 1.0);
positionWS = posWS.xyz;
gl_Position = ct.mViewProjMatrix * posWS;
gl_Position = ct.mViewProjMatrix * posWS;
//normalWS = mat3(totalMatrix) * inNormal;
normalWS = (transpose(inverse(totalMatrix)) * vec4(inNormal, 1.0)).xyz;
texCoord = inTexCoord;
Expand Down
20 changes: 10 additions & 10 deletions examples/vertex_skinning/source/animated_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,13 @@ void animated_model::initialize(
}
}
mBoneData.mBoneMatrices.resize(max_bone_mat_count * allMeshIndices.size(), glm::mat4(0));
/* mBoneData.mDualQuaternions.resize(
mBoneData.mDualQuaternions.resize(
max_bone_mat_count * allMeshIndices.size(),
DualQuaternionStruct{
dual_quaternion::dual_quaternion_struct {
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f),
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)
}
); TODO: uncomment for dualquaternions and cors */
);
}

std::cout << "Number Of Animations: " << mNumAnimations << std::endl;
Expand All @@ -162,10 +162,10 @@ void animated_model::initialize(
avk::storage_buffer_meta::create_from_size(total_num_bone_matrices * sizeof(glm::mat4))
);

/* mBoneData.mDualQuaternionsBuffer = gvk::context().create_buffer(
mBoneData.mDualQuaternionsBuffer = gvk::context().create_buffer(
avk::memory_usage::host_coherent, {},
avk::storage_buffer_meta::create_from_size(total_num_bone_matrices * sizeof(DualQuaternionStruct))
); TODO: uncomment for dual quaternions and cors */
avk::storage_buffer_meta::create_from_size(total_num_bone_matrices * sizeof(dual_quaternion::dual_quaternion_struct))
);
}

auto [gpuMaterials, imageSamplers] = gvk::convert_for_gpu_usage<gvk::material_gpu_data>(
Expand Down Expand Up @@ -249,20 +249,20 @@ void animated_model::update_bone_matrices() {
for (int i = 0; i < mBoneData.mBoneMatrices.size(); i++) {
auto m = glm::inverse(mParts[0].mMeshTransform) * mBoneData.mBoneMatrices[i];
mBoneData.mBoneMatrices[i] = m;
// mBoneData.mDualQuaternions[i] = DualQuaternion::to_struct(DualQuaternion::from_mat4(m)); TODO uncomment for dual quat and cors
mBoneData.mDualQuaternions[i] = dual_quaternion::to_struct(dual_quaternion::from_mat4(m));
}

mBoneData.mBoneMatricesBuffer->fill(
mBoneData.mBoneMatrices.data(), 0, 0,
mBoneData.mBoneMatrices.size() * sizeof(glm::mat4),
avk::sync::not_required()
);
/*

mBoneData.mDualQuaternionsBuffer->fill(
mBoneData.mDualQuaternions.data(), 0, 0,
mBoneData.mDualQuaternions.size() * sizeof(DualQuaternionStruct),
mBoneData.mDualQuaternions.size() * sizeof(dual_quaternion::dual_quaternion_struct),
avk::sync::not_required()
); TODO uncomment for dual quat and cors */
);
}

void animated_model::update() {
Expand Down
8 changes: 4 additions & 4 deletions examples/vertex_skinning/source/animated_model.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma once

#include "dual_quaternion.hpp"

class animated_model {
public:
Expand Down Expand Up @@ -32,9 +32,9 @@ class animated_model {

struct bone_data {
std::vector<glm::mat4> mBoneMatrices;
// std::vector<DualQuaternionStruct> mDualQuaternions; // TODO: uncomment for dualquaternions and cors
std::vector<dual_quaternion::dual_quaternion_struct> mDualQuaternions;
avk::buffer mBoneMatricesBuffer;
// avk::buffer mDualQuaternionsBuffer; // TODO: uncomment for dualquaternions and cors
avk::buffer mDualQuaternionsBuffer;
};

struct material_data {
Expand Down Expand Up @@ -101,5 +101,5 @@ class animated_model {
bool mFlipTexCoords = false;

// CoRCalculator mCoRCalc = CoRCalculator(0.1f, 0.1f, false, 128); // TODO: uncomment for cors
// bool mUseCoR = false; // TODO: uncomment for dualquaternions and cors
// bool mUseCoR = false; // TODO: uncomment for cors
};
Loading