commit acaef93bd85e3b4305a5e84f6d6b88f35a2f1791 Author: David Oberhollenzer Date: Sat Jul 16 22:01:12 2022 +0200 Initial commit Signed-off-by: David Oberhollenzer diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b69523 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*~ +shadermeh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d22af13 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +CFLAGS = -ansi -pedantic -Wall -Wextra -O2 -Ofast -D_DEFAULT_SOURCE +LDFLAGS = -lX11 -lGL -lGLEW -lm -lrt + +shadermeh: shadermeh.o window.o shader.o + +window.o: window.c shadermeh.h +shader.o: shader.c shadermeh.h +shadermeh.o: shadermeh.c shadermeh.h + +.PHONY: clean +clean: + $(RM) shadermeh *.o diff --git a/shader.c b/shader.c new file mode 100644 index 0000000..b77447b --- /dev/null +++ b/shader.c @@ -0,0 +1,181 @@ +#include "shadermeh.h" + +/* stub vertex shader */ +static const char *vert_src = +"#version 140\n" +"\n" +"in vec4 V_POSITION;\n" +"in vec4 V_COLOR;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = V_POSITION;\n" +"}\n"; + +/* stub fragment shader */ +static const char *frag_src_pre = +"#version 330\n" +"\n" +"uniform vec3 iResolution;\n" +"uniform float iTime;\n" +"uniform float iTimeDelta;\n" +"uniform float iFrame;\n" +"uniform float iChannelTime[4];\n" +"uniform vec4 iMouse;\n" +"uniform vec4 iDate;\n" +"uniform float iSampleRate;\n" +"uniform vec3 iChannelResolution[4];\n" +"\n"; + +static const char *frag_src_post = +"\n" +"void main()\n" +"{\n" +" mainImage(gl_FragColor, gl_FragCoord.xy);\n" +"}\n" +"\n"; + +static char* load_file(const char *path) +{ + size_t size, pre_len, post_len; + char *buffer; + FILE *f; + + if (!path) + return NULL; + + /* try to open the file */ + f = fopen(path, "r"); + if (!f) { + perror(path); + return NULL; + } + + /* get file size */ + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + pre_len = strlen(frag_src_pre); + post_len = strlen(frag_src_post); + + /* try to allocate buffer for file contents */ + buffer = calloc(1, pre_len + size + post_len + 1); + + if (!buffer) { + fclose(f); + return NULL; + } + + /* read file contents into buffer */ + memcpy(buffer, frag_src_pre, pre_len); + fread(buffer + pre_len, 1, size, f); + memcpy(buffer + pre_len + size, frag_src_post, post_len); + + /* cleanup */ + fclose(f); + return buffer; +} + +/****************************************************************************/ + +GLuint shader_program_load(const char *fsrc) +{ + GLuint vsh = 0, fsh = 0, prog = 0; + char *fs = NULL; + + if (!fsrc) + return 0; + + fs = load_file(fsrc); + + if (!fs) + goto done; + + vsh = glCreateShader(GL_VERTEX_SHADER); + fsh = glCreateShader(GL_FRAGMENT_SHADER); + + if (!vsh || !fsh) + goto done; + + prog = glCreateProgram(); + if (!prog) + goto done; + + glShaderSource(vsh, 1, (const GLchar **)&vert_src, NULL); + glShaderSource(fsh, 1, (const GLchar **)&fs, NULL); + + glCompileShader(vsh); + glCompileShader(fsh); + + glAttachShader(prog, vsh); + glAttachShader(prog, fsh); + + glLinkProgram(prog); +done: + if (vsh) + glDeleteShader(vsh); + if (fsh) + glDeleteShader(fsh); + free(fs); + return prog; +} + +int shader_program_get_build_status(GLuint prog) +{ + GLuint shaders[3]; + GLsizei i, count; + GLint v; + + glGetProgramiv(prog, GL_LINK_STATUS, &v); + if (!v) + return 0; + + glGetAttachedShaders(prog, 3, &count, shaders); + + for (i = 0; i < count; ++i) { + glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &v); + if (!v) + return 0; + } + + return 1; +} + +void shader_program_print_info_log(GLuint prog) +{ + GLsizei i, count, len; + char *buffer = NULL; + int buffersize = 0; + GLuint shaders[3]; + GLint v; + + glGetAttachedShaders(prog, 3, &count, shaders); + + for (i = 0; i < count; ++i) { + glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &v); + + if ((v + 1) > buffersize) { + buffer = realloc(buffer, v + 1); + buffersize = v + 1; + } + + glGetShaderInfoLog(shaders[i], buffersize, + &len, (GLchar *)buffer); + buffer[len] = '\0'; + printf("-------------\n%s\n-------------\n", buffer); + } + + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &v); + + if ((v + 1) > buffersize) { + buffer = realloc(buffer, v + 1); + buffersize = v + 1; + } + + glGetProgramInfoLog(prog, buffersize, &len, (GLchar *)buffer); + buffer[len] = '\0'; + printf("-------------\n%s\n-------------\n", buffer); + + free(buffer); +} diff --git a/shadermeh.c b/shadermeh.c new file mode 100644 index 0000000..7d1fdc6 --- /dev/null +++ b/shadermeh.c @@ -0,0 +1,179 @@ +#include "shadermeh.h" + +static GLfloat vertex_buffer[] = { + -1.0f, -1.0f, 0.0f, /* lower left corner */ + 1.0f, 1.0f, 1.0f, + + +1.0f, -1.0f, 0.0f, /* lower right corner */ + 1.0f, 1.0f, 1.0f, + + -1.0f, 1.0f, 0.0f, /* uper left corner */ + 1.0f, 1.0f, 1.0f, + + +1.0f, +1.0f, 0.0f, /* upper right corner */ + 1.0f, 1.0f, 1.0f, +}; + +static double diff_timespec(const struct timespec *time1, + const struct timespec *time0) +{ + return (time1->tv_sec - time0->tv_sec) + + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; +} + +static const struct option long_opts[] = { + { "width", required_argument, NULL, 'w' }, + { "height", required_argument, NULL, 'h' }, + { "shader", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "w:h:s:"; + +static const char *usage_str = +"shadermeh OPTIONS...\n" +"\n" +"Possible options:\n" +"\n" +" --width, -w \n" +" --height, -h \n" +"\n" +" --shader, -s \n" +"\n"; + +int main(int argc, char **argv) +{ + GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame; + struct timespec start, frame_start, frame_end; + unsigned int width, height, iFrame = 0; + const char *shader_file = NULL; + GLint major, minor, prog; + float iTime, iTimeDelta; + GLuint vao, vbo; + window *wnd; + int i; + + /******************** parse options ************************/ + width = 800; + height = 450; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'w': + width = strtol(optarg, NULL, 10); + break; + case 'h': + height = strtol(optarg, NULL, 10); + break; + case 's': + shader_file = optarg; + break; + default: + fputs(usage_str, stderr); + return EXIT_FAILURE; + } + } + + if (!shader_file) { + fputs(usage_str, stderr); + fputs("No shader file specified!\n", stderr); + return EXIT_FAILURE; + } + + /********** create window and make context current **********/ + wnd = window_create(width, height, "shader meh..."); + + if (!wnd) { + fputs("failed to create window", stderr); + return EXIT_FAILURE; + } + + window_make_current(wnd); + window_set_vsync(wnd, 1); + + /******************** load entry points ********************/ + glewExperimental = GL_TRUE; + + if (glewInit() != GLEW_OK) { + fputs("glewInit() error", stderr); + window_destroy(wnd); + return EXIT_FAILURE; + } + + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + printf("OpenGL version %d.%d\n", major, minor); + + /******************** initialization ********************/ + glViewport(0, 0, width, height); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + /* vertex buffer object & vertex array object */ + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), + vertex_buffer, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, + (GLvoid *)(3 * sizeof(GLfloat))); + + /* shader */ + prog = shader_program_load(shader_file); + + if (!shader_program_get_build_status(prog)) { + shader_program_print_info_log(prog); + } + + glUseProgram(prog); + + /* uniforms */ + u_iResolution = glGetUniformLocation(prog, "iResolution"); + u_iTime = glGetUniformLocation(prog, "iTime"); + u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta"); + u_iFrame = glGetUniformLocation(prog, "iFrame;"); + + glUniform3f(u_iResolution, width, height, 0.0f); + + clock_gettime(CLOCK_MONOTONIC_RAW, &start); + + /******************** drawing loop ********************/ + while (window_handle_events()) { + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + window_swap_buffers(wnd); + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end); + + iFrame += 1; + iTimeDelta = diff_timespec(&frame_end, &frame_start); + iTime = diff_timespec(&frame_end, &start); + + glUniform1f(u_iTimeDelta, iTimeDelta); + glUniform1f(u_iTime, iTime); + glUniform1ui(u_iFrame, iFrame); + } + + /******************** cleanup ********************/ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &vbo); + glBindVertexArray(0); + glDeleteVertexArrays(1, &vao); + glDeleteProgram(prog); + + window_make_current(NULL); + window_destroy(wnd); + return EXIT_SUCCESS; +} diff --git a/shadermeh.h b/shadermeh.h new file mode 100644 index 0000000..419bc47 --- /dev/null +++ b/shadermeh.h @@ -0,0 +1,45 @@ +#ifndef SHADERMEH_H +#define SHADERMEH_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct { + Window wnd; + GLXContext gl; +} window; + +window *window_create(unsigned int width, unsigned int height, + const char *caption); + +void window_make_current(window *wnd); + +void window_swap_buffers(window *wnd); + +void window_set_vsync(window *wnd, int enable); + +void window_destroy(window *wnd); + +int window_handle_events(void); + + +GLuint shader_program_load(const char *fsh); + +int shader_program_get_build_status(GLuint prog); + +void shader_program_print_info_log(GLuint prog); + +#endif /* SHADERMEH_H */ diff --git a/shaders/cyber.frag b/shaders/cyber.frag new file mode 100644 index 0000000..cc0c2ca --- /dev/null +++ b/shaders/cyber.frag @@ -0,0 +1,158 @@ + +float sun(vec2 uv, float battery) +{ + float val = smoothstep(0.3, 0.29, length(uv)); + float bloom = smoothstep(0.7, 0.0, length(uv)); + float cut = 3.0 * sin((uv.y + iTime * 0.2 * (battery + 0.02)) * 100.0) + + clamp(uv.y * 14.0 + 1.0, -6.0, 6.0); + cut = clamp(cut, 0.0, 1.0); + return clamp(val * cut, 0.0, 1.0) + bloom * 0.6; +} + +float grid(vec2 uv, float battery) +{ + vec2 size = vec2(uv.y, uv.y * uv.y * 0.2) * 0.01; + uv += vec2(0.0, iTime * 4.0 * (battery + 0.05)); + uv = abs(fract(uv) - 0.5); + vec2 lines = smoothstep(size, vec2(0.0), uv); + lines += smoothstep(size * 5.0, vec2(0.0), uv) * 0.4 * battery; + return clamp(lines.x + lines.y, 0.0, 3.0); +} + +float dot2(in vec2 v ) { return dot(v,v); } + +float sdTrapezoid( in vec2 p, in float r1, float r2, float he ) +{ + vec2 k1 = vec2(r2,he); + vec2 k2 = vec2(r2-r1,2.0*he); + p.x = abs(p.x); + vec2 ca = vec2(p.x-min(p.x,(p.y<0.0)?r1:r2), abs(p.y)-he); + vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot2(k2), 0.0, 1.0 ); + float s = (cb.x<0.0 && ca.y<0.0) ? -1.0 : 1.0; + return s*sqrt( min(dot2(ca),dot2(cb)) ); +} + +float sdLine( in vec2 p, in vec2 a, in vec2 b ) +{ + vec2 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ); +} + +float sdBox( in vec2 p, in vec2 b ) +{ + vec2 d = abs(p)-b; + return length(max(d,vec2(0))) + min(max(d.x,d.y),0.0); +} + +float opSmoothUnion(float d1, float d2, float k){ + float h = clamp(0.5 + 0.5 * (d2 - d1) /k,0.0,1.0); + return mix(d2, d1 , h) - k * h * ( 1.0 - h); +} + +float sdCloud(in vec2 p, in vec2 a1, in vec2 b1, in vec2 a2, in vec2 b2, float w) +{ + //float lineVal1 = smoothstep(w - 0.0001, w, sdLine(p, a1, b1)); + float lineVal1 = sdLine(p, a1, b1); + float lineVal2 = sdLine(p, a2, b2); + vec2 ww = vec2(w*1.5, 0.0); + vec2 left = max(a1 + ww, a2 + ww); + vec2 right = min(b1 - ww, b2 - ww); + vec2 boxCenter = (left + right) * 0.5; + //float boxW = right.x - left.x; + float boxH = abs(a2.y - a1.y) * 0.5; + //float boxVal = sdBox(p - boxCenter, vec2(boxW, boxH)) + w; + float boxVal = sdBox(p - boxCenter, vec2(0.04, boxH)) + w; + + float uniVal1 = opSmoothUnion(lineVal1, boxVal, 0.05); + float uniVal2 = opSmoothUnion(lineVal2, boxVal, 0.05); + + return min(uniVal1, uniVal2); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = (2.0 * fragCoord.xy - iResolution.xy)/iResolution.y; + float battery = 1.0; + //if (iMouse.x > 1.0 && iMouse.y > 1.0) battery = iMouse.y / iResolution.y; + //else battery = 0.8; + + //if (abs(uv.x) < (9.0 / 16.0)) + { + // Grid + float fog = smoothstep(0.1, -0.02, abs(uv.y + 0.2)); + vec3 col = vec3(0.0, 0.1, 0.2); + if (uv.y < -0.2) + { + uv.y = 3.0 / (abs(uv.y + 0.2) + 0.05); + uv.x *= uv.y * 1.0; + float gridVal = grid(uv, battery); + col = mix(col, vec3(1.0, 0.5, 1.0), gridVal); + } + else + { + float fujiD = min(uv.y * 4.5 - 0.5, 1.0); + uv.y -= battery * 1.1 - 0.51; + + vec2 sunUV = uv; + vec2 fujiUV = uv; + + // Sun + sunUV += vec2(0.75, 0.2); + //uv.y -= 1.1 - 0.51; + col = vec3(1.0, 0.2, 1.0); + float sunVal = sun(sunUV, battery); + + col = mix(col, vec3(1.0, 0.4, 0.1), sunUV.y * 2.0 + 0.2); + col = mix(vec3(0.0, 0.0, 0.0), col, sunVal); + + // fuji + float fujiVal = sdTrapezoid( uv + vec2(-0.75+sunUV.y * 0.0, 0.5), 1.75 + pow(uv.y * uv.y, 2.1), 0.2, 0.5); + float waveVal = uv.y + sin(uv.x * 20.0 + iTime * 2.0) * 0.05 + 0.2; + float wave_width = smoothstep(0.0,0.01,(waveVal)); + + // fuji color + col = mix( col, mix(vec3(0.0, 0.0, 0.25), vec3(1.0, 0.0, 0.5), fujiD), step(fujiVal, 0.0)); + // fuji top snow + col = mix( col, vec3(1.0, 0.5, 1.0), wave_width * step(fujiVal, 0.0)); + // fuji outline + col = mix( col, vec3(1.0, 0.5, 1.0), 1.0-smoothstep(0.0,0.01,abs(fujiVal)) ); + //col = mix( col, vec3(1.0, 1.0, 1.0), 1.0-smoothstep(0.03,0.04,abs(fujiVal)) ); + //col = vec3(1.0, 1.0, 1.0) *(1.0-smoothstep(0.03,0.04,abs(fujiVal))); + + // horizon color + col += mix( col, mix(vec3(1.0, 0.12, 0.8), vec3(0.0, 0.0, 0.2), clamp(uv.y * 3.5 + 3.0, 0.0, 1.0)), step(0.0, fujiVal) ); + + // cloud + vec2 cloudUV = uv; + cloudUV.x = mod(cloudUV.x + iTime * 0.1, 4.0) - 2.0; + float cloudTime = iTime * 0.5; + float cloudY = -0.5; + float cloudVal1 = sdCloud(cloudUV, + vec2(0.1 + sin(cloudTime + 140.5)*0.1,cloudY), + vec2(1.05 + cos(cloudTime * 0.9 - 36.56) * 0.1, cloudY), + vec2(0.2 + cos(cloudTime * 0.867 + 387.165) * 0.1,0.25+cloudY), + vec2(0.5 + cos(cloudTime * 0.9675 - 15.162) * 0.09, 0.25+cloudY), 0.075); + cloudY = -0.6; + float cloudVal2 = sdCloud(cloudUV, + vec2(-0.9 + cos(cloudTime * 1.02 + 541.75) * 0.1,cloudY), + vec2(-0.5 + sin(cloudTime * 0.9 - 316.56) * 0.1, cloudY), + vec2(-1.5 + cos(cloudTime * 0.867 + 37.165) * 0.1,0.25+cloudY), + vec2(-0.6 + sin(cloudTime * 0.9675 + 665.162) * 0.09, 0.25+cloudY), 0.075); + + float cloudVal = min(cloudVal1, cloudVal2); + + //col = mix(col, vec3(1.0,1.0,0.0), smoothstep(0.0751, 0.075, cloudVal)); + col = mix(col, vec3(0.0, 0.0, 0.2), 1.0 - smoothstep(0.075 - 0.0001, 0.075, cloudVal)); + col += vec3(1.0, 1.0, 1.0)*(1.0 - smoothstep(0.0,0.01,abs(cloudVal - 0.075))); + } + + col += fog * fog * fog; + col = mix(vec3(col.r, col.r, col.r) * 0.5, col, battery * 0.7); + + fragColor = vec4(col,1.0); + } + //else fragColor = vec4(0.0); + + +} diff --git a/shaders/fractal.frag b/shaders/fractal.frag new file mode 100644 index 0000000..69514be --- /dev/null +++ b/shaders/fractal.frag @@ -0,0 +1,66 @@ +vec3 palette(float d){ + return mix(vec3(0.2,0.7,0.9),vec3(1.,0.,1.),d); +} + +vec2 rotate(vec2 p,float a){ + float c = cos(a); + float s = sin(a); + return p*mat2(c,s,-s,c); +} + +float map(vec3 p){ + for( int i = 0; i<8; ++i){ + float t = iTime*0.2; + p.xz =rotate(p.xz,t); + p.xy =rotate(p.xy,t*1.89); + p.xz = abs(p.xz); + p.xz-=.5; + } + return dot(sign(p),p)/5.; +} + +vec4 rm (vec3 ro, vec3 rd){ + float t = 0.; + vec3 col = vec3(0.); + float d; + for(float i =0.; i<64.; i++){ + vec3 p = ro + rd*t; + d = map(p)*.5; + if(d<0.02){ + break; + } + if(d>100.){ + break; + } + //col+=vec3(0.6,0.8,0.8)/(400.*(d)); + col+=palette(length(p)*.1)/(400.*(d)); + t+=d; + } + return vec4(col,1./(d*100.)); +} +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = (fragCoord-(iResolution.xy/2.))/iResolution.x; + vec3 ro = vec3(0.,0.,-50.); + ro.xz = rotate(ro.xz,iTime); + vec3 cf = normalize(-ro); + vec3 cs = normalize(cross(cf,vec3(0.,1.,0.))); + vec3 cu = normalize(cross(cf,cs)); + + vec3 uuv = ro+cf*3. + uv.x*cs + uv.y*cu; + + vec3 rd = normalize(uuv-ro); + + vec4 col = rm(ro,rd); + + + fragColor = col; +} + +/** SHADERDATA +{ + "title": "fractal pyramid", + "description": "", + "model": "car" +} +*/ \ No newline at end of file diff --git a/shaders/mandelbrot.frag b/shaders/mandelbrot.frag new file mode 100644 index 0000000..05676bb --- /dev/null +++ b/shaders/mandelbrot.frag @@ -0,0 +1,26 @@ +#define MAX_ITERATIONS 1024 + +void mainImage(out vec4 fragColor, in vec2 fragCoord) +{ + float s = exp(-iTime / 2.0); + vec2 c = vec2(-0.770007,0.100001); + vec2 z = vec2(0.0); + vec2 del = (2.0 * fragCoord/iResolution.xy - 1.0) * s; + del.x /= (iResolution.y / iResolution.x); + vec2 eps = vec2(0.0); + + int iterations = 0; + while (length(z + eps) < 2.0 && iterations < MAX_ITERATIONS) { + eps = 2.0*vec2(z.x*eps.x - z.y*eps.y, z.x*eps.y + z.y*eps.x) + vec2(eps.x*eps.x - eps.y*eps.y, 2.0*eps.x*eps.y) + del; + z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + c; + if (length(eps) > 100.0) { + z += eps; + eps = vec2(0.0); + } + iterations += 1; + } + + + // Output to screen + fragColor = vec4(vec3(float(iterations) / float(MAX_ITERATIONS)), 1.0); +} diff --git a/shaders/seascape.frag b/shaders/seascape.frag new file mode 100644 index 0000000..5edaaaa --- /dev/null +++ b/shaders/seascape.frag @@ -0,0 +1,204 @@ +/* + * "Seascape" by Alexander Alekseev aka TDM - 2014 + * License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + * Contact: tdmaav@gmail.com + */ + +const int NUM_STEPS = 8; +const float PI = 3.141592; +const float EPSILON = 1e-3; +#define EPSILON_NRM (0.1 / iResolution.x) +#define AA + +// sea +const int ITER_GEOMETRY = 3; +const int ITER_FRAGMENT = 5; +const float SEA_HEIGHT = 0.6; +const float SEA_CHOPPY = 4.0; +const float SEA_SPEED = 0.8; +const float SEA_FREQ = 0.16; +const vec3 SEA_BASE = vec3(0.0,0.09,0.18); +const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6)*0.6; +#define SEA_TIME (1.0 + iTime * SEA_SPEED) +const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + +// math +mat3 fromEuler(vec3 ang) { + vec2 a1 = vec2(sin(ang.x),cos(ang.x)); + vec2 a2 = vec2(sin(ang.y),cos(ang.y)); + vec2 a3 = vec2(sin(ang.z),cos(ang.z)); + mat3 m; + m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x); + m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x); + m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y); + return m; +} +float hash( vec2 p ) { + float h = dot(p,vec2(127.1,311.7)); + return fract(sin(h)*43758.5453123); +} +float noise( in vec2 p ) { + vec2 i = floor( p ); + vec2 f = fract( p ); + vec2 u = f*f*(3.0-2.0*f); + return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), + hash( i + vec2(1.0,0.0) ), u.x), + mix( hash( i + vec2(0.0,1.0) ), + hash( i + vec2(1.0,1.0) ), u.x), u.y); +} + +// lighting +float diffuse(vec3 n,vec3 l,float p) { + return pow(dot(n,l) * 0.4 + 0.6,p); +} +float specular(vec3 n,vec3 l,vec3 e,float s) { + float nrm = (s + 8.0) / (PI * 8.0); + return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; +} + +// sky +vec3 getSkyColor(vec3 e) { + e.y = (max(e.y,0.0)*0.8+0.2)*0.8; + return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +} + +// sea +float sea_octave(vec2 uv, float choppy) { + uv += noise(uv); + vec2 wv = 1.0-abs(sin(uv)); + vec2 swv = abs(cos(uv)); + wv = mix(wv,swv,wv); + return pow(1.0-pow(wv.x * wv.y,0.65),choppy); +} + +float map(vec3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + vec2 uv = p.xz; uv.x *= 0.75; + + float d, h = 0.0; + for(int i = 0; i < ITER_GEOMETRY; i++) { + d = sea_octave((uv+SEA_TIME)*freq,choppy); + d += sea_octave((uv-SEA_TIME)*freq,choppy); + h += d * amp; + uv *= octave_m; freq *= 1.9; amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float map_detailed(vec3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + vec2 uv = p.xz; uv.x *= 0.75; + + float d, h = 0.0; + for(int i = 0; i < ITER_FRAGMENT; i++) { + d = sea_octave((uv+SEA_TIME)*freq,choppy); + d += sea_octave((uv-SEA_TIME)*freq,choppy); + h += d * amp; + uv *= octave_m; freq *= 1.9; amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) { + float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); + fresnel = pow(fresnel,3.0) * 0.5; + + vec3 reflected = getSkyColor(reflect(eye,n)); + vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; + + vec3 color = mix(refracted,reflected,fresnel); + + float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); + color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; + + color += vec3(specular(n,l,eye,60.0)); + + return color; +} + +// tracing +vec3 getNormal(vec3 p, float eps) { + vec3 n; + n.y = map_detailed(p); + n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; + n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; + n.y = eps; + return normalize(n); +} + +float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) { + float tm = 0.0; + float tx = 1000.0; + float hx = map(ori + dir * tx); + if(hx > 0.0) { + p = ori + dir * tx; + return tx; + } + float hm = map(ori + dir * tm); + float tmid = 0.0; + for(int i = 0; i < NUM_STEPS; i++) { + tmid = mix(tm,tx, hm/(hm-hx)); + p = ori + dir * tmid; + float hmid = map(p); + if(hmid < 0.0) { + tx = tmid; + hx = hmid; + } else { + tm = tmid; + hm = hmid; + } + } + return tmid; +} + +vec3 getPixel(in vec2 coord, float time) { + vec2 uv = coord / iResolution.xy; + uv = uv * 2.0 - 1.0; + uv.x *= iResolution.x / iResolution.y; + + // ray + vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time); + vec3 ori = vec3(0.0,3.5,time*5.0); + vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.14; + dir = normalize(dir) * fromEuler(ang); + + // tracing + vec3 p; + heightMapTracing(ori,dir,p); + vec3 dist = p - ori; + vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM); + vec3 light = normalize(vec3(0.0,1.0,0.8)); + + // color + return mix( + getSkyColor(dir), + getSeaColor(p,n,light,dir,dist), + pow(smoothstep(0.0,-0.02,dir.y),0.2)); +} + +// main +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + float time = iTime * 0.3 + iMouse.x*0.01; + +#ifdef AA + vec3 color = vec3(0.0); + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + vec2 uv = fragCoord+vec2(i,j)/3.0; + color += getPixel(uv, time); + } + } + color /= 9.0; +#else + vec3 color = getPixel(fragCoord, time); +#endif + + // post + fragColor = vec4(pow(color,vec3(0.65)), 1.0); +} diff --git a/shaders/tiling.frag b/shaders/tiling.frag new file mode 100644 index 0000000..e37f730 --- /dev/null +++ b/shaders/tiling.frag @@ -0,0 +1,71 @@ +#define sqrt25 0.6324555320 //sqrt(2./5.) +#define sqrt35 0.7745966692 //sqrt(3./5.) + +//edge distance of a Cube +float cubeDist(vec3 uvw) { + vec3 d = abs(uvw); //mirroring along axis + return min(d.x, min(d.y, d.z))*2.; //*2. for 0-1 range +} + +// Cube Tiling +vec4 cubeTile(vec3 uvw) { + vec3 grid = fract(uvw)-.5; // centered UVW coords + float edist = cubeDist(grid); // edge distance + //float cdist = dot(grid,grid); //squared center distance + //vec3 id = uvw-grid; // Cells IDs + + return vec4(grid, edist); +} + +// scaled with offset cube tiling +vec4 cubeCell(vec3 uvw, vec3 offset, float gridRes) { + vec4 cubeTiling = cubeTile(uvw*gridRes + offset); + vec3 tiledUV = (cubeTiling.xyz - offset)/gridRes; //cube pixaltion + + return vec4(tiledUV,cubeTiling.w); +} + +// rotates a vetor from SirBelfer4 (https://www.shadertoy.com/view/ssc3z4) +vec3 rotate(vec3 v, vec3 a) +{ + // https://math.stackexchange.com/questions/2975109/how-to-convert-euler-angles-to-quaternions-and-get-the-same-euler-angles-back-fr + vec4 q; + vec3 c = cos(a * 0.5), s = sin(a * 0.5); + q.x = s.x * c.y * c.z - c.x * s.y * s.z; + q.y = c.x * s.y * c.z + s.x * c.y * s.z; + q.z = c.x * c.y * s.z - s.x * s.y * c.z; + q.w = c.x * c.y * c.z + s.x * s.y * s.z; + + // https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ + vec3 qt = 2.0 * cross(q.xyz, v); + return v + q.w * qt + cross(q.xyz, qt); +} + +// makes RdYlBu_r colormap with polynimal 6 https://www.shadertoy.com/view/Nd3fR2 +vec3 RdYlBu_r(float t) { + const vec3 c0 = vec3(0.207621,0.196195,0.618832); + const vec3 c1 = vec3(-0.088125,3.196170,-0.353302); + const vec3 c2 = vec3(8.261232,-8.366855,14.368787); + const vec3 c3 = vec3(-2.922476,33.244294,-43.419173); + const vec3 c4 = vec3(-34.085327,-74.476041,37.159352); + const vec3 c5 = vec3(50.429790,67.145621,-1.750169); + const vec3 c6 = vec3(-21.188828,-20.935464,-6.501427); + return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6))))); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + vec2 uv = fragCoord/iResolution.y; //square UV pattern + float time = (0.05*iTime); // used as z dimension + float gridRes = 2.5; //size of cubes + + vec3 point = vec3(uv, time); //uvw cords + + //cosmetic rotate for fun triangles otherwise it looks so square + point = rotate(point, (vec3(sqrt25,sqrt35,0.))); //vec3 must be normalized + + vec4 a = cubeCell(point, vec3(0.), gridRes); + + vec3 col = RdYlBu_r(a.w); // cosmetic Colormap + + fragColor = vec4(col, 1.); +} diff --git a/window.c b/window.c new file mode 100644 index 0000000..b481a79 --- /dev/null +++ b/window.c @@ -0,0 +1,237 @@ +#include "shadermeh.h" + +#ifndef GLX_ARB_create_context +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#endif + +#ifndef GLX_ARB_create_context_profile +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#endif + +typedef GLXContext (*CREATECONTEXTATTRIBSPROC)(Display *, GLXFBConfig, + GLXContext, Bool, + const int *); + +typedef void (*SWAPINTERVALEXTPROC)(Display *, GLXDrawable, int); + +static int glversions[][2] = { {4,5}, {4,4}, {4,3}, {4,2}, {4,1}, {4,0}, + {3,3}, {3,2}, {3,1}, {3,0} }; + +static Display *dpy; +static int dpy_ref = 0; +static Atom atom_wm_delete; +static XContext xctx; + +static int xlib_swallow_errors(Display *display, XErrorEvent *event) +{ + (void)display; (void)event; + return 0; +} + +/****************************************************************************/ + +window *window_create(unsigned int width, unsigned int height, + const char *caption) +{ + CREATECONTEXTATTRIBSPROC CreateContextAttribs; + XSetWindowAttributes swa; + int fbcount, attr[20]; + GLXFBConfig fbc, *fbl; + unsigned int i = 0; + XSizeHints hints; + XVisualInfo *vi; + window *this; + + this = calloc(1, sizeof(*this)); + if (!this) + return NULL; + + if (dpy) { + ++dpy_ref; + } else { + dpy = XOpenDisplay(0); + dpy_ref = 1; + + if (!dpy) { + free(this); + return NULL; + } + + XSetErrorHandler(xlib_swallow_errors); + atom_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", True); + + xctx = XUniqueContext(); + } + + attr[ 0] = GLX_BUFFER_SIZE; attr[ 1] = 32; + attr[ 2] = GLX_RED_SIZE; attr[ 3] = 8; + attr[ 4] = GLX_GREEN_SIZE; attr[ 5] = 8; + attr[ 6] = GLX_BLUE_SIZE; attr[ 7] = 8; + attr[ 8] = GLX_ALPHA_SIZE; attr[ 9] = 8; + attr[10] = GLX_DEPTH_SIZE; attr[11] = 24; + attr[12] = GLX_STENCIL_SIZE; attr[13] = 8; + attr[14] = GLX_DOUBLEBUFFER; attr[15] = True; + attr[16] = None; + + fbl = glXChooseFBConfig(dpy, DefaultScreen(dpy), attr, &fbcount); + + if (!fbl || !fbcount) { + window_destroy(this); + return NULL; + } + + fbc = fbl[0]; + vi = glXGetVisualFromFBConfig(dpy, fbl[0]); + XFree(fbl); + + if (!vi) { + window_destroy(this); + return NULL; + } + + swa.colormap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), + vi->visual, AllocNone); + + if (!swa.colormap) { + XFree(vi); + window_destroy(this); + return NULL; + } + + swa.border_pixel = 0; + + this->wnd = XCreateWindow(dpy, RootWindow(dpy,vi->screen), 0, 0, + width, height, 0, vi->depth, InputOutput, + vi->visual, CWBorderPixel|CWColormap, &swa); + XFree(vi); + + if (!this->wnd) { + window_destroy(this); + return NULL; + } + + CreateContextAttribs = (CREATECONTEXTATTRIBSPROC) + glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + + if (CreateContextAttribs) { + attr[0] = GLX_CONTEXT_MAJOR_VERSION_ARB; + attr[1] = 0; + attr[2] = GLX_CONTEXT_MINOR_VERSION_ARB; + attr[3] = 0; + attr[4] = GLX_CONTEXT_PROFILE_MASK_ARB; + attr[5] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + attr[6] = GLX_CONTEXT_FLAGS_ARB; + attr[7] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + attr[8] = None; + + for (i = 0; !this->gl && i < sizeof(glversions) / + sizeof(glversions[0]); ++i) { + attr[1] = glversions[i][0]; + attr[3] = glversions[i][1]; + + this->gl = CreateContextAttribs(dpy, fbc, 0, + True, attr); + } + } + + if (!this->gl) { + this->gl = glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, + 0, GL_TRUE); + } + + if (!this->gl) { + window_destroy(this); + return NULL; + } + + hints.flags = PSize | PMinSize | PMaxSize; + hints.min_width = hints.max_width = hints.base_width = width; + hints.min_height = hints.max_height = hints.base_height = height; + + XSetWMNormalHints(dpy, this->wnd, &hints); + + XSelectInput(dpy, this->wnd, StructureNotifyMask | + KeyPressMask | KeyReleaseMask | + PointerMotionMask | PropertyChangeMask | + ButtonPressMask | ButtonReleaseMask); + XSetWMProtocols(dpy, this->wnd, &atom_wm_delete, 1); + XFlush(dpy); + + XStoreName(dpy, this->wnd, caption); + XMapWindow(dpy, this->wnd); + XSaveContext(dpy, this->wnd, xctx, (XPointer)this); + XFlush(dpy); + return this; +} + +void window_make_current(window *this) +{ + if (this) { + glXMakeContextCurrent(dpy, this->wnd, this->wnd, this->gl); + } else { + glXMakeContextCurrent(dpy, 0, 0, 0); + } +} + +void window_swap_buffers(window *this) +{ + glXSwapBuffers(dpy, this->wnd); +} + +void window_set_vsync(window *this, int enable) +{ + SWAPINTERVALEXTPROC SwapIntervalEXT; + + SwapIntervalEXT = (SWAPINTERVALEXTPROC) + glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); + + if (SwapIntervalEXT) + SwapIntervalEXT(dpy, this->wnd, enable ? 1 : 0); +} + +void window_destroy(window *this) +{ + if (this->gl) { + glXMakeContextCurrent(dpy, 0, 0, 0); + glXDestroyContext(dpy, this->gl); + } + + if (this->wnd) { + XDeleteContext(dpy, this->wnd, xctx); + XDestroyWindow(dpy, this->wnd); + } + + if ((--dpy_ref) <= 0) { + XCloseDisplay(dpy); + dpy_ref = 0; + dpy = NULL; + } + + free(this); +} + +int window_handle_events(void) +{ + window *this; + XEvent e; + + if (XPending(dpy)) { + XNextEvent(dpy, &e); + XFindContext(dpy, e.xany.window, xctx, (XPointer *)&this); + + switch (e.type) { + case ClientMessage: + if (e.xclient.data.l[0] == (long)atom_wm_delete) { + XUnmapWindow(dpy, e.xany.window); + --dpy_ref; + } + break; + } + } + + return (dpy_ref > 0); +}