mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-24 16:10:14 +00:00
Vulcan Nightmare
Experimentally bringing up vulcan support
This commit is contained in:
parent
863a786c48
commit
83b576e8d9
189 changed files with 12147 additions and 7820 deletions
|
|
@ -1,11 +1,14 @@
|
|||
#include "rendering/starfield.hpp"
|
||||
#include "rendering/shader.hpp"
|
||||
#include "rendering/camera.hpp"
|
||||
#include "rendering/vk_context.hpp"
|
||||
#include "rendering/vk_shader.hpp"
|
||||
#include "rendering/vk_pipeline.hpp"
|
||||
#include "rendering/vk_frame_data.hpp"
|
||||
#include "rendering/vk_utils.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
|
@ -16,76 +19,89 @@ StarField::~StarField() {
|
|||
shutdown();
|
||||
}
|
||||
|
||||
bool StarField::initialize() {
|
||||
bool StarField::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
||||
LOG_INFO("Initializing star field");
|
||||
|
||||
// Create star shader
|
||||
starShader = std::make_unique<Shader>();
|
||||
vkCtx = ctx;
|
||||
VkDevice device = vkCtx->getDevice();
|
||||
|
||||
// Vertex shader - simple point rendering
|
||||
const char* vertexShaderSource = R"(
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in float aBrightness;
|
||||
layout (location = 2) in float aTwinklePhase;
|
||||
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform float time;
|
||||
uniform float intensity;
|
||||
|
||||
out float Brightness;
|
||||
|
||||
void main() {
|
||||
// Remove translation from view matrix (stars are infinitely far)
|
||||
mat4 viewNoTranslation = mat4(mat3(view));
|
||||
|
||||
gl_Position = projection * viewNoTranslation * vec4(aPos, 1.0);
|
||||
|
||||
// Twinkle effect (subtle brightness variation)
|
||||
float twinkle = sin(time * 2.0 + aTwinklePhase) * 0.2 + 0.8; // 0.6 to 1.0
|
||||
|
||||
Brightness = aBrightness * twinkle * intensity;
|
||||
|
||||
// Point size based on brightness
|
||||
gl_PointSize = 2.0 + aBrightness * 2.0; // 2-4 pixels
|
||||
}
|
||||
)";
|
||||
|
||||
// Fragment shader - star color
|
||||
const char* fragmentShaderSource = R"(
|
||||
#version 330 core
|
||||
in float Brightness;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
// Circular point (not square)
|
||||
vec2 coord = gl_PointCoord - vec2(0.5);
|
||||
float dist = length(coord);
|
||||
if (dist > 0.5) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Soften edges
|
||||
float alpha = smoothstep(0.5, 0.3, dist);
|
||||
|
||||
// Star color (slightly blue-white)
|
||||
vec3 starColor = vec3(0.9, 0.95, 1.0);
|
||||
|
||||
FragColor = vec4(starColor * Brightness, alpha * Brightness);
|
||||
}
|
||||
)";
|
||||
|
||||
if (!starShader->loadFromSource(vertexShaderSource, fragmentShaderSource)) {
|
||||
LOG_ERROR("Failed to create star shader");
|
||||
// Load SPIR-V shaders
|
||||
VkShaderModule vertModule;
|
||||
if (!vertModule.loadFromFile(device, "assets/shaders/starfield.vert.spv")) {
|
||||
LOG_ERROR("Failed to load starfield vertex shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate random stars
|
||||
generateStars();
|
||||
VkShaderModule fragModule;
|
||||
if (!fragModule.loadFromFile(device, "assets/shaders/starfield.frag.spv")) {
|
||||
LOG_ERROR("Failed to load starfield fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create OpenGL buffers
|
||||
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
||||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
// Push constants: float time + float intensity = 8 bytes
|
||||
VkPushConstantRange pushRange{};
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = sizeof(float) * 2; // time, intensity
|
||||
|
||||
// Pipeline layout: set 0 = per-frame UBO, push constants
|
||||
pipelineLayout = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
||||
if (pipelineLayout == VK_NULL_HANDLE) {
|
||||
LOG_ERROR("Failed to create starfield pipeline layout");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vertex input: binding 0, stride = 5 * sizeof(float)
|
||||
// location 0: vec3 pos (offset 0)
|
||||
// location 1: float brightness (offset 12)
|
||||
// location 2: float twinklePhase (offset 16)
|
||||
VkVertexInputBindingDescription binding{};
|
||||
binding.binding = 0;
|
||||
binding.stride = 5 * sizeof(float);
|
||||
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
VkVertexInputAttributeDescription posAttr{};
|
||||
posAttr.location = 0;
|
||||
posAttr.binding = 0;
|
||||
posAttr.format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
posAttr.offset = 0;
|
||||
|
||||
VkVertexInputAttributeDescription brightnessAttr{};
|
||||
brightnessAttr.location = 1;
|
||||
brightnessAttr.binding = 0;
|
||||
brightnessAttr.format = VK_FORMAT_R32_SFLOAT;
|
||||
brightnessAttr.offset = 3 * sizeof(float);
|
||||
|
||||
VkVertexInputAttributeDescription twinkleAttr{};
|
||||
twinkleAttr.location = 2;
|
||||
twinkleAttr.binding = 0;
|
||||
twinkleAttr.format = VK_FORMAT_R32_SFLOAT;
|
||||
twinkleAttr.offset = 4 * sizeof(float);
|
||||
|
||||
pipeline = PipelineBuilder()
|
||||
.setShaders(vertStage, fragStage)
|
||||
.setVertexInput({binding}, {posAttr, brightnessAttr, twinkleAttr})
|
||||
.setTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
|
||||
.setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE)
|
||||
.setDepthTest(true, false, VK_COMPARE_OP_LESS_OR_EQUAL) // depth test, no write (stars behind sky)
|
||||
.setColorBlendAttachment(PipelineBuilder::blendAdditive())
|
||||
.setLayout(pipelineLayout)
|
||||
.setRenderPass(vkCtx->getImGuiRenderPass())
|
||||
.build(device);
|
||||
|
||||
vertModule.destroy();
|
||||
fragModule.destroy();
|
||||
|
||||
if (pipeline == VK_NULL_HANDLE) {
|
||||
LOG_ERROR("Failed to create starfield pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate star positions and upload to GPU
|
||||
generateStars();
|
||||
createStarBuffers();
|
||||
|
||||
LOG_INFO("Star field initialized: ", starCount, " stars");
|
||||
|
|
@ -94,62 +110,65 @@ bool StarField::initialize() {
|
|||
|
||||
void StarField::shutdown() {
|
||||
destroyStarBuffers();
|
||||
starShader.reset();
|
||||
|
||||
if (vkCtx) {
|
||||
VkDevice device = vkCtx->getDevice();
|
||||
if (pipeline != VK_NULL_HANDLE) {
|
||||
vkDestroyPipeline(device, pipeline, nullptr);
|
||||
pipeline = VK_NULL_HANDLE;
|
||||
}
|
||||
if (pipelineLayout != VK_NULL_HANDLE) {
|
||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||
pipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
vkCtx = nullptr;
|
||||
stars.clear();
|
||||
}
|
||||
|
||||
void StarField::render(const Camera& camera, float timeOfDay,
|
||||
float cloudDensity, float fogDensity) {
|
||||
if (!renderingEnabled || vao == 0 || !starShader || stars.empty()) {
|
||||
void StarField::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||
float timeOfDay, float cloudDensity, float fogDensity) {
|
||||
if (!renderingEnabled || pipeline == VK_NULL_HANDLE || vertexBuffer == VK_NULL_HANDLE
|
||||
|| stars.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get star intensity based on time of day
|
||||
// Compute intensity from time of day then attenuate for clouds/fog
|
||||
float intensity = getStarIntensity(timeOfDay);
|
||||
|
||||
// Reduce intensity based on cloud density and fog (more clouds/fog = fewer visible stars)
|
||||
intensity *= (1.0f - glm::clamp(cloudDensity * 0.7f, 0.0f, 1.0f));
|
||||
intensity *= (1.0f - glm::clamp(fogDensity * 0.3f, 0.0f, 1.0f));
|
||||
|
||||
// Don't render if stars would be invisible
|
||||
if (intensity <= 0.01f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable blending for star glow
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
// Push constants: time and intensity
|
||||
struct StarPushConstants {
|
||||
float time;
|
||||
float intensity;
|
||||
};
|
||||
StarPushConstants push{twinkleTime, intensity};
|
||||
|
||||
// Enable point sprites
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
|
||||
// Disable depth writing (stars are background)
|
||||
glDepthMask(GL_FALSE);
|
||||
// Bind per-frame descriptor set (set 0 — camera UBO with view/projection)
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout,
|
||||
0, 1, &perFrameSet, 0, nullptr);
|
||||
|
||||
starShader->use();
|
||||
vkCmdPushConstants(cmd, pipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(push), &push);
|
||||
|
||||
// Set uniforms
|
||||
glm::mat4 view = camera.getViewMatrix();
|
||||
glm::mat4 projection = camera.getProjectionMatrix();
|
||||
// Bind vertex buffer
|
||||
VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuffer, &offset);
|
||||
|
||||
starShader->setUniform("view", view);
|
||||
starShader->setUniform("projection", projection);
|
||||
starShader->setUniform("time", twinkleTime);
|
||||
starShader->setUniform("intensity", intensity);
|
||||
|
||||
// Render stars as points
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_POINTS, 0, starCount);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Restore state
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_BLEND);
|
||||
// Draw all stars as individual points
|
||||
vkCmdDraw(cmd, static_cast<uint32_t>(starCount), 1, 0, 0);
|
||||
}
|
||||
|
||||
void StarField::update(float deltaTime) {
|
||||
// Update twinkle animation
|
||||
twinkleTime += deltaTime;
|
||||
}
|
||||
|
||||
|
|
@ -157,30 +176,27 @@ void StarField::generateStars() {
|
|||
stars.clear();
|
||||
stars.reserve(starCount);
|
||||
|
||||
// Random number generator
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> phiDist(0.0f, M_PI / 2.0f); // 0 to 90 degrees (hemisphere)
|
||||
std::uniform_real_distribution<float> thetaDist(0.0f, 2.0f * M_PI); // 0 to 360 degrees
|
||||
std::uniform_real_distribution<float> brightnessDist(0.3f, 1.0f); // Varying brightness
|
||||
std::uniform_real_distribution<float> twinkleDist(0.0f, 2.0f * M_PI); // Random twinkle phase
|
||||
std::uniform_real_distribution<float> phiDist(0.0f, M_PI / 2.0f); // 0–90° (upper hemisphere)
|
||||
std::uniform_real_distribution<float> thetaDist(0.0f, 2.0f * M_PI); // 0–360°
|
||||
std::uniform_real_distribution<float> brightnessDist(0.3f, 1.0f);
|
||||
std::uniform_real_distribution<float> twinkleDist(0.0f, 2.0f * M_PI);
|
||||
|
||||
const float radius = 900.0f; // Slightly larger than skybox
|
||||
|
||||
for (int i = 0; i < starCount; i++) {
|
||||
Star star;
|
||||
|
||||
// Spherical coordinates (hemisphere)
|
||||
float phi = phiDist(gen); // Elevation angle
|
||||
float phi = phiDist(gen); // Elevation angle
|
||||
float theta = thetaDist(gen); // Azimuth angle
|
||||
|
||||
// Convert to Cartesian coordinates
|
||||
float x = radius * std::sin(phi) * std::cos(theta);
|
||||
float y = radius * std::sin(phi) * std::sin(theta);
|
||||
float z = radius * std::cos(phi);
|
||||
|
||||
star.position = glm::vec3(x, y, z);
|
||||
star.brightness = brightnessDist(gen);
|
||||
star.position = glm::vec3(x, y, z);
|
||||
star.brightness = brightnessDist(gen);
|
||||
star.twinklePhase = twinkleDist(gen);
|
||||
|
||||
stars.push_back(star);
|
||||
|
|
@ -190,9 +206,9 @@ void StarField::generateStars() {
|
|||
}
|
||||
|
||||
void StarField::createStarBuffers() {
|
||||
// Prepare vertex data (position, brightness, twinkle phase)
|
||||
// Interleaved vertex data: pos.x, pos.y, pos.z, brightness, twinklePhase
|
||||
std::vector<float> vertexData;
|
||||
vertexData.reserve(stars.size() * 5); // 3 pos + 1 brightness + 1 phase
|
||||
vertexData.reserve(stars.size() * 5);
|
||||
|
||||
for (const auto& star : stars) {
|
||||
vertexData.push_back(star.position.x);
|
||||
|
|
@ -202,57 +218,36 @@ void StarField::createStarBuffers() {
|
|||
vertexData.push_back(star.twinklePhase);
|
||||
}
|
||||
|
||||
// Create OpenGL buffers
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
VkDeviceSize bufferSize = vertexData.size() * sizeof(float);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
// Upload via staging buffer to GPU-local memory
|
||||
AllocatedBuffer gpuBuf = uploadBuffer(*vkCtx, vertexData.data(), bufferSize,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
||||
|
||||
// Upload vertex data
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);
|
||||
|
||||
// Set vertex attributes
|
||||
// Position
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
// Brightness
|
||||
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
// Twinkle phase
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(4 * sizeof(float)));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glBindVertexArray(0);
|
||||
vertexBuffer = gpuBuf.buffer;
|
||||
vertexAlloc = gpuBuf.allocation;
|
||||
}
|
||||
|
||||
void StarField::destroyStarBuffers() {
|
||||
if (vao != 0) {
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
vao = 0;
|
||||
}
|
||||
if (vbo != 0) {
|
||||
glDeleteBuffers(1, &vbo);
|
||||
vbo = 0;
|
||||
if (vkCtx && vertexBuffer != VK_NULL_HANDLE) {
|
||||
vmaDestroyBuffer(vkCtx->getAllocator(), vertexBuffer, vertexAlloc);
|
||||
vertexBuffer = VK_NULL_HANDLE;
|
||||
vertexAlloc = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
float StarField::getStarIntensity(float timeOfDay) const {
|
||||
// Stars visible at night (fade in/out at dusk/dawn)
|
||||
|
||||
// Full night: 20:00-4:00
|
||||
// Full night: 20:00–4:00
|
||||
if (timeOfDay >= 20.0f || timeOfDay < 4.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
// Fade in at dusk: 18:00-20:00
|
||||
// Fade in at dusk: 18:00–20:00
|
||||
else if (timeOfDay >= 18.0f && timeOfDay < 20.0f) {
|
||||
return (timeOfDay - 18.0f) / 2.0f; // 0 to 1 over 2 hours
|
||||
return (timeOfDay - 18.0f) / 2.0f; // 0 → 1 over 2 hours
|
||||
}
|
||||
// Fade out at dawn: 4:00-6:00
|
||||
// Fade out at dawn: 4:00–6:00
|
||||
else if (timeOfDay >= 4.0f && timeOfDay < 6.0f) {
|
||||
return 1.0f - (timeOfDay - 4.0f) / 2.0f; // 1 to 0 over 2 hours
|
||||
return 1.0f - (timeOfDay - 4.0f) / 2.0f; // 1 → 0 over 2 hours
|
||||
}
|
||||
// Daytime: no stars
|
||||
else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue