ON ERROR IF ERR=17 CHAIN @lib$+"../examples/tools/touchide" ELSE MODE 3 : PRINT REPORT$ : END REM 'Procedural Seascape' by Alexander Alekseev, tdmaav@gmail.com, 2014 REM BBCSDL version by Richard Russell, www.rtrussell.co.uk, 23-Jan-2019 REM This version has been adapted to work in both OpenGL and OpenGL ES2 REM Install libraries: INSTALL @lib$ + "shaderlib" REM Create arrays and structures: DIM Vertex$(10), Fragment$(999), Float{0%,1%} REM Fill vertex and fragment shader arrays from DATA statements: PROC_readshader(Vertex$()) PROC_readshader(Fragment$()) REM Initialise window and get its size: ON MOVE IF @msg% <> 5 RETURN ELSE PROCcleanup VDU 26 IF POS REM SDL thread sync ScrW% = @size.x% ScrH% = @size.y% REM Ensure cleanup on exit: ON CLOSE PROCcleanup : QUIT ON ERROR PROCcleanup : IF ERR=17 CHAIN @lib$+"../examples/tools/touchide" ELSE MODE 3 : PRINT REPORT$ : END REM Initialise shader library: PROC_shaderinit(oVertex%, oFragment%) REM Compile shaders: PROC_compileshader(oVertex%, Vertex$(), "Vertex") PROC_compileshader(oFragment%, Fragment$(), "Fragment") REM Link and use shaders: oProgram% = FN_useshaders(oVertex%, oFragment%) SYS `glGetUniformLocation`, oProgram%, "iTime", @memhdc% TO pTime% SYS `glGetUniformLocation`, oProgram%, "iMouse", @memhdc% TO pMouse% SYS `glGetUniformLocation`, oProgram%, "iResolution", @memhdc% TO pResolution% SYS `glGetUniformLocation`, oProgram%, "iChannel0", @memhdc% TO pChannel0% REM Load and bind noise terrain texture to uniform: SYS `glUniform1i`, pChannel0%, 0, @memhdc% SYS `glActiveTexture`, GL_TEXTURE0, @memhdc% Texture% = FN_loadtexture1(@dir$ + "seascape.png") IF Texture% = 0 ERROR 100, "Couldn't load seascape.png" REM Render loop: TIME = 0 REPEAT MOUSE X%,Y%,B% Float.0% = FN_f4(TIME/100) IF B%=0 SYS `glUniform1fv`, pTime%, 1, Float{}, @memhdc% Float.0% = FN_f4(X%/2) : Float.1% = FN_f4(Y%/2) SYS `glUniform2fv`, pMouse%, 1, Float{}, @memhdc% Float.0% = FN_f4(ScrW%) : Float.1% = FN_f4(ScrH%) SYS `glUniform2fv`, pResolution%, 1, Float{}, @memhdc% PROC_render UNTIL FALSE END DEF PROCcleanup ON ERROR OFF Texture% += 0 : IF Texture% SYS `glDeleteTextures`, 1, ^Texture%, @memhdc% oProgram% += 0 : oVertex% += 0 : oFragment% += 0 PROC_shaderexit(oProgram%, oVertex%, oFragment%) *REFRESH ON ENDPROC REM Minimal vertex shader: DATA "attribute vec4 vPosition;" DATA "void main()" DATA "{" DATA "gl_Position = vPosition ;" DATA "}" DATA "" REM Fragment Shader code from https://www.shadertoy.com/view/Ms2SD1 DATA "uniform float iTime;" DATA "uniform vec2 iMouse;" DATA "uniform vec2 iResolution;" DATA "uniform sampler2D iChannel0;" DATA "/*" DATA "* 'Seascape' by Alexander Alekseev aka TDM - 2014" DATA "* License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License." DATA "* Contact: tdmaav@gmail.com" DATA "*/" DATA "const int NUM_STEPS = 8;" DATA "const float PI = 3.141592;" DATA "#define EPSILON_NRM (0.1 / iResolution.y)" DATA "// sea" DATA "const int ITER_GEOMETRY = 3;" DATA "const int ITER_FRAGMENT = 5;" DATA "const float SEA_HEIGHT = 0.8;" DATA "const float SEA_CHOPPY = 4.0;" DATA "const float SEA_SPEED = 0.8;" DATA "const float SEA_FREQ = 0.16;" DATA "const vec3 SEA_BASE = vec3(0.1,0.19,0.22);" DATA "const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);" DATA "#define SEA_TIME (iTime * SEA_SPEED)" DATA "const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);" DATA "// math" DATA "mat3 fromEuler(vec3 ang) {" DATA "vec2 a1 = vec2(sin(ang.x),cos(ang.x));" DATA "vec2 a2 = vec2(sin(ang.y),cos(ang.y));" DATA "vec2 a3 = vec2(sin(ang.z),cos(ang.z));" DATA "mat3 m;" DATA "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);" DATA "m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);" DATA "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);" DATA "return m;" DATA "}" REM DATA "float hash( vec2 p ) {" REM DATA "float h = dot(p,vec2(127.1,311.7));" REM DATA "return fract(sin(h)*43758.5453123);" REM DATA "}" REM REM DATA "float noise( in vec2 p ) {" REM DATA "vec2 i = floor( p );" REM DATA "vec2 f = fract( p );" REM DATA "vec2 u = f*f*(3.0-2.0*f);" REM DATA "return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) )," REM DATA "hash( i + vec2(1.0,0.0) ), u.x)," REM DATA "mix( hash( i + vec2(0.0,1.0) )," REM DATA "hash( i + vec2(1.0,1.0) ), u.x), u.y);" REM DATA "}" REM This simplification uses a pre-computed 2D noise terrain and provides REM a worthwhile speed increase; to restore the original algorithm un-REM REM the hash() and noise() functions above, and REM-out this replacement: DATA "float noise( in vec2 p ) {" DATA "return 2.0 * texture2D(iChannel0, p / 16.0).x - 1.0;" DATA "}" DATA "// lighting" DATA "float diffuse(vec3 n,vec3 l,float p) {" DATA "return pow(dot(n,l) * 0.4 + 0.6,p);" DATA "}" DATA "float specular(vec3 n,vec3 l,vec3 e,float s) {" DATA "float nrm = (s + 8.0) / (PI * 8.0);" DATA "return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;" DATA "}" DATA "// sky" DATA "vec3 getSkyColor(vec3 e) {" DATA "e.y = max(e.y,0.0);" DATA "return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4);" DATA "}" DATA "// sea" DATA "float sea_octave(vec2 uv, float choppy) {" DATA "uv += noise(uv);" DATA "vec2 wv = 1.0-abs(sin(uv));" DATA "vec2 swv = abs(cos(uv));" DATA "wv = mix(wv,swv,wv);" DATA "return pow(1.0-pow(wv.x * wv.y,0.65),choppy);" DATA "}" DATA "float map(vec3 p) {" DATA "float freq = SEA_FREQ;" DATA "float amp = SEA_HEIGHT;" DATA "float choppy = SEA_CHOPPY;" DATA "vec2 uv = p.xz; uv.x *= 0.75;" DATA "float d, h = 0.0;" DATA "for(int i = 0; i < ITER_GEOMETRY; i++) {" DATA "d = sea_octave((uv+SEA_TIME)*freq,choppy);" DATA "d += sea_octave((uv-SEA_TIME)*freq,choppy);" DATA "h += d * amp;" DATA "uv *= octave_m; freq *= 1.9; amp *= 0.22;" DATA "choppy = mix(choppy,1.0,0.2);" DATA "}" DATA "return p.y - h;" DATA "}" DATA "float map_detailed(vec3 p) {" DATA "float freq = SEA_FREQ;" DATA "float amp = SEA_HEIGHT;" DATA "float choppy = SEA_CHOPPY;" DATA "vec2 uv = p.xz; uv.x *= 0.75;" DATA "float d, h = 0.0;" DATA "for(int i = 0; i < ITER_FRAGMENT; i++) {" DATA "d = sea_octave((uv+SEA_TIME)*freq,choppy);" DATA "d += sea_octave((uv-SEA_TIME)*freq,choppy);" DATA "h += d * amp;" DATA "uv *= octave_m; freq *= 1.9; amp *= 0.22;" DATA "choppy = mix(choppy,1.0,0.2);" DATA "}" DATA "return p.y - h;" DATA "}" DATA "vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {" DATA "float fresnel = 1.0 - max(dot(n,-eye),0.0);" DATA "fresnel = pow(fresnel,3.0) * 0.65;" DATA "vec3 reflected = getSkyColor(reflect(eye,n));" DATA "vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;" DATA "vec3 color = mix(refracted,reflected,fresnel);" DATA "float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);" DATA "color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;" DATA "color += vec3(specular(n,l,eye,60.0));" DATA "return color;" DATA "}" DATA "// tracing" DATA "vec3 getNormal(vec3 p, float eps) {" DATA "vec3 n;" DATA "n.y = map_detailed(p);" DATA "n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;" DATA "n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;" DATA "n.y = eps;" DATA "return normalize(n);" DATA "}" DATA "float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {" DATA "float tm = 0.0;" DATA "float tx = 1000.0;" DATA "float hx = map(ori + dir * tx);" DATA "if(hx > 0.0) return tx;" DATA "float hm = map(ori + dir * tm);" DATA "float tmid = 0.0;" DATA "for(int i = 0; i < NUM_STEPS; i++) {" DATA "tmid = mix(tm,tx, hm/(hm-hx));" DATA "p = ori + dir * tmid;" DATA "float hmid = map(p);" DATA "if(hmid < 0.0) {" DATA "tx = tmid;" DATA "hx = hmid;" DATA "} else {" DATA "tm = tmid;" DATA "hm = hmid;" DATA "}" DATA "}" DATA "return tmid;" DATA "}" DATA "// main" DATA "void main() {" DATA "vec2 uv = gl_FragCoord.xy / iResolution.xy;" DATA "uv = uv * 2.0 - 1.0;" DATA "uv.x *= iResolution.x / iResolution.y;" DATA "float time = iTime * 0.3 + iMouse.x*0.01;" DATA "// ray" DATA "vec3 ang = vec3(sin(time*3.0)*0.1,0.0,time);" DATA "vec3 ori = vec3(0.0,3.5,time*5.0);" DATA "vec3 dir = normalize(vec3(uv.xy,-2.0)); // dir.z += length(uv) * 0.15;" DATA "dir = normalize(dir) * fromEuler(ang);" DATA "// tracing" DATA "vec3 p;" DATA "heightMapTracing(ori,dir,p);" DATA "vec3 dist = p - ori;" DATA "vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);" DATA "vec3 light = normalize(vec3(0.0,1.0,0.8));" DATA "vec3 color = mix(" DATA "getSkyColor(dir), getSeaColor(p, n, light, dir, dist), pow(smoothstep(0.0, -0.05, dir.y), 0.3));" DATA "gl_FragColor = vec4(pow(color, vec3(0.75)), 1.0);" DATA "}" DATA ""