1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-25 13:17:46 +00:00

Expand Renderer's capabilities for materials and skeletons

The renderer can now store diffuse, normal, specular, and multi
textures. Skin textures now color with a hardcoded skin tone (to be
changed) and bone info buffers are now per-model instead of set globally

Level of detail above 0 is now loaded properly, although break for
other reasons.
This commit is contained in:
Joshua Goins 2023-04-09 15:26:27 -04:00
parent 8003571d13
commit 792da6da6a
6 changed files with 190 additions and 45 deletions

View file

@ -13,6 +13,8 @@ struct RenderPart {
VkBuffer vertexBuffer, indexBuffer; VkBuffer vertexBuffer, indexBuffer;
VkDeviceMemory vertexMemory, indexMemory; VkDeviceMemory vertexMemory, indexMemory;
int materialIndex = 0;
}; };
struct RenderTexture { struct RenderTexture {
@ -22,12 +24,28 @@ struct RenderTexture {
VkSampler sampler = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE;
}; };
enum class MaterialType {
Object,
Skin
};
struct RenderMaterial {
MaterialType type = MaterialType::Object;
RenderTexture* diffuseTexture = nullptr;
RenderTexture* normalTexture = nullptr;
RenderTexture* specularTexture = nullptr;
RenderTexture* multiTexture = nullptr;
};
struct RenderModel { struct RenderModel {
physis_MDL model; physis_MDL model;
std::vector<RenderPart> parts; std::vector<RenderPart> parts;
std::array<glm::mat4, 128> boneData; std::array<glm::mat4, 128> boneData;
std::vector<RenderMaterial> materials;
RenderTexture* texture = nullptr; VkBuffer boneInfoBuffer = VK_NULL_HANDLE;
VkDeviceMemory boneInfoMemory = VK_NULL_HANDLE;
}; };
class Renderer { class Renderer {
@ -67,11 +85,9 @@ public:
VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkBuffer boneInfoBuffer = VK_NULL_HANDLE;
VkDeviceMemory boneInfoMemory = VK_NULL_HANDLE;
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE; VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
std::map<VkImage, VkDescriptorSet> cachedDescriptors; std::map<int, VkDescriptorSet> cachedDescriptors;
VkPipeline pipeline; VkPipeline pipeline;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
@ -94,5 +110,7 @@ public:
VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VkPipelineStageFlags src_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
VkDescriptorSet createDescriptorFor(RenderTexture& texture); VkDescriptorSet createDescriptorFor(const RenderModel& model, const RenderMaterial& material);
int hash(const RenderModel& model, const RenderMaterial& material);
}; };

View file

@ -6,15 +6,32 @@ layout(location = 2) in vec2 inUV;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
layout(binding = 3) uniform sampler2D tex; layout(binding = 3) uniform sampler2D diffuseTexture;
layout(binding = 4) uniform sampler2D normalTexture;
layout(binding = 5) uniform sampler2D specularTexture;
layout(binding = 6) uniform sampler2D multiTexture;
layout(std430, push_constant) uniform PushConstant {
mat4 vp, model;
int boneOffset;
int type;
};
void main() { void main() {
const vec3 lightPos = vec3(3); const vec3 lightPos = vec3(3);
vec3 diffuse = texture(diffuseTexture, inUV).rgb;
if(type == 1) {
const float skinInfluence = texture(specularTexture, inUV).r;
vec3 skinColor = vec3(250 / 255.0, 199 / 255.0, 166 / 255.0);
diffuse = mix(texture(diffuseTexture, inUV).rgb, skinColor, 1.0);
}
vec3 norm = normalize(inNormal); vec3 norm = normalize(inNormal);
vec3 lightDir = normalize(lightPos - inFragPos); vec3 lightDir = normalize(lightPos - inFragPos);
float diff = max(dot(norm, lightDir), 0.0); float diff = max(dot(norm, lightDir), 0.0);
outColor = texture(tex, inUV) * diff; outColor = vec4(diffuse * diff, 1.0);
} }

Binary file not shown.

View file

@ -10,9 +10,15 @@ layout(location = 0) out vec3 outNormal;
layout(location = 1) out vec3 outFragPos; layout(location = 1) out vec3 outFragPos;
layout(location = 2) out vec2 outUV; layout(location = 2) out vec2 outUV;
layout(push_constant) uniform PushConstant { layout(binding = 3) uniform sampler2D diffuseTexture;
layout(binding = 4) uniform sampler2D normalTexture;
layout(binding = 5) uniform sampler2D specularTexture;
layout(binding = 6) uniform sampler2D multiTexture;
layout(std430, push_constant) uniform PushConstant {
mat4 vp, model; mat4 vp, model;
int boneOffset; int boneOffset;
int type;
}; };
layout(std430, binding = 2) buffer readonly BoneInformation { layout(std430, binding = 2) buffer readonly BoneInformation {

Binary file not shown.

View file

@ -469,45 +469,54 @@ void Renderer::render(std::vector<RenderModel> models) {
{ {
const size_t bufferSize = sizeof(glm::mat4) * 128; const size_t bufferSize = sizeof(glm::mat4) * 128;
void *mapped_data = nullptr; void *mapped_data = nullptr;
vkMapMemory(device, boneInfoMemory, 0, bufferSize, 0, &mapped_data); vkMapMemory(device, model.boneInfoMemory, 0, bufferSize, 0, &mapped_data);
memcpy(mapped_data, model.boneData.data(), bufferSize); memcpy(mapped_data, model.boneData.data(), bufferSize);
VkMappedMemoryRange range = {}; VkMappedMemoryRange range = {};
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.memory = boneInfoMemory; range.memory = model.boneInfoMemory;
range.size = bufferSize; range.size = bufferSize;
vkFlushMappedMemoryRanges(device, 1, &range); vkFlushMappedMemoryRanges(device, 1, &range);
vkUnmapMemory(device, boneInfoMemory); vkUnmapMemory(device, model.boneInfoMemory);
} }
if(model.texture == nullptr) if(model.materials.empty())
continue; continue;
if(!cachedDescriptors.count(model.texture->handle))
cachedDescriptors[model.texture->handle] = createDescriptorFor(*model.texture);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &cachedDescriptors[model.texture->handle], 0, nullptr);
for(const auto& part : model.parts) { for(const auto& part : model.parts) {
RenderMaterial& material = model.materials[part.materialIndex];
int h = hash(model, material);
if(!cachedDescriptors.count(h)) {
fmt::print("Caching descriptor for hash {}\n", h);
cachedDescriptors[h] = createDescriptorFor(model, material);
}
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &cachedDescriptors[h], 0, nullptr);
VkDeviceSize offsets[] = {0}; VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16);
glm::mat4 p = glm::perspective(glm::radians(45.0f), swapchainExtent.width / (float) swapchainExtent.height, glm::mat4 p = glm::perspective(glm::radians(45.0f), swapchainExtent.width / (float) swapchainExtent.height,
0.1f, 100.0f); 0.1f, 100.0f);
glm::mat4 v = glm::lookAt(glm::vec3(0, 1, 3), glm::vec3(0, 1, 0), glm::vec3(0, -1, 0)); glm::mat4 v = glm::lookAt(glm::vec3(0, 1, 1), glm::vec3(0, 1, 0), glm::vec3(0, -1, 0));
glm::mat4 vp = p * v; glm::mat4 vp = p * v;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &vp); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(glm::mat4), &vp);
glm::mat4 m = glm::mat4(1.0f); glm::mat4 m = glm::mat4(1.0f);
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::mat4), sizeof(glm::mat4), &m); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::mat4), sizeof(glm::mat4), &m);
int test = 0; int test = 0;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(glm::mat4) * 2, sizeof(int), &test); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::mat4) * 2, sizeof(int), &test);
int type = (int)material.type;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(glm::mat4) * 2 + sizeof(int), sizeof(int), &type);
vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0);
} }
@ -609,10 +618,12 @@ RenderModel Renderer::addModel(const physis_MDL& model, int lod) {
if(lod < 0 || lod > model.num_lod) if(lod < 0 || lod > model.num_lod)
return {}; return {};
for(int i = 0; i < model.lods[0].num_parts; i++) { for(int i = 0; i < model.lods[lod].num_parts; i++) {
RenderPart renderPart; RenderPart renderPart;
const physis_Part part = model.lods[0].parts[i]; const physis_Part part = model.lods[lod].parts[i];
renderPart.materialIndex = part.material_index;
size_t vertexSize = part.num_vertices * sizeof(Vertex); size_t vertexSize = part.num_vertices * sizeof(Vertex);
auto[vertexBuffer, vertexMemory] = createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); auto[vertexBuffer, vertexMemory] = createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
@ -663,6 +674,12 @@ RenderModel Renderer::addModel(const physis_MDL& model, int lod) {
renderModel.parts.push_back(renderPart); renderModel.parts.push_back(renderPart);
} }
const size_t bufferSize = sizeof(glm::mat4) * 128;
auto [buffer, memory] = createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
renderModel.boneInfoBuffer = buffer;
renderModel.boneInfoMemory = memory;
return renderModel; return renderModel;
} }
@ -759,8 +776,8 @@ void Renderer::initPipeline() {
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
VkPushConstantRange pushConstantRange = {}; VkPushConstantRange pushConstantRange = {};
pushConstantRange.size = (sizeof(glm::mat4) * 2) + sizeof(int); pushConstantRange.size = (sizeof(glm::mat4) * 2) + sizeof(int) * 2;
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
@ -852,10 +869,28 @@ void Renderer::initDescriptors() {
VkDescriptorSetLayoutBinding textureBinding = {}; VkDescriptorSetLayoutBinding textureBinding = {};
textureBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; textureBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
textureBinding.descriptorCount = 1; textureBinding.descriptorCount = 1;
textureBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; textureBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
textureBinding.binding = 3; textureBinding.binding = 3;
const std::array bindings = {boneInfoBufferBinding, textureBinding}; VkDescriptorSetLayoutBinding normalBinding = {};
normalBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
normalBinding.descriptorCount = 1;
normalBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
normalBinding.binding = 4;
VkDescriptorSetLayoutBinding specularBinding = {};
specularBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
specularBinding.descriptorCount = 1;
specularBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
specularBinding.binding = 5;
VkDescriptorSetLayoutBinding multiBinding = {};
multiBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
multiBinding.descriptorCount = 1;
multiBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
multiBinding.binding = 6;
const std::array bindings = {boneInfoBufferBinding, textureBinding, normalBinding, specularBinding, multiBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo = {}; VkDescriptorSetLayoutCreateInfo layoutInfo = {};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
@ -863,12 +898,6 @@ void Renderer::initDescriptors() {
layoutInfo.pBindings = bindings.data(); layoutInfo.pBindings = bindings.data();
vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &setLayout); vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &setLayout);
const size_t bufferSize = sizeof(glm::mat4) * 128;
auto [buffer, memory] = createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
boneInfoBuffer = buffer;
boneInfoMemory = memory;
} }
void Renderer::initDepth(int width, int height) { void Renderer::initDepth(int width, int height) {
@ -1122,7 +1151,21 @@ void Renderer::inlineTransitionImageLayout(VkCommandBuffer commandBuffer, VkImag
nullptr, 0, nullptr, 1, &barrier); nullptr, 0, nullptr, 1, &barrier);
} }
VkDescriptorSet Renderer::createDescriptorFor(RenderTexture &texture) { int Renderer::hash(const RenderModel& model, const RenderMaterial& material) {
int hash = 0;
hash += reinterpret_cast<intptr_t>((void*)&model);
if (material.diffuseTexture)
hash += reinterpret_cast<intptr_t>((void*)material.diffuseTexture);
if (material.normalTexture)
hash += reinterpret_cast<intptr_t>((void*)material.normalTexture);
if (material.specularTexture)
hash += reinterpret_cast<intptr_t>((void*)material.specularTexture);
if (material.multiTexture)
hash += reinterpret_cast<intptr_t>((void*)material.multiTexture);
return hash;
}
VkDescriptorSet Renderer::createDescriptorFor(const RenderModel& model, const RenderMaterial& material) {
VkDescriptorSet set; VkDescriptorSet set;
VkDescriptorSetAllocateInfo allocateInfo = {}; VkDescriptorSetAllocateInfo allocateInfo = {};
@ -1135,8 +1178,10 @@ VkDescriptorSet Renderer::createDescriptorFor(RenderTexture &texture) {
const size_t bufferSize = sizeof(glm::mat4) * 128; const size_t bufferSize = sizeof(glm::mat4) * 128;
std::vector<VkWriteDescriptorSet> writes;
VkDescriptorBufferInfo bufferInfo = {}; VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = boneInfoBuffer; bufferInfo.buffer = model.boneInfoBuffer;
bufferInfo.range = bufferSize; bufferInfo.range = bufferSize;
VkWriteDescriptorSet descriptorWrite = {}; VkWriteDescriptorSet descriptorWrite = {};
@ -1147,20 +1192,79 @@ VkDescriptorSet Renderer::createDescriptorFor(RenderTexture &texture) {
descriptorWrite.pBufferInfo = &bufferInfo; descriptorWrite.pBufferInfo = &bufferInfo;
descriptorWrite.dstBinding = 2; descriptorWrite.dstBinding = 2;
writes.push_back(descriptorWrite);
VkDescriptorImageInfo imageInfo = {}; VkDescriptorImageInfo imageInfo = {};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = texture.view;
imageInfo.sampler = texture.sampler;
VkWriteDescriptorSet descriptorWrite2 = {}; if(material.diffuseTexture) {
descriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; imageInfo.imageView = material.diffuseTexture->view;
descriptorWrite2.dstSet = set; imageInfo.sampler = material.diffuseTexture->sampler;
descriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite2.descriptorCount = 1;
descriptorWrite2.pImageInfo = &imageInfo;
descriptorWrite2.dstBinding = 3;
const std::array writes = {descriptorWrite, descriptorWrite2}; VkWriteDescriptorSet descriptorWrite2 = {};
descriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite2.dstSet = set;
descriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite2.descriptorCount = 1;
descriptorWrite2.pImageInfo = &imageInfo;
descriptorWrite2.dstBinding = 3;
writes.push_back(descriptorWrite2);
}
VkDescriptorImageInfo normalImageInfo = {};
normalImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if(material.normalTexture) {
normalImageInfo.imageView = material.normalTexture->view;
normalImageInfo.sampler = material.normalTexture->sampler;
VkWriteDescriptorSet normalDescriptorWrite2 = {};
normalDescriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
normalDescriptorWrite2.dstSet = set;
normalDescriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
normalDescriptorWrite2.descriptorCount = 1;
normalDescriptorWrite2.pImageInfo = &normalImageInfo;
normalDescriptorWrite2.dstBinding = 4;
writes.push_back(normalDescriptorWrite2);
}
VkDescriptorImageInfo specularImageInfo = {};
specularImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if(material.specularTexture) {
specularImageInfo.imageView = material.specularTexture->view;
specularImageInfo.sampler = material.specularTexture->sampler;
VkWriteDescriptorSet specularDescriptorWrite2 = {};
specularDescriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
specularDescriptorWrite2.dstSet = set;
specularDescriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
specularDescriptorWrite2.descriptorCount = 1;
specularDescriptorWrite2.pImageInfo = &specularImageInfo;
specularDescriptorWrite2.dstBinding = 5;
writes.push_back(specularDescriptorWrite2);
}
VkDescriptorImageInfo multiImageInfo = {};
multiImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if (material.multiTexture) {
multiImageInfo.imageView = material.multiTexture->view;
multiImageInfo.sampler = material.multiTexture->sampler;
VkWriteDescriptorSet multiDescriptorWrite2 = {};
multiDescriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
multiDescriptorWrite2.dstSet = set;
multiDescriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
multiDescriptorWrite2.descriptorCount = 1;
multiDescriptorWrite2.pImageInfo = &multiImageInfo;
multiDescriptorWrite2.dstBinding = 6;
writes.push_back(multiDescriptorWrite2);
}
vkUpdateDescriptorSets(device, writes.size(), writes.data(), 0, nullptr); vkUpdateDescriptorSets(device, writes.size(), writes.data(), 0, nullptr);