feat: shadow mapping — depth-only render pass from light POV + PCF
- VulkanShadowMap.cs: 2048x2048 D32_SFLOAT image, sampler (clamp-to-border), separate shadow pipeline (depth-only, no color, depth bias enabled, CULL_NONE) - Shadow shaders: shadow.vert (lightViewProj * model * pos), shadow.frag (empty) - Main shaders updated: fragLightSpacePos output from vertex, PCF 3x3 in fragment - Push constants expanded to 160B: model(64) + lightPos(16) + lightColor(16) + lightViewProj(64) - 3 push constant ranges: Vertex(model), Fragment(light), Vertex|Fragment(lightViewProj) - Descriptor set: 2 bindings — UBO(vp) + CombinedImageSampler(shadowMap) - Shadow pass: before main pass, renders scene depth from light position - Image transitions: shadow map UNDEFINED→DEPTH→SHADER_READ_ONLY each frame - vkCmdSetDepthBias(1.25, 0, 1.75) for acne prevention - Light VP: CreateLookAt(lightPos, origin, up) * Perspective(60°, 1.0, 0.1, 60) - 0 validation errors on build
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 inPosition;
|
||||||
|
|
||||||
|
layout(push_constant) uniform PC {
|
||||||
|
mat4 model;
|
||||||
|
vec4 lightPos;
|
||||||
|
vec4 lightColor;
|
||||||
|
mat4 lightViewProj;
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = pc.lightViewProj * pc.model * vec4(inPosition, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -3,6 +3,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -10,10 +11,13 @@ layout(set = 0, binding = 0) uniform CameraUBO {
|
|||||||
mat4 vp;
|
mat4 vp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layout(set = 0, binding = 1) uniform sampler2D shadowMap;
|
||||||
|
|
||||||
layout(push_constant) uniform PC {
|
layout(push_constant) uniform PC {
|
||||||
mat4 model;
|
mat4 model;
|
||||||
vec4 lightPos;
|
vec4 lightPos;
|
||||||
vec4 lightColor;
|
vec4 lightColor;
|
||||||
|
mat4 lightViewProj;
|
||||||
} pc;
|
} pc;
|
||||||
|
|
||||||
const vec3 AMBIENT = vec3(0.01, 0.01, 0.02);
|
const vec3 AMBIENT = vec3(0.01, 0.01, 0.02);
|
||||||
@@ -60,6 +64,33 @@ 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)
|
||||||
|
{
|
||||||
|
vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
|
||||||
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if (projCoords.x < 0.0 || projCoords.x > 1.0) return 1.0;
|
||||||
|
if (projCoords.y < 0.0 || projCoords.y > 1.0) return 1.0;
|
||||||
|
if (projCoords.z > 1.0) return 1.0;
|
||||||
|
|
||||||
|
float bias = max(0.005 * (1.0 - dot(N, L)), 0.0005);
|
||||||
|
float currentDepth = projCoords.z;
|
||||||
|
|
||||||
|
// 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()
|
||||||
{
|
{
|
||||||
vec3 N = normalize(fragNormal);
|
vec3 N = normalize(fragNormal);
|
||||||
@@ -68,8 +99,6 @@ void main()
|
|||||||
float roughness = 0.5;
|
float roughness = 0.5;
|
||||||
float metallic = 0.1;
|
float metallic = 0.1;
|
||||||
|
|
||||||
vec3 color = AMBIENT * albedo;
|
|
||||||
|
|
||||||
vec3 lightPos = pc.lightPos.xyz;
|
vec3 lightPos = pc.lightPos.xyz;
|
||||||
float lightIntensity = pc.lightPos.w;
|
float lightIntensity = pc.lightPos.w;
|
||||||
vec3 lightColor = pc.lightColor.xyz;
|
vec3 lightColor = pc.lightColor.xyz;
|
||||||
@@ -82,6 +111,9 @@ 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
|
||||||
|
float shadow = calcShadow(fragLightSpacePos, 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);
|
||||||
|
|
||||||
@@ -97,7 +129,9 @@ void main()
|
|||||||
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic);
|
||||||
|
|
||||||
float NdotL = max(dot(N, L), 0.0);
|
float NdotL = max(dot(N, L), 0.0);
|
||||||
color += (kD * albedo / PI + specular) * radiance * NdotL;
|
vec3 lighting = (kD * albedo / PI + specular) * radiance * NdotL * shadow;
|
||||||
|
|
||||||
|
vec3 color = AMBIENT * albedo + lighting;
|
||||||
|
|
||||||
color = acesTonemap(color);
|
color = acesTonemap(color);
|
||||||
color = pow(color, vec3(1.0 / 2.2));
|
color = pow(color, vec3(1.0 / 2.2));
|
||||||
|
|||||||
Binary file not shown.
@@ -7,6 +7,7 @@ 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;
|
||||||
@@ -16,13 +17,14 @@ layout(push_constant) uniform PC {
|
|||||||
mat4 model;
|
mat4 model;
|
||||||
vec4 lightPos;
|
vec4 lightPos;
|
||||||
vec4 lightColor;
|
vec4 lightColor;
|
||||||
|
mat4 lightViewProj;
|
||||||
} pc;
|
} pc;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
||||||
gl_Position = pc.model * vec4(inPosition, 1.0);
|
|
||||||
gl_Position = vp * worldPos;
|
gl_Position = vp * worldPos;
|
||||||
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.
@@ -64,6 +64,7 @@ internal static unsafe class Vk
|
|||||||
public delegate void VkCmdDraw(VkCommandBuffer commandBuffer, uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance);
|
public delegate void VkCmdDraw(VkCommandBuffer commandBuffer, uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance);
|
||||||
public delegate void VkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance);
|
public delegate void VkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance);
|
||||||
public delegate void VkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, ulong offset, int indexType);
|
public delegate void VkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, ulong offset, int indexType);
|
||||||
|
public delegate void VkCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor);
|
||||||
public delegate void VkCmdBeginRendering(VkCommandBuffer commandBuffer, VkRenderingInfo* pRenderingInfo);
|
public delegate void VkCmdBeginRendering(VkCommandBuffer commandBuffer, VkRenderingInfo* pRenderingInfo);
|
||||||
public delegate void VkCmdEndRendering(VkCommandBuffer commandBuffer);
|
public delegate void VkCmdEndRendering(VkCommandBuffer commandBuffer);
|
||||||
public delegate void VkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, VkDependencyInfo* pDependencyInfo);
|
public delegate void VkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, VkDependencyInfo* pDependencyInfo);
|
||||||
@@ -148,6 +149,7 @@ internal static unsafe class Vk
|
|||||||
public static VkCmdDraw vkCmdDraw;
|
public static VkCmdDraw vkCmdDraw;
|
||||||
public static VkCmdDrawIndexed vkCmdDrawIndexed;
|
public static VkCmdDrawIndexed vkCmdDrawIndexed;
|
||||||
public static VkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
public static VkCmdBindIndexBuffer vkCmdBindIndexBuffer;
|
||||||
|
public static VkCmdSetDepthBias vkCmdSetDepthBias;
|
||||||
public static VkCmdBeginRendering vkCmdBeginRendering;
|
public static VkCmdBeginRendering vkCmdBeginRendering;
|
||||||
public static VkCmdEndRendering vkCmdEndRendering;
|
public static VkCmdEndRendering vkCmdEndRendering;
|
||||||
public static VkCmdPipelineBarrier2 vkCmdPipelineBarrier2;
|
public static VkCmdPipelineBarrier2 vkCmdPipelineBarrier2;
|
||||||
@@ -239,6 +241,7 @@ internal static unsafe class Vk
|
|||||||
vkCmdDraw = LoadDev<VkCmdDraw>(p, "vkCmdDraw");
|
vkCmdDraw = LoadDev<VkCmdDraw>(p, "vkCmdDraw");
|
||||||
vkCmdDrawIndexed = LoadDev<VkCmdDrawIndexed>(p, "vkCmdDrawIndexed");
|
vkCmdDrawIndexed = LoadDev<VkCmdDrawIndexed>(p, "vkCmdDrawIndexed");
|
||||||
vkCmdBindIndexBuffer = LoadDev<VkCmdBindIndexBuffer>(p, "vkCmdBindIndexBuffer");
|
vkCmdBindIndexBuffer = LoadDev<VkCmdBindIndexBuffer>(p, "vkCmdBindIndexBuffer");
|
||||||
|
vkCmdSetDepthBias = LoadDev<VkCmdSetDepthBias>(p, "vkCmdSetDepthBias");
|
||||||
vkCmdBeginRendering = LoadDev<VkCmdBeginRendering>(p, "vkCmdBeginRendering");
|
vkCmdBeginRendering = LoadDev<VkCmdBeginRendering>(p, "vkCmdBeginRendering");
|
||||||
vkCmdEndRendering = LoadDev<VkCmdEndRendering>(p, "vkCmdEndRendering");
|
vkCmdEndRendering = LoadDev<VkCmdEndRendering>(p, "vkCmdEndRendering");
|
||||||
vkCmdPipelineBarrier2 = LoadDev<VkCmdPipelineBarrier2>(p, "vkCmdPipelineBarrier2");
|
vkCmdPipelineBarrier2 = LoadDev<VkCmdPipelineBarrier2>(p, "vkCmdPipelineBarrier2");
|
||||||
|
|||||||
@@ -140,19 +140,25 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
|
|
||||||
private void CreateDescriptorPoolAndSets(VkDescriptorSetLayout layout)
|
private void CreateDescriptorPoolAndSets(VkDescriptorSetLayout layout)
|
||||||
{
|
{
|
||||||
var poolSize = new VkDescriptorPoolSize
|
var poolSizes = stackalloc VkDescriptorPoolSize[2];
|
||||||
|
poolSizes[0] = new VkDescriptorPoolSize
|
||||||
{
|
{
|
||||||
type = VkDescriptorType.UniformBuffer,
|
type = VkDescriptorType.UniformBuffer,
|
||||||
descriptorCount = MaxFramesInFlight,
|
descriptorCount = MaxFramesInFlight,
|
||||||
};
|
};
|
||||||
|
poolSizes[1] = new VkDescriptorPoolSize
|
||||||
|
{
|
||||||
|
type = VkDescriptorType.CombinedImageSampler,
|
||||||
|
descriptorCount = MaxFramesInFlight,
|
||||||
|
};
|
||||||
|
|
||||||
var poolInfo = new VkDescriptorPoolCreateInfo
|
var poolInfo = new VkDescriptorPoolCreateInfo
|
||||||
{
|
{
|
||||||
sType = VkStructureType.DescriptorPoolCreateInfo,
|
sType = VkStructureType.DescriptorPoolCreateInfo,
|
||||||
flags = VkDescriptorPoolCreateFlags.FreeDescriptorSet,
|
flags = VkDescriptorPoolCreateFlags.FreeDescriptorSet,
|
||||||
maxSets = MaxFramesInFlight,
|
maxSets = MaxFramesInFlight,
|
||||||
poolSizeCount = 1,
|
poolSizeCount = 2,
|
||||||
pPoolSizes = &poolSize,
|
pPoolSizes = poolSizes,
|
||||||
};
|
};
|
||||||
|
|
||||||
var descPool = VkDescriptorPool.Null;
|
var descPool = VkDescriptorPool.Null;
|
||||||
@@ -204,6 +210,29 @@ internal sealed unsafe class VulkanFrameResources : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateShadowDescriptor(int frameIndex, VkSampler sampler, VkImageView shadowView)
|
||||||
|
{
|
||||||
|
var imageInfo = new VkDescriptorImageInfo
|
||||||
|
{
|
||||||
|
sampler = sampler,
|
||||||
|
imageView = shadowView,
|
||||||
|
imageLayout = VkImageLayout.ShaderReadOnlyOptimal,
|
||||||
|
};
|
||||||
|
|
||||||
|
var write = new VkWriteDescriptorSet
|
||||||
|
{
|
||||||
|
sType = VkStructureType.WriteDescriptorSet,
|
||||||
|
dstSet = DescriptorSets[frameIndex],
|
||||||
|
dstBinding = 1,
|
||||||
|
dstArrayElement = 0,
|
||||||
|
descriptorCount = 1,
|
||||||
|
descriptorType = VkDescriptorType.CombinedImageSampler,
|
||||||
|
pImageInfo = (nint)(&imageInfo),
|
||||||
|
};
|
||||||
|
|
||||||
|
Vk.vkUpdateDescriptorSets(_device, 1, &write, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateUbo(int frameIndex, void* data, ulong size)
|
public void UpdateUbo(int frameIndex, void* data, ulong size)
|
||||||
{
|
{
|
||||||
void* pData = null;
|
void* pData = null;
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
pDynamicStates = dynamicStates,
|
pDynamicStates = dynamicStates,
|
||||||
};
|
};
|
||||||
|
|
||||||
var pushConstantRanges = stackalloc VkPushConstantRange[2];
|
var pushConstantRanges = stackalloc VkPushConstantRange[3];
|
||||||
pushConstantRanges[0] = new VkPushConstantRange
|
pushConstantRanges[0] = new VkPushConstantRange
|
||||||
{
|
{
|
||||||
stageFlags = VkShaderStageFlags.Vertex,
|
stageFlags = VkShaderStageFlags.Vertex,
|
||||||
@@ -163,6 +163,12 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
offset = 64,
|
offset = 64,
|
||||||
size = 32,
|
size = 32,
|
||||||
};
|
};
|
||||||
|
pushConstantRanges[2] = new VkPushConstantRange
|
||||||
|
{
|
||||||
|
stageFlags = VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment,
|
||||||
|
offset = 96,
|
||||||
|
size = 64,
|
||||||
|
};
|
||||||
|
|
||||||
var descLayout = DescriptorSetLayout;
|
var descLayout = DescriptorSetLayout;
|
||||||
var layoutInfo = new VkPipelineLayoutCreateInfo
|
var layoutInfo = new VkPipelineLayoutCreateInfo
|
||||||
@@ -170,7 +176,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
sType = VkStructureType.PipelineLayoutCreateInfo,
|
sType = VkStructureType.PipelineLayoutCreateInfo,
|
||||||
setLayoutCount = 1,
|
setLayoutCount = 1,
|
||||||
pSetLayouts = &descLayout,
|
pSetLayouts = &descLayout,
|
||||||
pushConstantRangeCount = 2,
|
pushConstantRangeCount = 3,
|
||||||
pPushConstantRanges = (nint)pushConstantRanges,
|
pPushConstantRanges = (nint)pushConstantRanges,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -221,19 +227,27 @@ internal sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
|
|
||||||
private void CreateDescriptorSetLayout()
|
private void CreateDescriptorSetLayout()
|
||||||
{
|
{
|
||||||
var binding = new VkDescriptorSetLayoutBinding
|
var bindings = stackalloc VkDescriptorSetLayoutBinding[2];
|
||||||
|
bindings[0] = new VkDescriptorSetLayoutBinding
|
||||||
{
|
{
|
||||||
binding = 0,
|
binding = 0,
|
||||||
descriptorType = VkDescriptorType.UniformBuffer,
|
descriptorType = VkDescriptorType.UniformBuffer,
|
||||||
descriptorCount = 1,
|
descriptorCount = 1,
|
||||||
stageFlags = VkShaderStageFlags.Vertex,
|
stageFlags = VkShaderStageFlags.Vertex,
|
||||||
};
|
};
|
||||||
|
bindings[1] = new VkDescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
binding = 1,
|
||||||
|
descriptorType = VkDescriptorType.CombinedImageSampler,
|
||||||
|
descriptorCount = 1,
|
||||||
|
stageFlags = VkShaderStageFlags.Fragment,
|
||||||
|
};
|
||||||
|
|
||||||
var info = new VkDescriptorSetLayoutCreateInfo
|
var info = new VkDescriptorSetLayoutCreateInfo
|
||||||
{
|
{
|
||||||
sType = VkStructureType.DescriptorSetLayoutCreateInfo,
|
sType = VkStructureType.DescriptorSetLayoutCreateInfo,
|
||||||
bindingCount = 1,
|
bindingCount = 2,
|
||||||
pBindings = &binding,
|
pBindings = bindings,
|
||||||
};
|
};
|
||||||
|
|
||||||
var descLayout = VkDescriptorSetLayout.Null;
|
var descLayout = VkDescriptorSetLayout.Null;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
private readonly VulkanSwapchain _swapchain;
|
private readonly VulkanSwapchain _swapchain;
|
||||||
private readonly VulkanPipeline _pipeline;
|
private readonly VulkanPipeline _pipeline;
|
||||||
private readonly VulkanFrameResources _frameResources;
|
private readonly VulkanFrameResources _frameResources;
|
||||||
|
private readonly VulkanShadowMap _shadowMap;
|
||||||
internal readonly VulkanImGui? _imGui;
|
internal readonly VulkanImGui? _imGui;
|
||||||
|
|
||||||
private readonly Dictionary<ulong, (VulkanVertexBuffer vb, VulkanIndexBuffer ib, uint indexCount)> _meshCache = new();
|
private readonly Dictionary<ulong, (VulkanVertexBuffer vb, VulkanIndexBuffer ib, uint indexCount)> _meshCache = new();
|
||||||
@@ -39,6 +40,11 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex,
|
_frameResources = new VulkanFrameResources(ctx.Device, ctx.GraphicsQueueFamilyIndex,
|
||||||
swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout);
|
swapchain.ImageCount, ctx, _pipeline.DescriptorSetLayout);
|
||||||
|
|
||||||
|
_shadowMap = new VulkanShadowMap(ctx.Device, ctx, _pipeline.DescriptorSetLayout);
|
||||||
|
|
||||||
|
for (int i = 0; i < VulkanFrameResources.MaxFramesInFlight; i++)
|
||||||
|
_frameResources.UpdateShadowDescriptor(i, _shadowMap.ShadowSampler, _shadowMap.DepthImageView);
|
||||||
|
|
||||||
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
_imGui = new VulkanImGui(ctx, _frameResources.CommandPool, swapchain.Format, swapchain.DepthFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +111,13 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
var vpCopy = vp;
|
var vpCopy = vp;
|
||||||
System.Buffer.MemoryCopy(&vpCopy, uboData, 64, 64);
|
System.Buffer.MemoryCopy(&vpCopy, uboData, 64, 64);
|
||||||
|
|
||||||
|
// Compute light view-projection matrix for shadow mapping
|
||||||
|
var lightPosVec = new Vector3(lightPos.X, lightPos.Y, lightPos.Z);
|
||||||
|
var lightView = Matrix4x4.CreateLookAt(lightPosVec, Vector3.Zero, Vector3.UnitY);
|
||||||
|
var lightProj = Matrix4x4.CreatePerspectiveFieldOfView(MathF.PI / 3f, 1.0f, 0.1f, 60f);
|
||||||
|
lightProj.M22 *= -1;
|
||||||
|
var lightViewProj = lightView * lightProj;
|
||||||
|
|
||||||
var drawCalls = new List<(VkBuffer vertexBuf, VkBuffer indexBuf, uint indexCount, Matrix4x4 model)>();
|
var drawCalls = new List<(VkBuffer vertexBuf, VkBuffer indexBuf, uint indexCount, Matrix4x4 model)>();
|
||||||
|
|
||||||
world.Each((Entity e, ref Transform t, ref Mesh m) =>
|
world.Each((Entity e, ref Transform t, ref Mesh m) =>
|
||||||
@@ -123,10 +136,10 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
drawCalls.Add((entry.vb.Buffer, entry.ib.Buffer, entry.indexCount, t.GetMatrix()));
|
drawCalls.Add((entry.vb.Buffer, entry.ib.Buffer, entry.indexCount, t.GetMatrix()));
|
||||||
});
|
});
|
||||||
|
|
||||||
Render(vp, drawCalls, uboData, lightPos, lightColor);
|
Render(vp, drawCalls, uboData, lightPos, lightColor, lightViewProj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Render(Matrix4x4 vp, List<(VkBuffer vertexBuf, VkBuffer indexBuf, uint indexCount, Matrix4x4 model)> drawCalls, byte* uboData, Vector4 lightPos, Vector4 lightColor)
|
private void Render(Matrix4x4 vp, List<(VkBuffer vertexBuf, VkBuffer indexBuf, uint indexCount, Matrix4x4 model)> drawCalls, byte* uboData, Vector4 lightPos, Vector4 lightColor, Matrix4x4 lightViewProj)
|
||||||
{
|
{
|
||||||
_frameResources.WaitFrame(_frameIndex);
|
_frameResources.WaitFrame(_frameIndex);
|
||||||
|
|
||||||
@@ -138,7 +151,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
{
|
{
|
||||||
_swapchain.Recreate(_ctx.SurfaceExtent.Width == 0 ? 1280 : (int)_ctx.SurfaceExtent.Width,
|
_swapchain.Recreate(_ctx.SurfaceExtent.Width == 0 ? 1280 : (int)_ctx.SurfaceExtent.Width,
|
||||||
_ctx.SurfaceExtent.Height == 0 ? 720 : (int)_ctx.SurfaceExtent.Height);
|
_ctx.SurfaceExtent.Height == 0 ? 720 : (int)_ctx.SurfaceExtent.Height);
|
||||||
Render(vp, drawCalls, uboData, lightPos, lightColor);
|
Render(vp, drawCalls, uboData, lightPos, lightColor, lightViewProj);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +172,86 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
};
|
};
|
||||||
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
Vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
||||||
|
|
||||||
|
// === SHADOW PASS ===
|
||||||
|
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||||
|
VkImageLayout.Undefined, VkImageLayout.DepthStencilAttachmentOptimal,
|
||||||
|
0, 0, 0x100, 0x200);
|
||||||
|
|
||||||
|
var shadowDepthClear = new VkClearValue
|
||||||
|
{
|
||||||
|
DepthStencil = new VkClearDepthStencilValue { Depth = 1.0f, Stencil = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var shadowDepthAttachment = new VkRenderingAttachmentInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.RenderingAttachmentInfo,
|
||||||
|
imageView = _shadowMap.DepthImageView,
|
||||||
|
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 },
|
||||||
|
Extent = new VkExtent2D { Width = VulkanShadowMap.ShadowMapSize, Height = VulkanShadowMap.ShadowMapSize },
|
||||||
|
};
|
||||||
|
Vk.vkCmdSetScissor(cmd, 0, 1, &shadowScissor);
|
||||||
|
|
||||||
|
Vk.vkCmdSetDepthBias(cmd, 1.25f, 0.0f, 1.75f);
|
||||||
|
|
||||||
|
foreach (var dc in drawCalls)
|
||||||
|
{
|
||||||
|
var vertexBuf = dc.vertexBuf;
|
||||||
|
ulong offset = 0;
|
||||||
|
Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuf, &offset);
|
||||||
|
Vk.vkCmdBindIndexBuffer(cmd, dc.indexBuf, 0, 1);
|
||||||
|
|
||||||
|
var model = dc.model;
|
||||||
|
Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex, 0, 64, &model);
|
||||||
|
|
||||||
|
var lvpCopy = lightViewProj;
|
||||||
|
Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 96, 64, &lvpCopy);
|
||||||
|
|
||||||
|
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vk.vkCmdEndRendering(cmd);
|
||||||
|
|
||||||
|
// Transition shadow map: depth attachment → shader read
|
||||||
|
TransitionImageLayoutDepth(cmd, _shadowMap.DepthImage,
|
||||||
|
VkImageLayout.DepthStencilAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
|
||||||
|
0x100, 0x200, 0x8, 0x20);
|
||||||
|
|
||||||
|
// === MAIN PASS ===
|
||||||
TransitionImageLayout(cmd, _swapchain.Images[imageIndex],
|
TransitionImageLayout(cmd, _swapchain.Images[imageIndex],
|
||||||
VkImageLayout.Undefined, VkImageLayout.ColorAttachmentOptimal,
|
VkImageLayout.Undefined, VkImageLayout.ColorAttachmentOptimal,
|
||||||
0, 0,
|
0, 0,
|
||||||
@@ -253,6 +346,10 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
var lcCopy = lightColor;
|
var lcCopy = lightColor;
|
||||||
Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Fragment, 80, 16, &lcCopy);
|
Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Fragment, 80, 16, &lcCopy);
|
||||||
|
|
||||||
|
// Light view-proj at offset 96 (vertex + fragment)
|
||||||
|
var lvpCopy = lightViewProj;
|
||||||
|
Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 96, 64, &lvpCopy);
|
||||||
|
|
||||||
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,6 +539,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen
|
|||||||
}
|
}
|
||||||
_meshCache.Clear();
|
_meshCache.Clear();
|
||||||
|
|
||||||
|
_shadowMap?.Dispose();
|
||||||
_imGui?.Dispose();
|
_imGui?.Dispose();
|
||||||
_frameResources?.Dispose();
|
_frameResources?.Dispose();
|
||||||
_pipeline?.Dispose();
|
_pipeline?.Dispose();
|
||||||
|
|||||||
@@ -0,0 +1,305 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Engine.Graphics.Vulkan;
|
||||||
|
|
||||||
|
internal sealed unsafe class VulkanShadowMap : IDisposable
|
||||||
|
{
|
||||||
|
public const uint ShadowMapSize = 2048;
|
||||||
|
|
||||||
|
public VkImage DepthImage;
|
||||||
|
public VkDeviceMemory DepthImageMemory;
|
||||||
|
public VkImageView DepthImageView;
|
||||||
|
public VkSampler ShadowSampler;
|
||||||
|
public VkPipeline Pipeline;
|
||||||
|
public VkPipelineLayout PipelineLayout;
|
||||||
|
public VkShaderModule VertModule;
|
||||||
|
|
||||||
|
private readonly VkDevice _device;
|
||||||
|
private readonly VulkanContext _ctx;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public VulkanShadowMap(VkDevice device, VulkanContext ctx, VkDescriptorSetLayout descLayout)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_ctx = ctx;
|
||||||
|
|
||||||
|
CreateShadowImage();
|
||||||
|
CreateShadowSampler();
|
||||||
|
CreateShadowPipeline(descLayout);
|
||||||
|
|
||||||
|
Console.WriteLine("[Vulkan] Shadow map created (2048x2048 D32_SFLOAT)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateShadowImage()
|
||||||
|
{
|
||||||
|
var imageInfo = new VkImageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ImageCreateInfo,
|
||||||
|
imageType = VkImageType.Type2D,
|
||||||
|
format = VkFormat.D32Sfloat,
|
||||||
|
extent = new VkExtent3D { Width = ShadowMapSize, Height = ShadowMapSize, Depth = 1 },
|
||||||
|
mipLevels = 1,
|
||||||
|
arrayLayers = 1,
|
||||||
|
samples = VkSampleCountFlags.Count1,
|
||||||
|
tiling = VkImageTiling.Optimal,
|
||||||
|
usage = VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled,
|
||||||
|
sharingMode = VkSharingMode.Exclusive,
|
||||||
|
initialLayout = VkImageLayout.Undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
var img = VkImage.Null;
|
||||||
|
Vk.vkCreateImage(_device, &imageInfo, 0, &img);
|
||||||
|
DepthImage = img;
|
||||||
|
|
||||||
|
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;
|
||||||
|
Vk.vkBindImageMemory(_device, DepthImage, DepthImageMemory, 0);
|
||||||
|
|
||||||
|
var viewInfo = 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 = 0, LayerCount = 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var view = VkImageView.Null;
|
||||||
|
Vk.vkCreateImageView(_device, &viewInfo, 0, &view);
|
||||||
|
DepthImageView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateShadowSampler()
|
||||||
|
{
|
||||||
|
var samplerInfo = new VkSamplerCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.SamplerCreateInfo,
|
||||||
|
magFilter = VkFilter.Linear,
|
||||||
|
minFilter = VkFilter.Linear,
|
||||||
|
mipmapMode = VkSamplerMipmapMode.Linear,
|
||||||
|
addressModeU = VkSamplerAddressMode.ClampToBorder,
|
||||||
|
addressModeV = VkSamplerAddressMode.ClampToBorder,
|
||||||
|
addressModeW = VkSamplerAddressMode.ClampToBorder,
|
||||||
|
minLod = 0,
|
||||||
|
maxLod = 1,
|
||||||
|
borderColor = 1, // VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE
|
||||||
|
};
|
||||||
|
|
||||||
|
var samp = VkSampler.Null;
|
||||||
|
Vk.vkCreateSampler(_device, &samplerInfo, 0, &samp);
|
||||||
|
ShadowSampler = samp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateShadowPipeline(VkDescriptorSetLayout descLayout)
|
||||||
|
{
|
||||||
|
var vertSpv = LoadShader("Shaders/shadow.vert.spv");
|
||||||
|
var fragSpv = LoadShader("Shaders/shadow.frag.spv");
|
||||||
|
VertModule = CreateShaderModule(vertSpv);
|
||||||
|
var fragModule = CreateShaderModule(fragSpv);
|
||||||
|
|
||||||
|
fixed (byte* pName = "main\0"u8)
|
||||||
|
{
|
||||||
|
var stages = stackalloc VkPipelineShaderStageCreateInfo[2];
|
||||||
|
stages[0] = new VkPipelineShaderStageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
||||||
|
stage = VkShaderStageFlags.Vertex,
|
||||||
|
module = VertModule,
|
||||||
|
pName = pName,
|
||||||
|
};
|
||||||
|
stages[1] = new VkPipelineShaderStageCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineShaderStageCreateInfo,
|
||||||
|
stage = VkShaderStageFlags.Fragment,
|
||||||
|
module = fragModule,
|
||||||
|
pName = pName,
|
||||||
|
};
|
||||||
|
|
||||||
|
var bindings = stackalloc VkVertexInputBindingDescription[1];
|
||||||
|
bindings[0] = new VkVertexInputBindingDescription { binding = 0, stride = (uint)sizeof(Engine.Core.Vertex), inputRate = VkVertexInputRate.Vertex };
|
||||||
|
|
||||||
|
var attributes = stackalloc VkVertexInputAttributeDescription[1];
|
||||||
|
attributes[0] = new VkVertexInputAttributeDescription { location = 0, binding = 0, format = VkFormat.R32G32B32Sfloat, offset = 0 };
|
||||||
|
|
||||||
|
var vertexInputState = new VkPipelineVertexInputStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineVertexInputStateCreateInfo,
|
||||||
|
vertexBindingDescriptionCount = 1,
|
||||||
|
pVertexBindingDescriptions = bindings,
|
||||||
|
vertexAttributeDescriptionCount = 1,
|
||||||
|
pVertexAttributeDescriptions = attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputAssemblyState = new VkPipelineInputAssemblyStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineInputAssemblyStateCreateInfo,
|
||||||
|
topology = VkPrimitiveTopology.TriangleList,
|
||||||
|
primitiveRestartEnable = VkBool32.False,
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewportState = new VkPipelineViewportStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineViewportStateCreateInfo,
|
||||||
|
viewportCount = 1,
|
||||||
|
scissorCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var rasterizationState = new VkPipelineRasterizationStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineRasterizationStateCreateInfo,
|
||||||
|
depthClampEnable = VkBool32.False,
|
||||||
|
rasterizerDiscardEnable = VkBool32.False,
|
||||||
|
polygonMode = VkPolygonMode.Fill,
|
||||||
|
cullMode = VkCullModeFlags.None,
|
||||||
|
frontFace = VkFrontFace.CounterClockwise,
|
||||||
|
depthBiasEnable = VkBool32.True,
|
||||||
|
lineWidth = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
var multisampleState = new VkPipelineMultisampleStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineMultisampleStateCreateInfo,
|
||||||
|
rasterizationSamples = VkSampleCountFlags.Count1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var depthStencilState = new VkPipelineDepthStencilStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineDepthStencilStateCreateInfo,
|
||||||
|
depthTestEnable = VkBool32.True,
|
||||||
|
depthWriteEnable = VkBool32.True,
|
||||||
|
depthCompareOp = VkCompareOp.LessOrEqual,
|
||||||
|
depthBoundsTestEnable = VkBool32.False,
|
||||||
|
stencilTestEnable = VkBool32.False,
|
||||||
|
};
|
||||||
|
|
||||||
|
var colorBlendState = new VkPipelineColorBlendStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineColorBlendStateCreateInfo,
|
||||||
|
logicOpEnable = VkBool32.False,
|
||||||
|
attachmentCount = 0,
|
||||||
|
pAttachments = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
var dynamicStates = stackalloc VkDynamicState[3];
|
||||||
|
dynamicStates[0] = VkDynamicState.Viewport;
|
||||||
|
dynamicStates[1] = VkDynamicState.Scissor;
|
||||||
|
dynamicStates[2] = VkDynamicState.DepthBias;
|
||||||
|
|
||||||
|
var dynamicState = new VkPipelineDynamicStateCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineDynamicStateCreateInfo,
|
||||||
|
dynamicStateCount = 3,
|
||||||
|
pDynamicStates = dynamicStates,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pushConstantRanges = stackalloc VkPushConstantRange[2];
|
||||||
|
pushConstantRanges[0] = new VkPushConstantRange { stageFlags = VkShaderStageFlags.Vertex, offset = 0, size = 64 };
|
||||||
|
pushConstantRanges[1] = new VkPushConstantRange { stageFlags = VkShaderStageFlags.Fragment, offset = 64, size = 96 };
|
||||||
|
|
||||||
|
var layoutInfo = new VkPipelineLayoutCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineLayoutCreateInfo,
|
||||||
|
setLayoutCount = 0,
|
||||||
|
pushConstantRangeCount = 2,
|
||||||
|
pPushConstantRanges = (nint)pushConstantRanges,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pl = VkPipelineLayout.Null;
|
||||||
|
Vk.vkCreatePipelineLayout(_device, &layoutInfo, 0, &pl);
|
||||||
|
PipelineLayout = pl;
|
||||||
|
|
||||||
|
var depthFormat = VkFormat.D32Sfloat;
|
||||||
|
var renderingInfo = new VkPipelineRenderingCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.PipelineRenderingCreateInfo,
|
||||||
|
colorAttachmentCount = 0,
|
||||||
|
pColorAttachmentFormats = null,
|
||||||
|
depthAttachmentFormat = depthFormat,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pipelineInfo = new VkGraphicsPipelineCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.GraphicsPipelineCreateInfo,
|
||||||
|
pNext = (nint)(&renderingInfo),
|
||||||
|
stageCount = 2,
|
||||||
|
pStages = stages,
|
||||||
|
pVertexInputState = &vertexInputState,
|
||||||
|
pInputAssemblyState = &inputAssemblyState,
|
||||||
|
pViewportState = &viewportState,
|
||||||
|
pRasterizationState = &rasterizationState,
|
||||||
|
pMultisampleState = &multisampleState,
|
||||||
|
pDepthStencilState = &depthStencilState,
|
||||||
|
pColorBlendState = &colorBlendState,
|
||||||
|
pDynamicState = &dynamicState,
|
||||||
|
layout = PipelineLayout,
|
||||||
|
renderPass = new VkRenderPass { Handle = 0 },
|
||||||
|
subpass = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var pp = VkPipeline.Null;
|
||||||
|
Vk.vkCreateGraphicsPipelines(_device, 0, 1, &pipelineInfo, 0, &pp);
|
||||||
|
Pipeline = pp;
|
||||||
|
|
||||||
|
Vk.vkDestroyShaderModule(_device, fragModule, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VkShaderModule CreateShaderModule(byte[] spv)
|
||||||
|
{
|
||||||
|
fixed (byte* pCode = spv)
|
||||||
|
{
|
||||||
|
var info = new VkShaderModuleCreateInfo
|
||||||
|
{
|
||||||
|
sType = VkStructureType.ShaderModuleCreateInfo,
|
||||||
|
codeSize = (nuint)spv.Length,
|
||||||
|
pCode = (uint*)pCode,
|
||||||
|
};
|
||||||
|
var module = VkShaderModule.Null;
|
||||||
|
Vk.vkCreateShaderModule(_device, &info, 0, &module);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] LoadShader(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
var altPath = Path.Combine(AppContext.BaseDirectory, path);
|
||||||
|
if (!File.Exists(altPath))
|
||||||
|
{
|
||||||
|
altPath = Path.Combine(AppContext.BaseDirectory, "Shaders", Path.GetFileName(path));
|
||||||
|
if (!File.Exists(altPath))
|
||||||
|
throw new FileNotFoundException($"Shader file not found: {path}");
|
||||||
|
}
|
||||||
|
return File.ReadAllBytes(altPath);
|
||||||
|
}
|
||||||
|
return File.ReadAllBytes(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
if (Pipeline.Handle != 0) Vk.vkDestroyPipeline(_device, Pipeline, 0);
|
||||||
|
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 (DepthImageView.Handle != 0) Vk.vkDestroyImageView(_device, DepthImageView, 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