Skip to content

Instantly share code, notes, and snippets.

@possan
Created February 25, 2015 22:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save possan/f34f8c5ada300d99b14e to your computer and use it in GitHub Desktop.
Save possan/f34f8c5ada300d99b14e to your computer and use it in GitHub Desktop.
Minimal OpenGL Shader example
//
// Minimal self container OpenGL Example
//
// Compile with:
// xcrun gcc -o test -framework Foundation -framework OpenGL -framework CoreGraphics -framework AppKit main.m
//
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#import <Carbon/Carbon.h>
#import <CoreFoundation/CoreFoundation.h>
#import <AppKit/AppKit.h>
#include <math.h>
static const char *vertexshader2 =
"uniform vec2 iResolution;\n"
"uniform vec2 iMouse;\n"
"uniform float iGlobalTime;\n"
"void main() {\n"
" gl_Position = gl_Vertex;\n"
"}\n";
static const char *fragmentshaderheader =
"uniform sampler2D iChannel0;\n"
"uniform vec2 iResolution;\n"
"uniform vec2 iMouse;\n"
"uniform float iGlobalTime;\n\n";
@class MyWindow;
@interface MyRunner : NSObject
@property (nonatomic, readwrite) float time;
@property (nonatomic, readwrite) NSOpenGLContext *ctx;
@property (nonatomic, readwrite) MyWindow *window;
- (id)init;
- (void)doTick: (NSTimer*)timer;
@end
@interface MyWindow : NSWindow
@property (nonatomic, readwrite) MyRunner *runner;
@end
@implementation MyWindow
#define KEY_F 3
#define KEY_ESCAPE 53
#define KEY_RETURN 36
#define KEY_TAB 48
#define KEY_SPACE 49
#define KEY_Q 12
- (void) postCreate {
[self setLevel:NSMainMenuWindowLevel+1];
[self setOpaque:YES];
[self makeKeyAndOrderFront:nil];
CGLEnable((CGLContextObj)[self.runner.ctx CGLContextObj], kCGLCEMPEngine);
[self.runner.ctx setView:self.contentView];
[self.runner.ctx makeCurrentContext];
}
- (void) keyDown: (NSEvent*)ev {
if ([ev isARepeat])
return;
NSLog(@"key down: %d", [ev keyCode]);
if ([ev keyCode] == KEY_F || [ev keyCode] == KEY_RETURN) {
//
// Toggle fullscreen with 'F' or Return
//
if (self.styleMask & NSTitledWindowMask) {
NSLog(@"Go fullscreen...");
[self setStyleMask: NSFullScreenWindowMask ];
[self makeKeyAndOrderFront:nil];
NSRect viewRect = NSMakeRect(0.0, 0.0, self.screen.frame.size.width, self.screen.frame.size.height);
[self setFrame:viewRect display:YES];
[self.contentView setFrame:viewRect];
[self.runner.ctx setView:self.contentView];
[self.runner.ctx update];
} else {
NSLog(@"Go windowed...");
[self setStyleMask: NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask ];
NSRect viewRect = NSMakeRect(0.0, 0.0, 800, 600);
[self setFrame:viewRect display:YES];
[self.contentView setFrame:viewRect];
[self.runner.ctx setView:self.contentView];
[self makeKeyAndOrderFront:nil];
[self center];
[self.runner.ctx update];
}
}
if ([ev keyCode] == KEY_ESCAPE || [ev keyCode] == KEY_Q) {
//
// quit with 'Q' or Escape
//
[NSApp stop:nil];
[NSApp postEvent: [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:0.0 windowNumber:0 context:NULL subtype:0 data1:0 data2:0] atStart:NO];
}
if ([ev keyCode] == KEY_SPACE) {
self.runner.time = (float) (rand() % 10000);
}
}
@end
@implementation MyRunner {
GLuint time_loc;
GLuint mouse_loc;
GLuint resolution_loc;
float width;
float height;
float x;
float y;
int prog;
}
- (id) init {
self = [super init];
self.time = .0f;
x = .5f;
y = .5f;
width = 128;
height = 96;
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAScreenMask,
(NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
NSOpenGLPFADepthSize,
(NSOpenGLPixelFormatAttribute)16,
(NSOpenGLPixelFormatAttribute)0
};
NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: attrs];
self.ctx = [[NSOpenGLContext alloc] initWithFormat: fmt shareContext: nil];
NSRect viewRect = NSMakeRect(0, 0, 800, 600);
x = 400;
y = 300;
width = 800;
height = 600;
unsigned style = NSTitledWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask;
self.window = [[MyWindow alloc] initWithContentRect: viewRect styleMask:style backing:NSBackingStoreBuffered defer:YES];
self.window.runner = self;
[self.window postCreate];
[self.window center];
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
[NSTimer scheduledTimerWithTimeInterval: 1.0f / 60.0f target:self selector: @selector(doTick:) userInfo: nil repeats: YES];
// compile shaders
prog = glCreateProgram();
int prog_vs = glCreateShader(GL_VERTEX_SHADER);
int prog_fs = glCreateShader(GL_FRAGMENT_SHADER);
size_t len1;
GLint status;
char *tmp2 = malloc(81920);
FILE *f = fopen("pixelshader.fs", "rb");
long fl = 0;
fseek(f, 0, SEEK_END);
fl = ftell(f);
fseek(f, 0, SEEK_SET);
fread(tmp2, 1, fl, f);
fclose(f);
char *tmp = malloc(81920);
sprintf(tmp, "%s%s", fragmentshaderheader, tmp2);
NSLog(@"Final shader: %s\n", tmp);
len1 = strlen(vertexshader2);
glShaderSource(prog_vs, 1, (const char *)&vertexshader2, &len1);
glCompileShader(prog_vs);
glGetShaderiv(prog_vs, GL_COMPILE_STATUS, (GLint *)&status);
NSLog(@"prog_vs status %fd", status);
if (!status) {
GLchar infoLog[2048];
GLint infoLen = 2048;
glGetShaderiv(prog_vs, GL_INFO_LOG_LENGTH, &infoLen);
glGetShaderInfoLog(prog_vs, infoLen, NULL, infoLog);
NSLog(@"Error in vertex shader compilation: %s\n", infoLog);
}
glAttachShader(prog, prog_vs);
len1 = strlen(tmp);
glShaderSource(prog_fs, 1, (const char *)&tmp, &len1);
glCompileShader(prog_fs);
glGetShaderiv(prog_fs, GL_COMPILE_STATUS, (GLint *)&status);
NSLog(@"prog_fs status %d", status);
if (!status) {
GLchar infoLog[2048];
GLint infoLen = 2048;
glGetShaderiv(prog_fs, GL_INFO_LOG_LENGTH, &infoLen);
glGetShaderInfoLog(prog_fs, infoLen, NULL, infoLog);
NSLog(@"Error in fragment shader compilation: %s\n", infoLog);
}
glAttachShader(prog, prog_fs);
free(tmp);
free(tmp2);
glLinkProgram(prog);
time_loc = glGetUniformLocation(prog, "iGlobalTime");
resolution_loc = glGetUniformLocation(prog, "iResolution");
mouse_loc = glGetUniformLocation(prog, "iMouse");
NSLog(@"compiled program %d", prog);
return self;
}
- (void) doTick: (NSTimer*)timer {
float T = self.time;
self.time += 1.0 / 60.0f;
GLint value = 1;
[self.ctx setValues: &value
forParameter: NSOpenGLCPSwapInterval];
NSRect viewRect = ((NSView *)self.window.contentView).frame;
glViewport(0,0, viewRect.size.width,viewRect.size.height);
glClearColor(0.1f + 0.1f * sin(T * 2.0f), 0.1f + 0.1f * sin(T * 3.0f), 0.1f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_REPLACE);
glBlendFunc(GL_ONE, GL_ZERO);
glUseProgram(prog);
glUniform1f(time_loc, T);
glUniform2f(mouse_loc, x, y);
glUniform2f(resolution_loc, viewRect.size.width, viewRect.size.height);
glBegin(GL_TRIANGLES);
glColor3f(1, 1, 1);
glVertex3f(-1,-1,0);
glVertex3f(1,-1,0);
glVertex3f(1,1,0);
glVertex3f(-1,-1,0);
glVertex3f(1,1,0);
glVertex3f(-1,1,0);
glEnd();
[self.ctx flushBuffer];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[NSApplication sharedApplication];
[MyRunner new];
[NSApp run];
}
return 0;
}
// "Seascape" by Alexander Alekseev aka TDM - 2014
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
const int NUM_STEPS = 8;
const float PI = 3.1415;
const float EPSILON = 1e-3;
float EPSILON_NRM = 0.1 / iResolution.x;
// 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.1,0.19,0.22);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
float SEA_TIME = iGlobalTime * SEA_SPEED;
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) / (3.1415 * 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);
vec3 ret;
ret.x = pow(1.0-e.y,2.0);
ret.y = 1.0-e.y;
ret.z = 0.6+(1.0-e.y)*0.4;
return ret;
}
// 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 = 1.0 - max(dot(n,-eye),0.0);
fresnel = pow(fresnel,3.0) * 0.65;
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) 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;
}
// main
void main(void) {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float time = iGlobalTime * 0.3 + iMouse.x*0.01;
// 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.15;
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
vec3 color = mix(
getSkyColor(dir),
getSeaColor(p,n,light,dir,dist),
pow(smoothstep(0.0,-0.05,dir.y),0.3));
// post
gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment