slang-shaders/procedural/shane-latticetutorial.slang
2016-07-29 16:10:55 +10:00

304 lines
10 KiB
Plaintext

#version 450
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
vec4 OutputSize;
vec4 OriginalSize;
vec4 SourceSize;
uint FrameCount;
} global;
#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
const vec2 madd = vec2(0.5, 0.5);
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = gl_Position.xy;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
float iGlobalTime = float(global.FrameCount)*0.025;
vec2 iResolution = global.OutputSize.xy;
/*
Transparent Lattice
-------------------
Just a transparent lattice. Not much different to my other transparent examples,
except this one is point lit... In case it needs to be said, a lot of it is faked,
so is more of a novelty than anything else.
I wrote it some time ago, then forgot about it. I thought I'd put it up just in
case it's of any use to anyone. It runs reasonably fast, considering that the
lighting is calculated multiple times a pass, but could benefit from a little more
tweaking.
Related shaders:
Cloudy Spikeball - Duke
https://www.shadertoy.com/view/MljXDw
// Port from a demo by Las - Worth watching.
// http://www.pouet.net/topic.php?which=7920&page=29&x=14&y=9
Virtually the same thing, but with rounded cubes and less interesting lighting.
Transparent Cube Field - Shane
https://www.shadertoy.com/view/ll2SRy
*/
// Cheap vec3 to vec3 hash. Works well enough, but there are other ways.
vec3 hash33(vec3 p){
float n = sin(dot(p, vec3(7, 157, 113)));
return fract(vec3(2097152, 262144, 32768)*n);
}
/*
// Rounded cube field, for comparison. It runs at full speed, believe it or not.
float map(vec3 p){
// Creating the repeat cubes, with slightly convex faces. Standard,
// flat faced cubes don't capture the light quite as well.
// 3D space repetition.
p = fract(p)-.5; // + o
// A bit of roundness. Used to give the cube faces a touch of convexity.
float r = dot(p, p) - 0.21;
// Max of abs(x), abs(y) and abs(z) minus a constant gives a cube.
// Adding a little bit of "r," above, rounds off the surfaces a bit.
p = abs(p);
return max(max(p.x, p.y), p.z)*.95 + r*0.25 - 0.21;
// Alternative. Egg shapes... kind of.
//float perturb = sin(p.x*10.)*sin(p.y*10.)*sin(p.z*10.);
//p += hash33(floor(p))*.15;
//return length(fract(p)-.5)-0.3 + perturb*0.05;
}
*/
/*
// A fake noise looking field. Pretty interesting.
float map(vec3 p){
p = (cos(p*.315*2.5 + sin(p.zxy*.875*2.5))); // + iGlobalTime*.5
float n = length(p);
p = sin(p*6. + cos(p.yzx*6.));
return n - 1. - abs(p.x*p.y*p.z)*.05;
}
*/
float map(vec3 p){
vec2 c;
// SECTION 1
//
// Repeat field entity one, which is just some tubes repeated in all directions every
// two units, then combined with a smooth minimum function. Otherwise known as a lattice.
p = abs(fract(p/3.)*3.-1.5);
//c.x = sminP(length(p.xy),sminP(length(p.yz),length(p.xz), 0.25), 0.25)-0.75; // EQN 1
//c.x = sqrt(min(dot(p.xy, p.xy),min(dot(p.yz, p.yz),dot(p.xz, p.xz))))-0.75; // EQN 2
c.x = min(max(p.x, p.y),min(max(p.y, p.z),max(p.x, p.z)))-0.75; // EQN 3
//p = abs(p); c.x = max(p.x,max(p.y,p.z)) - .5;
// SECTION 2
//
// Repeat field entity two, which is just an abstract object repeated every half unit.
p = abs(fract(p*4./3.)*.75 - 0.375);
c.y = min(p.x,min(p.y,p.z)); // EQN 1
//c.y = min(max(p.x, p.y),min(max(p.y, p.z),max(p.x, p.z)))-0.125; //-0.175, etc. // EQN 2
//c.y = max(p.x,max(p.y,p.z)) - .4;
// SECTION 3
//
// Combining the two entities above.
//return length(c)-.1; // EQN 1
//return max(c.x, c.y)-.05; // EQN 2
return max(abs(c.x), abs(c.y))*.75 + length(c)*.25 - .1;
//return max(abs(c.x), abs(c.y))*.75 + abs(c.x+c.y)*.25 - .1;
//return max(abs(c.x), abs(c.y)) - .1;
}
// Not big on accuracy, but lower on operations. Few distance function calls are important
// during volumetric passes.
vec3 calcNormal(in vec3 p, float d) {
const vec2 e = vec2(0.01, 0);
return normalize(vec3(d - map(p - e.xyy), d - map(p - e.yxy), d - map(p - e.yyx)));
}
/*
// Tetrahedral normal, to save a couple of "map" calls. Courtesy of IQ. Unfortunately, still
// not fast enough in this particular instance.
vec3 calcNormal(in vec3 p){
// Note the slightly increased sampling distance, to alleviate artifacts due to hit point inaccuracies.
vec2 e = vec2(0.0025, -0.0025);
return normalize(e.xyy * map(p + e.xyy) + e.yyx * map(p + e.yyx) + e.yxy * map(p + e.yxy) + e.x * map(p + e.xxx));
}
*/
void mainImage( out vec4 fragColor, vec2 fragCoord ) {
// Screen coordinates.
vec2 uv = (fragCoord.xy - iResolution.xy*.5 )/iResolution.y;
// Unit direction ray. The last term is one of many ways to fish-lens the camera.
// For a regular view, set "rd.z" to something like "0.5."
vec3 rd = normalize(vec3(uv, (1.-dot(uv, uv)*.5)*.5)); // Fish lens, for that 1337, but tryhardish, demo look. :)
// There are a few ways to hide artifacts and inconsistencies. Making things go fast is one of them. :)
// Ray origin, scene color, and surface postion vector.
vec3 ro = vec3(0., 0., iGlobalTime*1.5), col=vec3(0), sp, sn, lp, ld, rnd;
// Swivel the unit ray to look around the scene.
// Compact 2D rotation matrix, courtesy of Shadertoy user, "Fabrice Neyret."
vec2 a = sin(vec2(1.5707963, 0) + iGlobalTime*0.375);
rd.xz = mat2(a, -a.y, a.x)*rd.xz;
rd.xy = mat2(a, -a.y, a.x)*rd.xy;
lp = vec3(0, 1, 4);
lp.xz = mat2(a, -a.y, a.x)*lp.xz;
lp.xy = mat2(a, -a.y, a.x)*lp.xy;
lp += ro;
// Unit ray jitter is another way to hide artifacts. It can also trick the viewer into believing
// something hard core, like global illumination, is happening. :)
//rd *= 0.99 + hash33(rd)*0.02;
// Some more randomization, to be used for color based jittering inside the loop.
rnd = hash33(rd+311.);
// Ray distance, bail out layer number, surface distance and normalized accumulated distance.
float t=0., layers=0., d, aD;
// Light variables.
float lDist, s, l;
// Surface distance threshold. Smaller numbers gives a thinner membrane, but lessens detail...
// hard to explain. It's easier to check it out for yourself.
float thD = .0125; // + smoothstep(-0.2, 0.2, sin(iGlobalTime*0.75 - 3.14159*0.4))*0.025;
// Only a few iterations seemed to be enough. Obviously, more looks better, but is slower.
for(float i=0.; i<64.; i++) {
// Break conditions. Anything that can help you bail early usually increases frame rate.
if(layers>31. || dot(col, vec3(.299, .587, .114)) > 1. || t>16.) break;
// Current ray postion. Slightly redundant here, but sometimes you may wish to reuse
// it during the accumulation stage.
sp = ro+rd*t;
d = map(sp); // Distance to nearest point on the noise surface.
// If we get within a certain distance of the surface, accumulate some surface values.
// Values further away have less influence on the total.
//
// aD - Accumulated distance. You could smoothly interpolate it, if you wanted.
//
// 1/.(1. + t*t*0.1) - Basic distance attenuation. Feel free to substitute your own.
// Normalized distance from the surface threshold value to our current isosurface value.
aD = (thD-abs(d)*31./32.)/thD;
// If we're within the surface threshold, accumulate some color.
// Two "if" statements in a shader loop makes me nervous. I don't suspect there'll be any
// problems, but if there are, let us know.
if(aD>0.) {
// Add the accumulated surface distance value, along with some basic falloff using the
// camera to light distance, "lDist." There's a bit of color jitter there, too.
sn = calcNormal(sp, d)*sign(d);
ld = (lp - sp); //vec3(.5773)
lDist = max(length(ld), .001);
ld /= lDist;
s = pow(max(dot(reflect(-ld, sn), -rd), 0.), 8.);
l = max(dot(ld, sn), 0.);
//float c = dot(sin(sp*128. - cos(sp.yzx*64.)), vec3(.166))+.5;
col += ((l + .1) + vec3(.5, .7, 1)*s)*aD/(1. + lDist*0.25 + lDist*lDist*0.05)*.2;
// Failed experiment with color jitter to take out more banding.
//col += ((l + .05 + fract(rnd + i*27.)*.1) + vec3(.5, .7, 1)*s)*aD/(1. + lDist*0.25 + lDist*lDist*0.05)*.2;
// The layer number is worth noting. Accumulating more layers gives a bit more glow.
// Lower layer numbers allow a quicker bailout. A lot of it is guess work.
layers++;
}
// Kind of weird the way this works. I think not allowing the ray to hone in properly is
// the very thing that gives an even spread of values. The figures are based on a bit
// of knowledge versus trial and error. If you have a faster computer, feel free to tweak
// them a bit.
t += max(abs(d)*.75, thD*.25);
}
t = min(t, 16.);
col = mix(col, vec3(0), 1.-exp(-0.025*t*t));////1.-exp(-0.01*t*t) 1.-1./(1. + t*t*.1)
// Mixing the greytone color with a firey orange vignette. There's no meaning
// behind it. I just thought the artsy greyscale was a little too artsy.
uv = abs(fragCoord.xy/iResolution.xy - .5); // Wasteful, but the GPU can handle it.
col = mix(col, vec3(min(col.x*1.5, 1.), pow(col.x, 2.5), pow(col.x, 12.)).yxz,
min( dot(pow(uv, vec2(4.)), vec2(1))*8., 1.));
//col = vec3(min(col.z*1.5, 1.), pow(col.z, 2.5), pow(col.z, 12.));
// Mixing the vignette colors up a bit more.
col = mix(col, col.zxy, dot(sin(rd*5.), vec3(.166)) + 0.166);
// Presenting the color to the screen.
fragColor = vec4( sqrt(clamp(col, 0., 1.)), 1.0 );
}
void main(void)
{
//just some shit to wrap shadertoy's stuff
vec2 FragCoord = vTexCoord.xy*global.OutputSize.xy;
FragCoord.y = -FragCoord.y;
mainImage(FragColor,FragCoord);
}