Files
updated-radar/shader_experiments/sweep_line/main.cpp

253 lines
7.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* MIT License
*
* Copyright (c) 2026 Mark Allyn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// Author: Mark Allyn
//
// sweep_line/main.cpp
//
// A green vertical line sweeps left to right across a black screen at
// 0.001 clip-space units per step (60 steps/sec). Behind the line, a
// phosphor-style trail fades from red (right behind the line) to black
// (at the left edge when the line reaches the right edge). Then repeats.
//
// A fullscreen quad is drawn every frame; the fragment shader computes
// line position, trail color, and fade entirely from u_anim_time.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <cstdio>
#include <cstdlib>
// ---------------------------------------------------------------------------
// Vertex shader
//
// Covers the screen with a fullscreen quad. v_pos carries the clip-space
// x,y of each fragment to the fragment shader.
// ---------------------------------------------------------------------------
static const char* vertex_shader_src = R"glsl(
#version 330 core
layout(location = 0) in vec2 a_pos;
out vec2 v_pos;
void main()
{
v_pos = a_pos;
gl_Position = vec4(a_pos, 0.0, 1.0);
}
)glsl";
// ---------------------------------------------------------------------------
// Fragment shader
//
// sweep_x travels from -1.0 to +1.0 (2.0 clip-space units) at
// 0.001 units/step × 60 steps/sec = 0.06 units/sec → one full sweep in ~33 s.
//
// For each fragment at clip-space x = px:
// px ≈ sweep_x → green (current line)
// px < sweep_x → red trail: intensity = 1 - dist/2 (full red at
// line, black when dist == 2.0, i.e. when sweep_x
// is at +1.0 and px is at -1.0)
// px > sweep_x → black
// ---------------------------------------------------------------------------
static const char* fragment_shader_src = R"glsl(
#version 330 core
uniform float u_anim_time;
in vec2 v_pos;
out vec4 frag_color;
const float STEPS_PER_SEC = 60.0;
const float STEP_SIZE = 0.001;
const float SWEEP_RANGE = 2.0; // clip space: -1.0 to +1.0
const float LINE_HALF_W = 0.004; // half-width of green line in clip space
void main()
{
float px = v_pos.x;
float step_f = floor(u_anim_time * STEPS_PER_SEC);
float travel = mod(step_f * STEP_SIZE, SWEEP_RANGE);
float sweep_x = -1.0 + travel;
// Current sweep line: green
if (abs(px - sweep_x) <= LINE_HALF_W)
{
frag_color = vec4(0.0, 1.0, 0.0, 1.0);
return;
}
// Phosphor trail behind the line: red fading to black
if (px < sweep_x)
{
float dist = sweep_x - px; // 0 at line edge, up to 2.0
float intensity = clamp(1.0 - dist / SWEEP_RANGE, 0.0, 1.0);
frag_color = vec4(intensity, 0.0, 0.0, 1.0);
return;
}
// Ahead of the sweep line: black
frag_color = vec4(0.0, 0.0, 0.0, 1.0);
}
)glsl";
// ---------------------------------------------------------------------------
// compile_shader
// ---------------------------------------------------------------------------
static GLuint compile_shader(GLenum type, const char* src)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint ok = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (!ok)
{
char log[512];
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
std::fprintf(stderr, "Shader compile error:\n%s\n", log);
}
return shader;
}
// ---------------------------------------------------------------------------
// build_program
// ---------------------------------------------------------------------------
static GLuint build_program(const char* vert_src, const char* frag_src)
{
GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src);
GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src);
GLuint program = glCreateProgram();
glAttachShader(program, vert);
glAttachShader(program, frag);
glLinkProgram(program);
GLint ok = 0;
glGetProgramiv(program, GL_LINK_STATUS, &ok);
if (!ok)
{
char log[512];
glGetProgramInfoLog(program, sizeof(log), nullptr, log);
std::fprintf(stderr, "Program link error:\n%s\n", log);
}
glDeleteShader(vert);
glDeleteShader(frag);
return program;
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main()
{
if (!glfwInit())
{
std::fprintf(stderr, "glfwInit failed\n");
return EXIT_FAILURE;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Sweep Line", nullptr, nullptr);
if (!window)
{
std::fprintf(stderr, "glfwCreateWindow failed\n");
glfwTerminate();
return EXIT_FAILURE;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
{
std::fprintf(stderr, "GLAD init failed\n");
return EXIT_FAILURE;
}
GLuint shader_program = build_program(vertex_shader_src, fragment_shader_src);
GLint loc_anim_time = glGetUniformLocation(shader_program, "u_anim_time");
/* Fullscreen quad as a triangle strip:
(-1,-1) → (1,-1) → (-1,1) → (1,1) */
float quad_verts[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
GLuint vao = 0;
GLuint vbo = 0;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_verts), quad_verts, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
2 * static_cast<int>(sizeof(float)),
reinterpret_cast<void*>(0));
glEnableVertexAttribArray(0);
glBindVertexArray(0);
double anim_start = glfwGetTime();
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
float anim_time = static_cast<float>(glfwGetTime() - anim_start);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
glUniform1f(loc_anim_time, anim_time);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteProgram(shader_program);
glfwDestroyWindow(window);
glfwTerminate();
return EXIT_SUCCESS;
}