diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f0a232..5f572d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ file(GLOB SHADER_SOURCE_FILES assets/shaders/*.frag assets/shaders/*.vert assets/shaders/*.geom + assets/shaders/*.tese + assets/shaders/*.tesc ) foreach(SHADER_SOURCE ${SHADER_SOURCE_FILES}) diff --git a/Memory/Buffer.hpp b/Memory/Buffer.hpp index c3f4550..cfd2880 100644 --- a/Memory/Buffer.hpp +++ b/Memory/Buffer.hpp @@ -20,7 +20,7 @@ struct Buffer { } template inline void upload(const std::vector& data) { - upload(reinterpret_cast(data.data()), data.size()*sizeof(T)); + upload(reinterpret_cast(data.data()), data.size()*sizeof(T)); } operator vk::Buffer& () { diff --git a/Renderer/Pipeline.cpp b/Renderer/Pipeline.cpp index d60c869..af80054 100644 --- a/Renderer/Pipeline.cpp +++ b/Renderer/Pipeline.cpp @@ -9,7 +9,7 @@ #include -GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy bindings, const VertexBuffer& vertex_buffer, enum Type type = Type::GLTF) : dev(dev) { +GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy bindings, const VertexBuffer& vertex_buffer, enum Type type) : dev(dev) { /* create layout * Eventually add a graphicspipline constructor that allows specification of layouts etc * kinda like how Image::Image has all those versions @@ -80,10 +80,21 @@ GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& sh .primitiveRestartEnable = vk::False, }; + const vk::PipelineTessellationStateCreateInfo* ptesselation_info = nullptr; + + const auto tess_info = vk::PipelineTessellationStateCreateInfo { + /* quads*/ + .patchControlPoints = 4, + }; + + if(type == Type::eTERRAIN) { + ptesselation_info = &tess_info; + } + const auto raster_info = vk::PipelineRasterizationStateCreateInfo { .depthClampEnable = vk::False, .polygonMode = type == Type::eGLTF? vk::PolygonMode::eFill : vk::PolygonMode::eLine, - .cullMode = vk::CullModeFlagBits::eBack, + .cullMode = vk::CullModeFlagBits::eNone, .frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = vk::False, .lineWidth = 1.0, @@ -154,7 +165,7 @@ GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector& sh .pStages = shader_info.data(), .pVertexInputState = &vertex_input_info, .pInputAssemblyState = &input_asm_info, - .pTessellationState = nullptr, + .pTessellationState = ptesselation_info, .pViewportState = &viewport_info, .pRasterizationState = &raster_info, .pMultisampleState = &multisample_info, diff --git a/Renderer/Renderer.cpp b/Renderer/Renderer.cpp index 49ac42c..ea89672 100644 --- a/Renderer/Renderer.cpp +++ b/Renderer/Renderer.cpp @@ -143,6 +143,8 @@ Renderer::Renderer(Window& win) : win(win) { const auto features = vk::PhysicalDeviceFeatures{ .geometryShader = vk::True, + .tessellationShader = vk::True, + .fillModeNonSolid = vk::True, }; auto dev_info = vk::DeviceCreateInfo{ @@ -182,6 +184,7 @@ Renderer::Renderer(Window& win) : win(win) { textures = createResources({ "assets/textures/oil.jpg", + "assets/textures/heightmap.png", }); std::vector shaders = { @@ -209,7 +212,7 @@ Renderer::Renderer(Window& win) : win(win) { /* create Terrain */ - terrain = std::make_unique(phys_dev, dev, textures[0]); + terrain = std::make_unique(phys_dev, dev, textures[1]); std::vector terrain_shaders = { { dev, "assets/shaders/terrain.vert.spv", vk::ShaderStageFlagBits::eVertex }, @@ -218,7 +221,7 @@ Renderer::Renderer(Window& win) : win(win) { { dev, "assets/shaders/terrain.frag.spv", vk::ShaderStageFlagBits::eFragment }, }; - terrain_pipeline = std::make_unique(dev, terrain_shaders, swapchain->extent, *render_pass, bindings, terrain->vertex_buffer, GraphicsPipeline::eTERRAIN); + terrain_pipeline = std::make_unique(dev, terrain_shaders, swapchain->extent, *render_pass, bindings, *terrain->vertex_buffer, GraphicsPipeline::eTERRAIN); for (auto& shader : shaders) shader.cleanup(); @@ -236,7 +239,7 @@ std::vector Renderer::createResources(const std::vector& n texture_cmd.begin(); for (const auto& name : names) { - ret.push_back({phys_dev, dev, texture_cmd, name}); + ret.push_back({phys_dev, dev, texture_cmd, name, false}); } texture_cmd.end(); @@ -338,11 +341,13 @@ void Renderer::draw() { uniform_buffer->upload(UniformData { .mvp = p * cam.view(), .time = time, - // .aspect_ratio= static_cast(sz.width) / static_cast(sz.height), .cam_pos = cam.pos, + .viewport = glm::vec2(viewport.width, viewport.y), + .tess_factor = tess_factor, + .tess_edge_size = tess_edge_size, }); - command_buffer->command_buffer.drawIndexed(models[0]->indices.size(), 10, 0, 0, 0); + //command_buffer->command_buffer.drawIndexed(models[0]->indices.size(), 10, 0, 0, 0); command_buffer->bind(*terrain_pipeline); command_buffer->command_buffer.setViewport(0, viewport); @@ -411,10 +416,14 @@ Renderer::~Renderer() { for(auto& model : models) model.reset(); + terrain.reset(); + uniform_buffer.reset(); vertex_buffer.reset(); + terrain_pipeline.reset(); pipeline.reset(); + for (auto& tex : textures) { tex.cleanup(); } diff --git a/Renderer/Renderer.hpp b/Renderer/Renderer.hpp index 396e5d2..f43dd23 100644 --- a/Renderer/Renderer.hpp +++ b/Renderer/Renderer.hpp @@ -72,4 +72,7 @@ struct Renderer { float time = 0.0; float speed = 1.0; bool running = true; + + float tess_factor; + float tess_edge_size = 20.0f; }; diff --git a/Renderer/UniformBuffer.hpp b/Renderer/UniformBuffer.hpp index da2ff63..b1b3c61 100644 --- a/Renderer/UniformBuffer.hpp +++ b/Renderer/UniformBuffer.hpp @@ -13,6 +13,9 @@ struct UniformData { glm::mat4 mvp; float time; glm::vec3 cam_pos; + glm::vec2 viewport; + float tess_factor; + float tess_edge_size; }; struct UniformBuffer { diff --git a/Resources/Texture.cpp b/Resources/Texture.cpp index 0fd8bdf..6c10739 100644 --- a/Resources/Texture.cpp +++ b/Resources/Texture.cpp @@ -18,6 +18,7 @@ Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& com image_data = stbi_load(fname.c_str(), reinterpret_cast(&extent.width), reinterpret_cast(&extent.height), &n_channels, STBI_rgb_alpha); + Log::info("Texture is %dx%d loaded at %p\n", extent.width, extent.height, image_data); extent.depth = 1; image = std::make_unique(phys_dev, dev, extent, vk::Format::eR8G8B8A8Unorm, @@ -31,8 +32,10 @@ Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& com vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); staging->upload(image_data); - if(free_memory) + if(free_memory) { stbi_image_free(image_data); + image_data = nullptr; + } /* pipeline memory barrier ensures this doesn't get reordered wrong */ diff --git a/Scene/Terrain.cpp b/Scene/Terrain.cpp index 5413d30..9e96954 100644 --- a/Scene/Terrain.cpp +++ b/Scene/Terrain.cpp @@ -5,17 +5,16 @@ float Terrain::getHeight(int32_t x, int32_t y) { + Log::debug("Height queried at %d, %d\n", x, y); if (x < 0) - x += heightmap_tex->extent.width; + x += 64; if (y < 0) - y += heightmap_tex->extent.height; + y += 64; - if (x <= heightmap_tex->extent.width) - x -= heightmap_tex->extent.width; - if (y <= heightmap_tex->extent.height) - y -= heightmap_tex->extent.height; + x %= 64; + y %= 64; - return heightmap_tex->image_data[y * heightmap_tex->extent.width + x * 4]; + return heightmap_tex->image_data[(y * heightmap_tex->extent.height * heightmap_tex->extent.width / 64 + x * heightmap_tex->extent.width / 64) * 4]; } Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : phys_dev(phys_dev), dev(dev) { @@ -26,12 +25,15 @@ Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : ph const auto uv_scale = 1.0f; const auto vertex_count = patch_size * patch_size; - vertices.reserve(vertex_count); + vertices.resize(vertex_count); for (size_t x = 0; x < patch_size; x++) for (size_t y = 0; y < patch_size; y++) - vertices.push_back(Vertex { - .pos = glm::vec3(2.0f*x+1.0f - patch_size, 0.0f, 2.0f * y + 1.0f - patch_size), + vertices[x + y*patch_size] = (Vertex { + .pos = glm::vec3( + 2.0f * x + 1.0f - patch_size, + 0.0f, + 2.0f * y * 1.0f - patch_size), .uv = glm::vec2(static_cast(x)/patch_size, static_cast(y) / patch_size) * uv_scale, }); @@ -54,8 +56,8 @@ Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : ph * +----+----+----+ */ - for(auto x = 0_u32; x < patch_size; x++) - for (auto y = 0_u32; y < patch_size; y++) { + for(auto x = 0_i32; x < patch_size; x++) + for (auto y = 0_i32; y < patch_size; y++) { float moores_heights[3][3] = { { getHeight(x - 1, y - 1), getHeight(x - 1, y), getHeight(x - 1, y + 1) }, { getHeight(x + 0, y - 1), getHeight(x + 0, y), getHeight(x + 0, y + 1) }, @@ -84,8 +86,8 @@ Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : ph const auto w = patch_size - 1; indices.resize(w * w * 4); - for (auto x = 0_u32; x < patch_size; x++) - for (auto y = 0_u32; y < patch_size; y++) { + for (auto x = 0_u32; x < w; x++) + for (auto y = 0_u32; y < w; y++) { auto idx = x + y * w * 4; indices[idx] = x+y*patch_size; indices[idx+1] = indices[idx] + patch_size; diff --git a/UI/UI.cpp b/UI/UI.cpp index bf1bd76..be28d5b 100644 --- a/UI/UI.cpp +++ b/UI/UI.cpp @@ -11,7 +11,7 @@ #include -UI::UI(Renderer* ren) : info{ .flycam = ren->flycam, .time = ren->time, .cam = ren->cam, }, dev(ren->dev) { +UI::UI(Renderer* ren) : info{ .flycam = ren->flycam, .time = ren->time, .cam = ren->cam, .tess_factor = ren->tess_factor, .tess_edge_size = ren->tess_edge_size }, dev(ren->dev) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -92,11 +92,8 @@ void UI::newFrame() { ImGui::Text("FPS: %f", info.fps); ImGui::Text("Time: %f", info.time); ImGui::Checkbox("Fly Camera", &info.flycam); - ImGui::SliderAngle("Theta", &info.cam.theta, 0.01, 179.9); - ImGui::SliderAngle("Phi", &info.cam.phi, 0.0, 360.0, "%.0f def"); - ImGui::SliderFloat("cam.x", &info.cam.pos.x, -1e2, 1e2); - ImGui::SliderFloat("cam.y", &info.cam.pos.y, -1e2, 1e2); - ImGui::SliderFloat("cam.z", &info.cam.pos.z, -1e2, 1e2); + ImGui::SliderFloat("Tessellation Factor: %f", &info.tess_factor, 0.0, 20.0); + ImGui::SliderFloat("Edge Size: %f", &info.tess_edge_size, 0.0, 40.0); ImGui::End(); } diff --git a/UI/UI.hpp b/UI/UI.hpp index 8bd8f85..af2683f 100644 --- a/UI/UI.hpp +++ b/UI/UI.hpp @@ -15,6 +15,8 @@ struct UI { float& time; /* camera stuff */ Camera& cam; + float& tess_factor; + float& tess_edge_size; } info; vk::Device dev; diff --git a/assets/shaders/terrain.frag b/assets/shaders/terrain.frag new file mode 100644 index 0000000..bebcef2 --- /dev/null +++ b/assets/shaders/terrain.frag @@ -0,0 +1,18 @@ +#version 460 core + +layout (location = 0) in vec3 norm; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in vec3 pos; +layout (location = 0) out vec4 FragColor; + +layout (set = 0, binding = 0) uniform Matrices { + mat4 mvp; + float time; + vec3 cam_pos; +}; + +layout (set = 0, binding = 1) uniform sampler2D tex; + +void main() { + FragColor = vec4(1.0); +} \ No newline at end of file diff --git a/assets/shaders/terrain.frag.spv b/assets/shaders/terrain.frag.spv new file mode 100644 index 0000000..dfca963 Binary files /dev/null and b/assets/shaders/terrain.frag.spv differ diff --git a/assets/shaders/terrain.tesc b/assets/shaders/terrain.tesc new file mode 100644 index 0000000..61de8fe --- /dev/null +++ b/assets/shaders/terrain.tesc @@ -0,0 +1,60 @@ +#version 450 core + +layout (set = 0, binding = 0) uniform Matrices { + mat4 mvp; + float time; + vec3 cam_pos; + vec2 viewport; + float tess_factor; + float tess_edge_size; +}; + +layout(set = 0, binding = 1) uniform sampler2D heightmap; + +layout (vertices = 4) out; + +layout (location = 0) in vec3 norm[]; +layout (location = 1) in vec2 texCoord[]; +layout (location = 2) in vec3 pos[]; + +layout (location = 0) out vec3 _norm[4]; +layout (location = 1) out vec2 _texCoord[4]; +layout (location = 2) out vec3 _pos[4]; + + +float screen_space_tess(vec4 p0, vec4 p1) { + /* calc midpoint + distance btw the two points */ + vec4 mid = (p0+p1) / 2.0; + float r = distance(p0, p1) / 2.0; + + vec4 v0 = mvp * mid; + + /* projevt into clip spaace */ + vec4 clip0 = mvp * (v0 - vec4(r, 0.0, 0.0, 0.0)); + vec4 clip1 = mvp * (v0 + vec4(r, 0.0, 0.0, 0.0)); + + clip0 /= clip0.w; + clip1 /= clip1.w; + + /* convert to viewport coords */ + clip0.xy * viewport; + clip1.xy * viewport; + + return clamp(distance(clip0, clip0) / tess_edge_size * tess_factor, 1.0, 64.0); +} + +void main() { + if(gl_InvocationID == 0) { + gl_TessLevelOuter[0] = screen_space_tess(gl_in[3].gl_Position, gl_in[0].gl_Position); + gl_TessLevelOuter[1] = screen_space_tess(gl_in[0].gl_Position, gl_in[1].gl_Position); + gl_TessLevelOuter[2] = screen_space_tess(gl_in[1].gl_Position, gl_in[2].gl_Position); + gl_TessLevelOuter[3] = screen_space_tess(gl_in[2].gl_Position, gl_in[3].gl_Position); + gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5); + gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5); + } + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + _norm[gl_InvocationID] = norm[gl_InvocationID]; + _texCoord[gl_InvocationID] = texCoord[gl_InvocationID]; + _pos[gl_InvocationID] = pos[gl_InvocationID]; +} \ No newline at end of file diff --git a/assets/shaders/terrain.tesc.spv b/assets/shaders/terrain.tesc.spv new file mode 100644 index 0000000..0752442 Binary files /dev/null and b/assets/shaders/terrain.tesc.spv differ diff --git a/assets/shaders/terrain.tese b/assets/shaders/terrain.tese new file mode 100644 index 0000000..ab83884 --- /dev/null +++ b/assets/shaders/terrain.tese @@ -0,0 +1,42 @@ +#version 450 core + +layout (set = 0, binding = 0) uniform Matrices { + mat4 mvp; + float time; + vec3 cam_pos; + vec2 viewport; + float tess_factor; + float tess_edge_size; +}; + +layout(set = 0, binding = 1) uniform sampler2D heightmap; + +layout(quads, equal_spacing, ccw) in; + + +layout (location = 0) in vec3 norm[]; +layout (location = 1) in vec2 texCoord[]; +layout (location = 2) in vec3 pos[]; + +layout (location = 0) out vec3 _norm; +layout (location = 1) out vec2 _texCoord; +layout (location = 2) out vec3 _pos; + +void main() { + /* interpolation of UV coordinates */ + vec2 uv1 = mix(texCoord[0], texCoord[1], gl_TessCoord.x); + vec2 uv2 = mix(texCoord[3], texCoord[2], gl_TessCoord.x); + _texCoord = mix(uv1, uv2, gl_TessCoord.y); + + + vec3 norm1 = mix(norm[0], norm[1], gl_TessCoord.x); + vec3 norm2 = mix(norm[3], norm[2], gl_TessCoord.x); + _norm = mix(norm1, norm2, gl_TessCoord.y); + + /* not displacing yet */ + vec4 pos1 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x); + vec4 pos2 = mix(gl_in[3].gl_Position, gl_in[2].gl_Position, gl_TessCoord.x); + _pos = mix(pos1, pos2, gl_TessCoord.y).xyz; + + gl_Position = mvp * mix(pos1, pos2, gl_TessCoord.y); +} \ No newline at end of file diff --git a/assets/shaders/terrain.tese.spv b/assets/shaders/terrain.tese.spv new file mode 100644 index 0000000..5253329 Binary files /dev/null and b/assets/shaders/terrain.tese.spv differ diff --git a/assets/shaders/terrain.vert b/assets/shaders/terrain.vert new file mode 100644 index 0000000..55b4d11 --- /dev/null +++ b/assets/shaders/terrain.vert @@ -0,0 +1,20 @@ +#version 460 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNorm; +layout (location = 2) in vec2 aTexCoord; + +layout (location = 0) out vec3 norm; +layout (location = 1) out vec2 texCoord; +layout (location = 2) out vec3 pos; + +layout (set = 0, binding = 0) uniform Matrices { + mat4 mvp; + float time; +}; + +void main() { + pos = aPos; + gl_Position = vec4(pos, 1.0); + texCoord = aTexCoord; + norm = aNorm; +} \ No newline at end of file diff --git a/assets/shaders/terrain.vert.spv b/assets/shaders/terrain.vert.spv new file mode 100644 index 0000000..f6a38da Binary files /dev/null and b/assets/shaders/terrain.vert.spv differ diff --git a/assets/textures/heightmap.png b/assets/textures/heightmap.png new file mode 100644 index 0000000..f5129a7 Binary files /dev/null and b/assets/textures/heightmap.png differ diff --git a/pléascach.cpp b/pléascach.cpp index 7ac76c9..e034535 100644 --- a/pléascach.cpp +++ b/pléascach.cpp @@ -28,7 +28,7 @@ int main(int argc, char* argv[]) { while (in->queue.size()) { /* take event from front of queue, then process it */ - const auto& event = in->queue.front(); + const auto event = in->queue.front(); in->queue.pop(); switch (event.tag) { case InputEvent::Tag::eRESIZE: