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 = 3) in vec3 fragViewDir;
|
||||
|
||||
layout(row_major, set = 0, binding = 0) uniform FrameUBO {
|
||||
layout(set = 0, binding = 0) uniform FrameUBO {
|
||||
vec3 cameraPosition;
|
||||
uint lightCount;
|
||||
vec3 ambientColor;
|
||||
@@ -47,11 +47,9 @@ void main() {
|
||||
float attenuation;
|
||||
|
||||
if (dirIntensity.w < 0.0) {
|
||||
// Directional light: direction stored as xyz, w = intensity (negative marks directional)
|
||||
lightDir = normalize(-dirIntensity.xyz);
|
||||
attenuation = abs(dirIntensity.w);
|
||||
} else {
|
||||
// Point light: position stored as xyz, w = intensity (positive marks point)
|
||||
vec3 toLight = dirIntensity.xyz - fragWorldPos;
|
||||
float dist = length(toLight);
|
||||
lightDir = toLight / max(dist, 0.001);
|
||||
@@ -63,7 +61,6 @@ void main() {
|
||||
vec3 H = normalize(V + lightDir);
|
||||
float NdotL = max(dot(N, lightDir), 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 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 = 2) in vec3 inNormal;
|
||||
|
||||
layout(row_major, set = 0, binding = 0) uniform FrameUBO {
|
||||
layout(set = 0, binding = 0) uniform FrameUBO {
|
||||
vec3 cameraPosition;
|
||||
uint lightCount;
|
||||
vec3 ambientColor;
|
||||
@@ -27,6 +27,10 @@ void main() {
|
||||
vec4 worldPos = pc.model * 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;
|
||||
fragNormal = normalize(mat3(pc.model) * inNormal);
|
||||
fragWorldPos = worldPos.xyz;
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
||||
private readonly VulkanContext _ctx;
|
||||
private bool _disposed;
|
||||
|
||||
public const int PushConstantSize = 128;
|
||||
public const int PushConstantSize = 144;
|
||||
public const int FrameUboSize = 16 + 16 + 16 * 4 * 8;
|
||||
|
||||
public VulkanPipeline(VulkanContext ctx, VkRenderPass renderPass)
|
||||
@@ -84,8 +84,8 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
||||
primitiveRestartEnable = 0
|
||||
};
|
||||
|
||||
var viewport = new VkViewport { x = 0, y = 0, width = 0, height = 0, minDepth = 0, maxDepth = 1 };
|
||||
var scissor = new VkRect2D { offset = new VkOffset2D { x = 0, y = 0 }, extent = new VkExtent2D { width = 0, height = 0 } };
|
||||
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 = 1280, height = 720 } };
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState;
|
||||
viewportState.sType = VkStructureType.PipelineViewportStateCreateInfo;
|
||||
@@ -163,6 +163,15 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
||||
colorBlendState.attachmentCount = 1;
|
||||
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
|
||||
{
|
||||
binding = 0,
|
||||
@@ -226,7 +235,7 @@ public sealed unsafe class VulkanPipeline : IDisposable
|
||||
pipelineInfo.pMultisampleState = &multisampleState;
|
||||
pipelineInfo.pDepthStencilState = &depthStencilState;
|
||||
pipelineInfo.pColorBlendState = &colorBlendState;
|
||||
pipelineInfo.pDynamicState = null;
|
||||
pipelineInfo.pDynamicState = &dynamicState;
|
||||
pipelineInfo.layout = pipeLayout;
|
||||
pipelineInfo.renderPass = renderPass;
|
||||
pipelineInfo.subpass = 0;
|
||||
|
||||
@@ -475,7 +475,7 @@ public sealed unsafe class VulkanRenderer : IRenderer, IScreenshotProvider
|
||||
};
|
||||
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);
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
var mvp = model * view * proj;
|
||||
var mvp = proj * view * model;
|
||||
|
||||
var pushData = new byte[VulkanPipeline.PushConstantSize];
|
||||
fixed (byte* pPush = pushData)
|
||||
|
||||
@@ -212,6 +212,12 @@ public enum VkCullModeFlags : uint
|
||||
FrontAndBack = 0x00000003,
|
||||
}
|
||||
|
||||
public enum VkDynamicState : int
|
||||
{
|
||||
Viewport = 0,
|
||||
Scissor = 1,
|
||||
}
|
||||
|
||||
public enum VkFrontFace : int
|
||||
{
|
||||
CounterClockwise = 0,
|
||||
@@ -241,10 +247,10 @@ public enum VkBlendOp : int
|
||||
|
||||
public enum VkColorComponentFlags : uint
|
||||
{
|
||||
R = 0x00000010,
|
||||
G = 0x00000020,
|
||||
B = 0x00000040,
|
||||
A = 0x00000080,
|
||||
R = 0x00000001,
|
||||
G = 0x00000002,
|
||||
B = 0x00000004,
|
||||
A = 0x00000008,
|
||||
}
|
||||
|
||||
public enum VkCompareOp : int
|
||||
@@ -1169,6 +1175,16 @@ unsafe public struct VkPipelineColorBlendStateCreateInfo
|
||||
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)]
|
||||
unsafe public struct VkGraphicsPipelineCreateInfo
|
||||
{
|
||||
@@ -1310,11 +1326,11 @@ unsafe public struct VkRenderPassBeginInfo
|
||||
public VkClearValue* pClearValues;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe public struct VkClearValue
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct VkClearValue
|
||||
{
|
||||
public VkClearColorValue color;
|
||||
public VkClearDepthStencilValue depthStencil;
|
||||
[FieldOffset(0)] public VkClearColorValue color;
|
||||
[FieldOffset(0)] public VkClearDepthStencilValue depthStencil;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
||||
Reference in New Issue
Block a user