draw shadows with percentage closer filtering

This commit is contained in:
Zack Buhman 2026-04-15 14:52:43 -05:00
parent 3e3175a085
commit fbe6b157ff
8 changed files with 167 additions and 21 deletions

View File

@ -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,
};

View File

@ -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);

View File

@ -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);

View File

@ -232,6 +232,7 @@ namespace collada::types {
};
struct material {
char const * const name;
types::effect const * const effect;
};

View File

@ -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<MaterialColor> 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")]

View File

@ -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);

View File

@ -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);

View File

@ -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);