#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
uniform float time;
uniform vec2 resolution;
uniform vec2 mouse;
// ISADORA_FLOAT_PARAM(bumpiness, bump, 0.0, 1.0, 0.04, "Bumpiness of the ball's surface.");
uniform float bumpiness;
//varying vec2 surfacePosition;
#define PI 3.1415926535898 // That was from memory, so if things start flying off the screen...
float eps = 1.0/resolution.y;
const int maxIterations = 64;
const float stepScale = 1.5;
float stopThreshold = 3.0/resolution.y;
float sphere(in vec3 p, in vec3 centerPos, float radius) {
return length(p-centerPos) - radius;
}
float sinusoidBumps(in vec3 p){
return sin(p.x*16.+mouse.x*0.57)*cos(p.y*16.+mouse.x*2.17)*sin(p.z*16.-mouse.x*1.31)
+ 0.5*sin(p.x*32.+mouse.x*0.07)*cos(p.y*32.+mouse.x*2.11)*sin(p.z*32.-mouse.x*1.23);
}
float scene(in vec3 p) {
// FOLLOWING LINE CHANGES H POS, V POS, ZOOM, BUMPS
return sphere(p, vec3(0., 0. , 8.), 5.) + bumpiness *sinusoidBumps(p);
}
vec3 getNormal(in vec3 p) {
return normalize(vec3(
scene(vec3(p.x+eps,p.y,p.z))-scene(vec3(p.x-eps,p.y,p.z)),
scene(vec3(p.x,p.y+eps,p.z))-scene(vec3(p.x,p.y-eps,p.z)),
scene(vec3(p.x,p.y,p.z+eps))-scene(vec3(p.x,p.y,p.z-eps))
));
}
float rayMarching( vec3 origin, vec3 dir, float start, float end ) {
float rayDepth = start;
for ( int i = 0; i < maxIterations; i++ ) {
float sceneDist = scene( origin + dir * rayDepth ); // Distance from the point along the ray to the nearest surface point in the scene.
if (( sceneDist < stopThreshold ) || (rayDepth >= end)) {
return rayDepth + sceneDist;
}
rayDepth += sceneDist * stepScale;
}
return end;
}
void main(void) {
// Setting up our screen coordinates: gl_FragCoord.xy represents our screen pixels (or screen coordinates, if you prefer), which range
// from [0 to resolution.x] along the x-axis, and [0 to resolution.y] along the y-axis.
//
// However, to make calculations easier later on, we'd like have screen coordinates that center on (0.0, 0.0) and also fall within a range
// of about [-1.0 to 1.0] along each of the axes. In order for the image to not appear distorted (squashed), we also factor in the screen's
// aspect ratio ( screen_width/screen_height ), which in this case is (resolution.x/resolution.y).
//
// By the way, you don't actually have to do this. However, having the center of the screen at (0.0, 0.0) just makes life easier...
// Besides, I'm a follower, and that what all the cool kids are doing these days.
vec2 aspect = vec2(resolution.x/resolution.y, 1.0); //
vec2 screenCoords = (2.0*gl_FragCoord.xy/resolution.xy - 1.0)*aspect;
// The camPos vector is the location of our vantage point, eye point, camera position, or whatever else people wish to call it. The lookAt vector
// effectively represents the center of the screen we're projecting the rays from the scene onto. In essence, camPos, is the vector position we're
// looking from, and lookAt is the position of the screen we're looking at, or through, if you prefer.
vec3 lookAt = vec3(0.,0.,0.); // This is the point you look towards, or at, if you prefer.
vec3 camPos = vec3(0., 0., -1.); // This is the point you look from, or camera you look at the scene through. Whichever way you wish to look at it.
// The following uses the above and some pretty standard vector math to set up the screen that we're going to project onto. Use the lookAt and camPos
// vectors to contruct a forward vector. Use that vector to construct a vector perpendicular to it, namely the "right" vector, then cross product
// the forward vector with the "right" vector to produce the "up" vector. All three of these are used to construct the unit direction ray, namely,
// "rd" that will be cast off into the scene from our vantage point (aka, eye, camera position, etc) , which we've called camPos.
// Camera setup.
vec3 forward = normalize(lookAt-camPos); // Forward vector.
vec3 right = normalize(vec3(forward.z, 0., -forward.x )); // Right vector... or is it left? Either way, so long as the correct-facing up-vector is produced.
vec3 up = normalize(cross(forward,right)); // Cross product the two vectors above to get the up vector.
// FOV - Field of view. Make it bigger, and the screen covers a larger area, which means more of the scene can be seen. This, in turn, means that our
// objects will appear smaller.
float FOV = 0.5;
// ro - Ray origin. Every ray starts from this point, then is cast in the rd direction.
vec3 ro = camPos;
// rd - Ray direction. This is our one-unit-long direction ray.
vec3 rd = normalize(forward + FOV*screenCoords.x*right + FOV*screenCoords.y*up);
// The screen's background color.
vec3 bgcolor = vec3(1.,0.97,0.92)*0.15;
// Oh great, another obfuscated mess to decipher. I could have left the background alone, but I wanted to give it a bit of a fake backlight.
// Then, I wanted to shape it a bit, etc. It's not essential, but it looks a little nicer.
float bgshade = (1.0-length(vec2(screenCoords.x/aspect.x, screenCoords.y+0.5) )*0.8);
bgcolor *= bgshade; //Shade the background a little.
// Ray marching.
// Set the near and far clipping planes, then supply them - along with the ray origin and ray direction vectors - to the rayMarching routine.
// If a surface point in the scene is hit, a distance value (dist) less than the maximum value (clipFar) will be returned.
const float clipNear = 0.0;
const float clipFar = 5.0;
float dist = rayMarching(ro, rd, clipNear, clipFar );
if ( dist >= clipFar ) {
gl_FragColor = vec4(bgcolor, 1.0);
return;
}
vec3 sp = ro + rd*dist;
// We can use the surface position to calculate the surface normal using a bit of vector math. I remember having to give long, drawn-out,
// sleep-inducing talks at uni on implicit surface geometry (or something like that) that involved normals on 3D surfaces and such.
// I barely remember the content, but I definitely remember there was always this hot chick in the room with a gigantic set of silicons who
// looked entirely out of place amongst all the nerds... and this was back in the days when those things weren't as common... Um, I forgot
// where I was going with this.
//
// Anyway, check out the function itself. It's a standard, but pretty clever way to get a surface normal on difficult-to-differentiate surfaces.
vec3 surfNormal = getNormal(sp);
// Lighting. Just the one light. It needs to have a position, a direction and a color. Obviously, it should be positioned away from the
// object's surface. The direction vector is the normalized vector running from the light position to the object's surface point that we're
// going to illuminate. You can choose any light color you want, but it's probably best to choose a color that works best with the colors
// in the scene. I've gone for a warmish white.
// lp - Light position. I've arrange for it to move in a bit of a circle about the xy-plane a couple of units away from the spherical object.
vec3 lp = vec3(1.5*sin(time*0.5), 0.75+0.25*cos(time*0.5), -0.5);
// ld - Light direction. The point light direction goes from the light's position to the surface point we've hit on the sphere. I haven't
// normalized it yet, because I'd like to take the length first, but it will be.
vec3 ld = lp-sp;
// lcolor - Light color. I have it in my head that light globes give off this color, but I swear I must have pulled that information right
// out of my a... Choose any color you want.
vec3 lcolor = vec3(9,1.5,55.5);
// Light falloff (attenuation), which depends on how far the surface point is from the light. Most of the time, I guess the falloff rate should be
// mixtures of inverse distance powers, but in real life, it's far more complicated than that. Either way, most of the time you should simply
// choose whatever makes the lighting look a little prettier. For instance, if things look too dark, I might decide to make the falloff drop off
// linearly, without any other terms. In this case, the light is falling off with the square of the distance, and no other terms.
float len = length( ld ); // Distance from the light to the surface point.
ld /= len; // Normalizing the light-to-surface, aka light-direction, vector.
float lightAtten = min( 0.5 / ( 0.05*len*len ), 0.5 ); // Keeps things between 0 and 1.
// The unit-length, reflected vector. Angle of incidence equals angle of reflection, if you remember rudimentary highschool physics, or math.
// Anyway, the incident (incoming... for want of a better description) vector is the vector representing our line of sight from the light position
// to the point on the suface of the object we've just hit. We get the reflected vector on the surface of the object by doing a quick calculation
// between the incident vector and the surface normal. The reflect function is ( ref=incidentNorm-2.0*dot(incidentNorm, surfNormal)*surfNormal ),
// or something to that effect. Either way, there's a function for it, which is used below.
//
// The reflected vector is useful, because we can use it to calculate the specular reflection component. For all intents and purposes, specular light
// is the light gathered in the mirror direction. I like it, because it looks pretty, and I like pretty things. One of the most common mistakes made
// with regard to specular light calculations is getting the vector directions wrong, and I've made the mistake more than a few times. So, if you
// notice I've got the signs wrong, or anything, feel free to let me know.
vec3 ref = reflect(-ld, surfNormal);
// Start with black. If we had global ambient lighting, then I guess we could add it here, or later. It's a preference thing.
vec3 sceneColor = vec3(0.0);
// The spherical object's color. My favorite color is black, but I don't think that will work, so I've gone with something greenish.
vec3 objColor = vec3(0.5, 1.0, 6.3);
// Just some really lame, fake shading/coloring for the object. You can comment the two lines out with no consequence.
float bumps = sinusoidBumps(sp);
objColor = clamp(objColor*0.3, 0.2, 5.0);
float ambient = .0; //The object's ambient property. You can also have a global and light ambient property, but we'll try to keep things simple.
float specularPower = 50.0; // The power of the specularity. Higher numbers can give the object a harder, shinier look.
float diffuse = max( 0.0, dot(surfNormal, ld) ); //The object's diffuse value, which depends on the angle that the light hits the object.
//The object's specular value, which depends on the angle that the reflected light hits the object, and the viewing angle... kind of.
float specular = max( 0.0, dot( ref, normalize(camPos-sp)) );
specular = pow(specular, specularPower); // Ramping up the specular value to the specular power for a bit of shininess.
// Bringing all the lighting components togethr to color the screen pixel.
sceneColor += lcolor*lightAtten*objColor*(diffuse*.8+specular*0.5+ambient);
// Clamping the lit pixel between black and while, then putting it on the screen.
gl_FragColor = vec4(clamp(sceneColor, 0.0, 5.0), 5.0);
}