Compare commits

...

10 commits

17 changed files with 326 additions and 64 deletions

View file

@ -1,12 +1,17 @@
all : glslview glslwrite all : glslview glslwrite
USE_INOTIFY = -DUSE_INOTIFY ifeq ($(shell uname -s),Linux)
USE_INOTIFY ?= -DUSE_INOTIFY
endif
CFLAGS = -O3 -Wall $(USE_INOTIFY) CFLAGS ?= -O3 -Wall
LIBS = -lSDL2 -lGL -lGLEW -lm LIBS := -lSDL2 -lGL -lGLEW -lm
glslview : glslview.c common.c common.h glslview : glslview.c common.c common.h
$(CC) $(CFLAGS) -o $@ glslview.c common.c $(LIBS) $(CC) $(CFLAGS) $(USE_INOTIFY) -o $@ glslview.c common.c $(LIBS)
glslwrite : glslwrite.c common.c common.h glslwrite : glslwrite.c common.c common.h
$(CC) $(CFLAGS) -o $@ glslwrite.c common.c $(LIBS) -lpng $(CC) $(CFLAGS) -o $@ glslwrite.c common.c $(LIBS) -lpng
clean :
rm -f glslview glslwrite

129
README.txt Normal file
View file

@ -0,0 +1,129 @@
glslview
========
glslview is a simple program to run GLSL fragment shaders and interact with them.
glslview will render a simple fullscreen rectangle, the shader will determine each
pixel's color.
The glslwrite tool can be used to write the generated frames to PNG files
(with transparency support!).
You will need a graphics adapter and drivers with modern OpenGL/GLSL support.
Building
~~~~~~~~
glslview has been tested on Linux only, but it should work on other systems as well.
If it doesn't, I'm happy to take patches.
glslview has the following dependencies:
* SDL2
* libGL
* libGLEW
glslwrite additionally depends on libpng.
On Linux, glslview will use inotify to watch the loaded shader file for changes.
Running
~~~~~~~
Shader
------
glslview will supply the following uniform parameters to the shader:
vec2 res
The current resolution of the window
float time
Current time in milliseconds (by default since start of the program)
int param0, param1, param2
Modifiable parameters, 0 by default
The shader must define an output vec4 called "fragColor" into which the fragment color
is to be written.
See the examples provided with the source for more information. The "symmetry"
example makes use of all parameters.
Command line
------------
glslview <options> <shader>
glslwrite <options> <shader> <output directory>
Common options:
-w <width>
Initial window width (default: 800)
-h <height>
Initial window height (default: 800)
-0, -1, -2 <param>
Initial value for the shader parameters
glslview options:
-s <speed>
Initial speed factor (default: 1.0, negative values make time run backwards)
-t <time>
Initial time value (default: 0.0)
glslwrite options:
-s <step>
Time step between frames (default: 20.0)
-f <frame>
Number of frames to generate (default: run forever)
glslview control
----------------
+/-, scroll wheel
Change speed
=
Reset speed
<
Run backwards
>
Run forwards
r
Reset time
R
Reset parameters
l
Reload shader (if you're using a system without inotify, or inotify fails)
Space
Pause/continue
Page up/down
Modify param0
Left/right
Modify param1
Up/down
Modify param2
Escape
Quit

View file

@ -36,9 +36,9 @@
char *filename; char *filename;
float current_time = 0; float current_time = 0;
GLint param0 = 0; int param0 = 0;
GLint param1 = 0; int param1 = 0;
GLint param2 = 0; int param2 = 0;
static GLint time_loc; static GLint time_loc;

View file

@ -25,18 +25,13 @@
#pragma once #pragma once
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
extern char *filename; extern char *filename;
extern float current_time; extern float current_time;
extern GLint param0; extern int param0;
extern GLint param1; extern int param1;
extern GLint param2; extern int param2;
void load(void); void load(void);

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;
@ -56,13 +56,13 @@ void main(void) {
float k = param1; float k = param1;
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
mat3 t = scale(0.3 + 0.02 * param2) mat3 t = scale(1 + 0.03 * param2)
* rot(-PI/2*(k*i/n + time/1500)) * rot(-2*PI*i/n)
* trans(0.5, 0.0) * trans(-0.5, 0.0)
* rot(2*PI*i/n) * rot(PI/2*(k*i/n + time/1500))
* scale(1 / (1 + 0.03 * param2)); * scale(1 / (0.3 + 0.02 * param2));
float c = square(p * inverse(t)); float c = square(p * t);
scene = abs(scene - c); scene = abs(scene - c);
} }

View file

@ -1,4 +1,4 @@
#version 330 #version 130
out vec4 fragColor; out vec4 fragColor;

View file

@ -25,14 +25,20 @@
#include "common.h" #include "common.h"
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#ifdef USE_INOTIFY #ifdef USE_INOTIFY
# include <sys/inotify.h> # include <sys/inotify.h>
# include <sys/stat.h> # include <sys/stat.h>
@ -40,6 +46,13 @@
#endif #endif
static int init_param0 = 0;
static int init_param1 = 0;
static int init_param2 = 0;
static float init_time = 0;
static float init_speed = 1.0;
static bool paused = false;
static float speed = 1.0; static float speed = 1.0;
static unsigned previous_ticks = 0; static unsigned previous_ticks = 0;
@ -53,22 +66,20 @@ static int inotify_watch = -1;
static void reset_watch(void) { static void reset_watch(void) {
if (inotify_watch >= 0) { if (inotify_watch >= 0) {
if (inotify_rm_watch(inotify_fd, inotify_watch)) { if (inotify_rm_watch(inotify_fd, inotify_watch)) {
fprintf(stderr, "unable to remove watch\n"); fprintf(stderr, "unable to remove watch: %s\n", strerror(errno));
exit(1); exit(1);
} }
} }
inotify_watch = inotify_add_watch(inotify_fd, filename, IN_CLOSE_WRITE); inotify_watch = inotify_add_watch(inotify_fd, filename, IN_CLOSE_WRITE);
if (inotify_watch < 0) { if (inotify_watch < 0)
fprintf(stderr, "unable to watch '%s' for changes\n", filename); fprintf(stderr, "unable to watch '%s' for changes: %s\n", filename, strerror(errno));
return;
}
} }
static void init_watch(void) { static void init_watch(void) {
inotify_fd = inotify_init(); inotify_fd = inotify_init();
if (inotify_fd < 0) { if (inotify_fd < 0) {
fprintf(stderr, "unable to initialize inotify\n"); fprintf(stderr, "unable to initialize inotify: %s\n", strerror(errno));
exit(1); exit(1);
} }
@ -94,9 +105,20 @@ static void check_reload(void) {}
static void update(void) { static void update(void) {
unsigned ticks = SDL_GetTicks(); unsigned ticks = SDL_GetTicks();
current_time += speed * (ticks - previous_ticks);
previous_ticks = ticks;
if (!paused)
current_time += speed * (ticks - previous_ticks);
previous_ticks = ticks;
}
static void set_speed(float v) {
speed = v;
printf("speed: %f\n", v);
}
static void print_params(void) {
printf("params: %i %i %i\n", (int)param0, (int)param1, (int)param2);
} }
static void handle_input(const char *input) { static void handle_input(const char *input) {
@ -108,46 +130,99 @@ static void handle_input(const char *input) {
break; break;
case 'r': case 'r':
current_time = 0; current_time = init_time;
break; break;
case 'R': case 'R':
param0 = param1 = param2 = 0; param0 = init_param0;
param1 = init_param1;
param2 = init_param2;
print_params();
break; break;
case '+': case '+':
speed *= 1.1; set_speed(speed * 1.1);
break; break;
case '-': case '-':
speed /= 1.1; set_speed(speed / 1.1);
break; break;
case '=': case '=':
if (speed < 0) if (speed < 0)
speed = -1.0; set_speed(-init_speed);
else else
speed = 1.0; set_speed(init_speed);
break; break;
case '<': case '<':
if (speed > 0) if (speed > 0)
speed = -speed; set_speed(-speed);
break; break;
case '>': case '>':
if (speed < 0) if (speed < 0)
speed = -speed; set_speed(-speed);
break;
case ' ':
paused = !paused;
} }
} }
} }
int main(int argc, char *argv[]) { static void usage(void) {
if (argc != 2) { fprintf(stderr, "Usage: glslview [-w <width>] [-h <height>] [-t <time>] [-s <speed>] [-0 <param0>] [-1 <param1>] [-2 <param2>] <shader>\n");
fprintf(stderr, "Usage: glslview <shader>\n");
exit(1); exit(1);
} }
int main(int argc, char *argv[]) {
int width = 800, height = 800;
while (true) {
int c = getopt(argc, argv, "w:h:t:s:0:1:2:");
if (c == -1)
break;
switch (c) {
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case 't':
current_time = init_time = atof(optarg);
break;
case 's':
speed = atof(optarg);
init_speed = fabsf(speed);
break;
case '0':
param0 = init_param0 = atoi(optarg);
break;
case '1':
param1 = init_param1 = atoi(optarg);
break;
case '2':
param2 = init_param2 = atoi(optarg);
break;
default:
fprintf(stderr, "Invalid option '%c'\n", optopt);
usage();
}
}
if (argc - optind != 1)
usage();
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@ -155,10 +230,10 @@ int main(int argc, char *argv[]) {
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_Window *window = SDL_CreateWindow("glslview", 0, 0, 800, 800, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_Window *window = SDL_CreateWindow("glslview", 0, 0, width, height, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
SDL_GLContext ctx = SDL_GL_CreateContext(window); SDL_GLContext ctx = SDL_GL_CreateContext(window);
filename = argv[1]; filename = argv[optind];
init_watch(); init_watch();
init(); init();
@ -175,26 +250,32 @@ int main(int argc, char *argv[]) {
switch (e.key.keysym.sym) { switch (e.key.keysym.sym) {
case SDLK_PAGEDOWN: case SDLK_PAGEDOWN:
param0++; param0++;
print_params();
break; break;
case SDLK_PAGEUP: case SDLK_PAGEUP:
param0--; param0--;
print_params();
break; break;
case SDLK_RIGHT: case SDLK_RIGHT:
param1++; param1++;
print_params();
break; break;
case SDLK_LEFT: case SDLK_LEFT:
param1--; param1--;
print_params();
break; break;
case SDLK_DOWN: case SDLK_DOWN:
param2++; param2++;
print_params();
break; break;
case SDLK_UP: case SDLK_UP:
param2--; param2--;
print_params();
break; break;
case SDLK_ESCAPE: case SDLK_ESCAPE:
@ -207,7 +288,7 @@ int main(int argc, char *argv[]) {
break; break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
speed /= pow(1.1, e.wheel.y); set_speed(speed / pow(1.1, e.wheel.y));
break; break;
case SDL_QUIT: case SDL_QUIT:

View file

@ -27,6 +27,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <png.h> #include <png.h>
@ -36,12 +37,17 @@
#include <SDL2/SDL_opengl.h> #include <SDL2/SDL_opengl.h>
static float step = 20;
static unsigned frame = 0;
static unsigned max_frame = 0;
static void savePNG(int width, int height, const char *output_dir) { static void savePNG(int width, int height, const char *output_dir) {
uint8_t pixels[width*height*4]; uint8_t pixels[width*height*4];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels);
char filename[strlen(output_dir) + 20]; char filename[strlen(output_dir) + 20];
snprintf(filename, sizeof(filename), "%s/%010u.png", output_dir, (unsigned)current_time); snprintf(filename, sizeof(filename), "%s/%010u.png", output_dir, frame);
FILE *f = fopen(filename, "wb"); FILE *f = fopen(filename, "wb");
if (!f) { if (!f) {
@ -84,12 +90,57 @@ static void savePNG(int width, int height, const char *output_dir) {
fclose(f); fclose(f);
} }
int main(int argc, char *argv[]) { static void usage(void) {
if (argc != 3) { fprintf(stderr, "Usage: glslwrite [-w <width>] [-h <height>] [-s <step>] [-f <frames>] [-0 <param0>] [-1 <param1>] [-2 <param2>] <shader>\n");
fprintf(stderr, "Usage: glslwrite <shader> <output directory>\n");
exit(1); exit(1);
} }
int main(int argc, char *argv[]) {
int width = 800, height = 800;
while (true) {
int c = getopt(argc, argv, "w:h:s:f:0:1:2:");
if (c == -1)
break;
switch (c) {
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case 's':
step = atof(optarg);
break;
case 'f':
max_frame = atoi(optarg);
break;
case '0':
param0 = atoi(optarg);
break;
case '1':
param1 = atoi(optarg);
break;
case '2':
param2 = atoi(optarg);
break;
default:
fprintf(stderr, "Invalid option '%c'\n", optopt);
usage();
}
}
if (argc - optind != 2)
usage();
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@ -97,14 +148,14 @@ int main(int argc, char *argv[]) {
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_Window *window = SDL_CreateWindow("glslwrite", 0, 0, 800, 800, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_Window *window = SDL_CreateWindow("glslwrite", 0, 0, width, height, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
SDL_GLContext ctx = SDL_GL_CreateContext(window); SDL_GLContext ctx = SDL_GL_CreateContext(window);
filename = argv[1]; filename = argv[optind];
init(); init();
load(); load();
while (true) { while (!max_frame || frame < max_frame) {
SDL_Event e; SDL_Event e;
while (SDL_PollEvent(&e)) { while (SDL_PollEvent(&e)) {
@ -128,8 +179,9 @@ int main(int argc, char *argv[]) {
render(width, height); render(width, height);
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
savePNG(width, height, argv[2]); savePNG(width, height, argv[optind+1]);
current_time += 20; current_time += step;
frame++;
} }
quit: quit: