diff --git a/data/scenes/shadow_test/shadow_test.cpp b/data/scenes/shadow_test/shadow_test.cpp index 18646d3..d736a0f 100644 --- a/data/scenes/shadow_test/shadow_test.cpp +++ b/data/scenes/shadow_test/shadow_test.cpp @@ -1989,30 +1989,37 @@ effect const effect_lightmaterial = { }; material const material_coloreffectr5g54b179_material = { + .name = "ColorEffectR5G54B179-material", .effect = &effect_coloreffectr5g54b179, }; material const material_coloreffectr255g229b0_material = { + .name = "ColorEffectR255G229B0-material", .effect = &effect_coloreffectr255g229b0, }; material const material_planematerial_material = { + .name = "PlaneMaterial", .effect = &effect_planematerial, }; material const material_torusmaterial_material = { + .name = "TorusMaterial", .effect = &effect_torusmaterial, }; material const material_conematerial_material = { + .name = "ConeMaterial", .effect = &effect_conematerial, }; material const material_boxmaterial_material = { + .name = "BoxMaterial", .effect = &effect_boxmaterial, }; material const material_lightmaterial_material = { + .name = "LightMaterial", .effect = &effect_lightmaterial, }; @@ -2275,9 +2282,9 @@ instance_light const instance_lights_node_camera001_target[] = { }; channel const * const node_channels_node_camera001_target[] = { - &node_channel_node_camera001_target_translation_x, &node_channel_node_camera001_target_translation_z, &node_channel_node_camera001_target_translation_y, + &node_channel_node_camera001_target_translation_x, }; node const node_node_camera001_target = { @@ -2563,9 +2570,9 @@ instance_light const instance_lights_node_lighthelper[] = { }; channel const * const node_channels_node_lighthelper[] = { - &node_channel_node_lighthelper_translation_x, &node_channel_node_lighthelper_translation_z, &node_channel_node_lighthelper_translation_y, + &node_channel_node_lighthelper_translation_x, }; node const node_node_lighthelper = { @@ -2752,8 +2759,8 @@ instance_light const instance_lights_node_camerahelper[] = { }; channel const * const node_channels_node_camerahelper[] = { - &node_channel_node_camerahelper_translation_y, &node_channel_node_camerahelper_translation_x, + &node_channel_node_camerahelper_translation_y, &node_channel_node_camerahelper_translation_z, }; diff --git a/include/collada/scene.h b/include/collada/scene.h index ae29849..d6137eb 100644 --- a/include/collada/scene.h +++ b/include/collada/scene.h @@ -17,6 +17,7 @@ namespace collada::scene { void draw(); int find_node_index_by_name(const char * name); + int find_material_index_by_name(const char * name); void update(float t); diff --git a/include/collada/scene/vulkan.h b/include/collada/scene/vulkan.h index 871499e..1821385 100644 --- a/include/collada/scene/vulkan.h +++ b/include/collada/scene/vulkan.h @@ -39,6 +39,9 @@ namespace collada::scene { // externally initialized, enum VkFormat colorFormat; VkFormat depthFormat; + // externally initialized + VkSampler linearSampler; + VkImageView shadowDepthImageView; // // method initialized @@ -57,6 +60,7 @@ namespace collada::scene { static constexpr uint32_t perFrameDescriptorCount = 2; static constexpr uint32_t constantDescriptorCount = 1; static constexpr uint32_t uniformBufferDescriptorCount = maxFrames * perFrameDescriptorCount + constantDescriptorCount; + static constexpr uint32_t descriptorCount = uniformBufferDescriptorCount + 2; VkDescriptorSetLayout descriptorSetLayouts[2]; // unrelated to maxFrames, unrelated to descriptorCount VkDescriptorSet descriptorSets0[maxFrames]; @@ -93,6 +97,7 @@ namespace collada::scene { VkCommandBuffer commandBuffer; uint32_t frameIndex; uint32_t pipelineIndex; + int excludeMaterialIndex; ////////////////////////////////////////////////////////////////////// // called directly @@ -103,7 +108,9 @@ namespace collada::scene { VkPhysicalDeviceProperties const & physicalDeviceProperties, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkFormat colorFormat, - VkFormat depthFormat); + VkFormat depthFormat, + VkSampler linearSampler, + VkImageView shadowDepthImageView); void change_frame(VkCommandBuffer commandBuffer, uint32_t frameIndex); void destroy_all(collada::types::descriptor const * const descriptor); diff --git a/include/collada/types.h b/include/collada/types.h index 490264f..c6e3671 100644 --- a/include/collada/types.h +++ b/include/collada/types.h @@ -232,6 +232,7 @@ namespace collada::types { }; struct material { + char const * const name; types::effect const * const effect; }; diff --git a/shader/collada.hlsl b/shader/collada.hlsl index c6bfddc..16dbac8 100644 --- a/shader/collada.hlsl +++ b/shader/collada.hlsl @@ -8,11 +8,12 @@ struct VSInput struct VSOutput { float4 Position : SV_POSITION; + float4 ShadowPosition : ShadowPosition; float3 Normal : NORMAL0; float2 Texture : TEXCOORD0; float3 LightDirection : NORMAL1; float3 ViewDirection : NORMAL2; - nointerpolation int MaterialIndex : materialindex; + nointerpolation int MaterialIndex : MaterialIndex; }; struct VSShadowOutput @@ -48,6 +49,8 @@ struct MaterialColor // set 1: constant [[vk::binding(0, 1)]] StructuredBuffer MaterialColors; +[[vk::binding(1, 1)]] SamplerState LinearSampler; +[[vk::binding(2, 1)]] Texture2D ShadowTexture; struct PushConstant { int NodeIndex; @@ -71,9 +74,11 @@ float4 getProjection(float4x4 projection, float4 viewPosition) VSOutput VSMain(VSInput input) { float4 viewPosition = getView(Scene.View, input.Position); + float4 shadowPosition = getProjection(Scene.ShadowProjection, getView(Scene.ShadowView, input.Position)); VSOutput output = (VSOutput)0; output.Position = getProjection(Scene.Projection, viewPosition); + output.ShadowPosition = shadowPosition * float4(0.5, 0.5, 1.0, 1.0) + float4(0.5, 0.5, 0.0, 0.0); output.Normal = mul((float3x3)Scene.View, mul((float3x3)Nodes[constants.NodeIndex].World, input.Normal)); output.Texture = input.Texture.xy * 1.0; @@ -83,6 +88,31 @@ VSOutput VSMain(VSInput input) return output; } +float Shadow(float3 position, float bias) +{ + float sampledDepth = ShadowTexture.Sample(LinearSampler, position.xy).x; + float shadow = (position.z - bias) > sampledDepth ? 0.1 : 1.0; + return shadow; +} + +float ShadowPCF(float3 position, float bias) +{ + float2 dimensions; + ShadowTexture.GetDimensions(dimensions.x, dimensions.y); + float2 texelSize = 1.0 / dimensions; + + float shadow = 0.0; + + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + float2 offset = texelSize * float2(x, y); + shadow += Shadow(position + float3(offset, 0), bias); + } + } + + return shadow / 9.0; +} + [shader("pixel")] float4 PSMain(VSOutput input) : SV_TARGET { @@ -101,7 +131,14 @@ float4 PSMain(VSOutput input) : SV_TARGET float3 specular = pow(max(dot(R, V), 0), a) * specularIntensity; float3 diffuse = max(dot(N, L), 0.001); - return float4(diffuse * diffuseColor.xyz + specular * specularColor.xyz + emissionColor.xyz, 1.0); + float3 diffuseSpecular = diffuse * diffuseColor.xyz + specular * specularColor.xyz; + + float3 shadowPosition = input.ShadowPosition.xyz / input.ShadowPosition.w; + float shadowBias = max(0.05 * (1.0 - dot(N, L)), 0.005); + + //float shadowIntensity = Shadow(shadowPosition, shadowBias); + float shadowIntensity = ShadowPCF(shadowPosition, shadowBias); + return float4(diffuseSpecular * shadowIntensity + emissionColor.xyz, 1.0); } [shader("vertex")] diff --git a/src/collada/scene.cpp b/src/collada/scene.cpp index 95dd994..d8efa0a 100644 --- a/src/collada/scene.cpp +++ b/src/collada/scene.cpp @@ -49,6 +49,17 @@ namespace collada::scene { exit(EXIT_FAILURE); } + int state::find_material_index_by_name(const char * name) + { + for (int i = 0; i < descriptor->materials_count; i++) { + if (strcmp(descriptor->materials[i]->name, name) == 0) { + return i; + } + } + fprintf(stderr, "node `%s` not found in scene\n", name); + exit(EXIT_FAILURE); + } + void state::update(float t) { t = animate::loop(t, 3.3f); diff --git a/src/collada/scene/vulkan.cpp b/src/collada/scene/vulkan.cpp index f767c43..996419c 100644 --- a/src/collada/scene/vulkan.cpp +++ b/src/collada/scene/vulkan.cpp @@ -105,7 +105,9 @@ namespace collada::scene { VkPhysicalDeviceProperties const & physicalDeviceProperties, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkFormat colorFormat, - VkFormat depthFormat) + VkFormat depthFormat, + VkSampler linearSampler, + VkImageView shadowDepthImageView) { this->instance = instance; this->device = device; @@ -116,6 +118,9 @@ namespace collada::scene { this->colorFormat = colorFormat; this->depthFormat = depthFormat; + this->linearSampler = linearSampler; + this->shadowDepthImageView = shadowDepthImageView; + load_shader(); } @@ -275,7 +280,8 @@ namespace collada::scene { // // pool // - VkDescriptorPoolSize descriptorPoolSizes[2]{ + constexpr int descriptorPoolSizesCount = 4; + VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{ { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = maxFrames + 1, // why +1? @@ -283,12 +289,20 @@ namespace collada::scene { { .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = maxFrames + 1, // +1 for materialColors - } + }, + { + .type = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = 1, + }, + { + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 1, + }, }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = 2, - .poolSizeCount = 2, + .poolSizeCount = descriptorPoolSizesCount, .pPoolSizes = descriptorPoolSizes }; VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); @@ -338,7 +352,7 @@ namespace collada::scene { // uniform buffer descriptor set layout/allocation (set 1, constant) // { - constexpr int bindingCount = 1; + constexpr int bindingCount = 3; VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{ { .binding = 0, @@ -346,6 +360,18 @@ namespace collada::scene { .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT }, + { + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + }, + { + .binding = 2, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + } }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{ @@ -371,7 +397,7 @@ namespace collada::scene { void vulkan::write_descriptor_sets(collada::types::descriptor const * const descriptor) { - VkWriteDescriptorSet writeDescriptorSets[uniformBufferDescriptorCount]; + VkWriteDescriptorSet writeDescriptorSets[descriptorCount]; uint32_t writeIndex = 0; VkDescriptorBufferInfo sceneDescriptorBufferInfos[maxFrames]; @@ -419,8 +445,31 @@ namespace collada::scene { .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &materialColorsDescriptorBufferInfo }; + VkDescriptorImageInfo samplerDescriptorImageInfo = { + .sampler = linearSampler, + }; + writeDescriptorSets[writeIndex++] = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSet1, + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .pImageInfo = &samplerDescriptorImageInfo + }; + VkDescriptorImageInfo sampledImageDescriptorImageInfo = { + .imageView = shadowDepthImageView, + .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL + }; + writeDescriptorSets[writeIndex++] = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSet1, + .dstBinding = 2, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .pImageInfo = &sampledImageDescriptorImageInfo + }; - assert(writeIndex == uniformBufferDescriptorCount); + assert(writeIndex == descriptorCount); vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr); } @@ -622,6 +671,13 @@ namespace collada::scene { .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .lineWidth = 1.0f }; + VkPipelineRasterizationStateCreateInfo shadowRasterizationState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + //.cullMode = VK_CULL_MODE_BACK_BIT, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, + .lineWidth = 1.0f + }; VkPipelineMultisampleStateCreateInfo multisampleState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT @@ -647,7 +703,7 @@ namespace collada::scene { .pVertexInputState = &vertexInputStates[i], .pInputAssemblyState = &inputAssemblyState, .pViewportState = &viewportState, - .pRasterizationState = &rasterizationState, + .pRasterizationState = &shadowRasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &depthStencilState, .pColorBlendState = &colorBlendState, @@ -699,11 +755,14 @@ namespace collada::scene { for (int j = 0; j < instance_materials_count; j++) { types::instance_material const& instance_material = instance_materials[j]; + int materialIndex = instance_material.material_index; + if (materialIndex == excludeMaterialIndex) { + continue; + } types::triangles const& triangles = mesh.triangles[instance_material.element_index]; VkShaderStageFlags stageFlags{ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT }; - uint32_t materialIndex = instance_material.material_index; - uint32_t offset{ (offsetof (PushConstant, materialIndex)) }; + constexpr uint32_t offset{ (offsetof (PushConstant, materialIndex)) }; vkCmdPushConstants(commandBuffer, pipelineLayout, stageFlags, offset, (sizeof (uint32_t)), &materialIndex); VkDeviceSize vertexOffset{ (VkDeviceSize)mesh.vertex_buffer_offset }; @@ -778,7 +837,7 @@ namespace collada::scene { instance_types::node const & node_instance) { VkShaderStageFlags stageFlags{ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT }; - uint32_t offset{ (offsetof (PushConstant, nodeIndex)) }; + constexpr uint32_t offset{ (offsetof (PushConstant, nodeIndex)) }; vkCmdPushConstants(commandBuffer, pipelineLayout, stageFlags, offset, (sizeof (uint32_t)), &node_index); draw_instance_geometries(node.instance_geometries, node.instance_geometries_count); diff --git a/src/main.cpp b/src/main.cpp index b17189d..a1f8177 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ VkDeviceMemory depthMemory{ VK_NULL_HANDLE }; VkImage shadowDepthImage{ VK_NULL_HANDLE }; VkImageView shadowDepthImageView{ VK_NULL_HANDLE }; +VkImageView shadowDepthImageViewDepth{ VK_NULL_HANDLE }; VkDeviceMemory shadowDepthMemory{ VK_NULL_HANDLE }; VkBuffer vertexIndexBuffer{ VK_NULL_HANDLE }; @@ -505,6 +506,20 @@ int main() &shadowDepthMemory, &shadowDepthImageView); + + VkImageViewCreateInfo imageViewCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = shadowDepthImage, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = depthFormat, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .levelCount = 1, + .layerCount = 1 + } + }; + VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, &shadowDepthImageViewDepth)); + ////////////////////////////////////////////////////////////////////// // mesh ////////////////////////////////////////////////////////////////////// @@ -1221,7 +1236,9 @@ int main() physicalDeviceProperties, physicalDeviceMemoryProperties, surfaceFormat.format, - depthFormat); + depthFormat, + textureSamplers[0], + shadowDepthImageViewDepth); collada::types::descriptor const * collada_scene_descriptor = &shadow_test::descriptor; collada_state.load_scene(collada_scene_descriptor); @@ -1242,6 +1259,7 @@ int main() int cameraTargetIndex = collada_state.find_node_index_by_name("Camera001.Target"); int lightIndex = collada_state.find_node_index_by_name("DirectLight"); int lightTargetIndex = collada_state.find_node_index_by_name("DirectLight.Target"); + int lightMaterialIndex = collada_state.find_material_index_by_name("LightMaterial"); while (quit == false) { SDL_Event event; @@ -1278,7 +1296,7 @@ int main() ////////////////////////////////////////////////////////////////////// double time = getTime(start_time); - collada_state.update(time / 5.0f); + collada_state.update(time / 8.0f); ////////////////////////////////////////////////////////////////////// // fence @@ -1370,7 +1388,7 @@ int main() }; vkCmdSetScissor(commandBuffer, 0, 1, &shadowScissor); - // draw + // transfer { collada_state.vulkan.change_frame(commandBuffer, frameIndex); @@ -1378,7 +1396,7 @@ int main() XMMATRIX projection = currentProjection(); XMMATRIX view = currentView(collada_state.node_state.node_instances[cameraIndex], collada_state.node_state.node_instances[cameraTargetIndex]); - XMMATRIX shadowProjection = XMMatrixOrthographicLH(300, 300, 0.1, 1000); + XMMATRIX shadowProjection = XMMatrixOrthographicLH(300, 300, 0.1, 500); XMMATRIX shadowView = currentView(collada_state.node_state.node_instances[lightIndex], collada_state.node_state.node_instances[lightTargetIndex]); @@ -1394,6 +1412,9 @@ int main() collada_state.node_state.node_instances); } + // draw + + collada_state.vulkan.excludeMaterialIndex = lightMaterialIndex; collada_state.vulkan.pipelineIndex = 0; // shadow pipeline collada_state.draw(); @@ -1514,6 +1535,7 @@ int main() // draw + collada_state.vulkan.excludeMaterialIndex = -1; collada_state.vulkan.pipelineIndex = 1; // non-shadow pipeline collada_state.draw(); @@ -1627,6 +1649,7 @@ int main() vkDestroyImage(device, shadowDepthImage, nullptr); vkFreeMemory(device, shadowDepthMemory, nullptr); vkDestroyImageView(device, shadowDepthImageView, nullptr); + vkDestroyImageView(device, shadowDepthImageViewDepth, nullptr); vkDestroyBuffer(device, vertexIndexBuffer, nullptr); vkFreeMemory(device, vertexIndexBufferMemory, nullptr);