First part of terrain generation (hopefully) done, next part is to make the tesselation shaders

This commit is contained in:
connellpaxton 2024-02-05 07:53:37 -05:00
parent 6870cb61f9
commit a778a406d0
11 changed files with 211 additions and 36 deletions

View File

@ -18,8 +18,9 @@ struct Buffer {
inline void upload(const uint8_t* data) {
upload(data, size);
}
inline void upload(const std::vector<uint8_t>& data) {
upload(data.data(), size);
template<typename T>
inline void upload(const std::vector<T>& data) {
upload(reinterpret_cast<uint8_t*>(data.data()), data.size()*sizeof(T));
}
operator vk::Buffer& () {

View File

@ -2,6 +2,8 @@
#include <Renderer/Pipeline.hpp>
#include <Renderer/VertexBuffer.hpp>
#include <Scene/Terrain.hpp>
#include <Model/Model.hpp>
#include <Memory/Buffer.hpp>
@ -80,6 +82,11 @@ void CommandBuffer::bind(std::shared_ptr<Model> model) {
command_buffer.bindIndexBuffer(*model->index_buffer, 0, vk::IndexType::eUint16);
}
void CommandBuffer::bind(Terrain* terrain) {
bind(*terrain->vertex_buffer);
command_buffer.bindIndexBuffer(*terrain->index_buffer, 0, vk::IndexType::eUint32);
}
void CommandBuffer::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance) {
command_buffer.draw(vertex_count, instance_count, first_vertex, first_instance);
}

View File

@ -13,6 +13,7 @@ struct GraphicsPipeline;
struct ComputePipeline;
struct VertexBuffer;
struct Model;
struct Terrain;
struct CommandBuffer {
CommandBuffer(vk::Device dev, u32 queue_family);
@ -31,6 +32,7 @@ struct CommandBuffer {
void bind(vk::PipelineLayout layout, vk::ArrayProxy<vk::DescriptorSet> desc_sets);
void bind(const VertexBuffer& vertex_buffer, uint32_t binding = 0);
void bind(std::shared_ptr<Model> model);
void bind(Terrain* terrain);
void draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex = 0, uint32_t first_instance = 0);

View File

@ -9,7 +9,7 @@
#include <util/log.hpp>
GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector<Shader>& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy<vk::DescriptorSetLayoutBinding> bindings, const VertexBuffer& vertex_buffer) : dev(dev) {
GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector<Shader>& shaders, const vk::Extent2D& extent, const RenderPass& render_pass, vk::ArrayProxy<vk::DescriptorSetLayoutBinding> bindings, const VertexBuffer& vertex_buffer, enum Type type = Type::GLTF) : dev(dev) {
/* create layout
* Eventually add a graphicspipline constructor that allows specification of layouts etc
* kinda like how Image::Image has all those versions
@ -74,16 +74,16 @@ GraphicsPipeline::GraphicsPipeline(vk::Device dev, const std::vector<Shader>& sh
.pVertexAttributeDescriptions = attrs.data(),
};
const auto input_asm_info = vk::PipelineInputAssemblyStateCreateInfo {
.topology = vk::PrimitiveTopology::eTriangleList,
const auto input_asm_info = vk::PipelineInputAssemblyStateCreateInfo{
.topology = type == Type::eGLTF ? vk::PrimitiveTopology::eTriangleList : vk::PrimitiveTopology::ePatchList,
/* matters later if we use strip primitives */
.primitiveRestartEnable = vk::False,
};
const auto raster_info = vk::PipelineRasterizationStateCreateInfo {
.depthClampEnable = vk::False,
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eNone,
.polygonMode = type == Type::eGLTF? vk::PolygonMode::eFill : vk::PolygonMode::eLine,
.cullMode = vk::CullModeFlagBits::eBack,
.frontFace = vk::FrontFace::eCounterClockwise,
.depthBiasEnable = vk::False,
.lineWidth = 1.0,

View File

@ -10,10 +10,15 @@ struct RenderPass;
struct Texture;
struct GraphicsPipeline {
enum Type {
eGLTF,
eTERRAIN,
};
GraphicsPipeline(vk::Device dev, const std::vector<Shader>& shaders,
const vk::Extent2D& extent, const RenderPass& render_pass,
vk::ArrayProxy<vk::DescriptorSetLayoutBinding> bindings,
const VertexBuffer& vertex_buffer);
const VertexBuffer& vertex_buffer, enum Type type = Type::eGLTF);
vk::Device dev;
vk::Pipeline pipeline;
vk::PipelineLayout layout;
@ -32,4 +37,3 @@ struct GraphicsPipeline {
~GraphicsPipeline();
};

View File

@ -22,7 +22,7 @@ using namespace std::string_literals;
Renderer::Renderer(Window& win) : win(win) {
/* Create Instance object */
auto app_info = vk::ApplicationInfo {
auto app_info = vk::ApplicationInfo{
.pApplicationName = "Pléascach Demo",
.applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
.pEngineName = "Pléascach",
@ -54,7 +54,7 @@ Renderer::Renderer(Window& win) : win(win) {
}
const char* my_layers[] = {
// "VK_LAYER_LUNARG_api_dump",
// "VK_LAYER_LUNARG_api_dump",
"VK_LAYER_KHRONOS_validation",
};
@ -115,7 +115,7 @@ Renderer::Renderer(Window& win) : win(win) {
Log::info("Selected queue family: %i\n", queue_family);
float priorities[] = {1.0f};
float priorities[] = { 1.0f };
auto queue_info = vk::DeviceQueueCreateInfo{
.queueFamilyIndex = static_cast<u32>(queue_family),
.queueCount = 1,
@ -141,11 +141,11 @@ Renderer::Renderer(Window& win) : win(win) {
"VK_LAYER_KHRONOS_validation"
};
const auto features = vk::PhysicalDeviceFeatures {
const auto features = vk::PhysicalDeviceFeatures{
.geometryShader = vk::True,
};
auto dev_info = vk::DeviceCreateInfo {
auto dev_info = vk::DeviceCreateInfo{
.flags = vk::DeviceCreateFlagBits(0),
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_info,
@ -167,7 +167,7 @@ Renderer::Renderer(Window& win) : win(win) {
queue = dev.getQueue(queue_family, 0);
render_fence = dev.createFence(vk::FenceCreateInfo { .flags = vk::FenceCreateFlagBits::eSignaled });
render_fence = dev.createFence(vk::FenceCreateInfo {.flags = vk::FenceCreateFlagBits::eSignaled });
image_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{});
render_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{});
@ -180,12 +180,12 @@ Renderer::Renderer(Window& win) : win(win) {
uniform_buffer = std::make_unique<UniformBuffer>(phys_dev, dev);
textures = createTextures({
textures = createResources({
"assets/textures/oil.jpg",
});
std::vector<Shader> shaders = {
{dev, "assets/shaders/fraglight.vert.spv", vk::ShaderStageFlagBits::eVertex},
{dev, "assets/shaders/fraglight.vert.spv", vk::ShaderStageFlagBits::eVertex },
{ dev, "assets/shaders/fraglight.geom.spv", vk::ShaderStageFlagBits::eGeometry },
{ dev, "assets/shaders/lambert.frag.spv", vk::ShaderStageFlagBits::eFragment },
};
@ -207,13 +207,28 @@ Renderer::Renderer(Window& win) : win(win) {
pipeline->update(0, *uniform_buffer);
pipeline->update(1, textures[0]);
/* create Terrain */
terrain = std::make_unique<Terrain>(phys_dev, dev, textures[0]);
std::vector<Shader> terrain_shaders = {
{ dev, "assets/shaders/terrain.vert.spv", vk::ShaderStageFlagBits::eVertex },
{ dev, "assets/shaders/terrain.tesc.spv", vk::ShaderStageFlagBits::eTessellationControl },
{ dev, "assets/shaders/terrain.tese.spv", vk::ShaderStageFlagBits::eTessellationEvaluation },
{ dev, "assets/shaders/terrain.frag.spv", vk::ShaderStageFlagBits::eFragment },
};
terrain_pipeline = std::make_unique<GraphicsPipeline>(dev, terrain_shaders, swapchain->extent, *render_pass, bindings, terrain->vertex_buffer, GraphicsPipeline::eTERRAIN);
for (auto& shader : shaders)
shader.cleanup();
for (auto& shader : terrain_shaders)
shader.cleanup();
ui = std::make_unique<UI>(this);
}
std::vector<Texture> Renderer::createTextures(const std::vector<std::string>& names) {
std::vector<Texture> Renderer::createResources(const std::vector<std::string>& names) {
std::vector<Texture> ret;
CommandBuffer texture_cmd(dev, queue_family);
@ -327,13 +342,14 @@ void Renderer::draw() {
.cam_pos = cam.pos,
});
//command_buffer->draw(std::size(triangle), 1, 0, 0);
command_buffer->command_buffer.drawIndexed(models[0]->indices.size(), 10, 0, 0, 0);
/*command_buffer->bind(*models[1]->vertex_buffer);
command_buffer->command_buffer.bindIndexBuffer(*models[1]->index_buffer, 0, vk::IndexType::eUint16);
command_buffer->command_buffer.drawIndexed(models[1]->indices.size(), 1, 0, 0, 0);*/
command_buffer->bind(*terrain_pipeline);
command_buffer->command_buffer.setViewport(0, viewport);
command_buffer->command_buffer.setScissor(0, scissor);
command_buffer->bind(terrain.get());
command_buffer->command_buffer.drawIndexed(terrain->indices.size(), 1, 0, 0, 0);
/* draw User Interface stuff */
ui->newFrame();

View File

@ -10,6 +10,7 @@
#include <Renderer/RenderPass.hpp>
#include <Scene/Camera.hpp>
#include <Scene/Terrain.hpp>
#include <Model/Model.hpp>
@ -25,7 +26,7 @@ struct Renderer {
Renderer(Window& win);
~Renderer();
std::vector<Texture> createTextures(const std::vector<std::string>& names);
std::vector<Texture> createResources(const std::vector<std::string>& names);
void draw();
void present();
@ -47,11 +48,14 @@ struct Renderer {
std::unique_ptr<CommandBuffer> command_buffer;
std::unique_ptr<RenderPass> render_pass;
/* For now, couple it all together as one pipeline with one uniform buffer, vertex buffer, etc */
std::unique_ptr<GraphicsPipeline> pipeline;
std::unique_ptr<GraphicsPipeline> terrain_pipeline;
std::unique_ptr<VertexBuffer> vertex_buffer;
std::unique_ptr<UniformBuffer> uniform_buffer;
std::unique_ptr<Terrain> terrain;
std::vector<Texture> textures;
uint32_t current_image_idx;

View File

@ -11,13 +11,13 @@
#include <stb_image.h>
/* externall synchonized, just writes to a command buffer, you still need to pull the trigger (allows bulk texture image preperation) */
Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname) : dev(dev) {
Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname, bool free_memory) : dev(dev), free_memory(free_memory) {
Log::debug("Starting texture creation for: %s\n", fname.c_str());
int n_channels;
vk::Extent3D extent;
auto image_data = stbi_load(fname.c_str(), reinterpret_cast<int*>(&extent.width), reinterpret_cast<int*>(&extent.height), &n_channels, STBI_rgb_alpha);
image_data = stbi_load(fname.c_str(), reinterpret_cast<int*>(&extent.width), reinterpret_cast<int*>(&extent.height), &n_channels, STBI_rgb_alpha);
extent.depth = 1;
image = std::make_unique<Image>(phys_dev, dev, extent, vk::Format::eR8G8B8A8Unorm,
@ -31,6 +31,7 @@ Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& com
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
staging->upload(image_data);
if(free_memory)
stbi_image_free(image_data);
/* pipeline memory barrier ensures this doesn't get reordered wrong */
@ -84,9 +85,9 @@ void Texture::finishCreation() {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
.addressModeW = vk::SamplerAddressMode::eClampToEdge,
.addressModeU = vk::SamplerAddressMode::eMirroredRepeat,
.addressModeV = vk::SamplerAddressMode::eMirroredRepeat,
.addressModeW = vk::SamplerAddressMode::eMirroredRepeat,
.mipLodBias = 0.0,
.anisotropyEnable = vk::False,
.maxAnisotropy = 0.0,
@ -100,6 +101,8 @@ void Texture::finishCreation() {
}
void Texture::cleanup() {
if (!free_memory)
free(image_data);
staging.reset();
image->cleanup();
dev.destroyImageView(view);

View File

@ -14,10 +14,13 @@ struct Texture {
std::unique_ptr<Image> image;
vk::ImageView view;
vk::Sampler sampler;
bool free_memory;
uint8_t* image_data;
vk::Extent3D extent;
std::unique_ptr<Buffer> staging;
Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname);
Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer& command_buffer, const std::string& fname, bool free_memory = true);
inline vk::DescriptorSetLayoutBinding binding(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eAll) {
return vk::DescriptorSetLayoutBinding {

109
Scene/Terrain.cpp Normal file
View File

@ -0,0 +1,109 @@
#include <Renderer/CommandBuffer.hpp>
#include <Scene/Terrain.hpp>
#include <util/int.hpp>
float Terrain::getHeight(int32_t x, int32_t y) {
if (x < 0)
x += heightmap_tex->extent.width;
if (y < 0)
y += heightmap_tex->extent.height;
if (x <= heightmap_tex->extent.width)
x -= heightmap_tex->extent.width;
if (y <= heightmap_tex->extent.height)
y -= heightmap_tex->extent.height;
return heightmap_tex->image_data[y * heightmap_tex->extent.width + x * 4];
}
Terrain::Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& tex) : phys_dev(phys_dev), dev(dev) {
/* tell Texture() not to free so we can apply our sobel filter */
heightmap_tex = &tex;
const auto patch_size = 64_u32;
const auto uv_scale = 1.0f;
const auto vertex_count = patch_size * patch_size;
vertices.reserve(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),
.uv = glm::vec2(static_cast<float>(x)/patch_size, static_cast<float>(y) / patch_size) * uv_scale,
});
/* use sobel filters to get normal:
* X sobel:
* +----+----+----+
* | +1 | +0 | -1 |
* +----+----+----+
* | +2 | +0 | -2 |
* +----+----+----+
* | +1 | +0 | -1 |
* +----+----+----+
* Y sobel:
* +----+----+----+
* | +1 | +2 | +1 |
* +----+----+----+
* | +0 | +0 | +0 |
* +----+----+----+
* | -1 | -2 | -1 |
* +----+----+----+
*/
for(auto x = 0_u32; x < patch_size; x++)
for (auto y = 0_u32; 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) },
{ getHeight(x + 1, y - 1), getHeight(x + 1, y), getHeight(x + 1, y + 1) },
};
auto normal = glm::vec3(
/* x gets X sobel filter */
moores_heights[0][0] + 2.0f * moores_heights[0][1] + moores_heights[0][2]
- moores_heights[2][0] - 2.0f * moores_heights[2][1] - moores_heights[2][2],
0.0,
/* z gets Y sobel filter */
moores_heights[0][0] + 2.0f * moores_heights[1][0] + moores_heights[2][0]
- moores_heights[0][2] - 2.0f * moores_heights[1][2] - moores_heights[2][2]
);
/* fill in missing component, first scalar scales bump */
normal.y = 0.25 * glm::sqrt(1.0 -glm::dot(normal, normal));
vertices[x + y * patch_size].norm = glm::normalize(normal * glm::vec3(2.0f, 1.0f, 2.0f));
}
vertex_buffer = std::make_unique<VertexBuffer>(phys_dev, dev, vertices.size());
vertex_buffer->upload(vertices);
/* index generation */
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++) {
auto idx = x + y * w * 4;
indices[idx] = x+y*patch_size;
indices[idx+1] = indices[idx] + patch_size;
indices[idx+2] = indices[idx+1] + 1;
indices[idx + 3] = indices[idx] + 1;
}
index_buffer = std::make_unique<Buffer>(phys_dev, dev, sizeof(uint32_t)*indices.size(),
vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eHostCoherent
| vk::MemoryPropertyFlagBits::eHostVisible);
index_buffer->upload(indices);
}
void Terrain::draw(CommandBuffer& cmd) {
}
Terrain::~Terrain() {
index_buffer.reset();
vertex_buffer.reset();
}

26
Scene/Terrain.hpp Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <Resources/Texture.hpp>
#include <Renderer/VertexBuffer.hpp>
#include <Memory/Buffer.hpp>
#include <string>
struct CommandBuffer;
struct Terrain {
vk::PhysicalDevice phys_dev;
vk::Device dev;
Texture* heightmap_tex;
std::unique_ptr<VertexBuffer> vertex_buffer;
std::unique_ptr<Buffer> index_buffer;
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
Terrain(vk::PhysicalDevice phys_dev, vk::Device dev, Texture& hieghtmap);
float getHeight(int32_t x, int32_t y);
void draw(CommandBuffer& cmd);
~Terrain();
};