Skip to content

Instantly share code, notes, and snippets.

@erichlof
Last active September 26, 2018 16:34
Show Gist options
  • Save erichlof/bbf227b97e92b8d425cc0e2ef8ab08d0 to your computer and use it in GitHub Desktop.
Save erichlof/bbf227b97e92b8d425cc0e2ef8ab08d0 to your computer and use it in GitHub Desktop.
my version of the minified Antimatter glsl shader, expanding and decompressing variables and function names for maximum readability
const vec2 scale = vec2(0.5, 0.5);
attribute vec2 aPosition;
varying vec2 vQuadCoords;
void main()
{
vQuadCoords = aPosition * scale + scale;
gl_Position = vec4(aPosition, 0.0, 1.0);
}
precision highp float;
precision highp int;
struct StackElement_data
{
float id;
float rayT;
} stackElements[20];
struct BoxNode
{
vec3 minCorner;
vec3 maxCorner;
float branch_A_Index;
float branch_B_Index;
};
struct TRIANGLE
{
vec3 vertex0;
vec3 normal0;
vec2 uv0;
vec3 edge1;
vec3 normal1;
vec2 uv1;
vec3 edge2;
vec3 normal2;
vec2 uv2;
float material_IndexOffset;
float useDiffuseMap;
};
struct MATERIAL
{
vec3 diffuseColor;
vec3 specularColor;
vec3 transparentColor;
vec3 emissiveColor;
float indexOfRefraction;
float roughness;
float reflectivity;
float transparency;
};
const float EPS = 0.0000001;
const float HUGE = 1000000.;
const float PI = 3.14159265358979323846;
const float U0 = 0.25 / 2048.;
const float U1 = 1. / 2048.;
const float U2 = 2. / 2048.;
const float U3 = 3. / 2048.;
const float U4 = 4. / 2048.;
const float U5 = 5. / 2048.;
const float U6 = 6. / 2048.;
const vec3 GAMMA = vec3(2.2);
uniform bool uDoDOF;
uniform float uBounces;
uniform float uBlurRadius;
uniform float uFocalDistance;
uniform float uEnvironmentRotation;
uniform float uNormalMappingStrength;
uniform float uSamplingWeight;
uniform vec2 uResolution;
uniform vec2 uRandomSeed;
uniform vec3 uCameraOrigin;
uniform vec4 uBackgroundColor;
uniform vec4 uLightSphere;
uniform vec4 uGroundPlane;
uniform mat4 uCameraMatrix;
uniform sampler2D uBVHDataMap;
uniform sampler2D uMultipassMap;
uniform sampler2D uEnvironmentMap;
uniform sampler2D uColorMap;
uniform sampler2D uNormalMap;
varying vec2 vQuadCoords;
float rnd = 0.;
float seed = 0.;
float global_HitIndex = 0.;
float rand()
{
rnd = sin(rnd + seed);
return fract(rnd * 43758.5453);
}
// Workaround for WebGL 1.0 limitation of no dynamic array indexing
StackElement_data getStackElement_byIndex(const in float ix)
{
if (ix == 0.)
return stackElements[0];
if (ix == 1.)
return stackElements[1];
if (ix == 2.)
return stackElements[2];
if (ix == 3.)
return stackElements[3];
if (ix == 4.)
return stackElements[4];
if (ix == 5.)
return stackElements[5];
if (ix == 6.)
return stackElements[6];
if (ix == 7.)
return stackElements[7];
if (ix == 8.)
return stackElements[8];
if (ix == 9.)
return stackElements[9];
if (ix == 10.)
return stackElements[10];
if (ix == 11.)
return stackElements[11];
if (ix == 12.)
return stackElements[12];
if (ix == 13.)
return stackElements[13];
if (ix == 14.)
return stackElements[14];
if (ix == 15.)
return stackElements[15];
if (ix == 16.)
return stackElements[16];
if (ix == 17.)
return stackElements[17];
if (ix == 18.)
return stackElements[18];
return stackElements[19];
}
// Workaround for WebGL 1.0 limitation of no dynamic array indexing
void setStackElement_data(in float ix, const in StackElement_data value)
{
if (ix == 0.)
stackElements[0] = value;
else if (ix == 1.)
stackElements[1] = value;
else if (ix == 2.)
stackElements[2] = value;
else if (ix == 3.)
stackElements[3] = value;
else if (ix == 4.)
stackElements[4] = value;
else if (ix == 5.)
stackElements[5] = value;
else if (ix == 6.)
stackElements[6] = value;
else if (ix == 7.)
stackElements[7] = value;
else if (ix == 8.)
stackElements[8] = value;
else if (ix == 9.)
stackElements[9] = value;
else if (ix == 10.)
stackElements[10] = value;
else if (ix == 11.)
stackElements[11] = value;
else if (ix == 12.)
stackElements[12] = value;
else if (ix == 13.)
stackElements[13] = value;
else if (ix == 14.)
stackElements[14] = value;
else if (ix == 15.)
stackElements[15] = value;
else if (ix == 16.)
stackElements[16] = value;
else if (ix == 17.)
stackElements[17] = value;
else if (ix == 18.)
stackElements[18] = value;
else
stackElements[19] = value;
}
BoxNode GetBoxNode(const in float off)
{
float span = off * U0;
float f = floor(span);
float u = span - f;
float v = f * U1;
vec4 d0 = texture2D(uBVHDataMap, vec2(u, v));
vec4 d1 = texture2D(uBVHDataMap, vec2(u + U1, v));
BoxNode BN;
BN.minCorner = d0.rgb;
BN.maxCorner = vec3(d0.a, d1.rg);
BN.branch_A_Index = d1.b;
BN.branch_B_Index = d1.a;
return BN;
}
TRIANGLE GetTriangleData(const in float off)
{
float span = off * U0;
float f = floor(span);
float u = span - f;
float v = f * U1;
vec4 d0 = texture2D(uBVHDataMap, vec2(u, v));
vec4 d1 = texture2D(uBVHDataMap, vec2(u + U1, v));
vec4 d2 = texture2D(uBVHDataMap, vec2(u + U2, v));
vec4 d3 = texture2D(uBVHDataMap, vec2(u + U3, v));
vec4 d4 = texture2D(uBVHDataMap, vec2(u + U4, v));
vec4 d5 = texture2D(uBVHDataMap, vec2(u + U5, v));
vec4 d6 = texture2D(uBVHDataMap, vec2(u + U6, v));
TRIANGLE tri;
tri.vertex0 = d0.rgb;
tri.normal0 = vec3(d0.a, d1.rg);
tri.uv0 = d1.ba;
tri.edge1 = d2.rgb;
tri.normal1 = vec3(d2.a, d3.rg);
tri.uv1 = d3.ba;
tri.edge2 = d4.rgb;
tri.normal2 = vec3(d4.a, d5.rg);
tri.uv2 = d5.ba;
tri.material_IndexOffset = d6.r;
tri.useDiffuseMap = d6.g;
return tri;
}
MATERIAL getMaterial(const in float off)
{
float span = off * U0;
float f = floor(span);
float u = span - f;
float v = f * U1;
vec4 d0 = texture2D(uBVHDataMap, vec2(u + 0., v));
vec4 d1 = texture2D(uBVHDataMap, vec2(u + U1, v));
vec4 d2 = texture2D(uBVHDataMap, vec2(u + U2, v));
vec4 d3 = texture2D(uBVHDataMap, vec2(u + U3, v));
MATERIAL mt;
mt.diffuseColor = pow(d0.rgb, GAMMA);
mt.reflectivity = d0.a;
mt.emissiveColor = d1.rgb;
mt.transparency = d1.a;
mt.transparentColor = pow(d2.rgb, GAMMA);
mt.indexOfRefraction = d2.a;
mt.specularColor = pow(d3.rgb, GAMMA);
mt.roughness = d3.a;
return mt;
}
vec3 randCosWeightedDirHemisphere(const in vec3 n)
{
float r1 = rand() * 2. * PI;
float r2 = rand();
vec3 u = normalize(abs(n.x) > 0.1 ? vec3(n.z, 0., -n.x) : vec3(0., -n.z, n.y));
vec3 v = cross(n, u);
return (u * cos(r1) + v * sin(r1)) * sqrt(r2) + n * sqrt(1. - r2);
}
vec3 randConeDirHemisphere(const in vec3 n, const float theta)
{
float r1 = rand() * 2. * PI;
float r2 = rand();
float m0 = theta * (1. - 2. * acos(r2) / PI);
vec3 w = normalize(abs(n.x) > 0.1 ? vec3(n.z, 0., -n.x) : vec3(0., -n.z, n.y));
vec3 u = cross(n, w);
vec3 v = cross(n, u);
return (u * cos(r1) + v * sin(r1)) * sin(m0) + n * cos(m0);
}
vec3 getBackgroundColor(const in vec3 dir)
{
if (uBackgroundColor.r == -1.)
{
vec2 coord = vec2(0.5 + uEnvironmentRotation, 0.5) - vec2(atan(dir.z, dir.x), asin(dir.y)) / vec2(2. * PI, PI);
return texture2D(uEnvironmentMap, coord).xyz * uBackgroundColor.a;
}
else
{
return pow(uBackgroundColor.rgb, GAMMA);
}
}
float fresnelReflectance(const in vec3 dir, const in vec3 n, const in float n1, const in float n2)
{
float nr = n1 / n2;
float cosI = -dot(dir, n);
float sinT2 = nr * nr * (1. - cosI * cosI);
if (sinT2 > 1.)
{
return 1.;
}
float cosT = sqrt(1. - sinT2);
float rOrth = (n1 * cosI - n2 * cosT) / (n1 * cosI + n2 * cosT);
float rPar = (n2 * cosI - n1 * cosT) / (n2 * cosI + n1 * cosT);
return (rOrth * rOrth + rPar * rPar) / 2.0;
}
float intersectSphere(const in vec4 sphere, const in vec3 o, const in vec3 dir, const in float maxt)
{
vec3 L = sphere.xyz - o;
float tca = dot(L, dir);
float d2 = dot(L, L) - tca * tca;
if (d2 > sphere.w * sphere.w)
return HUGE;
float thc = sqrt(sphere.w * sphere.w - d2);
float t0 = tca - thc;
float t1 = tca + thc;
if (t0 > t1)
{
float temp = t0;
t0 = t1;
t1 = temp;
}
if (t0 < 0.)
{
t0 = t1;
}
if (t0 < 0.)
return HUGE;
return t0 < maxt ? t0 : HUGE;
}
float intersectAABB(const in vec3 boxMin, const in vec3 boxMax, const in vec3 o, const in vec3 dir, const in float maxt)
{
vec3 f = (boxMax - o) / dir;
vec3 n = (boxMin - o) / dir;
vec3 tmax = max(f, n);
vec3 tmin = min(f, n);
float t1 = min(min(tmax.x, min(tmax.y, tmax.z)), maxt);
float t0 = max(max(tmin.x, max(tmin.y, tmin.z)), EPS);
return t0 <= t1 ? t0 : HUGE;
}
bool intersectTriangle(const in vec3 vertex0, const in vec3 edge1, const in vec3 edge2, const in vec3 o, const in vec3 dir, const in float maxt, out vec3 r)
{
vec3 s = o - vertex0;
vec3 q = cross(s, edge1);
vec3 p = cross(dir, edge2);
r = vec3(dot(edge2, q), dot(s, p), dot(dir, q)) / vec3(dot(edge1, p));
return r.x > EPS && r.x < maxt && r.y >= 0. && r.y <= 1. && r.z >= 0. && r.z + r.y <= 1.;
}
float intersectBVH(const in vec3 o, const in vec3 dir, inout vec3 result)
{
bool skip = false;
float hitIndex = 0.0;
float stackptr = 0.0;
// first set current node to root node (index 0.0)
BoxNode currentBoxNode = GetBoxNode(0.0);
// now intersect rood node
StackElement_data currentStackData =
StackElement_data(0.0, intersectAABB(currentBoxNode.minCorner, currentBoxNode.maxCorner, o, dir, result.x));
for (int g = 0; g == 0; g += 0) // infinite loop with break condition, mimics 'while' loop
{
if (currentStackData.rayT < result.x) // is current t < closest t stored (in result.x) so far?
{
if (currentBoxNode.branch_A_Index < 0.0) // < 0.0 signifies a leaf node
{
TRIANGLE leaf;
vec3 isect;
if (-currentBoxNode.branch_A_Index != global_HitIndex)
{
leaf = GetTriangleData(-currentBoxNode.branch_A_Index);
if (intersectTriangle(leaf.vertex0, leaf.edge1, leaf.edge2, o, dir, result.x, isect))
{
result = isect;
hitIndex = -currentBoxNode.branch_A_Index;
}
}
if (-currentBoxNode.branch_B_Index != global_HitIndex && currentBoxNode.branch_B_Index != currentBoxNode.branch_A_Index)
{
leaf = GetTriangleData(-currentBoxNode.branch_B_Index);
if (intersectTriangle(leaf.vertex0, leaf.edge1, leaf.edge2, o, dir, result.x, isect))
{
result = isect;
hitIndex = -currentBoxNode.branch_B_Index;
}
}
}
else // else this is a branch
{
BoxNode nodeA = GetBoxNode(currentBoxNode.branch_A_Index);
BoxNode nodeB = GetBoxNode(currentBoxNode.branch_B_Index);
StackElement_data seDataA = StackElement_data(currentBoxNode.branch_A_Index, intersectAABB(nodeA.minCorner, nodeA.maxCorner, o, dir, result.x));
StackElement_data seDataB = StackElement_data(currentBoxNode.branch_B_Index, intersectAABB(nodeB.minCorner, nodeB.maxCorner, o, dir, result.x));
// first sort the branch node data so that b is smallest
if (seDataA.rayT < seDataB.rayT)
{
StackElement_data tmp = seDataA;
seDataA = seDataB;
seDataB = tmp;
BoxNode tnp = nodeA;
nodeA = nodeB;
nodeB = tnp;
} // branch 'a' now has the larger rayT value of 'a' and 'b'
if (seDataA.rayT < result.x) // see if branch 'a' needs to be processed
{
currentStackData = seDataA;
currentBoxNode = nodeA;
skip = true; // this will prevent the pointer from decreasing by 1
}
if (seDataB.rayT < result.x) // see if branch 'b' (the smaller rayT) needs to be processed
{
if (skip) // if 'a' needed to be processed also,
setStackElement_data(stackptr++, seDataA); // cue larger branch 'a' for future round
// also, increase pointer by 1
currentStackData = seDataB;
currentBoxNode = nodeB;
skip = true; // this will prevent the pointer from decreasing by 1
}
}
}
if (!skip) // if no valid aabb intersections were found, backtrack
{
// decrease pointer by 1 (0.0 is root, 19.0 is maximum depth)
if (--stackptr < 0.0) // went past the root node, terminate loop
break;
currentStackData = getStackElement_byIndex(stackptr); // get the next stackElement_Data
currentBoxNode = GetBoxNode(currentStackData.id);
}
skip = false; // reset skip
}
return hitIndex;
}
bool intersectScene(const in vec3 o, const in vec3 dir, out vec3 pos, out vec3 n, out MATERIAL ml)
{
vec3 resultData = vec3(HUGE, 0., 0.);
float hitGeometryIndex = intersectBVH(o, dir, resultData);
if (hitGeometryIndex > 0.) // signifies the ray hit a triangle
{
TRIANGLE tri = GetTriangleData(hitGeometryIndex);
pos = o + dir * resultData.x;
float u = 1. - resultData.y - resultData.z;
n = normalize(u * tri.normal0 + resultData.y * tri.normal1 + resultData.z * tri.normal2);
ml = getMaterial(tri.material_IndexOffset);
if (tri.useDiffuseMap > 0.)
{
ml.diffuseColor = pow(ml.diffuseColor * texture2D(uColorMap, u * tri.uv0 + resultData.y * tri.uv1 + resultData.z * tri.uv2).xyz, GAMMA);
}
}
if (global_HitIndex != -1. && uGroundPlane.y > 0.)
{ // test for ray/plane intersection
vec4 pl = uGroundPlane;
float t = -(dot(pl.xyz, o) + pl.w) / dot(pl.xyz, dir);
if (t > 0. && t < resultData.x)
{
hitGeometryIndex = -1.;
resultData.x = t;
pos = o + dir * resultData.x;
n = texture2D(uNormalMap, vec2(pos.x * 0.5, pos.z * 0.5)).rgb * 2.0 - 1.;
n.xy *= min(1., uNormalMappingStrength / resultData.x);
n = normalize(vec3(n.x, n.z, n.y));
ml = getMaterial(16777184.0);
}
}
if (global_HitIndex != -2.0 && uLightSphere.w > 0.)
{ // test for ray/sphere intersection
vec4 sphere = uLightSphere;
float t = intersectSphere(sphere, o, dir, resultData.x);
if (t > 0. && t < resultData.x)
{
hitGeometryIndex = -2.0;
resultData.x = t;
pos = o + dir * resultData.x;
n = (o + dir * resultData.x - sphere.xyz) / sphere.w;
ml = getMaterial(16777152.0);
}
}
global_HitIndex = hitGeometryIndex; // set global hit index to current geometry hit
return resultData.x < HUGE;
}
void main()
{
MATERIAL ml;
vec3 o, dir, pos, n, accumColor = vec3(0.), mask = vec3(1.);
seed = dot(gl_FragCoord.xy, uRandomSeed);
vec4 p = uCameraMatrix * vec4(2. * (gl_FragCoord.xy + vec2(rand(), rand())) / uResolution - 1., 1., 1.);
dir = normalize(p.xyz / p.w);
o = uCameraOrigin;
if (uDoDOF)
{
float r1 = rand() * 2. * PI;
float r2 = rand() * uBlurRadius;
o = uCameraOrigin + r2 * (normalize(uCameraMatrix[1].xyz) * sin(r1) + normalize(uCameraMatrix[0].xyz) * cos(r1));
dir = normalize(uCameraOrigin + dir * uFocalDistance - o);
}
float dist = HUGE;
vec3 bgColor = getBackgroundColor(dir);
for (float depth = 0.; depth < 16.0; ++depth)
{
if (depth == uBounces)
{
break;
}
if (!intersectScene(o, dir, pos, n, ml))
{
accumColor += mask * getBackgroundColor(dir);
break;
}
if (depth == 0.)
{
dist = length(pos - o);
}
else if (depth > 3.0)
{
// Russian roulette
float p = max(mask.x, max(mask.y, mask.z));
if (rand() < p)
mask *= 1. / p;
else
break;
}
o = pos;
accumColor += mask * ml.emissiveColor;
float random = rand();
if (random < ml.transparency)
{
float n1 = 1.;
float n2 = ml.indexOfRefraction;
if (dot(dir, n) > 0.)
{
n = -n;
n1 = ml.indexOfRefraction;
n2 = 1.;
}
if (random < fresnelReflectance(dir, n, n1, n2))
{
dir = randConeDirHemisphere(reflect(dir, n), ml.roughness);
mask *= ml.specularColor;
}
else
{
dir = randConeDirHemisphere(refract(dir, n, n1 / n2), ml.roughness);
mask *= ml.transparentColor;
}
}
else if (random < ml.reflectivity)
{
dir = randConeDirHemisphere(reflect(dir, n), ml.roughness);
mask *= ml.specularColor;
}
else if (random < (ml.indexOfRefraction > 1. ? fresnelReflectance(dir, n, 1., ml.indexOfRefraction) : 0.))
{
dir = randConeDirHemisphere(reflect(dir, n), ml.roughness);
mask *= ml.specularColor;
}
else
{
dir = randCosWeightedDirHemisphere(n);
mask *= ml.diffuseColor;
}
}
accumColor = mix(accumColor, bgColor, clamp(pow(max(0., (dist - 20.)) / 20., 1.15), 0., 1.));
gl_FragColor = vec4(mix(accumColor, texture2D(uMultipassMap, vQuadCoords).rgb, uSamplingWeight), dist);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment