Slightly reworked glTF samples

Parent matrices are now applied (where available)
Now more in line with the base code glTF loader
Refs #964
This commit is contained in:
Sascha Willems 2022-08-06 10:16:54 +02:00
parent 465c18862c
commit f084d6093c
11 changed files with 71 additions and 50 deletions

View file

@ -17,7 +17,7 @@ void main()
vec3 N = normalize(inNormal); vec3 N = normalize(inNormal);
vec3 L = normalize(inLightVec); vec3 L = normalize(inLightVec);
vec3 V = normalize(inViewVec); vec3 V = normalize(inViewVec);
vec3 R = reflect(-L, N); vec3 R = reflect(L, N);
vec3 diffuse = max(dot(N, L), 0.15) * inColor; vec3 diffuse = max(dot(N, L), 0.15) * inColor;
vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.75); vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.75);
outFragColor = vec4(diffuse * color.rgb + specular, 1.0); outFragColor = vec4(diffuse * color.rgb + specular, 1.0);

View file

@ -10,6 +10,7 @@ layout (set = 0, binding = 0) uniform UBOScene
mat4 projection; mat4 projection;
mat4 view; mat4 view;
vec4 lightPos; vec4 lightPos;
vec4 viewPos;
} uboScene; } uboScene;
layout(push_constant) uniform PushConsts { layout(push_constant) uniform PushConsts {
@ -32,6 +33,6 @@ void main()
vec4 pos = uboScene.view * vec4(inPos, 1.0); vec4 pos = uboScene.view * vec4(inPos, 1.0);
outNormal = mat3(uboScene.view) * inNormal; outNormal = mat3(uboScene.view) * inNormal;
vec3 lPos = mat3(uboScene.view) * uboScene.lightPos.xyz; vec3 lPos = mat3(uboScene.view) * uboScene.lightPos.xyz;
outLightVec = lPos - pos.xyz; outLightVec = uboScene.lightPos.xyz - pos.xyz;
outViewVec = -pos.xyz; outViewVec = uboScene.viewPos.xyz - pos.xyz;
} }

View file

@ -24,7 +24,7 @@ float4 main(VSOutput input) : SV_TARGET
float3 N = normalize(input.Normal); float3 N = normalize(input.Normal);
float3 L = normalize(input.LightVec); float3 L = normalize(input.LightVec);
float3 V = normalize(input.ViewVec); float3 V = normalize(input.ViewVec);
float3 R = reflect(-L, N); float3 R = reflect(L, N);
float3 diffuse = max(dot(N, L), 0.0) * input.Color; float3 diffuse = max(dot(N, L), 0.0) * input.Color;
float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75); float3 specular = pow(max(dot(R, V), 0.0), 16.0) * float3(0.75, 0.75, 0.75);
return float4(diffuse * color.rgb + specular, 1.0); return float4(diffuse * color.rgb + specular, 1.0);

View file

@ -13,6 +13,7 @@ struct UBO
float4x4 projection; float4x4 projection;
float4x4 view; float4x4 view;
float4 lightPos; float4 lightPos;
float4 viewPos;
}; };
cbuffer ubo : register(b0) { UBO ubo; } cbuffer ubo : register(b0) { UBO ubo; }
@ -42,8 +43,7 @@ VSOutput main(VSInput input)
float4 pos = mul(ubo.view, float4(input.Pos, 1.0)); float4 pos = mul(ubo.view, float4(input.Pos, 1.0));
output.Normal = mul((float3x3)ubo.view, input.Normal); output.Normal = mul((float3x3)ubo.view, input.Normal);
float3 lPos = mul((float3x3)ubo.view, ubo.lightPos.xyz); output.LightVec = ubo.lightPos.xyz - pos.xyz;
output.LightVec = lPos - pos.xyz; output.ViewVec = ubo.viewPos.xyz - pos.xyz;
output.ViewVec = -pos.xyz;
return output; return output;
} }

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - glTF scene loading and rendering * Vulkan Example - glTF scene loading and rendering
* *
* Copyright (C) 2020-2021 by Sascha Willems - www.saschawillems.de * Copyright (C) 2020-2022 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -78,9 +78,14 @@ public:
// A node represents an object in the glTF scene graph // A node represents an object in the glTF scene graph
struct Node { struct Node {
Node* parent; Node* parent;
std::vector<Node> children; std::vector<Node*> children;
Mesh mesh; Mesh mesh;
glm::mat4 matrix; glm::mat4 matrix;
~Node() {
for (auto& child : children) {
delete child;
}
}
}; };
// A glTF material stores information in e.g. the texture that is attached to it and colors // A glTF material stores information in e.g. the texture that is attached to it and colors
@ -109,10 +114,13 @@ public:
std::vector<Image> images; std::vector<Image> images;
std::vector<Texture> textures; std::vector<Texture> textures;
std::vector<Material> materials; std::vector<Material> materials;
std::vector<Node> nodes; std::vector<Node*> nodes;
~VulkanglTFModel() ~VulkanglTFModel()
{ {
for (auto node : nodes) {
delete node;
}
// Release all Vulkan resources allocated for the model // Release all Vulkan resources allocated for the model
vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr); vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr);
vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr); vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr);
@ -195,29 +203,30 @@ public:
void loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer) void loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
{ {
VulkanglTFModel::Node node{}; VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
node.matrix = glm::mat4(1.0f); node->matrix = glm::mat4(1.0f);
node->parent = parent;
// Get the local node matrix // Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix // It's either made up from translation, rotation, scale or a 4x4 matrix
if (inputNode.translation.size() == 3) { if (inputNode.translation.size() == 3) {
node.matrix = glm::translate(node.matrix, glm::vec3(glm::make_vec3(inputNode.translation.data()))); node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
} }
if (inputNode.rotation.size() == 4) { if (inputNode.rotation.size() == 4) {
glm::quat q = glm::make_quat(inputNode.rotation.data()); glm::quat q = glm::make_quat(inputNode.rotation.data());
node.matrix *= glm::mat4(q); node->matrix *= glm::mat4(q);
} }
if (inputNode.scale.size() == 3) { if (inputNode.scale.size() == 3) {
node.matrix = glm::scale(node.matrix, glm::vec3(glm::make_vec3(inputNode.scale.data()))); node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
} }
if (inputNode.matrix.size() == 16) { if (inputNode.matrix.size() == 16) {
node.matrix = glm::make_mat4x4(inputNode.matrix.data()); node->matrix = glm::make_mat4x4(inputNode.matrix.data());
}; };
// Load node's children // Load node's children
if (inputNode.children.size() > 0) { if (inputNode.children.size() > 0) {
for (size_t i = 0; i < inputNode.children.size(); i++) { for (size_t i = 0; i < inputNode.children.size(); i++) {
loadNode(input.nodes[inputNode.children[i]], input , &node, indexBuffer, vertexBuffer); loadNode(input.nodes[inputNode.children[i]], input , node, indexBuffer, vertexBuffer);
} }
} }
@ -309,7 +318,7 @@ public:
primitive.firstIndex = firstIndex; primitive.firstIndex = firstIndex;
primitive.indexCount = indexCount; primitive.indexCount = indexCount;
primitive.materialIndex = glTFPrimitive.material; primitive.materialIndex = glTFPrimitive.material;
node.mesh.primitives.push_back(primitive); node->mesh.primitives.push_back(primitive);
} }
} }
@ -326,20 +335,20 @@ public:
*/ */
// Draw a single node including child nodes (if present) // Draw a single node including child nodes (if present)
void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node node) void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node)
{ {
if (node.mesh.primitives.size() > 0) { if (node->mesh.primitives.size() > 0) {
// Pass the node's matrix via push constants // Pass the node's matrix via push constants
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node // Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
glm::mat4 nodeMatrix = node.matrix; glm::mat4 nodeMatrix = node->matrix;
VulkanglTFModel::Node* currentParent = node.parent; VulkanglTFModel::Node* currentParent = node->parent;
while (currentParent) { while (currentParent) {
nodeMatrix = currentParent->matrix * nodeMatrix; nodeMatrix = currentParent->matrix * nodeMatrix;
currentParent = currentParent->parent; currentParent = currentParent->parent;
} }
// Pass the final matrix to the vertex shader using push constants // Pass the final matrix to the vertex shader using push constants
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix);
for (VulkanglTFModel::Primitive& primitive : node.mesh.primitives) { for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) {
if (primitive.indexCount > 0) { if (primitive.indexCount > 0) {
// Get the texture index for this primitive // Get the texture index for this primitive
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex]; VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
@ -349,7 +358,7 @@ public:
} }
} }
} }
for (auto& child : node.children) { for (auto& child : node->children) {
drawNode(commandBuffer, pipelineLayout, child); drawNode(commandBuffer, pipelineLayout, child);
} }
} }
@ -382,6 +391,7 @@ public:
glm::mat4 projection; glm::mat4 projection;
glm::mat4 model; glm::mat4 model;
glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, -5.0f, 1.0f); glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, -5.0f, 1.0f);
glm::vec4 viewPos;
} values; } values;
} shaderData; } shaderData;
@ -404,7 +414,7 @@ public:
camera.type = Camera::CameraType::lookat; camera.type = Camera::CameraType::lookat;
camera.flipY = true; camera.flipY = true;
camera.setPosition(glm::vec3(0.0f, -0.1f, -1.0f)); camera.setPosition(glm::vec3(0.0f, -0.1f, -1.0f));
camera.setRotation(glm::vec3(0.0f, -135.0f, 0.0f)); camera.setRotation(glm::vec3(0.0f, 45.0f, 0.0f));
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
} }
@ -707,6 +717,7 @@ public:
{ {
shaderData.values.projection = camera.matrices.perspective; shaderData.values.projection = camera.matrices.perspective;
shaderData.values.model = camera.matrices.view; shaderData.values.model = camera.matrices.view;
shaderData.values.viewPos = camera.viewPos;
memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values)); memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values));
} }

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - Scene rendering * Vulkan Example - Scene rendering
* *
* Copyright (C) 2020-2021 by Sascha Willems - www.saschawillems.de * Copyright (C) 2020-202- by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
* *
@ -20,6 +20,9 @@
VulkanglTFScene::~VulkanglTFScene() VulkanglTFScene::~VulkanglTFScene()
{ {
for (auto node : nodes) {
delete node;
}
// Release all Vulkan resources allocated for the model // Release all Vulkan resources allocated for the model
vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr); vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr);
vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr); vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr);
@ -87,30 +90,31 @@ void VulkanglTFScene::loadMaterials(tinygltf::Model& input)
void VulkanglTFScene::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFScene::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFScene::Vertex>& vertexBuffer) void VulkanglTFScene::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFScene::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFScene::Vertex>& vertexBuffer)
{ {
VulkanglTFScene::Node node{}; VulkanglTFScene::Node* node = new VulkanglTFScene::Node{};
node.name = inputNode.name; node->name = inputNode.name;
node->parent = parent;
// Get the local node matrix // Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix // It's either made up from translation, rotation, scale or a 4x4 matrix
node.matrix = glm::mat4(1.0f); node->matrix = glm::mat4(1.0f);
if (inputNode.translation.size() == 3) { if (inputNode.translation.size() == 3) {
node.matrix = glm::translate(node.matrix, glm::vec3(glm::make_vec3(inputNode.translation.data()))); node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
} }
if (inputNode.rotation.size() == 4) { if (inputNode.rotation.size() == 4) {
glm::quat q = glm::make_quat(inputNode.rotation.data()); glm::quat q = glm::make_quat(inputNode.rotation.data());
node.matrix *= glm::mat4(q); node->matrix *= glm::mat4(q);
} }
if (inputNode.scale.size() == 3) { if (inputNode.scale.size() == 3) {
node.matrix = glm::scale(node.matrix, glm::vec3(glm::make_vec3(inputNode.scale.data()))); node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
} }
if (inputNode.matrix.size() == 16) { if (inputNode.matrix.size() == 16) {
node.matrix = glm::make_mat4x4(inputNode.matrix.data()); node->matrix = glm::make_mat4x4(inputNode.matrix.data());
}; };
// Load node's children // Load node's children
if (inputNode.children.size() > 0) { if (inputNode.children.size() > 0) {
for (size_t i = 0; i < inputNode.children.size(); i++) { for (size_t i = 0; i < inputNode.children.size(); i++) {
loadNode(input.nodes[inputNode.children[i]], input, &node, indexBuffer, vertexBuffer); loadNode(input.nodes[inputNode.children[i]], input, node, indexBuffer, vertexBuffer);
} }
} }
@ -210,7 +214,7 @@ void VulkanglTFScene::loadNode(const tinygltf::Node& inputNode, const tinygltf::
primitive.firstIndex = firstIndex; primitive.firstIndex = firstIndex;
primitive.indexCount = indexCount; primitive.indexCount = indexCount;
primitive.materialIndex = glTFPrimitive.material; primitive.materialIndex = glTFPrimitive.material;
node.mesh.primitives.push_back(primitive); node->mesh.primitives.push_back(primitive);
} }
} }
@ -232,23 +236,23 @@ VkDescriptorImageInfo VulkanglTFScene::getTextureDescriptor(const size_t index)
*/ */
// Draw a single node including child nodes (if present) // Draw a single node including child nodes (if present)
void VulkanglTFScene::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFScene::Node node) void VulkanglTFScene::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFScene::Node* node)
{ {
if (!node.visible) { if (!node->visible) {
return; return;
} }
if (node.mesh.primitives.size() > 0) { if (node->mesh.primitives.size() > 0) {
// Pass the node's matrix via push constants // Pass the node's matrix via push constants
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node // Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
glm::mat4 nodeMatrix = node.matrix; glm::mat4 nodeMatrix = node->matrix;
VulkanglTFScene::Node* currentParent = node.parent; VulkanglTFScene::Node* currentParent = node->parent;
while (currentParent) { while (currentParent) {
nodeMatrix = currentParent->matrix * nodeMatrix; nodeMatrix = currentParent->matrix * nodeMatrix;
currentParent = currentParent->parent; currentParent = currentParent->parent;
} }
// Pass the final matrix to the vertex shader using push constants // Pass the final matrix to the vertex shader using push constants
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix);
for (VulkanglTFScene::Primitive& primitive : node.mesh.primitives) { for (VulkanglTFScene::Primitive& primitive : node->mesh.primitives) {
if (primitive.indexCount > 0) { if (primitive.indexCount > 0) {
VulkanglTFScene::Material& material = materials[primitive.materialIndex]; VulkanglTFScene::Material& material = materials[primitive.materialIndex];
// POI: Bind the pipeline for the node's material // POI: Bind the pipeline for the node's material
@ -258,7 +262,7 @@ void VulkanglTFScene::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout p
} }
} }
} }
for (auto& child : node.children) { for (auto& child : node->children) {
drawNode(commandBuffer, pipelineLayout, child); drawNode(commandBuffer, pipelineLayout, child);
} }
} }
@ -641,12 +645,12 @@ void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay)
if (overlay->header("Visibility")) { if (overlay->header("Visibility")) {
if (overlay->button("All")) { if (overlay->button("All")) {
std::for_each(glTFScene.nodes.begin(), glTFScene.nodes.end(), [](VulkanglTFScene::Node &node) { node.visible = true; }); std::for_each(glTFScene.nodes.begin(), glTFScene.nodes.end(), [](VulkanglTFScene::Node* node) { node->visible = true; });
buildCommandBuffers(); buildCommandBuffers();
} }
ImGui::SameLine(); ImGui::SameLine();
if (overlay->button("None")) { if (overlay->button("None")) {
std::for_each(glTFScene.nodes.begin(), glTFScene.nodes.end(), [](VulkanglTFScene::Node &node) { node.visible = false; }); std::for_each(glTFScene.nodes.begin(), glTFScene.nodes.end(), [](VulkanglTFScene::Node* node) { node->visible = false; });
buildCommandBuffers(); buildCommandBuffers();
} }
ImGui::NewLine(); ImGui::NewLine();
@ -655,7 +659,7 @@ void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay)
ImGui::BeginChild("#nodelist", ImVec2(200.0f * overlay->scale, 340.0f * overlay->scale), false); ImGui::BeginChild("#nodelist", ImVec2(200.0f * overlay->scale, 340.0f * overlay->scale), false);
for (auto& node : glTFScene.nodes) for (auto& node : glTFScene.nodes)
{ {
if (overlay->checkBox(node.name.c_str(), &node.visible)) if (overlay->checkBox(node->name.c_str(), &node->visible))
{ {
buildCommandBuffers(); buildCommandBuffers();
} }

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - Scene rendering * Vulkan Example - Scene rendering
* *
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de * Copyright (C) 2020-2022 by Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
* *
@ -76,11 +76,16 @@ public:
// A node represents an object in the glTF scene graph // A node represents an object in the glTF scene graph
struct Node { struct Node {
Node* parent; Node* parent;
std::vector<Node> children; std::vector<Node*> children;
Mesh mesh; Mesh mesh;
glm::mat4 matrix; glm::mat4 matrix;
std::string name; std::string name;
bool visible = true; bool visible = true;
~Node() {
for (auto& child : children) {
delete child;
}
}
}; };
// A glTF material stores information in e.g. the texture that is attached to it and colors // A glTF material stores information in e.g. the texture that is attached to it and colors
@ -113,7 +118,7 @@ public:
std::vector<Image> images; std::vector<Image> images;
std::vector<Texture> textures; std::vector<Texture> textures;
std::vector<Material> materials; std::vector<Material> materials;
std::vector<Node> nodes; std::vector<Node*> nodes;
std::string path; std::string path;
@ -123,7 +128,7 @@ public:
void loadTextures(tinygltf::Model& input); void loadTextures(tinygltf::Model& input);
void loadMaterials(tinygltf::Model& input); void loadMaterials(tinygltf::Model& input);
void loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFScene::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFScene::Vertex>& vertexBuffer); void loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFScene::Node* parent, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFScene::Vertex>& vertexBuffer);
void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFScene::Node node); void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFScene::Node* node);
void draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout); void draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout);
}; };