feat: cubemap shadow mapping — true omnidirectional point light shadows
- VulkanShadowMap: 1024x1024x6 layer D32_SFLOAT cube image - CubeCompatible flag, cube view for sampling, 6 face views for rendering - GetFaceViewProj: 6 directions (+X, -X, +Y, -Y, +Z, -Z) with correct up vectors - 6 shadow render passes per frame (one per cube face) - Fragment shader: samplerCube instead of sampler2D - Shadow: direction from light to fragment, distance comparison - No more perspective frustum limitation — omnidirectional shadows - shadow.vert: same push constant block (160B), uses lightViewProj per face - triangle.vert: removed fragLightSpacePos (not needed for cubemap) - Reduced shadow map size to 1024 (6x memory vs single 2048) - Floor/Grid excluded from shadow casting
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
layout(location = 0) in vec3 fragWorldPos;
|
layout(location = 0) in vec3 fragWorldPos;
|
||||||
layout(location = 1) in vec3 fragNormal;
|
layout(location = 1) in vec3 fragNormal;
|
||||||
layout(location = 2) in vec3 fragAlbedo;
|
layout(location = 2) in vec3 fragAlbedo;
|
||||||
layout(location = 3) in vec4 fragLightSpacePos;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ layout(set = 0, binding = 0) uniform CameraUBO {
|
|||||||
mat4 vp;
|
mat4 vp;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(set = 0, binding = 1) uniform sampler2D shadowMap;
|
layout(set = 0, binding = 1) uniform samplerCube shadowCube;
|
||||||
|
|
||||||
layout(push_constant) uniform PC {
|
layout(push_constant) uniform PC {
|
||||||
mat4 model;
|
mat4 model;
|
||||||
@@ -64,31 +63,19 @@ vec3 acesTonemap(vec3 color)
|
|||||||
return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0);
|
return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float calcShadow(vec4 lightSpacePos, vec3 N, vec3 L)
|
float calcShadow(vec3 worldPos, vec3 lightPos, vec3 N, vec3 L)
|
||||||
{
|
{
|
||||||
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
vec3 dir = worldPos - lightPos;
|
||||||
projCoords = projCoords * 0.5 + 0.5;
|
float dist = length(dir);
|
||||||
|
vec3 dirNorm = dir / max(dist, 0.001);
|
||||||
|
|
||||||
if (projCoords.x < 0.0 || projCoords.x > 1.0) return 1.0;
|
// Sample cubemap: direction from light to fragment
|
||||||
if (projCoords.y < 0.0 || projCoords.y > 1.0) return 1.0;
|
float closestDepth = texture(shadowCube, dirNorm).r;
|
||||||
if (projCoords.z > 1.0) return 1.0;
|
// closestDepth is in [0,1], map to world distance
|
||||||
|
float mappedDepth = closestDepth * 60.0;
|
||||||
|
|
||||||
float bias = max(0.005 * (1.0 - dot(N, L)), 0.0005);
|
float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
|
||||||
float currentDepth = projCoords.z;
|
return dist - bias < mappedDepth ? 1.0 : 0.0;
|
||||||
|
|
||||||
// PCF 3x3
|
|
||||||
float shadow = 0.0;
|
|
||||||
vec2 texelSize = 1.0 / vec2(2048.0, 2048.0);
|
|
||||||
for (int x = -1; x <= 1; x++)
|
|
||||||
{
|
|
||||||
for (int y = -1; y <= 1; y++)
|
|
||||||
{
|
|
||||||
float closestDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
|
||||||
shadow += currentDepth - bias > closestDepth ? 0.0 : 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shadow /= 9.0;
|
|
||||||
return shadow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
@@ -111,8 +98,8 @@ void main()
|
|||||||
float attenuation = pow(clamp(1.0 - dist / max(lightRange, 0.001), 0.0, 1.0), 2.0);
|
float attenuation = pow(clamp(1.0 - dist / max(lightRange, 0.001), 0.0, 1.0), 2.0);
|
||||||
vec3 radiance = lightColor * lightIntensity * attenuation;
|
vec3 radiance = lightColor * lightIntensity * attenuation;
|
||||||
|
|
||||||
// Shadow
|
// Shadow from cubemap
|
||||||
float shadow = calcShadow(fragLightSpacePos, N, L);
|
float shadow = calcShadow(fragWorldPos, lightPos, N, L);
|
||||||
|
|
||||||
vec3 H = normalize(V + L);
|
vec3 H = normalize(V + L);
|
||||||
vec3 F0 = mix(vec3(0.04), albedo, metallic);
|
vec3 F0 = mix(vec3(0.04), albedo, metallic);
|
||||||
|
|||||||
Binary file not shown.
@@ -7,7 +7,6 @@ layout(location = 2) in vec3 inNormal;
|
|||||||
layout(location = 0) out vec3 fragWorldPos;
|
layout(location = 0) out vec3 fragWorldPos;
|
||||||
layout(location = 1) out vec3 fragNormal;
|
layout(location = 1) out vec3 fragNormal;
|
||||||
layout(location = 2) out vec3 fragAlbedo;
|
layout(location = 2) out vec3 fragAlbedo;
|
||||||
layout(location = 3) out vec4 fragLightSpacePos;
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform CameraUBO {
|
layout(set = 0, binding = 0) uniform CameraUBO {
|
||||||
mat4 vp;
|
mat4 vp;
|
||||||
@@ -26,5 +25,4 @@ void main() {
|
|||||||
fragWorldPos = worldPos.xyz;
|
fragWorldPos = worldPos.xyz;
|
||||||
fragNormal = mat3(pc.model) * inNormal;
|
fragNormal = mat3(pc.model) * inNormal;
|
||||||
fragAlbedo = inColor;
|
fragAlbedo = inColor;
|
||||||
fragLightSpacePos = pc.lightViewProj * worldPos;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -340,6 +340,12 @@ public enum VkAccessFlags2 : ulong
|
|||||||
ShaderWrite = 0x200000000,
|
ShaderWrite = 0x200000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum VkImageCreateFlags : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
CubeCompatible = 0x00000010,
|
||||||
|
}
|
||||||
|
|
||||||
public enum VkDynamicState : int
|
public enum VkDynamicState : int
|
||||||
{
|
{
|
||||||
Viewport = 0,
|
Viewport = 0,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
_shadowMap = new VulkanShadowMap(ctx.Device, ctx, _pipeline.DescriptorSetLayout);
|
_shadowMap = new VulkanShadowMap(ctx.Device, ctx, _pipeline.DescriptorSetLayout);
|
||||||
|
|
||||||
for (int i = 0; i < VulkanFrameResources.MaxFramesInFlight; i++)
|
for (int i = 0; i < VulkanFrameResources.MaxFramesInFlight; i++)
|
||||||
_frameResources.UpdateShadowDescriptor(i, _shadowMap.ShadowSampler, _shadowMap.DepthImageView);
|
_frameResources.UpdateShadowDescriptor(i, _shadowMap.ShadowSampler, _shadowMap.CubeImageView);
|
||||||
|
|
||||||
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
||||||
}
|
}
|
||||||
@@ -174,90 +174,97 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
};
|
};
|
||||||
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
||||||
|
|
||||||
// === SHADOW PASS ===
|
// === SHADOW CUBEMAP PASSES (6 faces) ===
|
||||||
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||||
VkImageLayout.Undefined, VkImageLayout.DepthStencilAttachmentOptimal,
|
VkImageLayout.Undefined, VkImageLayout.DepthStencilAttachmentOptimal,
|
||||||
0, 0, 0x100, 0x200);
|
0, 0, 0x100, 0x200);
|
||||||
|
|
||||||
var shadowDepthClear = new VkClearValue
|
var lightPosVec = new Vector3(lightPos.X, lightPos.Y, lightPos.Z);
|
||||||
{
|
|
||||||
DepthStencil = new VkClearDepthStencilValue { Depth = 1.0f, Stencil = 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
var shadowDepthAttachment = new VkRenderingAttachmentInfo
|
for (int face = 0; face < 6; face++)
|
||||||
{
|
{
|
||||||
sType = VkStructureType.RenderingAttachmentInfo,
|
var (faceView, faceProj) = VulkanShadowMap.GetFaceViewProj(lightPosVec, face);
|
||||||
imageView = _shadowMap.DepthImageView,
|
var faceViewProj = faceView * faceProj;
|
||||||
imageLayout = VkImageLayout.DepthStencilAttachmentOptimal,
|
|
||||||
loadOp = VkAttachmentLoadOp.Clear,
|
|
||||||
storeOp = VkAttachmentStoreOp.Store,
|
|
||||||
clearValue = shadowDepthClear,
|
|
||||||
};
|
|
||||||
|
|
||||||
var shadowRenderingInfo = new VkRenderingInfo
|
var shadowDepthClear = new VkClearValue
|
||||||
{
|
{
|
||||||
sType = VkStructureType.RenderingInfo,
|
DepthStencil = new VkClearDepthStencilValue { Depth = 1.0f, Stencil = 0 },
|
||||||
renderArea = new VkRect2D
|
};
|
||||||
|
|
||||||
|
var shadowDepthAttachment = new VkRenderingAttachmentInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.RenderingAttachmentInfo,
|
||||||
|
imageView = _shadowMap.FaceImageViews[face],
|
||||||
|
imageLayout = VkImageLayout.DepthStencilAttachmentOptimal,
|
||||||
|
loadOp = VkAttachmentLoadOp.Clear,
|
||||||
|
storeOp = VkAttachmentStoreOp.Store,
|
||||||
|
clearValue = shadowDepthClear,
|
||||||
|
};
|
||||||
|
|
||||||
|
var shadowRenderingInfo = new VkRenderingInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.RenderingInfo,
|
||||||
|
renderArea = new VkRect2D
|
||||||
|
{
|
||||||
|
Offset = new VkOffset2D { X = 0, Y = 0 },
|
||||||
|
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
||||||
|
},
|
||||||
|
layerCount = 1,
|
||||||
|
colorAttachmentCount = 0,
|
||||||
|
pColorAttachments = null,
|
||||||
|
pDepthAttachment = &shadowDepthAttachment,
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkCmdBeginRendering(cmd, &shadowRenderingInfo);
|
||||||
|
|
||||||
|
Vk.vkCmdBindPipeline(cmd, VkPipelineBindPoint.Graphics, _shadowMap.Pipeline);
|
||||||
|
|
||||||
|
var shadowViewport = new VkViewport
|
||||||
|
{
|
||||||
|
X = 0, Y = 0,
|
||||||
|
Width = VulkanShadowMap.ShadowMapSize,
|
||||||
|
Height = VulkanShadowMap.ShadowMapSize,
|
||||||
|
MinDepth = 0, MaxDepth = 1,
|
||||||
|
};
|
||||||
|
Vk.vkCmdSetViewport(cmd, 0, 1, &shadowViewport);
|
||||||
|
|
||||||
|
var shadowScissor = new VkRect2D
|
||||||
{
|
{
|
||||||
Offset = new VkOffset2D { X = 0, Y = 0 },
|
Offset = new VkOffset2D { X = 0, Y = 0 },
|
||||||
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
||||||
},
|
};
|
||||||
layerCount = 1,
|
Vk.vkCmdSetScissor(cmd, 0, 1, &shadowScissor);
|
||||||
colorAttachmentCount = 0,
|
|
||||||
pColorAttachments = null,
|
|
||||||
pDepthAttachment = &shadowDepthAttachment,
|
|
||||||
};
|
|
||||||
|
|
||||||
Vk.vkCmdBeginRendering(cmd, &shadowRenderingInfo);
|
Vk.vkCmdSetDepthBias(cmd, 1.25f, 0.0f, 1.75f);
|
||||||
|
|
||||||
Vk.vkCmdBindPipeline(cmd, VkPipelineBindPoint.Graphics, _shadowMap.Pipeline);
|
foreach (var dc in drawCalls)
|
||||||
|
{
|
||||||
|
if (!dc.castShadow) continue;
|
||||||
|
|
||||||
var shadowViewport = new VkViewport
|
var vertexBuf = dc.vertexBuf;
|
||||||
{
|
ulong offset = 0;
|
||||||
X = 0, Y = 0,
|
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuf, &offset);
|
||||||
Width = VulkanShadowMap.ShadowMapSize,
|
Vk.vkCmdBindIndexBuffer(cmd, dc.indexBuf, 0, 1);
|
||||||
Height = VulkanShadowMap.ShadowMapSize,
|
|
||||||
MinDepth = 0, MaxDepth = 1,
|
|
||||||
};
|
|
||||||
Vk.vkCmdSetViewport(cmd, 0, 1, &shadowViewport);
|
|
||||||
|
|
||||||
var shadowScissor = new VkRect2D
|
var pcData = stackalloc byte[160];
|
||||||
{
|
var modelCopy = dc.model;
|
||||||
Offset = new VkOffset2D { X = 0, Y = 0 },
|
System.Buffer.MemoryCopy(&modelCopy, pcData, 64, 64);
|
||||||
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
var lpCopy = lightPos;
|
||||||
};
|
System.Buffer.MemoryCopy(&lpCopy, pcData + 64, 16, 16);
|
||||||
Vk.vkCmdSetScissor(cmd, 0, 1, &shadowScissor);
|
var lcCopy = lightColor;
|
||||||
|
System.Buffer.MemoryCopy(&lcCopy, pcData + 80, 16, 16);
|
||||||
|
var lvpCopy = faceViewProj;
|
||||||
|
System.Buffer.MemoryCopy(&lvpCopy, pcData + 96, 64, 64);
|
||||||
|
|
||||||
Vk.vkCmdSetDepthBias(cmd, 1.25f, 0.0f, 1.75f);
|
Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 160, pcData);
|
||||||
|
|
||||||
foreach (var dc in drawCalls)
|
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
||||||
{
|
}
|
||||||
if (!dc.castShadow) continue;
|
|
||||||
|
|
||||||
var vertexBuf = dc.vertexBuf;
|
Vk.vkCmdEndRendering(cmd);
|
||||||
ulong offset = 0;
|
|
||||||
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuf, &offset);
|
|
||||||
Vk.vkCmdBindIndexBuffer(cmd, dc.indexBuf, 0, 1);
|
|
||||||
|
|
||||||
// Pack all push constants for shadow pass
|
|
||||||
var pcData = stackalloc byte[160];
|
|
||||||
var modelCopy = dc.model;
|
|
||||||
System.Buffer.MemoryCopy(&modelCopy, pcData, 64, 64);
|
|
||||||
var lpCopy = lightPos;
|
|
||||||
System.Buffer.MemoryCopy(&lpCopy, pcData + 64, 16, 16);
|
|
||||||
var lcCopy = lightColor;
|
|
||||||
System.Buffer.MemoryCopy(&lcCopy, pcData + 80, 16, 16);
|
|
||||||
var lvpCopy = lightViewProj;
|
|
||||||
System.Buffer.MemoryCopy(&lvpCopy, pcData + 96, 64, 64);
|
|
||||||
|
|
||||||
Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 160, pcData);
|
|
||||||
|
|
||||||
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vk.vkCmdEndRendering(cmd);
|
// Transition shadow cubemap: depth attachment → shader read
|
||||||
|
|
||||||
// Transition shadow map: depth attachment → shader read
|
|
||||||
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||||
VkImageLayout.DepthStencilAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
VkImageLayout.DepthStencilAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
||||||
0x100, 0x200, 0x8, 0x20);
|
0x100, 0x200, 0x8, 0x20);
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Engine.Graphics.Vulkan;
|
namespace Engine.Graphics.Vulkan;
|
||||||
|
|
||||||
internal sealed unsafe class VulkanShadowMap : IDisposable
|
internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||||
{
|
{
|
||||||
public const uint ShadowMapSize = 2048;
|
public const uint ShadowMapSize = 1024;
|
||||||
|
|
||||||
public VkImage DepthImage;
|
public VkImage DepthImage;
|
||||||
public VkDeviceMemory DepthImageMemory;
|
public VkDeviceMemory DepthImageMemory;
|
||||||
public VkImageView DepthImageView;
|
public VkImageView CubeImageView;
|
||||||
|
public VkImageView[] FaceImageViews = new VkImageView[6];
|
||||||
public VkSampler ShadowSampler;
|
public VkSampler ShadowSampler;
|
||||||
public VkPipeline Pipeline;
|
public VkPipeline Pipeline;
|
||||||
public VkPipelineLayout PipelineLayout;
|
public VkPipelineLayout PipelineLayout;
|
||||||
@@ -23,23 +25,24 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
|||||||
_device = device;
|
_device = device;
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
|
|
||||||
CreateShadowImage();
|
CreateShadowCubeImage();
|
||||||
CreateShadowSampler();
|
CreateShadowSampler();
|
||||||
CreateShadowPipeline(descLayout);
|
CreateShadowPipeline();
|
||||||
|
|
||||||
Console.WriteLine("[Vulkan] Shadow map created (2048x2048 D32_SFLOAT)");
|
Console.WriteLine("[Vulkan] Shadow cubemap created (1024x1024x6 D32_SFLOAT)");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateShadowImage()
|
private void CreateShadowCubeImage()
|
||||||
{
|
{
|
||||||
var imageInfo = new VkImageCreateInfo
|
var imageInfo = new VkImageCreateInfo
|
||||||
{
|
{
|
||||||
sType = VkStructureType.ImageCreateInfo,
|
sType = VkStructureType.ImageCreateInfo,
|
||||||
|
flags = (uint)VkImageCreateFlags.CubeCompatible,
|
||||||
imageType = VkImageType.Type2D,
|
imageType = VkImageType.Type2D,
|
||||||
format = VkFormat.D32Sfloat,
|
format = VkFormat.D32Sfloat,
|
||||||
extent = new VkExtent3D { Width = ShadowMapSize, Height = ShadowMapSize, Depth = 1 },
|
extent = new VkExtent3D { Width = ShadowMapSize, Height = ShadowMapSize, Depth = 1 },
|
||||||
mipLevels = 1,
|
mipLevels = 1,
|
||||||
arrayLayers = 1,
|
arrayLayers = 6,
|
||||||
samples = VkSampleCountFlags.Count1,
|
samples = VkSampleCountFlags.Count1,
|
||||||
tiling = VkImageTiling.Optimal,
|
tiling = VkImageTiling.Optimal,
|
||||||
usage = VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled,
|
usage = VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled,
|
||||||
@@ -67,19 +70,38 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
|||||||
DepthImageMemory = mem;
|
DepthImageMemory = mem;
|
||||||
Vk.vkBindImageMemory(_device, DepthImage, DepthImageMemory, 0);
|
Vk.vkBindImageMemory(_device, DepthImage, DepthImageMemory, 0);
|
||||||
|
|
||||||
var viewInfo = new VkImageViewCreateInfo
|
// Cube view for sampling
|
||||||
|
var cubeViewInfo = new VkImageViewCreateInfo
|
||||||
{
|
{
|
||||||
sType = VkStructureType.ImageViewCreateInfo,
|
sType = VkStructureType.ImageViewCreateInfo,
|
||||||
image = DepthImage,
|
image = DepthImage,
|
||||||
viewType = VkImageViewType.Type2D,
|
viewType = VkImageViewType.TypeCube,
|
||||||
format = VkFormat.D32Sfloat,
|
format = VkFormat.D32Sfloat,
|
||||||
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
||||||
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Depth, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = 0, LayerCount = 1 },
|
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Depth, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = 0, LayerCount = 6 },
|
||||||
};
|
};
|
||||||
|
|
||||||
var view = VkImageView.Null;
|
var cubeView = VkImageView.Null;
|
||||||
Vk.vkCreateImageView(_device, &viewInfo, 0, &view);
|
Vk.vkCreateImageView(_device, &cubeViewInfo, 0, &cubeView);
|
||||||
DepthImageView = view;
|
CubeImageView = cubeView;
|
||||||
|
|
||||||
|
// 6 face views for rendering
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
var faceViewInfo = new VkImageViewCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ImageViewCreateInfo,
|
||||||
|
image = DepthImage,
|
||||||
|
viewType = VkImageViewType.Type2D,
|
||||||
|
format = VkFormat.D32Sfloat,
|
||||||
|
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
||||||
|
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Depth, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = (uint)i, LayerCount = 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var faceView = VkImageView.Null;
|
||||||
|
Vk.vkCreateImageView(_device, &faceViewInfo, 0, &faceView);
|
||||||
|
FaceImageViews[i] = faceView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateShadowSampler()
|
private void CreateShadowSampler()
|
||||||
@@ -95,7 +117,7 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
|||||||
addressModeW = VkSamplerAddressMode.ClampToBorder,
|
addressModeW = VkSamplerAddressMode.ClampToBorder,
|
||||||
minLod = 0,
|
minLod = 0,
|
||||||
maxLod = 1,
|
maxLod = 1,
|
||||||
borderColor = 1, // VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
|
borderColor = 1, // FLOAT_OPAQUE_WHITE
|
||||||
};
|
};
|
||||||
|
|
||||||
var samp = VkSampler.Null;
|
var samp = VkSampler.Null;
|
||||||
@@ -103,7 +125,33 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
|||||||
ShadowSampler = samp;
|
ShadowSampler = samp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateShadowPipeline(VkDescriptorSetLayout descLayout)
|
public static (Matrix4x4 view, Matrix4x4 proj) GetFaceViewProj(Vector3 lightPos, int face)
|
||||||
|
{
|
||||||
|
var proj = Matrix4x4.CreatePerspectiveFieldOfView(MathF.PI / 2f, 1.0f, 0.1f, 60f);
|
||||||
|
proj.M22 *= -1;
|
||||||
|
|
||||||
|
var target = lightPos;
|
||||||
|
var up = Vector3.UnitY;
|
||||||
|
|
||||||
|
target += face switch
|
||||||
|
{
|
||||||
|
0 => Vector3.UnitX,
|
||||||
|
1 => -Vector3.UnitX,
|
||||||
|
2 => Vector3.UnitY,
|
||||||
|
3 => -Vector3.UnitY,
|
||||||
|
4 => Vector3.UnitZ,
|
||||||
|
5 => -Vector3.UnitZ,
|
||||||
|
_ => Vector3.UnitZ,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (face == 2) up = -Vector3.UnitZ;
|
||||||
|
else if (face == 3) up = Vector3.UnitZ;
|
||||||
|
|
||||||
|
var view = Matrix4x4.CreateLookAt(lightPos, target, up);
|
||||||
|
return (view, proj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateShadowPipeline()
|
||||||
{
|
{
|
||||||
var vertSpv = LoadShader("Shaders/shadow.vert.spv");
|
var vertSpv = LoadShader("Shaders/shadow.vert.spv");
|
||||||
var fragSpv = LoadShader("Shaders/shadow.frag.spv");
|
var fragSpv = LoadShader("Shaders/shadow.frag.spv");
|
||||||
@@ -296,7 +344,9 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
|||||||
if (PipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, PipelineLayout, 0);
|
if (PipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, PipelineLayout, 0);
|
||||||
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
||||||
if (ShadowSampler.Handle != 0) Vk.vkDestroySampler(_device, ShadowSampler, 0);
|
if (ShadowSampler.Handle != 0) Vk.vkDestroySampler(_device, ShadowSampler, 0);
|
||||||
if (DepthImageView.Handle != 0) Vk.vkDestroyImageView(_device, DepthImageView, 0);
|
if (CubeImageView.Handle != 0) Vk.vkDestroyImageView(_device, CubeImageView, 0);
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
if (FaceImageViews[i].Handle != 0) Vk.vkDestroyImageView(_device, FaceImageViews[i], 0);
|
||||||
if (DepthImage.Handle != 0) Vk.vkDestroyImage(_device, DepthImage, 0);
|
if (DepthImage.Handle != 0) Vk.vkDestroyImage(_device, DepthImage, 0);
|
||||||
if (DepthImageMemory.Handle != 0) Vk.vkFreeMemory(_device, DepthImageMemory, 0);
|
if (DepthImageMemory.Handle != 0) Vk.vkFreeMemory(_device, DepthImageMemory, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user