better handling of models, textures and shaders
This commit is contained in:
parent
4a4939e65b
commit
edbcf2406b
14
meson.build
14
meson.build
|
@ -31,13 +31,25 @@ deps = [
|
|||
dependency('vulkan', include_type: 'system'),
|
||||
]
|
||||
|
||||
glslang_dep = cpp.find_library('glslang', required: false)
|
||||
|
||||
if not glslang_dep.found()
|
||||
glslang_dep = dependency('glslang', required: true, include_type: 'system')
|
||||
endif
|
||||
|
||||
spirv_dep = cpp.find_library('SPIRV', required: false)
|
||||
|
||||
if not spirv_dep.found()
|
||||
spirv_dep = dependency('SPIRV', required: true, include_type: 'system')
|
||||
endif
|
||||
|
||||
imgui_dep = dependency('imgui', required: false, include_type: 'system')
|
||||
|
||||
if not imgui_dep.found()
|
||||
imgui_dep = cpp.find_library('imgui', required: true)
|
||||
endif
|
||||
|
||||
deps += imgui_dep
|
||||
deps += [glslang_dep, spirv_dep, imgui_dep]
|
||||
|
||||
executable(
|
||||
'graphics-test',
|
||||
|
|
BIN
shaders/frag.spv
BIN
shaders/frag.spv
Binary file not shown.
|
@ -1,12 +0,0 @@
|
|||
#version 450
|
||||
|
||||
layout(binding = 1) uniform sampler2D texSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = texture(texSampler, fragTexCoord);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#version 450
|
||||
|
||||
layout(binding = 0) uniform UniformBufferObject {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
} ubo;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
fragTexCoord = inTexCoord;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR" || exit
|
||||
|
||||
glslc -c ./shader.vert -o ./vert.spv
|
||||
glslc -c ./shader.frag -o ./frag.spv
|
BIN
shaders/vert.spv
BIN
shaders/vert.spv
Binary file not shown.
41
src/main.cpp
41
src/main.cpp
|
@ -22,6 +22,7 @@
|
|||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
||||
// Include custom utility headers
|
||||
#include "util/shader.h" // Shader compilation utilities
|
||||
#include "util/types.h" // Custom type definitions
|
||||
#include "util/unique_image.h" // Custom image handling utilities
|
||||
#include "util/vertex.h" // Custom vertex structure definition
|
||||
|
@ -39,14 +40,6 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
|||
constexpr i32 WIDTH = 800;
|
||||
constexpr i32 HEIGHT = 600;
|
||||
|
||||
// File paths for 3D model and texture
|
||||
constexpr const char* MODEL_PATH = "models/viking_room.obj";
|
||||
constexpr const char* TEXTURE_PATH = "textures/viking_room.png";
|
||||
|
||||
// File paths for shader programs
|
||||
constexpr const char* FRAGMENT_SHADER_PATH = "shaders/frag.spv";
|
||||
constexpr const char* VERTEX_SHADER_PATH = "shaders/vert.spv";
|
||||
|
||||
// Maximum number of frames that can be processed concurrently
|
||||
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
|
@ -73,10 +66,12 @@ class VulkanApp {
|
|||
* It also cleans up resources when the application is closed.
|
||||
*/
|
||||
fn run() -> void {
|
||||
InitGlslang();
|
||||
initWindow(); // Initialize the application window
|
||||
initVulkan(); // Initialize Vulkan
|
||||
mainLoop(); // Enter the main rendering loop
|
||||
|
||||
CleanupGlslang();
|
||||
cleanupSwapChain(); // Clean up swap chain resources
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
|
@ -472,7 +467,7 @@ class VulkanApp {
|
|||
|
||||
// Application metadata
|
||||
vk::ApplicationInfo appInfo {
|
||||
.pApplicationName = "Hello Triangle",
|
||||
.pApplicationName = "Vulkan App",
|
||||
.applicationVersion = 1,
|
||||
.pEngineName = "No Engine",
|
||||
.engineVersion = 1,
|
||||
|
@ -848,8 +843,8 @@ class VulkanApp {
|
|||
* states.
|
||||
*/
|
||||
fn createGraphicsPipeline() -> void {
|
||||
std::vector<char> vertShaderCode = readFile(VERTEX_SHADER_PATH);
|
||||
std::vector<char> fragShaderCode = readFile(FRAGMENT_SHADER_PATH);
|
||||
std::vector<u32> vertShaderCode = CompileShader(vertShaderSrc, EShLangVertex);
|
||||
std::vector<u32> fragShaderCode = CompileShader(fragShaderSrc, EShLangFragment);
|
||||
|
||||
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
|
||||
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
|
||||
|
@ -1105,7 +1100,7 @@ class VulkanApp {
|
|||
* @param format The format to check.
|
||||
* @return True if the format has a stencil component, false otherwise.
|
||||
*/
|
||||
static fn hasStencilComponent(vk::Format format) {
|
||||
static fn hasStencilComponent(const vk::Format& format) {
|
||||
return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1112,8 @@ class VulkanApp {
|
|||
* buffer to the texture image. It also generates mipmaps for the texture.
|
||||
*/
|
||||
fn createTextureImage() -> void {
|
||||
stb::UniqueImage image(TEXTURE_PATH);
|
||||
std::filesystem::path texturePath = std::filesystem::current_path() / "textures" / "viking_room.png";
|
||||
stb::UniqueImage image(texturePath);
|
||||
|
||||
u8* pixels = image.getData();
|
||||
i32 texWidth = image.getWidth(), texHeight = image.getHeight();
|
||||
|
@ -1176,8 +1172,13 @@ class VulkanApp {
|
|||
* This function generates mipmaps for the given texture image by repeatedly scaling down
|
||||
* the image by half until reaching the smallest mip level.
|
||||
*/
|
||||
fn generateMipmaps(vk::Image image, vk::Format imageFormat, i32 texWidth, i32 texHeight, u32 mipLevels)
|
||||
-> void {
|
||||
fn generateMipmaps(
|
||||
const vk::Image& image,
|
||||
const vk::Format& imageFormat,
|
||||
const i32& texWidth,
|
||||
const i32& texHeight,
|
||||
const u32& mipLevels
|
||||
) -> void {
|
||||
vk::FormatProperties formatProperties = mPhysicalDevice.getFormatProperties(imageFormat);
|
||||
|
||||
if (!(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear))
|
||||
|
@ -1527,7 +1528,9 @@ class VulkanApp {
|
|||
std::vector<tinyobj::material_t> materials;
|
||||
std::string warn, err;
|
||||
|
||||
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH))
|
||||
std::filesystem::path modelPath = std::filesystem::current_path() / "models" / "viking_room.obj";
|
||||
|
||||
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, modelPath.string().c_str()))
|
||||
throw std::runtime_error(warn + err);
|
||||
|
||||
std::unordered_map<Vertex, u32> uniqueVertices {};
|
||||
|
@ -2183,10 +2186,10 @@ class VulkanApp {
|
|||
*
|
||||
* This function takes compiled shader code and creates a Vulkan shader module from it.
|
||||
*/
|
||||
fn createShaderModule(const std::vector<char>& code) -> vk::UniqueShaderModule {
|
||||
fn createShaderModule(const std::vector<u32>& code) -> vk::UniqueShaderModule {
|
||||
vk::ShaderModuleCreateInfo createInfo {
|
||||
.codeSize = code.size(),
|
||||
.pCode = std::bit_cast<const u32*>(code.data()),
|
||||
.codeSize = code.size() * sizeof(u32),
|
||||
.pCode = code.data(),
|
||||
};
|
||||
|
||||
return mDevice->createShaderModuleUnique(createInfo);
|
||||
|
|
196
src/util/shader.h
Normal file
196
src/util/shader.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/SPIRV/GlslangToSpv.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
const TBuiltInResource DefaultTBuiltInResource = {
|
||||
.maxLights = 32,
|
||||
.maxClipPlanes = 6,
|
||||
.maxTextureUnits = 32,
|
||||
.maxTextureCoords = 32,
|
||||
.maxVertexAttribs = 64,
|
||||
.maxVertexUniformComponents = 4096,
|
||||
.maxVaryingFloats = 64,
|
||||
.maxVertexTextureImageUnits = 32,
|
||||
.maxCombinedTextureImageUnits = 80,
|
||||
.maxTextureImageUnits = 32,
|
||||
.maxFragmentUniformComponents = 4096,
|
||||
.maxDrawBuffers = 32,
|
||||
.maxVertexUniformVectors = 128,
|
||||
.maxVaryingVectors = 8,
|
||||
.maxFragmentUniformVectors = 16,
|
||||
.maxVertexOutputVectors = 16,
|
||||
.maxFragmentInputVectors = 15,
|
||||
.minProgramTexelOffset = -8,
|
||||
.maxProgramTexelOffset = 7,
|
||||
.maxClipDistances = 8,
|
||||
.maxComputeWorkGroupCountX = 65535,
|
||||
.maxComputeWorkGroupCountY = 65535,
|
||||
.maxComputeWorkGroupCountZ = 65535,
|
||||
.maxComputeWorkGroupSizeX = 1024,
|
||||
.maxComputeWorkGroupSizeY = 1024,
|
||||
.maxComputeWorkGroupSizeZ = 64,
|
||||
.maxComputeUniformComponents = 1024,
|
||||
.maxComputeTextureImageUnits = 16,
|
||||
.maxComputeImageUniforms = 8,
|
||||
.maxComputeAtomicCounters = 8,
|
||||
.maxComputeAtomicCounterBuffers = 1,
|
||||
.maxVaryingComponents = 60,
|
||||
.maxVertexOutputComponents = 64,
|
||||
.maxGeometryInputComponents = 64,
|
||||
.maxGeometryOutputComponents = 128,
|
||||
.maxFragmentInputComponents = 128,
|
||||
.maxImageUnits = 8,
|
||||
.maxCombinedImageUnitsAndFragmentOutputs = 8,
|
||||
.maxCombinedShaderOutputResources = 8,
|
||||
.maxImageSamples = 0,
|
||||
.maxVertexImageUniforms = 0,
|
||||
.maxTessControlImageUniforms = 0,
|
||||
.maxTessEvaluationImageUniforms = 0,
|
||||
.maxGeometryImageUniforms = 0,
|
||||
.maxFragmentImageUniforms = 8,
|
||||
.maxCombinedImageUniforms = 8,
|
||||
.maxGeometryTextureImageUnits = 16,
|
||||
.maxGeometryOutputVertices = 256,
|
||||
.maxGeometryTotalOutputComponents = 1024,
|
||||
.maxGeometryUniformComponents = 1024,
|
||||
.maxGeometryVaryingComponents = 64,
|
||||
.maxTessControlInputComponents = 128,
|
||||
.maxTessControlOutputComponents = 128,
|
||||
.maxTessControlTextureImageUnits = 16,
|
||||
.maxTessControlUniformComponents = 1024,
|
||||
.maxTessControlTotalOutputComponents = 4096,
|
||||
.maxTessEvaluationInputComponents = 128,
|
||||
.maxTessEvaluationOutputComponents = 128,
|
||||
.maxTessEvaluationTextureImageUnits = 16,
|
||||
.maxTessEvaluationUniformComponents = 1024,
|
||||
.maxTessPatchComponents = 120,
|
||||
.maxPatchVertices = 32,
|
||||
.maxTessGenLevel = 64,
|
||||
.maxViewports = 16,
|
||||
.maxVertexAtomicCounters = 0,
|
||||
.maxTessControlAtomicCounters = 0,
|
||||
.maxTessEvaluationAtomicCounters = 0,
|
||||
.maxGeometryAtomicCounters = 0,
|
||||
.maxFragmentAtomicCounters = 8,
|
||||
.maxCombinedAtomicCounters = 8,
|
||||
.maxAtomicCounterBindings = 1,
|
||||
.maxVertexAtomicCounterBuffers = 0,
|
||||
.maxTessControlAtomicCounterBuffers = 0,
|
||||
.maxTessEvaluationAtomicCounterBuffers = 0,
|
||||
.maxGeometryAtomicCounterBuffers = 0,
|
||||
.maxFragmentAtomicCounterBuffers = 1,
|
||||
.maxCombinedAtomicCounterBuffers = 1,
|
||||
.maxAtomicCounterBufferSize = 16384,
|
||||
.maxTransformFeedbackBuffers = 4,
|
||||
.maxTransformFeedbackInterleavedComponents = 64,
|
||||
.maxCullDistances = 8,
|
||||
.maxCombinedClipAndCullDistances = 8,
|
||||
.maxSamples = 4,
|
||||
.maxMeshOutputVerticesNV = 256,
|
||||
.maxMeshOutputPrimitivesNV = 512,
|
||||
.maxMeshWorkGroupSizeX_NV = 32,
|
||||
.maxMeshWorkGroupSizeY_NV = 1,
|
||||
.maxMeshWorkGroupSizeZ_NV = 1,
|
||||
.maxTaskWorkGroupSizeX_NV = 32,
|
||||
.maxTaskWorkGroupSizeY_NV = 1,
|
||||
.maxTaskWorkGroupSizeZ_NV = 1,
|
||||
.maxMeshViewCountNV = 4,
|
||||
.maxMeshOutputVerticesEXT = 256,
|
||||
.maxMeshOutputPrimitivesEXT = 512,
|
||||
.maxMeshWorkGroupSizeX_EXT = 32,
|
||||
.maxMeshWorkGroupSizeY_EXT = 1,
|
||||
.maxMeshWorkGroupSizeZ_EXT = 1,
|
||||
.maxTaskWorkGroupSizeX_EXT = 32,
|
||||
.maxTaskWorkGroupSizeY_EXT = 1,
|
||||
.maxTaskWorkGroupSizeZ_EXT = 1,
|
||||
.maxMeshViewCountEXT = 4,
|
||||
.maxDualSourceDrawBuffersEXT = 1,
|
||||
.limits = {
|
||||
.nonInductiveForLoops = true,
|
||||
.whileLoops = true,
|
||||
.doWhileLoops = true,
|
||||
.generalUniformIndexing = true,
|
||||
.generalAttributeMatrixVectorIndexing = true,
|
||||
.generalVaryingIndexing = true,
|
||||
.generalSamplerIndexing = true,
|
||||
.generalVariableIndexing = true,
|
||||
.generalConstantMatrixVectorIndexing = true,
|
||||
},
|
||||
};
|
||||
|
||||
constexpr const char* vertShaderSrc = R"glsl(
|
||||
#version 450
|
||||
|
||||
layout(binding = 0) uniform UniformBufferObject {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
} ubo;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
fragTexCoord = inTexCoord;
|
||||
}
|
||||
)glsl";
|
||||
|
||||
constexpr const char* fragShaderSrc = R"glsl(
|
||||
#version 450
|
||||
|
||||
layout(binding = 1) uniform sampler2D texSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = texture(texSampler, fragTexCoord);
|
||||
}
|
||||
)glsl";
|
||||
|
||||
inline void InitGlslang() { glslang::InitializeProcess(); }
|
||||
|
||||
inline void CleanupGlslang() { glslang::FinalizeProcess(); }
|
||||
|
||||
inline fn CompileShader(const char* source, EShLanguage shaderType) -> std::vector<uint32_t> {
|
||||
glslang::TShader shader(shaderType);
|
||||
shader.setStrings(&source, 1);
|
||||
|
||||
// Set shader environment details (language version, profile)
|
||||
shader.setEnvInput(glslang::EShSourceGlsl, shaderType, glslang::EShClientVulkan, 450);
|
||||
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2);
|
||||
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
|
||||
|
||||
// Parse GLSL to AST (abstract syntax tree)
|
||||
if (!shader.parse(&DefaultTBuiltInResource, 450, false, EShMsgDefault)) {
|
||||
std::cerr << "GLSL parsing failed: " << shader.getInfoLog() << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a program to hold the compiled shader
|
||||
glslang::TProgram program;
|
||||
program.addShader(&shader);
|
||||
|
||||
// Link the program
|
||||
if (!program.link(EShMsgDefault)) {
|
||||
std::cerr << "Linking failed: " << program.getInfoLog() << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Convert AST to SPIR-V
|
||||
std::vector<uint32_t> spirv;
|
||||
glslang::SpvOptions spvOptions;
|
||||
glslang::GlslangToSpv(*program.getIntermediate(shaderType), spirv, &spvOptions);
|
||||
|
||||
return spirv;
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
#include <filesystem>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
|
@ -7,7 +9,7 @@ namespace stb {
|
|||
class UniqueImage {
|
||||
public:
|
||||
// Constructor
|
||||
UniqueImage(const char* filename) { load(filename); }
|
||||
UniqueImage(const std::filesystem::path& path) { load(path.string().c_str()); }
|
||||
|
||||
// Deleted copy constructor and assignment operator
|
||||
UniqueImage(const UniqueImage&) = delete;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 75 KiB |
Loading…
Reference in a new issue