fix: rendering now works — three critical bugs fixed
1. VkColorComponentFlags: wrong bit values (0x10-0x80 instead of 0x01-0x08) → colorWriteMask was 0xF0 instead of 0x0F → no color channels written 2. VkClearValue: was Sequential layout instead of Explicit (union) → depthStencil clear value written at wrong offset → depth buffer cleared to 0.0 instead of 1.0 → all fragments failed depth test 3. Vulkan clip space correction in vertex shader: gl_Position.y = -gl_Position.y (Vulkan Y-down vs OpenGL Y-up) gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5 (Z [0,1] vs [-1,1]) Also: - PushConstantSize fixed from 128 to 144 (two mat4 + vec4) - Dynamic viewport/scissor state added to pipeline - Push constants use column_major (default) to match System.Numerics row-major layout - Camera.GetProjectionMatrix reverted to pure OpenGL-style (correction in shader) - Screenshots show actual 3D geometry (calibration cubes visible) - 66/66 tests pass
This commit is contained in:
@@ -5,7 +5,7 @@ layout(location = 1) in vec3 fragNormal;
|
|||||||
layout(location = 2) in vec3 fragWorldPos;
|
layout(location = 2) in vec3 fragWorldPos;
|
||||||
layout(location = 3) in vec3 fragViewDir;
|
layout(location = 3) in vec3 fragViewDir;
|
||||||
|
|
||||||
layout(row_major, set = 0, binding = 0) uniform FrameUBO {
|
layout(set = 0, binding = 0) uniform FrameUBO {
|
||||||
vec3 cameraPosition;
|
vec3 cameraPosition;
|
||||||
uint lightCount;
|
uint lightCount;
|
||||||
vec3 ambientColor;
|
vec3 ambientColor;
|
||||||
@@ -47,11 +47,9 @@ void main() {
|
|||||||
float attenuation;
|
float attenuation;
|
||||||
|
|
||||||
if (dirIntensity.w < 0.0) {
|
if (dirIntensity.w < 0.0) {
|
||||||
// Directional light: direction stored as xyz, w = intensity (negative marks directional)
|
|
||||||
lightDir = normalize(-dirIntensity.xyz);
|
lightDir = normalize(-dirIntensity.xyz);
|
||||||
attenuation = abs(dirIntensity.w);
|
attenuation = abs(dirIntensity.w);
|
||||||
} else {
|
} else {
|
||||||
// Point light: position stored as xyz, w = intensity (positive marks point)
|
|
||||||
vec3 toLight = dirIntensity.xyz - fragWorldPos;
|
vec3 toLight = dirIntensity.xyz - fragWorldPos;
|
||||||
float dist = length(toLight);
|
float dist = length(toLight);
|
||||||
lightDir = toLight / max(dist, 0.001);
|
lightDir = toLight / max(dist, 0.001);
|
||||||
@@ -63,7 +61,6 @@ void main() {
|
|||||||
vec3 H = normalize(V + lightDir);
|
vec3 H = normalize(V + lightDir);
|
||||||
float NdotL = max(dot(N, lightDir), 0.0);
|
float NdotL = max(dot(N, lightDir), 0.0);
|
||||||
float NdotH = max(dot(N, H), 0.0);
|
float NdotH = max(dot(N, H), 0.0);
|
||||||
float VdotH = max(dot(V, H), 0.0);
|
|
||||||
|
|
||||||
float specPower = mix(128.0, 4.0, roughness);
|
float specPower = mix(128.0, 4.0, roughness);
|
||||||
float specIntensity = pow(NdotH, specPower);
|
float specIntensity = pow(NdotH, specPower);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@ layout(location = 0) in vec3 inPosition;
|
|||||||
layout(location = 1) in vec3 inColor;
|
layout(location = 1) in vec3 inColor;
|
||||||
layout(location = 2) in vec3 inNormal;
|
layout(location = 2) in vec3 inNormal;
|
||||||
|
|
||||||
layout(row_major, set = 0, binding = 0) uniform FrameUBO {
|
layout(set = 0, binding = 0) uniform FrameUBO {
|
||||||
vec3 cameraPosition;
|
vec3 cameraPosition;
|
||||||
uint lightCount;
|
uint lightCount;
|
||||||
vec3 ambientColor;
|
vec3 ambientColor;
|
||||||
@@ -27,6 +27,10 @@ void main() {
|
|||||||
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
|
||||||
gl_Position = pc.mvp * vec4(inPosition, 1.0);
|
gl_Position = pc.mvp * vec4(inPosition, 1.0);
|
||||||
|
|
||||||
|
// Vulkan clip space: Y-down, Z [0,1] — convert from OpenGL Y-up, Z [-1,1]
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;
|
||||||
|
|
||||||
fragColor = inColor;
|
fragColor = inColor;
|
||||||
fragNormal = normalize(mat3(pc.model) * inNormal);
|
fragNormal = normalize(mat3(pc.model) * inNormal);
|
||||||
fragWorldPos = worldPos.xyz;
|
fragWorldPos = worldPos.xyz;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
private readonly VulkanContext _ctx;
|
private readonly VulkanContext _ctx;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public const int PushConstantSize = 128;
|
public const int PushConstantSize = 144;
|
||||||
public const int FrameUboSize = 16 + 16 + 16 * 4 * 8;
|
public const int FrameUboSize = 16 + 16 + 16 * 4 * 8;
|
||||||
|
|
||||||
public VulkanPipeline(VulkanContext ctx, VkRenderPass renderPass)
|
public VulkanPipeline(VulkanContext ctx, VkRenderPass renderPass)
|
||||||
@@ -84,8 +84,8 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
primitiveRestartEnable = 0
|
primitiveRestartEnable = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewport = new VkViewport { x = 0, y = 0, width = 0, height = 0, minDepth = 0, maxDepth = 1 };
|
var viewport = new VkViewport { x = 0, y = 0, width = 1280, height = 720, minDepth = 0, maxDepth = 1 };
|
||||||
var scissor = new VkRect2D { offset = new VkOffset2D { x = 0, y = 0 }, extent = new VkExtent2D { width = 0, height = 0 } };
|
var scissor = new VkRect2D { offset = new VkOffset2D { x = 0, y = 0 }, extent = new VkExtent2D { width = 1280, height = 720 } };
|
||||||
|
|
||||||
VkPipelineViewportStateCreateInfo viewportState;
|
VkPipelineViewportStateCreateInfo viewportState;
|
||||||
viewportState.sType = VkStructureType.PipelineViewportStateCreateInfo;
|
viewportState.sType = VkStructureType.PipelineViewportStateCreateInfo;
|
||||||
@@ -163,6 +163,15 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
colorBlendState.attachmentCount = 1;
|
colorBlendState.attachmentCount = 1;
|
||||||
colorBlendState.pAttachments = &blendAttachment;
|
colorBlendState.pAttachments = &blendAttachment;
|
||||||
|
|
||||||
|
var dynamicStates = stackalloc VkDynamicState[2];
|
||||||
|
dynamicStates[0] = VkDynamicState.Viewport;
|
||||||
|
dynamicStates[1] = VkDynamicState.Scissor;
|
||||||
|
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamicState = default;
|
||||||
|
dynamicState.sType = VkStructureType.PipelineDynamicStateCreateInfo;
|
||||||
|
dynamicState.dynamicStateCount = 2;
|
||||||
|
dynamicState.pDynamicStates = dynamicStates;
|
||||||
|
|
||||||
var uboBinding = new VkDescriptorSetLayoutBinding
|
var uboBinding = new VkDescriptorSetLayoutBinding
|
||||||
{
|
{
|
||||||
binding = 0,
|
binding = 0,
|
||||||
@@ -226,7 +235,7 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
|||||||
pipelineInfo.pMultisampleState = &multisampleState;
|
pipelineInfo.pMultisampleState = &multisampleState;
|
||||||
pipelineInfo.pDepthStencilState = &depthStencilState;
|
pipelineInfo.pDepthStencilState = &depthStencilState;
|
||||||
pipelineInfo.pColorBlendState = &colorBlendState;
|
pipelineInfo.pColorBlendState = &colorBlendState;
|
||||||
pipelineInfo.pDynamicState = null;
|
pipelineInfo.pDynamicState = &dynamicState;
|
||||||
pipelineInfo.layout = pipeLayout;
|
pipelineInfo.layout = pipeLayout;
|
||||||
pipelineInfo.renderPass = renderPass;
|
pipelineInfo.renderPass = renderPass;
|
||||||
pipelineInfo.subpass = 0;
|
pipelineInfo.subpass = 0;
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ public sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
|
|||||||
};
|
};
|
||||||
Vk.vkCmdSetScissor(cmd, 0, 1, &scissor);
|
Vk.vkCmdSetScissor(cmd, 0, 1, &scissor);
|
||||||
|
|
||||||
VkDescriptorSet ds = _descriptorSets[_currentFrame];
|
var ds = _descriptorSets[_currentFrame];
|
||||||
Vk.vkCmdBindDescriptorSets(cmd, 0, _pipeline.PipelineLayout, 0, 1, &ds, 0, null);
|
Vk.vkCmdBindDescriptorSets(cmd, 0, _pipeline.PipelineLayout, 0, 1, &ds, 0, null);
|
||||||
|
|
||||||
world.Each((Entity e, ref Transform transform, ref Mesh mesh, ref Material material) =>
|
world.Each((Entity e, ref Transform transform, ref Mesh mesh, ref Material material) =>
|
||||||
@@ -502,7 +502,7 @@ public sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
|
|||||||
proj = cam.GetProjectionMatrix();
|
proj = cam.GetProjectionMatrix();
|
||||||
});
|
});
|
||||||
|
|
||||||
var mvp = model * view * proj;
|
var mvp = proj * view * model;
|
||||||
|
|
||||||
var pushData = new byte[VulkanPipeline.PushConstantSize];
|
var pushData = new byte[VulkanPipeline.PushConstantSize];
|
||||||
fixed (byte* pPush = pushData)
|
fixed (byte* pPush = pushData)
|
||||||
|
|||||||
@@ -212,6 +212,12 @@ public enum VkCullModeFlags : uint
|
|||||||
FrontAndBack = 0x00000003,
|
FrontAndBack = 0x00000003,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum VkDynamicState : int
|
||||||
|
{
|
||||||
|
Viewport = 0,
|
||||||
|
Scissor = 1,
|
||||||
|
}
|
||||||
|
|
||||||
public enum VkFrontFace : int
|
public enum VkFrontFace : int
|
||||||
{
|
{
|
||||||
CounterClockwise = 0,
|
CounterClockwise = 0,
|
||||||
@@ -241,10 +247,10 @@ public enum VkBlendOp : int
|
|||||||
|
|
||||||
public enum VkColorComponentFlags : uint
|
public enum VkColorComponentFlags : uint
|
||||||
{
|
{
|
||||||
R = 0x00000010,
|
R = 0x00000001,
|
||||||
G = 0x00000020,
|
G = 0x00000002,
|
||||||
B = 0x00000040,
|
B = 0x00000004,
|
||||||
A = 0x00000080,
|
A = 0x00000008,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum VkCompareOp : int
|
public enum VkCompareOp : int
|
||||||
@@ -1169,6 +1175,16 @@ unsafe public struct VkPipelineColorBlendStateCreateInfo
|
|||||||
public fixed float blendConstants[4];
|
public fixed float blendConstants[4];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
unsafe public struct VkPipelineDynamicStateCreateInfo
|
||||||
|
{
|
||||||
|
public VkStructureType sType;
|
||||||
|
public void* pNext;
|
||||||
|
public uint flags;
|
||||||
|
public uint dynamicStateCount;
|
||||||
|
public VkDynamicState* pDynamicStates;
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
unsafe public struct VkGraphicsPipelineCreateInfo
|
unsafe public struct VkGraphicsPipelineCreateInfo
|
||||||
{
|
{
|
||||||
@@ -1310,11 +1326,11 @@ unsafe public struct VkRenderPassBeginInfo
|
|||||||
public VkClearValue* pClearValues;
|
public VkClearValue* pClearValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
unsafe public struct VkClearValue
|
public struct VkClearValue
|
||||||
{
|
{
|
||||||
public VkClearColorValue color;
|
[FieldOffset(0)] public VkClearColorValue color;
|
||||||
public VkClearDepthStencilValue depthStencil;
|
[FieldOffset(0)] public VkClearDepthStencilValue depthStencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
|||||||
Reference in New Issue
Block a user