Add self-feedback system and water shader
Features: - Self-feedback rendering system for shaders with feedback loops - Automatic FBO/texture management for feedback - Metadata parser detects iChannel feedback configuration - Adaptive render loop (with/without feedback) - Water shader from Shadertoy (adapted and working) - Fixed variable initialization issues in shader code Technical details: - FBO creation/destruction on shader switch - Texture binding to iChannel0-3 based on metadata - Auto-resize feedback buffers on window resize - Cleanup on exit and shader switch Files: - src/main.cpp: Feedback system implementation - shaders/water.glsl: Water shader with fixes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
BIN
release/icon.ico
BIN
release/icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 137 KiB |
BIN
release/icon.png
BIN
release/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 364 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 703 KiB |
BIN
release/icon/icon.pxd
Normal file
BIN
release/icon/icon.pxd
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
// Name: Water
|
// Name: Water
|
||||||
// Author: diatribes
|
// Author: diatribes (FabriceNeyret2)
|
||||||
#version 330 core
|
#version 330 core
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
@@ -14,18 +14,22 @@ uniform float iTime;
|
|||||||
thanks!! :D
|
thanks!! :D
|
||||||
|
|
||||||
If it doesn't display correctly, change line 17 "r/r" to "vec3(1)"
|
If it doesn't display correctly, change line 17 "r/r" to "vec3(1)"
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void mainImage( out vec4 o, vec2 u ) {
|
void mainImage( out vec4 o, vec2 u ) {
|
||||||
float s=.002,i,n;
|
float s=.002, i=0., n; // FIXED: Initialize i=0
|
||||||
vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y);
|
vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y);
|
||||||
vec3 p = vec3(0);
|
vec3 p = vec3(0);
|
||||||
u = (u-r.xy/2.)/r.y-.3;
|
u = (u-r.xy/2.)/r.y-.3;
|
||||||
for(o *= i; i++ < 32. && s > .001;o += vec4(5,2,1,0)/max(length(u-.1), 0.001))
|
|
||||||
for (p += vec3(u*s,s),s = 1. + p.y,
|
o = vec4(0); // FIXED: Initialize output to black
|
||||||
n =.01; n < 1.;n+=n)
|
|
||||||
|
for(; i < 32. && s > .001; i++) {
|
||||||
|
o += vec4(5,2,1,0)/max(length(u-.1), 0.001);
|
||||||
|
for (p += vec3(u*s,s), s = 1. + p.y, n =.01; n < 1.; n+=n) {
|
||||||
s += abs(dot(sin(p.z+iTime+p / n), vec3(1))) * n*.1;
|
s += abs(dot(sin(p.z+iTime+p / n), vec3(1))) * n*.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
o = tanh(o/5e2);
|
o = tanh(o/5e2);
|
||||||
}
|
}
|
||||||
|
|
||||||
309
src/main.cpp
309
src/main.cpp
@@ -34,6 +34,32 @@ struct DisplayMonitor {
|
|||||||
int refresh_rate = 0;
|
int refresh_rate = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Forward declarations of structs
|
||||||
|
struct ShaderMetadata {
|
||||||
|
std::string name;
|
||||||
|
std::string author;
|
||||||
|
std::string iChannel0; // "BufferA", "BufferB", "none", etc.
|
||||||
|
std::string iChannel1;
|
||||||
|
std::string iChannel2;
|
||||||
|
std::string iChannel3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderBuffer {
|
||||||
|
GLuint program = 0; // Shader program for this buffer
|
||||||
|
GLuint fbo = 0; // Framebuffer object
|
||||||
|
GLuint texture = 0; // Output texture
|
||||||
|
std::string name; // "BufferA", "BufferB", etc.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderPass {
|
||||||
|
std::string shaderName; // Base name (e.g., "water")
|
||||||
|
std::string displayName; // Custom name from metadata
|
||||||
|
std::string author; // Author from metadata
|
||||||
|
GLuint imageProgram = 0; // Main image shader program
|
||||||
|
std::vector<ShaderBuffer> buffers; // BufferA, BufferB, etc.
|
||||||
|
ShaderMetadata metadata; // iChannel configuration
|
||||||
|
};
|
||||||
|
|
||||||
// Globales simplificados (tu proyecto puede integrarlo en clases)
|
// Globales simplificados (tu proyecto puede integrarlo en clases)
|
||||||
static DisplayMonitor display_monitor_;
|
static DisplayMonitor display_monitor_;
|
||||||
static SDL_Window* window_ = nullptr;
|
static SDL_Window* window_ = nullptr;
|
||||||
@@ -52,6 +78,12 @@ static std::vector<ShaderPass> shader_passes_;
|
|||||||
static int current_window_width_ = 0;
|
static int current_window_width_ = 0;
|
||||||
static int current_window_height_ = 0;
|
static int current_window_height_ = 0;
|
||||||
|
|
||||||
|
// Self-feedback system (for shaders that use their own output as input)
|
||||||
|
static GLuint feedback_fbo_ = 0;
|
||||||
|
static GLuint feedback_texture_ = 0;
|
||||||
|
static bool current_shader_uses_feedback_ = false;
|
||||||
|
static int feedback_channel_ = -1; // Which iChannel (0-3) is used for feedback
|
||||||
|
|
||||||
// FPS tracking
|
// FPS tracking
|
||||||
static Uint32 fps_frame_count_ = 0;
|
static Uint32 fps_frame_count_ = 0;
|
||||||
static Uint32 fps_last_update_ticks_ = 0;
|
static Uint32 fps_last_update_ticks_ = 0;
|
||||||
@@ -83,31 +115,6 @@ static bool loadFileToString(const std::filesystem::path& path, std::string& out
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ShaderMetadata {
|
|
||||||
std::string name;
|
|
||||||
std::string author;
|
|
||||||
std::string iChannel0; // "BufferA", "BufferB", "none", etc.
|
|
||||||
std::string iChannel1;
|
|
||||||
std::string iChannel2;
|
|
||||||
std::string iChannel3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ShaderBuffer {
|
|
||||||
GLuint program = 0; // Shader program for this buffer
|
|
||||||
GLuint fbo = 0; // Framebuffer object
|
|
||||||
GLuint texture = 0; // Output texture
|
|
||||||
std::string name; // "BufferA", "BufferB", etc.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ShaderPass {
|
|
||||||
std::string shaderName; // Base name (e.g., "water")
|
|
||||||
std::string displayName; // Custom name from metadata
|
|
||||||
std::string author; // Author from metadata
|
|
||||||
GLuint imageProgram = 0; // Main image shader program
|
|
||||||
std::vector<ShaderBuffer> buffers; // BufferA, BufferB, etc.
|
|
||||||
ShaderMetadata metadata; // iChannel configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string trimString(const std::string& str) {
|
static std::string trimString(const std::string& str) {
|
||||||
size_t start = str.find_first_not_of(" \t\r\n");
|
size_t start = str.find_first_not_of(" \t\r\n");
|
||||||
size_t end = str.find_last_not_of(" \t\r\n");
|
size_t end = str.find_last_not_of(" \t\r\n");
|
||||||
@@ -325,6 +332,171 @@ static bool resizeBuffersIfNeeded(ShaderPass& pass, int width, int height) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Self-Feedback System =====
|
||||||
|
|
||||||
|
static bool createFeedbackFBO(int width, int height) {
|
||||||
|
// Delete existing if any
|
||||||
|
if (feedback_fbo_ != 0) glDeleteFramebuffers(1, &feedback_fbo_);
|
||||||
|
if (feedback_texture_ != 0) glDeleteTextures(1, &feedback_texture_);
|
||||||
|
|
||||||
|
// Create texture
|
||||||
|
glGenTextures(1, &feedback_texture_);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, feedback_texture_);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
// Clear to black initially
|
||||||
|
std::vector<float> black(width * height * 4, 0.0f);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, black.data());
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
// Create FBO
|
||||||
|
glGenFramebuffers(1, &feedback_fbo_);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, feedback_texture_, 0);
|
||||||
|
|
||||||
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
Logger::error("Feedback FBO creation failed: " + std::to_string(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("Created feedback FBO (" + std::to_string(width) + "x" + std::to_string(height) + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroyFeedbackFBO() {
|
||||||
|
if (feedback_fbo_ != 0) {
|
||||||
|
glDeleteFramebuffers(1, &feedback_fbo_);
|
||||||
|
feedback_fbo_ = 0;
|
||||||
|
}
|
||||||
|
if (feedback_texture_ != 0) {
|
||||||
|
glDeleteTextures(1, &feedback_texture_);
|
||||||
|
feedback_texture_ = 0;
|
||||||
|
}
|
||||||
|
current_shader_uses_feedback_ = false;
|
||||||
|
feedback_channel_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int detectFeedbackChannel(const ShaderMetadata& metadata) {
|
||||||
|
// Check which iChannel uses "self" feedback
|
||||||
|
if (metadata.iChannel0 == "self") return 0;
|
||||||
|
if (metadata.iChannel1 == "self") return 1;
|
||||||
|
if (metadata.iChannel2 == "self") return 2;
|
||||||
|
if (metadata.iChannel3 == "self") return 3;
|
||||||
|
return -1; // No feedback
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Multi-pass Shader Loading =====
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
static GLuint compileShader(GLenum type, const char* src);
|
||||||
|
static GLuint linkProgram(GLuint vs, GLuint fs);
|
||||||
|
|
||||||
|
static std::vector<std::string> findBufferFiles(const std::filesystem::path& imagePath) {
|
||||||
|
std::vector<std::string> buffers;
|
||||||
|
std::filesystem::path dir = imagePath.parent_path();
|
||||||
|
std::string basename = imagePath.stem().stem().string(); // Remove .image.glsl -> get base name
|
||||||
|
|
||||||
|
// Check for BufferA, BufferB, BufferC, BufferD
|
||||||
|
std::vector<std::string> bufferNames = {"bufferA", "bufferB", "bufferC", "bufferD"};
|
||||||
|
for (const auto& bufName : bufferNames) {
|
||||||
|
std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl");
|
||||||
|
if (std::filesystem::exists(bufferPath)) {
|
||||||
|
buffers.push_back(bufName);
|
||||||
|
Logger::info("Found buffer: " + bufferPath.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint loadMultiPassShader(const std::filesystem::path& imagePath, ShaderPass& outPass, int width, int height) {
|
||||||
|
std::string basename = imagePath.stem().stem().string();
|
||||||
|
outPass.shaderName = basename;
|
||||||
|
|
||||||
|
// Load and compile Image shader
|
||||||
|
std::string imageSrc;
|
||||||
|
if (!loadFileToString(imagePath, imageSrc)) {
|
||||||
|
Logger::error("Failed to load image shader: " + imagePath.string());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract metadata from Image shader
|
||||||
|
outPass.metadata = extractShaderMetadata(imageSrc);
|
||||||
|
outPass.displayName = outPass.metadata.name.empty() ? basename : outPass.metadata.name;
|
||||||
|
outPass.author = outPass.metadata.author;
|
||||||
|
|
||||||
|
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||||
|
GLuint fs = compileShader(GL_FRAGMENT_SHADER, imageSrc.c_str());
|
||||||
|
|
||||||
|
if (!vs || !fs) {
|
||||||
|
if (vs) glDeleteShader(vs);
|
||||||
|
if (fs) glDeleteShader(fs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPass.imageProgram = linkProgram(vs, fs);
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
|
||||||
|
if (!outPass.imageProgram) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and load buffer shaders
|
||||||
|
std::vector<std::string> bufferNames = findBufferFiles(imagePath);
|
||||||
|
std::filesystem::path dir = imagePath.parent_path();
|
||||||
|
|
||||||
|
for (const auto& bufName : bufferNames) {
|
||||||
|
ShaderBuffer buffer;
|
||||||
|
buffer.name = bufName;
|
||||||
|
|
||||||
|
// Load buffer shader source
|
||||||
|
std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl");
|
||||||
|
std::string bufferSrc;
|
||||||
|
if (!loadFileToString(bufferPath, bufferSrc)) {
|
||||||
|
Logger::error("Failed to load buffer: " + bufferPath.string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile buffer shader
|
||||||
|
GLuint bufVs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||||
|
GLuint bufFs = compileShader(GL_FRAGMENT_SHADER, bufferSrc.c_str());
|
||||||
|
|
||||||
|
if (!bufVs || !bufFs) {
|
||||||
|
if (bufVs) glDeleteShader(bufVs);
|
||||||
|
if (bufFs) glDeleteShader(bufFs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.program = linkProgram(bufVs, bufFs);
|
||||||
|
glDeleteShader(bufVs);
|
||||||
|
glDeleteShader(bufFs);
|
||||||
|
|
||||||
|
if (!buffer.program) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create FBO and texture for this buffer
|
||||||
|
if (!createBufferFBO(buffer, width, height)) {
|
||||||
|
glDeleteProgram(buffer.program);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
outPass.buffers.push_back(buffer);
|
||||||
|
Logger::info("Loaded buffer: " + bufName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("Multi-pass shader loaded: " + outPass.displayName + " (" + std::to_string(outPass.buffers.size()) + " buffers)");
|
||||||
|
return outPass.imageProgram;
|
||||||
|
}
|
||||||
|
|
||||||
static void updateWindowTitle() {
|
static void updateWindowTitle() {
|
||||||
if (!window_ || shader_list_.empty()) return;
|
if (!window_ || shader_list_.empty()) return;
|
||||||
|
|
||||||
@@ -404,7 +576,7 @@ static GLuint loadAndCompileShader(size_t index) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract custom shader metadata (name and author) from source code
|
// Extract custom shader metadata (name, author, iChannels) from source code
|
||||||
ShaderMetadata metadata = extractShaderMetadata(fragSrc);
|
ShaderMetadata metadata = extractShaderMetadata(fragSrc);
|
||||||
if (!metadata.name.empty()) {
|
if (!metadata.name.empty()) {
|
||||||
shader_names_[index] = metadata.name;
|
shader_names_[index] = metadata.name;
|
||||||
@@ -415,6 +587,14 @@ static GLuint loadAndCompileShader(size_t index) {
|
|||||||
Logger::info("Shader author: " + metadata.author);
|
Logger::info("Shader author: " + metadata.author);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect self-feedback
|
||||||
|
feedback_channel_ = detectFeedbackChannel(metadata);
|
||||||
|
current_shader_uses_feedback_ = (feedback_channel_ >= 0);
|
||||||
|
|
||||||
|
if (current_shader_uses_feedback_) {
|
||||||
|
Logger::info("Shader uses self-feedback on iChannel" + std::to_string(feedback_channel_));
|
||||||
|
}
|
||||||
|
|
||||||
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str());
|
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str());
|
||||||
|
|
||||||
@@ -527,6 +707,9 @@ void switchShader(int direction) {
|
|||||||
glDeleteProgram(current_program_);
|
glDeleteProgram(current_program_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy feedback FBO from previous shader
|
||||||
|
destroyFeedbackFBO();
|
||||||
|
|
||||||
current_program_ = new_program;
|
current_program_ = new_program;
|
||||||
current_shader_index_ = new_index;
|
current_shader_index_ = new_index;
|
||||||
shader_start_ticks_ = SDL_GetTicks();
|
shader_start_ticks_ = SDL_GetTicks();
|
||||||
@@ -765,26 +948,77 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
int w, h;
|
int w, h;
|
||||||
SDL_GetWindowSize(window_, &w, &h);
|
SDL_GetWindowSize(window_, &w, &h);
|
||||||
|
|
||||||
|
// Create/resize feedback FBO if needed
|
||||||
|
if (current_shader_uses_feedback_) {
|
||||||
|
if (feedback_fbo_ == 0 || current_window_width_ != w || current_window_height_ != h) {
|
||||||
|
createFeedbackFBO(w, h);
|
||||||
|
current_window_width_ = w;
|
||||||
|
current_window_height_ = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glUseProgram(current_program_);
|
||||||
|
|
||||||
|
// Obtener uniform locations
|
||||||
|
GLint locRes = glGetUniformLocation(current_program_, "iResolution");
|
||||||
|
GLint locTime = glGetUniformLocation(current_program_, "iTime");
|
||||||
|
|
||||||
|
float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f;
|
||||||
|
|
||||||
|
// === FEEDBACK RENDERING ===
|
||||||
|
if (current_shader_uses_feedback_) {
|
||||||
|
// Step 1: Bind feedback texture to iChannel
|
||||||
|
std::string channelName = "iChannel" + std::to_string(feedback_channel_);
|
||||||
|
GLint locChannel = glGetUniformLocation(current_program_, channelName.c_str());
|
||||||
|
|
||||||
|
if (locChannel >= 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + feedback_channel_);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, feedback_texture_);
|
||||||
|
glUniform1i(locChannel, feedback_channel_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Render to feedback FBO
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_);
|
||||||
glViewport(0, 0, w, h);
|
glViewport(0, 0, w, h);
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
glUseProgram(current_program_);
|
|
||||||
|
|
||||||
// Obtener uniform locations (se recalculan porque el shader puede cambiar)
|
|
||||||
GLint locRes = glGetUniformLocation(current_program_, "iResolution");
|
|
||||||
GLint locTime = glGetUniformLocation(current_program_, "iTime");
|
|
||||||
|
|
||||||
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||||
if (locTime >= 0) {
|
if (locTime >= 0) glUniform1f(locTime, t);
|
||||||
float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f;
|
|
||||||
glUniform1f(locTime, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Step 3: Render to screen (using the same FBO texture)
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
|
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||||
|
if (locTime >= 0) glUniform1f(locTime, t);
|
||||||
|
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Unbind texture
|
||||||
|
glActiveTexture(GL_TEXTURE0 + feedback_channel_);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
} else {
|
||||||
|
// === NORMAL RENDERING (no feedback) ===
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||||
|
if (locTime >= 0) glUniform1f(locTime, t);
|
||||||
|
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
SDL_GL_SwapWindow(window_);
|
SDL_GL_SwapWindow(window_);
|
||||||
if (!Options_video.vsync) {
|
if (!Options_video.vsync) {
|
||||||
SDL_Delay(1); // Prevent CPU spinning when vsync is off
|
SDL_Delay(1); // Prevent CPU spinning when vsync is off
|
||||||
@@ -798,6 +1032,9 @@ int main(int argc, char** argv) {
|
|||||||
glDeleteProgram(current_program_);
|
glDeleteProgram(current_program_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup feedback FBO
|
||||||
|
destroyFeedbackFBO();
|
||||||
|
|
||||||
// Cleanup audio
|
// Cleanup audio
|
||||||
if (current_music_) {
|
if (current_music_) {
|
||||||
JA_DeleteMusic(current_music_);
|
JA_DeleteMusic(current_music_);
|
||||||
|
|||||||
Reference in New Issue
Block a user