summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcompile_shaders.sh5
-rwxr-xr-xmain.cpp327
-rwxr-xr-xshaders/triangle.vert8
3 files changed, 309 insertions, 31 deletions
diff --git a/compile_shaders.sh b/compile_shaders.sh
new file mode 100755
index 0000000..2e985f0
--- /dev/null
+++ b/compile_shaders.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+shader_dir="./shaders"
+glslc $shader_dir/triangle.vert -o $shader_dir/triangle.vert.spv
+glslc $shader_dir/triangle.frag -o $shader_dir/triangle.frag.spv
diff --git a/main.cpp b/main.cpp
index 0925c89..af91be9 100755
--- a/main.cpp
+++ b/main.cpp
@@ -21,6 +21,8 @@ Progress Notes: Hello Triangle Lesson Completed
- Graphics Pipeline chapter completed
- Drawing chapter completed.
- Swapchain recreation (resizing completed)
+- Vextex Buffer completed
+- Uniform Buffer completed
*/
/*
@@ -39,24 +41,33 @@ Progress Notes: Hello Triangle Lesson Completed
* 7. resize is broken on linux, no idea why. It could just be linux being linux but like,
* the resize window is very choppy/laggy on wayland and the resize operation is laggy on x11.
* Meanwhile, everything works correctly on W11.
+* 8. Driver developers recommend that you also store multiple
+* buffers, like the vertex and index buffer, into a single VkBuffer
+* and use offsets in commands like vkCmdBindVertexBuffers
+* 9. Push Constants: a more effective way of passing a small buffer of data to
+* shaders
+* 10. Desriptors -> Descriptor Sets -> Descriptor Pools, I need to clarify the
+* relationship between these items a bit better.
*/
#include <array>
-#include <chrono>
-#include <iostream>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <thread>
#include <vector>
#include <vulkan/vulkan_core.h>
#include "SDL2/SDL.h"
+#include "SDL2/SDL_scancode.h"
#include "SDL2/SDL_timer.h"
#include "SDL2/SDL_video.h"
#include "SDL2/SDL_vulkan.h"
#include "vulkan/vulkan.h"
+
+#define GLM_FORCE_RADIANS
+#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
#include "glm/glm.hpp"
+#include "glm/gtc/matrix_transform.hpp"
// Start utils
@@ -94,6 +105,12 @@ struct Vertex {
glm::vec3 col;
};
+struct UniformBufferObject {
+ alignas(16) glm::mat4 model;
+ alignas(16) glm::mat4 view;
+ alignas(16) glm::mat4 proj;
+};
+
// string
struct Str256 {
char buffer[256];
@@ -196,10 +213,19 @@ struct MegaState {
VkRenderPass vk_render_pass;
- VkBuffer vk_staging_vertex_buffer;
- VkDeviceMemory vk_mem_staging_vertex_buffer;
VkBuffer vk_vertex_buffer;
VkDeviceMemory vk_mem_vertex_buffer;
+ VkBuffer vk_index_buffer;
+ VkDeviceMemory vk_mem_index_buffer;
+ std::vector<VkBuffer> vk_uniform_buffers;
+ std::vector<VkDeviceMemory> vk_mem_uniform_buffers;
+ std::vector<void*> vk_mapped_uniform_buffers;
+
+ VkDescriptorSetLayout vk_descriptor_set_layout;
+ VkDescriptorPool vk_descriptor_pool;
+ std::vector<VkDescriptorSet> vk_descriptor_sets;
+ VkPipelineLayout vk_pipeline_layout;
+ VkPipeline vk_pipeline;
};
internal b8 resizable = 0;
@@ -1044,16 +1070,40 @@ int main() {
return -1;
}
}
+ // @func: create_descriptor_set_layout:
+ {
+ VkDescriptorSetLayoutBinding lb_ubo{};
+ lb_ubo.binding = 0;
+ lb_ubo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ lb_ubo.descriptorCount = 1;
+ lb_ubo.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ lb_ubo.pImmutableSamplers = nullptr;
+
+ VkDescriptorSetLayoutCreateInfo ci_desc_set{};
+ ci_desc_set.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ ci_desc_set.bindingCount = 1;
+ ci_desc_set.pBindings = &lb_ubo;
+
+ if (vkCreateDescriptorSetLayout(state.vk_device, &ci_desc_set, nullptr,
+ &state.vk_descriptor_set_layout)
+ != VK_SUCCESS) {
+ printf("Failed to create descriptor set layout\n");
+ return -1;
+ }
+ }
// @func: create_graphics_pipeline
- VkPipelineLayout vk_pipeline_layout;
- VkPipeline vk_pipeline;
-
// Vertex Data
const std::vector<Vertex> vertices = {
- {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}},
- {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}},
- {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}
+ {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
+ {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
+ {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
+ {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
+ };
+
+ const std::vector<u16> indices = {
+ 0, 1, 2, 2, 3, 0
};
+
{
Arena temp_pipeline_arena = state.arena;
ArrUChar vertex_shader = {};
@@ -1199,7 +1249,7 @@ int main() {
ci_pipeline_raster.polygonMode = VK_POLYGON_MODE_FILL;
ci_pipeline_raster.lineWidth = 1.0f;
ci_pipeline_raster.cullMode = VK_CULL_MODE_BACK_BIT;
- ci_pipeline_raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
+ ci_pipeline_raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
ci_pipeline_raster.depthBiasEnable = VK_FALSE;
ci_pipeline_raster.depthBiasClamp = 0.0f;
ci_pipeline_raster.depthBiasConstantFactor = 0.0f;
@@ -1246,11 +1296,14 @@ int main() {
// pipeline layout
VkPipelineLayoutCreateInfo ci_pipeline_layout = {};
ci_pipeline_layout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
- ci_pipeline_layout.setLayoutCount = 0;
- ci_pipeline_layout.pSetLayouts = nullptr;
+ ci_pipeline_layout.setLayoutCount = 1;
+ ci_pipeline_layout.pSetLayouts = &state.vk_descriptor_set_layout;
ci_pipeline_layout.pushConstantRangeCount = 0;
ci_pipeline_layout.pPushConstantRanges = nullptr;
- VkResult vk_result = vkCreatePipelineLayout(state.vk_device, &ci_pipeline_layout, nullptr, &vk_pipeline_layout);
+ VkResult vk_result = vkCreatePipelineLayout(state.vk_device,
+ &ci_pipeline_layout,
+ nullptr,
+ &state.vk_pipeline_layout);
if (vk_result != VK_SUCCESS) {
printf("failed to create a pipeline layout. Error: %d", vk_result);
return -1;
@@ -1271,14 +1324,18 @@ int main() {
ci_pipeline_graphics.pDepthStencilState = ci_pipeline_depth_stencil;
ci_pipeline_graphics.pColorBlendState = &ci_pipeline_color_blend;
- ci_pipeline_graphics.layout = vk_pipeline_layout;
+ ci_pipeline_graphics.layout = state.vk_pipeline_layout;
ci_pipeline_graphics.renderPass = state.vk_render_pass;
ci_pipeline_graphics.subpass = 0;
ci_pipeline_graphics.basePipelineHandle = VK_NULL_HANDLE;
ci_pipeline_graphics.basePipelineIndex = -1;
- if (vkCreateGraphicsPipelines(state.vk_device, VK_NULL_HANDLE, 1, &ci_pipeline_graphics, nullptr, &vk_pipeline) != VK_SUCCESS) {
+ if (vkCreateGraphicsPipelines(state.vk_device,
+ VK_NULL_HANDLE, 1,
+ &ci_pipeline_graphics,
+ nullptr,
+ &state.vk_pipeline) != VK_SUCCESS) {
// no printf from this point as validation layers should cover it.
// todo: remove older printf.
return -1;
@@ -1324,7 +1381,81 @@ int main() {
return -1;
}
}
- // create_vertex_buffer:
+ // @func: create_index_buffer:
+ {
+ u64 buffer_size = sizeof(indices[0]) * indices.size();
+ VkBuffer staging_buffer;
+ VkDeviceMemory staging_buffer_memory;
+ vk_create_buffer(state.vk_device, state.vk_physical_device,
+ buffer_size,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &staging_buffer,
+ &staging_buffer_memory);
+
+ // fill index buffer
+ void *index_data;
+ vkMapMemory(state.vk_device, staging_buffer_memory, 0, buffer_size, 0,
+ &index_data);
+ memcpy(index_data, indices.data(), buffer_size);
+ vkUnmapMemory(state.vk_device, staging_buffer_memory);
+
+ vk_create_buffer(state.vk_device, state.vk_physical_device,
+ buffer_size,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT |
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ &state.vk_index_buffer,
+ &state.vk_mem_index_buffer);
+
+ // copy buffer from SRC -> DEST
+ // vk_copy_buffer:
+ {
+ VkCommandBufferAllocateInfo alloc_info{};
+ alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ alloc_info.commandPool = vk_command_pool;
+ alloc_info.commandBufferCount = 1;
+
+ VkCommandBuffer command_buffer;
+ vkAllocateCommandBuffers(state.vk_device, &alloc_info, &command_buffer);
+
+ {
+ VkCommandBufferBeginInfo begin_info{};
+ begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+
+ vkBeginCommandBuffer(command_buffer, &begin_info);
+
+ VkBufferCopy copy_region{};
+ copy_region.srcOffset = 0;
+ copy_region.dstOffset = 0;
+ copy_region.size = buffer_size;
+ vkCmdCopyBuffer(command_buffer, staging_buffer,
+ state.vk_index_buffer, 1, &copy_region);
+
+ vkEndCommandBuffer(command_buffer);
+
+ VkSubmitInfo submit_info{};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &command_buffer;
+
+ vkQueueSubmit(state.vk_queue_gfx, 1, &submit_info, VK_NULL_HANDLE);
+ vkQueueWaitIdle(state.vk_queue_gfx);
+
+ }
+
+ vkFreeCommandBuffers(state.vk_device, vk_command_pool,
+ 1, &command_buffer);
+ }
+
+ vkDestroyBuffer(state.vk_device, staging_buffer, nullptr);
+ vkFreeMemory(state.vk_device, staging_buffer_memory, nullptr);
+
+ }
+ // @func: create_vertex_buffer:
{
// staging buffer
@@ -1397,6 +1528,92 @@ int main() {
vkDestroyBuffer(state.vk_device, staging_buffer, nullptr);
vkFreeMemory(state.vk_device, staging_buffer_memory, nullptr);
}
+ // create_uniform_buffer:
+ {
+ VkDeviceSize buffer_size = sizeof(UniformBufferObject);
+
+ state.vk_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT);
+ state.vk_mem_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT);
+ state.vk_mapped_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT);
+
+ for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ vk_create_buffer(state.vk_device,
+ state.vk_physical_device,
+ buffer_size,
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &state.vk_uniform_buffers[i],
+ &state.vk_mem_uniform_buffers[i]);
+
+ vkMapMemory(state.vk_device,
+ state.vk_mem_uniform_buffers[i],
+ 0, buffer_size, 0,
+ &state.vk_mapped_uniform_buffers[i]);
+ }
+ }
+ // create_descriptor_pool:
+ {
+ VkDescriptorPoolSize pool_size;
+ pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ pool_size.descriptorCount = (u32)MAX_FRAMES_IN_FLIGHT;
+
+ VkDescriptorPoolCreateInfo ci_desc_pool{};
+ ci_desc_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ ci_desc_pool.poolSizeCount = 1;
+ ci_desc_pool.pPoolSizes = &pool_size;
+ ci_desc_pool.maxSets = (u32)MAX_FRAMES_IN_FLIGHT;
+
+ if (vkCreateDescriptorPool(state.vk_device,
+ &ci_desc_pool, nullptr,
+ &state.vk_descriptor_pool) != VK_SUCCESS) {
+ printf("Error :: Failed to create descriptor pool\n");
+ return -1;
+ }
+ }
+ // create_descriptor_sets:
+ {
+ std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT,
+ state.vk_descriptor_set_layout);
+ VkDescriptorSetAllocateInfo alloc_info{};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorPool = state.vk_descriptor_pool;
+ alloc_info.descriptorSetCount = (u32)MAX_FRAMES_IN_FLIGHT;
+ alloc_info.pSetLayouts = layouts.data();
+
+ state.vk_descriptor_sets.resize(MAX_FRAMES_IN_FLIGHT);
+ if(vkAllocateDescriptorSets(state.vk_device,
+ &alloc_info,
+ state.vk_descriptor_sets.data())
+ != VK_SUCCESS) {
+ printf("Error :: Failed to allocate descriptor sets\n");
+ return -1;
+ }
+
+ // populate_descriptors:
+ {
+ for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ VkDescriptorBufferInfo buffer_info{};
+ buffer_info.buffer = state.vk_uniform_buffers[i];
+ buffer_info.offset = 0;
+ buffer_info.range = sizeof(UniformBufferObject);
+
+ VkWriteDescriptorSet desc_write{};
+ desc_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ desc_write.dstSet = state.vk_descriptor_sets[i];
+ desc_write.dstBinding = 0;
+ desc_write.dstArrayElement = 0;
+ desc_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ desc_write.descriptorCount = 1;
+ desc_write.pBufferInfo = &buffer_info;
+ desc_write.pImageInfo = nullptr;
+ desc_write.pTexelBufferView = nullptr;
+
+ vkUpdateDescriptorSets(state.vk_device, 1, &desc_write,
+ 0, nullptr);
+ }
+ }
+ }
// create command buffer
VkCommandBuffer vk_command_buffer[MAX_FRAMES_IN_FLIGHT] = {};
{
@@ -1442,7 +1659,9 @@ int main() {
u32 current_frame = 0;
b8 running = 1;
+ u32 start_time = SDL_GetTicks();
while (running) {
+ u32 current_time = SDL_GetTicks();
// draw frame
SDL_Event event;
while(SDL_PollEvent(&event)) {
@@ -1454,7 +1673,6 @@ int main() {
case SDL_WINDOWEVENT: {
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED: {
- printf("sdl window size changed\n");
resizable = 1;
} break;
@@ -1467,7 +1685,7 @@ int main() {
switch (event.key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:{
- printf("Closing game now\n");
+ printf("Escape code, closing app\n");
running = 0;
} break;
@@ -1490,7 +1708,6 @@ int main() {
VK_NULL_HANDLE, &sw_image_index);
if (vk_result == VK_ERROR_OUT_OF_DATE_KHR) {
- printf("swapchain is out of date\n");
// recreate swapchain -> currently only resizing is supported
if (resize_swapchain(&state) != 0) {
return -1;
@@ -1510,6 +1727,26 @@ int main() {
vkResetFences(state.vk_device, 1, &vk_fence_frame_flight[current_frame]);
vkResetCommandBuffer(vk_command_buffer[current_frame], 0);
+ // @func: update_uniform_buffer:
+ {
+ r32 rotation_inc = (r32)(current_time - start_time)/1000.0f;
+ UniformBufferObject ubo{};
+ ubo.model = glm::rotate(glm::mat4(1.0f),
+ rotation_inc*glm::radians(90.0f),
+ glm::vec3(0.0f, 0.0f, 1.0f));
+ ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f),
+ glm::vec3(0.0f, 0.0f, 0.0f),
+ glm::vec3(0.0f, 0.0f, 1.0f));
+ ubo.proj = glm::perspective(glm::radians(45.0f),
+ state.surface_resolution.width /
+ (r32)state.surface_resolution.height,
+ 0.0f, 10.0f);
+ ubo.proj[1][1] *= -1;
+
+ memcpy(state.vk_mapped_uniform_buffers[current_frame],
+ &ubo, sizeof(ubo));
+ }
+
// @func: record_command_buffer
{
VkCommandBufferBeginInfo bi_command_buffer = {};
@@ -1534,9 +1771,13 @@ int main() {
bi_render_pass.clearValueCount = 1;
bi_render_pass.pClearValues = &clear_color;
- vkCmdBeginRenderPass(vk_command_buffer[current_frame], &bi_render_pass, VK_SUBPASS_CONTENTS_INLINE);
+ vkCmdBeginRenderPass(vk_command_buffer[current_frame],
+ &bi_render_pass,
+ VK_SUBPASS_CONTENTS_INLINE);
{
- vkCmdBindPipeline(vk_command_buffer[current_frame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk_pipeline);
+ vkCmdBindPipeline(vk_command_buffer[current_frame],
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ state.vk_pipeline);
// viewport
VkViewport vk_viewport = {};
@@ -1557,8 +1798,19 @@ int main() {
// vertex buffer
VkBuffer vertex_buffers[] = {state.vk_vertex_buffer};
VkDeviceSize offsets[] = {0};
- vkCmdBindVertexBuffers(vk_command_buffer[current_frame], 0, 1, vertex_buffers, offsets);
- vkCmdDraw(vk_command_buffer[current_frame], (u32)vertices.size(), 1, 0, 0);
+ vkCmdBindVertexBuffers(vk_command_buffer[current_frame],
+ 0, 1, vertex_buffers, offsets);
+ vkCmdBindIndexBuffer(vk_command_buffer[current_frame],
+ state.vk_index_buffer,
+ 0, VK_INDEX_TYPE_UINT16);
+ vkCmdBindDescriptorSets(vk_command_buffer[current_frame],
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ state.vk_pipeline_layout,
+ 0, 1,
+ &state.vk_descriptor_sets[current_frame],
+ 0, nullptr);
+ vkCmdDrawIndexed(vk_command_buffer[current_frame],
+ indices.size(), 1, 0, 0, 0);
}
vkCmdEndRenderPass(vk_command_buffer[current_frame]);
}
@@ -1611,11 +1863,11 @@ int main() {
if ((vk_result == VK_ERROR_OUT_OF_DATE_KHR ||
vk_result == VK_SUBOPTIMAL_KHR) || resizable) {
- // @note: the suboptimal check which usually works when the surface is changed
- // will not change anything because the recreate function only resizes swapchain
+ // @note: the suboptimal check which usually works when the
+ // surface is changed will not change anything because
+ // the recreate function only resizes swapchain
// @todo: I guess I can add that for completeness,
// even though I have no way to test it out realistically
- printf("swapchain is out of date\n");
if (resize_swapchain(&state) != 0) return -1;
}
@@ -1640,8 +1892,13 @@ int main() {
vkDestroySemaphore(state.vk_device, vk_smp_render_done[i], nullptr);
}
vkDestroyCommandPool(state.vk_device, vk_command_pool, nullptr);
+
+ // destroy buffers
vkDestroyBuffer(state.vk_device, state.vk_vertex_buffer, nullptr);
+ vkDestroyBuffer(state.vk_device, state.vk_index_buffer, nullptr);
vkFreeMemory(state.vk_device, state.vk_mem_vertex_buffer, nullptr);
+ vkFreeMemory(state.vk_device, state.vk_mem_index_buffer, nullptr);
+
// destroy framebuffers
{
for (u32 i= 0; i < state.vk_framebuffers.size; i++) {
@@ -1649,8 +1906,8 @@ int main() {
vkDestroyFramebuffer(state.vk_device, framebuffer_item, nullptr);
}
}
- vkDestroyPipeline(state.vk_device, vk_pipeline, NULL);
- vkDestroyPipelineLayout(state.vk_device, vk_pipeline_layout, NULL);
+ vkDestroyPipeline(state.vk_device, state.vk_pipeline, NULL);
+ vkDestroyPipelineLayout(state.vk_device, state.vk_pipeline_layout, NULL);
vkDestroyRenderPass(state.vk_device, state.vk_render_pass, NULL);
// destroy image views
{
@@ -1659,6 +1916,16 @@ int main() {
}
}
vkDestroySwapchainKHR(state.vk_device, state.vk_khr_swapchain, NULL);
+ for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
+ vkDestroyBuffer(state.vk_device,
+ state.vk_uniform_buffers[i], nullptr);
+ vkFreeMemory(state.vk_device, state.vk_mem_uniform_buffers[i], nullptr);
+ }
+ vkDestroyDescriptorPool(state.vk_device,
+ state.vk_descriptor_pool,
+ nullptr);
+ vkDestroyDescriptorSetLayout(state.vk_device, state.vk_descriptor_set_layout,
+ nullptr);
vkDestroyDevice(state.vk_device, NULL);
if (enable_validation_layers) {
diff --git a/shaders/triangle.vert b/shaders/triangle.vert
index 663fea0..c61f894 100755
--- a/shaders/triangle.vert
+++ b/shaders/triangle.vert
@@ -1,5 +1,11 @@
#version 450
+layout(binding = 0) uniform UniformBufferObject {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+} ubo;
+
layout(location = 0) in vec2 inPos;
layout(location = 1) in vec3 inCol;
@@ -7,6 +13,6 @@ layout(location = 1) in vec3 inCol;
layout(location = 0) out vec3 fragColor;
void main() {
- gl_Position = vec4(inPos, 0.0, 1.0);
+ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPos, 0.0, 1.0);
fragColor = inCol;
}