【问题标题】:Rendering a cube in Vulkan vs OpenGL在 Vulkan 与 OpenGL 中渲染立方体
【发布时间】:2018-09-27 01:40:03
【问题描述】:

我写了一个简单的 OpenGL 程序,它只是从一个角度渲染一个立方体。它很简单:仅顶点缓冲区(无索引缓冲区),仅将顶点乘以来自统一缓冲区的 MVP 矩阵的顶点着色器,以及仅返回红色的静态片段着色器。最近,我尝试在 Vulkan 中编写同样的程序,但遇到了一些问题。

我开始遵循 Intel API without secrets 教程来设置一个简单的 2d 纹理渲染程序,但是当我跨入 3d 时,我开始遇到问题。为了调试它,我简化了程序以匹配我的旧 OpenGL 程序(删除了我在 Vulkan 中所做的纹理和其他一些额外的东西),甚至使用完全相同的顶点和 MVP 数据。但是,我就是无法让立方体在 Vulkan 中正确渲染。

我知道 OpenGL 坐标不会直接映射到 Vulkan 坐标,因为 Y 坐标是翻转的,但如果有任何东西应该只是将图像倒置,我已经尝试在 MVP 中切换 Y 值。我觉得我在这里缺少一些其他的坐标细节,但我无法弄清楚搜索并查看有关将 OpenGL 代码库转换为 Vulkan 的指南。

我将上传到着色器的数据以及来自 Vulkan 代码库的一些核心代码包括在内。 Vulkan 代码在 D 中,因此它与 C++ 类似,但略有不同。使用我用于包装 Vulkan(爆发)的库,设备级函数被加载到设备调度中(在代码中作为 device.dispatch 访问),并且当它们在没有 vk 前缀的调度上被调用时,设备和命令缓冲区(分配给代码中的调度)函数的参数是自动填充的。

顶点数据:

[ [1, 1, 1, 1],
  [1, 1, -1, 1],
  [-1, 1, -1, 1],
  [1, 1, 1, 1],
  [-1, 1, -1, 1],
  [-1, 1, 1, 1],
  [1, 1, 1, 1],
  [1, -1, 1, 1],
  [1, -1, -1, 1],
  [1, 1, 1, 1],
  [1, -1, -1, 1],
  [1, 1, -1, 1],
  [1, 1, -1, 1],
  [1, -1, -1, 1],
  [-1, -1, -1, 1],
  [1, 1, -1, 1],
  [-1, -1, -1, 1],
  [-1, 1, -1, 1],
  [-1, 1, -1, 1],
  [-1, -1, -1, 1],
  [-1, -1, 1, 1],
  [-1, 1, -1, 1],
  [-1, -1, 1, 1],
  [-1, 1, 1, 1],
  [-1, 1, 1, 1],
  [-1, -1, 1, 1],
  [1, -1, 1, 1],
  [-1, 1, 1, 1],
  [1, -1, 1, 1],
  [1, 1, 1, 1],
  [1, -1, 1, 1],
  [1, -1, -1, 1],
  [-1, -1, -1, 1],
  [1, -1, 1, 1],
  [-1, -1, -1, 1],
  [-1, -1, 1, 1] ]

MVP:

[ [-1.0864, -0.993682, -0.687368, -0.685994],
  [0, 2.07017, 0.515526, -0.514496],
  [-1.44853, 0.745262, 0.515526, 0.514496],
  [-8.04095e-16, 0, 5.64243, 5.83095] ]

图形管道设置:

VkPipelineShaderStageCreateInfo[] shader_stage_infos = [
  {
    stage: VK_SHADER_STAGE_VERTEX_BIT,
    _module: vertex_shader,
    pName: "main"
  },
  {
    stage: VK_SHADER_STAGE_FRAGMENT_BIT,
    _module: fragment_shader,
    pName: "main"
  }
];
VkVertexInputBindingDescription[] vertex_binding_descriptions = [
  {
    binding: 0,
    stride: VertexData.sizeof,
    inputRate: VK_VERTEX_INPUT_RATE_VERTEX
  }
];
VkVertexInputAttributeDescription[] vertex_attribute_descriptions = [
  {
    location: 0,
    binding: vertex_binding_descriptions[0].binding,
    format: VK_FORMAT_R32G32B32A32_SFLOAT,
    offset: VertexData.x.offsetof
  },
  {
    location: 1,
    binding: vertex_binding_descriptions[0].binding,
    format: VK_FORMAT_R32G32_SFLOAT,
    offset: VertexData.u.offsetof
  }
];
VkPipelineVertexInputStateCreateInfo vertex_input_state_info = {
  vertexBindingDescriptionCount: vertex_binding_descriptions.length.to!uint,
  pVertexBindingDescriptions: vertex_binding_descriptions.ptr,
  vertexAttributeDescriptionCount: vertex_attribute_descriptions.length.to!uint,
  pVertexAttributeDescriptions: vertex_attribute_descriptions.ptr
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_state_info = {
  topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
  primitiveRestartEnable: VK_FALSE
};
VkPipelineViewportStateCreateInfo viewport_state_info = {
  viewportCount: 1,
  pViewports: null,
  scissorCount: 1,
  pScissors: null
};
VkPipelineRasterizationStateCreateInfo rasterization_state_info = {
  depthBiasClamp: 0.0,
  polygonMode: VK_POLYGON_MODE_FILL,
  cullMode: VK_CULL_MODE_FRONT_AND_BACK,
  frontFace: VK_FRONT_FACE_COUNTER_CLOCKWISE,
  lineWidth: 1
};
VkPipelineMultisampleStateCreateInfo multisample_state_info = {
  rasterizationSamples: VK_SAMPLE_COUNT_1_BIT,
  minSampleShading: 1
};
VkPipelineColorBlendAttachmentState[] color_blend_attachment_states = [
  {
    blendEnable: VK_FALSE,
    srcColorBlendFactor: VK_BLEND_FACTOR_ONE,
    dstColorBlendFactor: VK_BLEND_FACTOR_ZERO,
    colorBlendOp: VK_BLEND_OP_ADD,
    srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE,
    dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO,
    alphaBlendOp: VK_BLEND_OP_ADD,
    colorWriteMask:
      VK_COLOR_COMPONENT_R_BIT |
      VK_COLOR_COMPONENT_G_BIT |
      VK_COLOR_COMPONENT_B_BIT |
      VK_COLOR_COMPONENT_A_BIT
  }
];
VkPipelineColorBlendStateCreateInfo color_blend_state_info = {
  logicOpEnable: VK_FALSE,
  logicOp: VK_LOGIC_OP_COPY,
  attachmentCount: color_blend_attachment_states.length.to!uint,
  pAttachments: color_blend_attachment_states.ptr,
  blendConstants: [ 0, 0, 0, 0 ]
};
VkDynamicState[] dynamic_states = [
  VK_DYNAMIC_STATE_VIEWPORT,
  VK_DYNAMIC_STATE_SCISSOR
];
VkPipelineDynamicStateCreateInfo dynamic_state_info = {
  dynamicStateCount: dynamic_states.length.to!uint,
  pDynamicStates: dynamic_states.ptr
};
VkGraphicsPipelineCreateInfo pipeline_info = {
  stageCount: shader_stage_infos.length.to!uint,
  pStages: shader_stage_infos.ptr,
  pVertexInputState: &vertex_input_state_info,
  pInputAssemblyState: &input_assembly_state_info,
  pTessellationState: null,
  pViewportState: &viewport_state_info,
  pRasterizationState: &rasterization_state_info,
  pMultisampleState: &multisample_state_info,
  pDepthStencilState: null,
  pColorBlendState: &color_blend_state_info,
  pDynamicState: &dynamic_state_info,
  layout: pipeline_layout,
  renderPass: render_pass,
  subpass: 0,
  basePipelineHandle: VK_NULL_HANDLE,
  basePipelineIndex: -1
};
VkPipeline[1] pipelines;
checkVk(device.dispatch.CreateGraphicsPipelines(VK_NULL_HANDLE, 1, [pipeline_info].ptr, pipelines.ptr));
pipeline = pipelines[0];

绘图:

if(device.dispatch.WaitForFences(1, [fence].ptr, VK_FALSE, 1000000000) != VK_SUCCESS)
  throw new StringException("timed out waiting for fence");
device.dispatch.ResetFences(1, [fence].ptr);

uint image_index;
switch(device.dispatch.AcquireNextImageKHR(swapchain.swapchain, uint64_t.max, image_available_semaphore, VK_NULL_HANDLE, &image_index)) {
  case VK_SUCCESS:
  case VK_SUBOPTIMAL_KHR:
    break;
  case VK_ERROR_OUT_OF_DATE_KHR:
    on_window_size_changed();
    break;
  default:
    throw new StringException("unhandled vk result on swapchain image acquisition");
}

if(framebuffer != VK_NULL_HANDLE) device.dispatch.DestroyFramebuffer(framebuffer);

VkFramebufferCreateInfo framebuffer_info = {
  renderPass: swapchain.render_pass,
  attachmentCount: 1,
  pAttachments: [swapchain.image_resources[image_index].image_view].ptr,
  width: swapchain.extent.width,
  height: swapchain.extent.height,
  layers: 1
};
checkVk(device.dispatch.CreateFramebuffer(&framebuffer_info, &framebuffer));

VkCommandBufferBeginInfo cmd_begin_info = { flags: VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT };
VkImageSubresourceRange image_subresource_range = {
  aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
  baseMipLevel: 0,
  levelCount: 1,
  baseArrayLayer: 0,
  layerCount: 1,
};
VkImageMemoryBarrier barrier_from_present_to_draw = {
  srcAccessMask: VK_ACCESS_MEMORY_READ_BIT,
  dstAccessMask: VK_ACCESS_MEMORY_READ_BIT,
  oldLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
  newLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
  srcQueueFamilyIndex: device.present_queue.family_index,
  dstQueueFamilyIndex: device.graphics_queue.family_index,
  image: swapchain.image_resources[image_index].image,
  subresourceRange: image_subresource_range
};
VkImageMemoryBarrier barrier_from_draw_to_present = {
  srcAccessMask: VK_ACCESS_MEMORY_READ_BIT,
  dstAccessMask: VK_ACCESS_MEMORY_READ_BIT,
  oldLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
  newLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
  srcQueueFamilyIndex: device.graphics_queue.family_index,
  dstQueueFamilyIndex: device.present_queue.family_index,
  image: swapchain.image_resources[image_index].image,
  subresourceRange: image_subresource_range
};
VkViewport viewport = {
  x: 0,
  y: 0,
  width: swapchain.extent.width,
  height: swapchain.extent.height,
  minDepth: 0,
  maxDepth: 1
};
VkRect2D scissor = {
  offset: {
    x: 0,
    y: 0
  },
  extent: swapchain.extent
};
VkClearValue[] clear_values = [
  { color: { [ 1.0, 0.8, 0.4, 0.0 ] } }
];
VkRenderPassBeginInfo render_pass_begin_info = {
  renderPass: swapchain.render_pass,
  framebuffer: framebuffer,
  renderArea: {
    offset: {
      x: 0,
      y: 0
    },
    extent: swapchain.extent
  },
  clearValueCount: clear_values.length.to!uint,
  pClearValues: clear_values.ptr
};

device.dispatch.commandBuffer = command_buffer;
device.dispatch.BeginCommandBuffer(&cmd_begin_info);
  if(device.graphics_queue.handle != device.present_queue.handle)
    device.dispatch.CmdPipelineBarrier(
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        0, 0, null, 0, null, 1,
        &barrier_from_present_to_draw
    );
  device.dispatch.CmdBeginRenderPass(&render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
    device.dispatch.CmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, swapchain.pipeline);
    device.dispatch.CmdSetViewport(0, 1, &viewport);
    device.dispatch.CmdSetScissor(0, 1, &scissor);
    const(ulong) vertex_buffer_offset = 0;
    device.dispatch.CmdBindVertexBuffers(0, 1, &vertex_buffer, &vertex_buffer_offset);
    device.dispatch.CmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, null);
    device.dispatch.CmdDraw(draw_count, 1, 0, 0);
  device.dispatch.CmdEndRenderPass();
  if(device.graphics_queue.handle != device.present_queue.handle)
    device.dispatch.CmdPipelineBarrier(
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        0, 0, null, 0, null, 1,
        &barrier_from_draw_to_present
    );
checkVk(device.dispatch.EndCommandBuffer());
device.dispatch.commandBuffer = VK_NULL_HANDLE;

VkSubmitInfo submit_info = {
  waitSemaphoreCount: 1,
  pWaitSemaphores: [image_available_semaphore].ptr,
  pWaitDstStageMask: castFrom!(VkPipelineStageFlagBits*).to!(const(uint)*)([VK_PIPELINE_STAGE_TRANSFER_BIT].ptr),
  commandBufferCount: 1,
  pCommandBuffers: [command_buffer].ptr,
  signalSemaphoreCount: 1,
  pSignalSemaphores: [rendering_finished_semaphore].ptr
};
checkVk(device.dispatch.vkQueueSubmit(device.graphics_queue.handle, 1, [submit_info].ptr, fence));

VkPresentInfoKHR present_info = {
  waitSemaphoreCount: 1,
  pWaitSemaphores: [rendering_finished_semaphore].ptr,
  swapchainCount: 1,
  pSwapchains: [swapchain.swapchain].ptr,
  pImageIndices: [image_index].ptr
};
switch(device.dispatch.vkQueuePresentKHR(device.present_queue.handle, &present_info)) {
  case VK_SUCCESS:
    break;

  case VK_ERROR_OUT_OF_DATE_KHR:
  case VK_SUBOPTIMAL_KHR:
    on_window_size_changed();
    break;

  default:
    throw new StringException("unhandled vk result on presentation");
}

(我的rep太低,无法嵌入图片,抱歉)

程序输出:

OpenGL 按预期绘制立方体 OpenGL Output

Vulkan 不渲染任何东西,除了清晰的颜色。

更新:

通过将剔除模式更改为 VK_CULL_MODE_NONE 来修复剔除模式后,这是我得到的结果: Output after cull mode fix

【问题讨论】:

  • > device.dispatch.CmdDraw(draw_count, 1, 0, 0); draw_count 指的是顶点数吗?
  • 是的。在这种情况下,抽奖次数设置为 36。
  • 你检查过三角绕组的顺序吗?也许关闭背面剔除作为测试。我也会查看 renderdoc 捕获网格输出。
  • omg 只需将视口偏移 y 设置为视口高度并将视口高度设置为 -(视口高度) 即可进行简单的快速修复

标签: vulkan


【解决方案1】:

VK_CULL_MODE_FRONT_AND_BACK

我认为这是你的问题:)

【讨论】:

  • 嗯,这当然是其中的一部分。我在这方面倒退了,因为在 open gl 中你提供了你想要渲染的面,但在 vulkan 中你提供了你不提供的面,这是有道理的,因为该字段被称为“cullMode”。然而,现在它只在一个奇怪的地方画了一个三角形。我将用新结果更新我的问题。
  • 啊,搞错了。之前我在渲染纹理时使用了 VertexData,并且在那里有纹理坐标字段。但是,当我从原始 OpenGL 程序中复制数据时,它不包含任何纹理坐标。我需要对顶点数据进行更好的抽象,这样这个错误就不会再发生了。谢谢!
  • 没问题!很高兴为您提供帮助
【解决方案2】:

剔除模式修复后,您的顶点数据布局似乎存在问题。 Vulkan 期望(根据您的布局绑定)类似于

struct Vertex {
    vec4 x;
    vec2 u;
};

Vertex VertexData[] = {...};

因为您在 vertex_binding_descriptions.inputRate 字段中设置了 VK_VERTEX_INPUT_RATE_VERTEX

在您的情况下,您似乎应该设置 VK_VERTEX_INPUT_RATE_INSTANCE 而不是一个接一个地使用缓冲区。

修复:看到你的新评论,看来我误解了你的顶点布局,所以没有帮助。

【讨论】:

  • 根据他提供的顶点数据,您认为顶点设置看起来错误是正确的。
猜你喜欢
  • 2014-05-21
  • 1970-01-01
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-20
相关资源
最近更新 更多