|
| 1 | +# Drawing a Triangle |
| 2 | + |
| 3 | +We shall create two pipelines: one for standard draws, one for wireframe draws. Add new `App` members: |
| 4 | + |
| 5 | +```cpp |
| 6 | +void create_pipeline_builder(); |
| 7 | +void create_pipelines(); |
| 8 | + |
| 9 | +// ... |
| 10 | +std::optional<PipelineBuilder> m_pipeline_builder{}; |
| 11 | + |
| 12 | +vk::UniquePipelineLayout m_pipeline_layout{}; |
| 13 | +struct { |
| 14 | + vk::UniquePipeline standard{}; |
| 15 | + vk::UniquePipeline wireframe{}; |
| 16 | +} m_pipelines{}; |
| 17 | +float m_line_width{1.0f}; |
| 18 | +bool m_wireframe{}; |
| 19 | +``` |
| 20 | +
|
| 21 | +Implement and call `create_pipeline_builder()`: |
| 22 | +
|
| 23 | +```cpp |
| 24 | +void App::create_pipeline_builder() { |
| 25 | + auto const pipeline_builder_ci = PipelineBuilder::CreateInfo{ |
| 26 | + .device = *m_device, |
| 27 | + .samples = vk::SampleCountFlagBits::e1, |
| 28 | + .color_format = m_swapchain->get_format(), |
| 29 | + }; |
| 30 | + m_pipeline_builder.emplace(pipeline_builder_ci); |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +Complete the implementation of `create_pipelines()`: |
| 35 | + |
| 36 | +```cpp |
| 37 | +// ... |
| 38 | +m_pipeline_layout = m_device->createPipelineLayoutUnique({}); |
| 39 | + |
| 40 | +auto pipeline_state = PipelineState{ |
| 41 | + .vertex_shader = *vertex, |
| 42 | + .fragment_shader = *fragment, |
| 43 | +}; |
| 44 | +m_pipelines.standard = |
| 45 | + m_pipeline_builder->build(*m_pipeline_layout, pipeline_state); |
| 46 | +pipeline_state.polygon_mode = vk::PolygonMode::eLine; |
| 47 | +m_pipelines.wireframe = |
| 48 | + m_pipeline_builder->build(*m_pipeline_layout, pipeline_state); |
| 49 | +if (!m_pipelines.standard || !m_pipelines.wireframe) { |
| 50 | + throw std::runtime_error{"Failed to create Graphics Pipelines"}; |
| 51 | +} |
| 52 | +``` |
| 53 | +
|
| 54 | +Before `render()` grows to an unwieldy size, extract the higher level logic into two member functions: |
| 55 | +
|
| 56 | +```cpp |
| 57 | +// ImGui code goes here. |
| 58 | +void inspect(); |
| 59 | +// Issue draw calls here. |
| 60 | +void draw(vk::Rect2D const& render_area, |
| 61 | + vk::CommandBuffer command_buffer) const; |
| 62 | +
|
| 63 | +// ... |
| 64 | +void App::inspect() { |
| 65 | + ImGui::ShowDemoWindow(); |
| 66 | + // TODO |
| 67 | +} |
| 68 | +
|
| 69 | +// ... |
| 70 | +command_buffer.beginRendering(rendering_info); |
| 71 | +inspect(); |
| 72 | +draw(render_area, command_buffer); |
| 73 | +command_buffer.endRendering(); |
| 74 | +``` |
| 75 | + |
| 76 | +We can now bind a pipeline and use it to draw the triangle in the shader. Making `draw()` `const` forces us to ensure no `App` state is changed: |
| 77 | + |
| 78 | +```cpp |
| 79 | +void App::draw(vk::Rect2D const& render_area, |
| 80 | + vk::CommandBuffer const command_buffer) const { |
| 81 | + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, |
| 82 | + *m_pipelines.standard); |
| 83 | + // we are creating pipelines with dynamic viewport and scissor states. |
| 84 | + // they must be set here after binding (before drawing). |
| 85 | + auto viewport = vk::Viewport{}; |
| 86 | + // flip the viewport about the X-axis (negative height): |
| 87 | + // https://www.saschawillems.de/blog/2019/03/29/flipping-the-vulkan-viewport/ |
| 88 | + viewport.setX(0.0f) |
| 89 | + .setY(static_cast<float>(m_render_target->extent.height)) |
| 90 | + .setWidth(static_cast<float>(m_render_target->extent.width)) |
| 91 | + .setHeight(-viewport.y); |
| 92 | + command_buffer.setViewport(0, viewport); |
| 93 | + command_buffer.setScissor(0, render_area); |
| 94 | + // current shader has hard-coded logic for 3 vertices. |
| 95 | + command_buffer.draw(3, 1, 0, 0); |
| 96 | +} |
| 97 | +``` |
| 98 | +
|
| 99 | + |
| 100 | +
|
| 101 | +Updating our shaders to use interpolated RGB on each vertex: |
| 102 | +
|
| 103 | +```glsl |
| 104 | +// shader.vert |
| 105 | +
|
| 106 | +layout (location = 0) out vec3 out_color; |
| 107 | +
|
| 108 | +// ... |
| 109 | +const vec3 colors[] = { |
| 110 | + vec3(1.0, 0.0, 0.0), |
| 111 | + vec3(0.0, 1.0, 0.0), |
| 112 | + vec3(0.0, 0.0, 1.0), |
| 113 | +}; |
| 114 | +
|
| 115 | +// ... |
| 116 | +out_color = colors[gl_VertexIndex]; |
| 117 | +
|
| 118 | +// shader.frag |
| 119 | +
|
| 120 | +layout (location = 0) in vec3 in_color; |
| 121 | +
|
| 122 | +// ... |
| 123 | +out_color = vec4(in_color, 1.0); |
| 124 | +``` |
| 125 | + |
| 126 | +> Make sure to recompile both the SPIR-V shaders in assets/. |
| 127 | +
|
| 128 | +And a black clear color: |
| 129 | + |
| 130 | +```cpp |
| 131 | +// ... |
| 132 | +.setClearValue(vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f}); |
| 133 | +``` |
| 134 | +
|
| 135 | +Gives us the renowned Vulkan sRGB triangle: |
| 136 | +
|
| 137 | + |
| 138 | +
|
0 commit comments