feat: cubemap shadows with R32_SFLOAT color attachment — linear distance
Root cause of broken shadows: depth buffer stores non-linear NDC depth, not linear distance. closestDepth * 60.0 was wrong conversion. Fix: switch from depth-only to R32_SFLOAT color attachment approach: - Shadow vertex shader outputs world position to fragment - Shadow fragment shader writes length(fragPos - lightPos) / farPlane - Main fragment shader samples cubemap, multiplies by FAR_PLANE=60 - Separate color cube (R32_SFLOAT, sampled) + depth cube (D32_SFLOAT, depth test) - Shadow pipeline: 1 color attachment (R) + depth attachment - Color clear = 1.0 (max distance), depth clear = 1.0 Tests: 227 total, all pass - ShadowMapFaceDirectionTests: 6 face directions, 90° FOV, up vectors, valid matrices, far plane consistency - ShadowShaderTests: all 6 SPIR-V shaders exist
This commit is contained in:
@@ -1,4 +1,17 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragPos;
|
||||
layout(location = 0) out float outDepth;
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
mat4 model;
|
||||
vec4 lightPos;
|
||||
vec4 lightColor;
|
||||
mat4 lightViewProj;
|
||||
} pc;
|
||||
|
||||
void main() {
|
||||
vec3 toLight = fragPos - pc.lightPos.xyz;
|
||||
float dist = length(toLight);
|
||||
outDepth = dist / 60.0;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -9,6 +9,10 @@ layout(push_constant) uniform PC {
|
||||
mat4 lightViewProj;
|
||||
} pc;
|
||||
|
||||
layout(location = 0) out vec3 fragPos;
|
||||
|
||||
void main() {
|
||||
gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0);
|
||||
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
||||
fragPos = worldPos.xyz;
|
||||
gl_Position = pc.lightViewProj * worldPos;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -21,6 +21,7 @@ layout(push_constant) uniform PC {
|
||||
|
||||
const vec3 AMBIENT = vec3(0.01, 0.01, 0.02);
|
||||
const float PI = 3.14159265359;
|
||||
const float FAR_PLANE = 60.0;
|
||||
|
||||
float distributionGGX(vec3 N, vec3 H, float roughness)
|
||||
{
|
||||
@@ -67,14 +68,12 @@ float calcShadow(vec3 worldPos, vec3 lightPos, vec3 N, vec3 L)
|
||||
{
|
||||
vec3 dir = worldPos - lightPos;
|
||||
float dist = length(dir);
|
||||
vec3 dirNorm = dir / max(dist, 0.001);
|
||||
vec3 dirNorm = normalize(dir);
|
||||
|
||||
// Sample cubemap: direction from light to fragment
|
||||
float closestDepth = texture(shadowCube, dirNorm).r;
|
||||
// closestDepth is in [0,1], map to world distance
|
||||
float mappedDepth = closestDepth * 60.0;
|
||||
float mappedDepth = closestDepth * FAR_PLANE;
|
||||
|
||||
float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
|
||||
float bias = 0.05;
|
||||
return dist - bias < mappedDepth ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
@@ -98,7 +97,6 @@ void main()
|
||||
float attenuation = pow(clamp(1.0 - dist / max(lightRange, 0.001), 0.0, 1.0), 2.0);
|
||||
vec3 radiance = lightColor * lightIntensity * attenuation;
|
||||
|
||||
// Shadow from cubemap
|
||||
float shadow = calcShadow(fragWorldPos, lightPos, N, L);
|
||||
|
||||
vec3 H = normalize(V + L);
|
||||
|
||||
Binary file not shown.
@@ -89,6 +89,7 @@ public enum VkFormat : int
|
||||
R8G8B8A8Srgb = 43,
|
||||
B8G8R8A8Srgb = 50,
|
||||
R32G32Sfloat = 103,
|
||||
R32Sfloat = 100,
|
||||
R32G32B32Sfloat = 106,
|
||||
R32G32B32A32Sfloat = 109,
|
||||
D32Sfloat = 126,
|
||||
|
||||
@@ -43,7 +43,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
_shadowMap = new VulkanShadowMap(ctx.Device, ctx, _pipeline.DescriptorSetLayout);
|
||||
|
||||
for (int i = 0; i < VulkanFrameResources.MaxFramesInFlight; i++)
|
||||
_frameResources.UpdateShadowDescriptor(i, _shadowMap.ShadowSampler, _shadowMap.CubeImageView);
|
||||
_frameResources.UpdateShadowDescriptor(i, _shadowMap.ShadowSampler, _shadowMap.CubeColorView);
|
||||
|
||||
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
||||
}
|
||||
@@ -175,6 +175,11 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
||||
|
||||
// === SHADOW CUBEMAP PASSES (6 faces) ===
|
||||
// Transition color cube: Undefined → ColorAttachmentOptimal
|
||||
TransitionImageLayout(cmd, _shadowMap.ColorImage,
|
||||
VkImageLayout.Undefined, VkImageLayout.ColorAttachmentOptimal,
|
||||
0, 0, 0x400, 0x100, 6);
|
||||
// Transition depth cube: Undefined → DepthStencilAttachmentOptimal
|
||||
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||
VkImageLayout.Undefined, VkImageLayout.DepthStencilAttachmentOptimal,
|
||||
0, 0, 0x100, 0x200, 6);
|
||||
@@ -186,6 +191,21 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
var (faceView, faceProj) = VulkanShadowMap.GetFaceViewProj(lightPosVec, face);
|
||||
var faceViewProj = faceView * faceProj;
|
||||
|
||||
var colorClear = new VkClearValue
|
||||
{
|
||||
Color = new VkClearColorValue { Float0 = 1.0f, Float1 = 0, Float2 = 0, Float3 = 0 },
|
||||
};
|
||||
|
||||
var shadowColorAttachment = new VkRenderingAttachmentInfo
|
||||
{
|
||||
sType = VkStructureType.RenderingAttachmentInfo,
|
||||
imageView = _shadowMap.FaceColorViews[face],
|
||||
imageLayout = VkImageLayout.ColorAttachmentOptimal,
|
||||
loadOp = VkAttachmentLoadOp.Clear,
|
||||
storeOp = VkAttachmentStoreOp.Store,
|
||||
clearValue = colorClear,
|
||||
};
|
||||
|
||||
var shadowDepthClear = new VkClearValue
|
||||
{
|
||||
DepthStencil = new VkClearDepthStencilValue { Depth = 1.0f, Stencil = 0 },
|
||||
@@ -194,7 +214,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
var shadowDepthAttachment = new VkRenderingAttachmentInfo
|
||||
{
|
||||
sType = VkStructureType.RenderingAttachmentInfo,
|
||||
imageView = _shadowMap.FaceImageViews[face],
|
||||
imageView = _shadowMap.FaceDepthViews[face],
|
||||
imageLayout = VkImageLayout.DepthStencilAttachmentOptimal,
|
||||
loadOp = VkAttachmentLoadOp.Clear,
|
||||
storeOp = VkAttachmentStoreOp.Store,
|
||||
@@ -210,8 +230,8 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
||||
},
|
||||
layerCount = 1,
|
||||
colorAttachmentCount = 0,
|
||||
pColorAttachments = null,
|
||||
colorAttachmentCount = 1,
|
||||
pColorAttachments = &shadowColorAttachment,
|
||||
pDepthAttachment = &shadowDepthAttachment,
|
||||
};
|
||||
|
||||
@@ -264,10 +284,10 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
Vk.vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
// Transition shadow cubemap: depth attachment → shader read
|
||||
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||
VkImageLayout.DepthStencilAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
||||
0x100, 0x200, 0x8, 0x20, 6);
|
||||
// Transition shadow color cube: ColorAttachmentOptimal → ShaderReadOnlyOptimal
|
||||
TransitionImageLayout(cmd, _shadowMap.ColorImage,
|
||||
VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
||||
0x400, 0x100, 0x8, 0x20, 6);
|
||||
|
||||
// === MAIN PASS ===
|
||||
TransitionImageLayout(cmd, _swapchain.Images[imageIndex],
|
||||
@@ -445,7 +465,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
private static void TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
|
||||
VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
ulong srcStage, ulong srcAccess,
|
||||
ulong dstStage, ulong dstAccess)
|
||||
ulong dstStage, ulong dstAccess, uint layerCount = 1)
|
||||
{
|
||||
var barrier = new VkImageMemoryBarrier2
|
||||
{
|
||||
@@ -461,7 +481,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
||||
{
|
||||
AspectMask = VkImageAspectFlags.Color,
|
||||
LevelCount = 1,
|
||||
LayerCount = 1,
|
||||
LayerCount = layerCount,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,11 +6,17 @@ namespace Engine.Graphics.Vulkan;
|
||||
internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
{
|
||||
public const uint ShadowMapSize = 1024;
|
||||
public const float FarPlane = 60.0f;
|
||||
|
||||
public VkImage ColorImage;
|
||||
public VkDeviceMemory ColorImageMemory;
|
||||
public VkImageView CubeColorView;
|
||||
public VkImageView[] FaceColorViews = new VkImageView[6];
|
||||
|
||||
public VkImage DepthImage;
|
||||
public VkDeviceMemory DepthImageMemory;
|
||||
public VkImageView CubeImageView;
|
||||
public VkImageView[] FaceImageViews = new VkImageView[6];
|
||||
public VkImageView[] FaceDepthViews = new VkImageView[6];
|
||||
|
||||
public VkSampler ShadowSampler;
|
||||
public VkPipeline Pipeline;
|
||||
public VkPipelineLayout PipelineLayout;
|
||||
@@ -25,16 +31,78 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
_device = device;
|
||||
_ctx = ctx;
|
||||
|
||||
CreateShadowCubeImage();
|
||||
CreateShadowImages();
|
||||
CreateShadowSampler();
|
||||
CreateShadowPipeline();
|
||||
|
||||
Console.WriteLine("[Vulkan] Shadow cubemap created (1024x1024x6 D32_SFLOAT)");
|
||||
Console.WriteLine("[Vulkan] Shadow cubemap created (1024x1024x6, R32_SFLOAT + D32_SFLOAT)");
|
||||
}
|
||||
|
||||
private void CreateShadowCubeImage()
|
||||
private void CreateShadowImages()
|
||||
{
|
||||
var imageInfo = new VkImageCreateInfo
|
||||
// Color cube (R32_SFLOAT) — stores linear distance
|
||||
var colorInfo = new VkImageCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageCreateInfo,
|
||||
flags = (uint)VkImageCreateFlags.CubeCompatible,
|
||||
imageType = VkImageType.Type2D,
|
||||
format = VkFormat.R32Sfloat,
|
||||
extent = new VkExtent3D { Width = ShadowMapSize, Height = ShadowMapSize, Depth = 1 },
|
||||
mipLevels = 1,
|
||||
arrayLayers = 6,
|
||||
samples = VkSampleCountFlags.Count1,
|
||||
tiling = VkImageTiling.Optimal,
|
||||
usage = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.Sampled,
|
||||
sharingMode = VkSharingMode.Exclusive,
|
||||
initialLayout = VkImageLayout.Undefined,
|
||||
};
|
||||
|
||||
var colorImg = VkImage.Null;
|
||||
Vk.vkCreateImage(_device, &colorInfo, 0, &colorImg);
|
||||
ColorImage = colorImg;
|
||||
|
||||
var colorReqs = new VkMemoryRequirements();
|
||||
Vk.vkGetImageMemoryRequirements(_device, ColorImage, &colorReqs);
|
||||
var colorMemType = _ctx.FindMemoryType(colorReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal);
|
||||
var colorAlloc = new VkMemoryAllocateInfo { sType = VkStructureType.MemoryAllocateInfo, allocationSize = colorReqs.size, memoryTypeIndex = colorMemType };
|
||||
var colorMem = VkDeviceMemory.Null;
|
||||
Vk.vkAllocateMemory(_device, &colorAlloc, 0, &colorMem);
|
||||
ColorImageMemory = colorMem;
|
||||
Vk.vkBindImageMemory(_device, ColorImage, ColorImageMemory, 0);
|
||||
|
||||
// Cube color view for sampling
|
||||
var cubeColorViewInfo = new VkImageViewCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageViewCreateInfo,
|
||||
image = ColorImage,
|
||||
viewType = VkImageViewType.TypeCube,
|
||||
format = VkFormat.R32Sfloat,
|
||||
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
||||
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Color, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = 0, LayerCount = 6 },
|
||||
};
|
||||
var cubeCV = VkImageView.Null;
|
||||
Vk.vkCreateImageView(_device, &cubeColorViewInfo, 0, &cubeCV);
|
||||
CubeColorView = cubeCV;
|
||||
|
||||
// 6 face color views
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
var faceViewInfo = new VkImageViewCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageViewCreateInfo,
|
||||
image = ColorImage,
|
||||
viewType = VkImageViewType.Type2D,
|
||||
format = VkFormat.R32Sfloat,
|
||||
components = new VkComponentMapping { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity },
|
||||
subresourceRange = new VkImageSubresourceRange { AspectMask = VkImageAspectFlags.Color, BaseMipLevel = 0, LevelCount = 1, BaseArrayLayer = (uint)i, LayerCount = 1 },
|
||||
};
|
||||
var fv = VkImageView.Null;
|
||||
Vk.vkCreateImageView(_device, &faceViewInfo, 0, &fv);
|
||||
FaceColorViews[i] = fv;
|
||||
}
|
||||
|
||||
// Depth cube (D32_SFLOAT) — for depth testing during shadow render
|
||||
var depthInfo = new VkImageCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageCreateInfo,
|
||||
flags = (uint)VkImageCreateFlags.CubeCompatible,
|
||||
@@ -45,50 +113,28 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
arrayLayers = 6,
|
||||
samples = VkSampleCountFlags.Count1,
|
||||
tiling = VkImageTiling.Optimal,
|
||||
usage = VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled,
|
||||
usage = VkImageUsageFlags.DepthStencilAttachment,
|
||||
sharingMode = VkSharingMode.Exclusive,
|
||||
initialLayout = VkImageLayout.Undefined,
|
||||
};
|
||||
|
||||
var img = VkImage.Null;
|
||||
Vk.vkCreateImage(_device, &imageInfo, 0, &img);
|
||||
DepthImage = img;
|
||||
var depthImg = VkImage.Null;
|
||||
Vk.vkCreateImage(_device, &depthInfo, 0, &depthImg);
|
||||
DepthImage = depthImg;
|
||||
|
||||
var reqs = new VkMemoryRequirements();
|
||||
Vk.vkGetImageMemoryRequirements(_device, DepthImage, &reqs);
|
||||
var memTypeIndex = _ctx.FindMemoryType(reqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal);
|
||||
|
||||
var allocInfo = new VkMemoryAllocateInfo
|
||||
{
|
||||
sType = VkStructureType.MemoryAllocateInfo,
|
||||
allocationSize = reqs.size,
|
||||
memoryTypeIndex = memTypeIndex,
|
||||
};
|
||||
|
||||
var mem = VkDeviceMemory.Null;
|
||||
Vk.vkAllocateMemory(_device, &allocInfo, 0, &mem);
|
||||
DepthImageMemory = mem;
|
||||
var depthReqs = new VkMemoryRequirements();
|
||||
Vk.vkGetImageMemoryRequirements(_device, DepthImage, &depthReqs);
|
||||
var depthMemType = _ctx.FindMemoryType(depthReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal);
|
||||
var depthAlloc = new VkMemoryAllocateInfo { sType = VkStructureType.MemoryAllocateInfo, allocationSize = depthReqs.size, memoryTypeIndex = depthMemType };
|
||||
var depthMem = VkDeviceMemory.Null;
|
||||
Vk.vkAllocateMemory(_device, &depthAlloc, 0, &depthMem);
|
||||
DepthImageMemory = depthMem;
|
||||
Vk.vkBindImageMemory(_device, DepthImage, DepthImageMemory, 0);
|
||||
|
||||
// Cube view for sampling
|
||||
var cubeViewInfo = new VkImageViewCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageViewCreateInfo,
|
||||
image = DepthImage,
|
||||
viewType = VkImageViewType.TypeCube,
|
||||
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 = 0, LayerCount = 6 },
|
||||
};
|
||||
|
||||
var cubeView = VkImageView.Null;
|
||||
Vk.vkCreateImageView(_device, &cubeViewInfo, 0, &cubeView);
|
||||
CubeImageView = cubeView;
|
||||
|
||||
// 6 face views for rendering
|
||||
// 6 face depth views
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
var faceViewInfo = new VkImageViewCreateInfo
|
||||
var faceDepthViewInfo = new VkImageViewCreateInfo
|
||||
{
|
||||
sType = VkStructureType.ImageViewCreateInfo,
|
||||
image = DepthImage,
|
||||
@@ -97,10 +143,9 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
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;
|
||||
var fdv = VkImageView.Null;
|
||||
Vk.vkCreateImageView(_device, &faceDepthViewInfo, 0, &fdv);
|
||||
FaceDepthViews[i] = fdv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,12 +278,18 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
stencilTestEnable = VkBool32.False,
|
||||
};
|
||||
|
||||
var blendAttachment = new VkPipelineColorBlendAttachmentState
|
||||
{
|
||||
blendEnable = VkBool32.False,
|
||||
colorWriteMask = VkColorComponentFlags.R,
|
||||
};
|
||||
|
||||
var colorBlendState = new VkPipelineColorBlendStateCreateInfo
|
||||
{
|
||||
sType = VkStructureType.PipelineColorBlendStateCreateInfo,
|
||||
logicOpEnable = VkBool32.False,
|
||||
attachmentCount = 0,
|
||||
pAttachments = null,
|
||||
attachmentCount = 1,
|
||||
pAttachments = &blendAttachment,
|
||||
};
|
||||
|
||||
var dynamicStates = stackalloc VkDynamicState[3];
|
||||
@@ -267,12 +318,13 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
Vk.vkCreatePipelineLayout(_device, &layoutInfo, 0, &pl);
|
||||
PipelineLayout = pl;
|
||||
|
||||
var colorFormat = VkFormat.R32Sfloat;
|
||||
var depthFormat = VkFormat.D32Sfloat;
|
||||
var renderingInfo = new VkPipelineRenderingCreateInfo
|
||||
{
|
||||
sType = VkStructureType.PipelineRenderingCreateInfo,
|
||||
colorAttachmentCount = 0,
|
||||
pColorAttachmentFormats = null,
|
||||
colorAttachmentCount = 1,
|
||||
pColorAttachmentFormats = &colorFormat,
|
||||
depthAttachmentFormat = depthFormat,
|
||||
};
|
||||
|
||||
@@ -344,9 +396,14 @@ internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||
if (PipelineLayout.Handle != 0) Vk.vkDestroyPipelineLayout(_device, PipelineLayout, 0);
|
||||
if (VertModule.Handle != 0) Vk.vkDestroyShaderModule(_device, VertModule, 0);
|
||||
if (ShadowSampler.Handle != 0) Vk.vkDestroySampler(_device, ShadowSampler, 0);
|
||||
if (CubeImageView.Handle != 0) Vk.vkDestroyImageView(_device, CubeImageView, 0);
|
||||
if (CubeColorView.Handle != 0) Vk.vkDestroyImageView(_device, CubeColorView, 0);
|
||||
for (int i = 0; i < 6; i++)
|
||||
if (FaceImageViews[i].Handle != 0) Vk.vkDestroyImageView(_device, FaceImageViews[i], 0);
|
||||
{
|
||||
if (FaceColorViews[i].Handle != 0) Vk.vkDestroyImageView(_device, FaceColorViews[i], 0);
|
||||
if (FaceDepthViews[i].Handle != 0) Vk.vkDestroyImageView(_device, FaceDepthViews[i], 0);
|
||||
}
|
||||
if (ColorImage.Handle != 0) Vk.vkDestroyImage(_device, ColorImage, 0);
|
||||
if (ColorImageMemory.Handle != 0) Vk.vkFreeMemory(_device, ColorImageMemory, 0);
|
||||
if (DepthImage.Handle != 0) Vk.vkDestroyImage(_device, DepthImage, 0);
|
||||
if (DepthImageMemory.Handle != 0) Vk.vkFreeMemory(_device, DepthImageMemory, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user