313 lines
7.5 KiB
GLSL
313 lines
7.5 KiB
GLSL
//bio.jpg, hill.jpg, check.jpg
|
|
//Raymarch settings
|
|
|
|
#define MIN_DIST 0.001
|
|
#define MAX_DIST 32.0
|
|
#define MAX_STEPS 96
|
|
#define STEP_MULT 0.9
|
|
#define NORMAL_OFFS 0.01
|
|
#define FOCAL_LENGTH 0.8
|
|
|
|
//Scene settings
|
|
|
|
//#define SHOW_RAY_COST
|
|
|
|
//Colors
|
|
#define SKY_COLOR_1 vec3(0.60,0.00,0.00)
|
|
#define SKY_COLOR_2 vec3(1.00,0.50,0.00)
|
|
|
|
#define SUN_COLOR_1 vec3(1.00, 0.00, 0.00)
|
|
#define SUN_COLOR_2 vec3(1.00, 1.00, 0.00)
|
|
|
|
#define GRID_COLOR_1 vec3(0.00, 0.05, 0.20)
|
|
#define GRID_COLOR_2 vec3(1.00, 0.20, 0.60)
|
|
|
|
#define WATER_COLOR vec3(0.50, 1.00, 2.90)
|
|
|
|
//Parameters
|
|
#define GRID_SIZE 0.20
|
|
#define GRID_LINE_SIZE 1.25
|
|
|
|
#define WATER_LEVEL 0.20
|
|
#define WATER_FOG_SIZE 0.05
|
|
|
|
#define SUN_DIRECTION vec3( 0.10,-1.00,-0.03)
|
|
|
|
#define CLOUD_SCROLL vec2(0.002, 0.001)
|
|
#define CLOUD_BLUR 2.0
|
|
#define CLOUD_SCALE vec2(0.04, 0.10)
|
|
|
|
#define MOUNTAIN_SCALE 6.0
|
|
#define MOUNTAIN_SHIFT 5.3
|
|
|
|
//Color modes
|
|
//vec3(#,#,#) Number of bits per channel
|
|
|
|
//24 bit color
|
|
#define RGB888 vec3(8,8,8)
|
|
//16 bit color
|
|
#define RGB565 vec3(5,6,5)
|
|
#define RGB664 vec3(6,6,4)
|
|
//8 bit color
|
|
#define RGB332 vec3(3,3,2)
|
|
#define RGB242 vec3(2,4,2)
|
|
#define RGB222 vec3(2,2,2) //+2 unused
|
|
|
|
//#define DITHER_ENABLE
|
|
#define COLOR_MODE RGB242
|
|
|
|
//Object IDs
|
|
#define SKYDOME 0.
|
|
#define FLOOR 1.
|
|
#define RIVER 2.
|
|
|
|
float pi = atan(1.0) * 4.0;
|
|
float tau = atan(1.0) * 8.0;
|
|
|
|
vec3 dither(vec3 color, vec3 bits, vec2 pixel)
|
|
{
|
|
vec3 cmax = exp2(bits)-1.0;
|
|
|
|
vec3 dithfactor = mod(color, 1.0 / cmax) * cmax;
|
|
float dithlevel = texture(iChannel2,pixel / iChannelResolution[2].xy).r;
|
|
|
|
vec3 cl = floor(color * cmax)/cmax;
|
|
vec3 ch = ceil(color * cmax)/cmax;
|
|
|
|
return mix(cl, ch, step(dithlevel, dithfactor));
|
|
}
|
|
|
|
struct MarchResult
|
|
{
|
|
vec3 position;
|
|
vec3 normal;
|
|
float dist;
|
|
float steps;
|
|
float id;
|
|
};
|
|
|
|
//Returns a rotation matrix for the given angles around the X,Y,Z axes.
|
|
mat3 Rotate(vec3 angles)
|
|
{
|
|
vec3 c = cos(angles);
|
|
vec3 s = sin(angles);
|
|
|
|
mat3 rotX = mat3( 1.0, 0.0, 0.0, 0.0,c.x,s.x, 0.0,-s.x, c.x);
|
|
mat3 rotY = mat3( c.y, 0.0,-s.y, 0.0,1.0,0.0, s.y, 0.0, c.y);
|
|
mat3 rotZ = mat3( c.z, s.z, 0.0,-s.z,c.z,0.0, 0.0, 0.0, 1.0);
|
|
|
|
return rotX * rotY * rotZ;
|
|
}
|
|
|
|
//==== Distance field operators/functions by iq. ====
|
|
vec2 opU(vec2 d1, vec2 d2)
|
|
{
|
|
return (d1.x < d2.x) ? d1 : d2;
|
|
}
|
|
|
|
vec2 opS(vec2 d1, vec2 d2)
|
|
{
|
|
return (-d1.x > d2.x) ? d1*vec2(-1,1) : d2;
|
|
}
|
|
|
|
vec2 sdSphere(vec3 p, float s, float id)
|
|
{
|
|
return vec2(length(p) - s, id);
|
|
}
|
|
|
|
vec2 sdPlane(vec3 p, vec4 n, float id)
|
|
{
|
|
// n must be normalized
|
|
return vec2(dot(p,n.xyz) + n.w, id);
|
|
}
|
|
|
|
vec2 sdColumn(vec3 p, float r, float id)
|
|
{
|
|
return vec2(((abs(p.x)+abs(p.y))-r)/sqrt(2.0), id);
|
|
}
|
|
|
|
vec2 dfRiver(vec3 p, float id)
|
|
{
|
|
float offs = sin(p.y)*0.15 + sin(p.y * 0.2);
|
|
|
|
return sdColumn(p.xzy + vec3(offs,0,0), 0.4, id);
|
|
}
|
|
|
|
//Distance to the scene
|
|
vec2 Scene(vec3 p)
|
|
{
|
|
vec2 d = vec2(MAX_DIST, SKYDOME);
|
|
|
|
d = opU(sdPlane(p, vec4(0, 0,-1, 0), FLOOR), d);
|
|
|
|
d = opS(dfRiver(p, RIVER), d);
|
|
|
|
return d;
|
|
}
|
|
|
|
//Surface normal at the current position
|
|
vec3 Normal(vec3 p)
|
|
{
|
|
vec3 off = vec3(NORMAL_OFFS, 0, 0);
|
|
return normalize
|
|
(
|
|
vec3
|
|
(
|
|
Scene(p + off.xyz).x - Scene(p - off.xyz).x,
|
|
Scene(p + off.zxy).x - Scene(p - off.zxy).x,
|
|
Scene(p + off.yzx).x - Scene(p - off.yzx).x
|
|
)
|
|
);
|
|
}
|
|
|
|
//Raymarch the scene with the given ray
|
|
MarchResult MarchRay(vec3 orig,vec3 dir)
|
|
{
|
|
float steps = 0.0;
|
|
float dist = 0.0;
|
|
float id = 0.0;
|
|
|
|
for(int i = 0;i < MAX_STEPS;i++)
|
|
{
|
|
vec2 object = Scene(orig + dir * dist);
|
|
|
|
//Add the sky dome and have it follow the camera.
|
|
object = opU(object, -sdSphere(dir * dist, MAX_DIST, SKYDOME));
|
|
|
|
dist += object.x * STEP_MULT;
|
|
|
|
id = object.y;
|
|
|
|
steps++;
|
|
|
|
if(abs(object.x) < MIN_DIST * dist)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MarchResult result;
|
|
|
|
result.position = orig + dir * dist;
|
|
result.normal = Normal(result.position);
|
|
result.dist = dist;
|
|
result.steps = steps;
|
|
result.id = id;
|
|
|
|
return result;
|
|
}
|
|
|
|
//Scene texturing/shading
|
|
vec3 Shade(MarchResult hit, vec3 direction, vec3 camera)
|
|
{
|
|
vec3 color = vec3(0.0);
|
|
|
|
if(hit.id == FLOOR)
|
|
{
|
|
vec2 uv = abs(mod(hit.position.xy + GRID_SIZE/2.0, GRID_SIZE) - GRID_SIZE/2.0);
|
|
|
|
uv /= fwidth(hit.position.xy);
|
|
|
|
float riverEdge = dfRiver(hit.position, 0.0).x / fwidth(hit.position.xy).x;
|
|
|
|
float gln = min(min(uv.x, uv.y), riverEdge) / GRID_SIZE;
|
|
|
|
color = mix(GRID_COLOR_1, GRID_COLOR_2, 1.0 - smoothstep(0.0, GRID_LINE_SIZE / GRID_SIZE, gln));
|
|
}
|
|
|
|
if(hit.id == RIVER)
|
|
{
|
|
vec2 uv = vec2(hit.position.z, abs(mod(hit.position.y + GRID_SIZE/2.0, GRID_SIZE) - GRID_SIZE/2.0));
|
|
uv /= fwidth(hit.position.xy);
|
|
|
|
float gln = min(uv.x, uv.y) / GRID_SIZE;
|
|
|
|
color = mix(GRID_COLOR_1, GRID_COLOR_2, 1.0 - smoothstep(0.0, GRID_LINE_SIZE / GRID_SIZE, gln));
|
|
}
|
|
|
|
//Distance fog
|
|
color *= 1.0 - smoothstep(0.0, MAX_DIST*0.9, hit.dist);
|
|
|
|
//Water
|
|
float waterMix = smoothstep(WATER_LEVEL - WATER_FOG_SIZE, WATER_LEVEL + WATER_FOG_SIZE, hit.position.z);
|
|
|
|
color = mix(color, WATER_COLOR, waterMix);
|
|
|
|
if(hit.id == SKYDOME)
|
|
{
|
|
//Sky gradient
|
|
//Causes weird position-colored artefacts around the horizon (AMD R9 270)
|
|
//color = mix(SKY_COLOR_1, SKY_COLOR_2, -hit.position.z/16.0);
|
|
color += mix(SKY_COLOR_1, SKY_COLOR_2, -hit.position.z/16.0);
|
|
|
|
//Sun
|
|
vec3 sunDir = normalize(SUN_DIRECTION);
|
|
|
|
float sun = smoothstep(0.950, 0.952, dot(direction, sunDir));
|
|
|
|
vec3 sunCol = mix(SUN_COLOR_1, SUN_COLOR_2, -hit.position.z/16.0);
|
|
|
|
color = mix(color, sunCol, sun);
|
|
|
|
//Clouds
|
|
vec2 cloudUV = CLOUD_SCALE * direction.xy / dot(direction, vec3(0, 0,-1));
|
|
cloudUV += CLOUD_SCROLL * iGlobalTime;
|
|
|
|
color *= smoothstep(0.5, 0.3, texture(iChannel1, cloudUV, CLOUD_BLUR).r) * 0.5 + 0.5;
|
|
|
|
//Mountains
|
|
float a = atan(hit.position.y, hit.position.x)/tau + 0.5;
|
|
a -= 3.28;
|
|
|
|
float mountains = MOUNTAIN_SCALE * texture(iChannel0, vec2(a, 0.1),-99.0).r - hit.position.z - MOUNTAIN_SHIFT;
|
|
|
|
color = mix(color, vec3(0.0), 1.0 - smoothstep(0.6, 0.7, mountains));
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
{
|
|
vec2 res = iResolution.xy / iResolution.y;
|
|
vec2 uv = fragCoord.xy / iResolution.y;
|
|
|
|
//Camera stuff
|
|
vec3 angles = vec3(0);
|
|
|
|
//Auto mode
|
|
if(iMouse.xy == vec2(0,0))
|
|
{
|
|
angles.y = tau * (1.8 / 8.0);
|
|
angles.x = tau * (3.9 / 8.0) + sin(iGlobalTime * 0.1) * 0.3;
|
|
}
|
|
else
|
|
{
|
|
angles = vec3((iMouse.xy / iResolution.xy) * pi, 0);
|
|
angles.xy *= vec2(2.0, 1.0);
|
|
}
|
|
|
|
angles.y = clamp(angles.y, 0.0, 15.5 * tau / 64.0);
|
|
|
|
mat3 rotate = Rotate(angles.yzx);
|
|
|
|
vec3 orig = vec3(0, 0,-2) * rotate;
|
|
|
|
vec3 dir = normalize(vec3(uv - res / 2.0, FOCAL_LENGTH)) * rotate;
|
|
|
|
//Ray marching
|
|
MarchResult hit = MarchRay(orig, dir);
|
|
|
|
//Shading
|
|
vec3 color = Shade(hit, dir, orig);
|
|
|
|
#ifdef SHOW_RAY_COST
|
|
color = mix(vec3(0,1,0), vec3(1,0,0), hit.steps / float(MAX_STEPS));
|
|
#endif
|
|
|
|
#ifdef DITHER_ENABLE
|
|
color = dither(color, COLOR_MODE, fragCoord);
|
|
#endif
|
|
|
|
fragColor = vec4(color, 1.0);
|
|
} |