Created
February 25, 2015 22:06
-
-
Save possan/f34f8c5ada300d99b14e to your computer and use it in GitHub Desktop.
Minimal OpenGL Shader example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// "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