// Shader is looseley based on the ray tracing coding session by Inigo Quilez (www.iquilezles.org) #version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (local_size_x = 16, local_size_y = 16) in; layout (binding = 0, rgba8) uniform writeonly image2D resultImage; #define EPSILON 0.0001 #define MAXLEN 1000.0 #define SHADOW 0.5 #define RAYBOUNCES 2 #define REFLECTIONS true #define REFLECTIONSTRENGTH 0.4 #define REFLECTIONFALLOFF 0.5 struct Camera { vec3 pos; vec3 lookat; float fov; }; layout (binding = 1) uniform UBO { vec3 lightPos; float aspectRatio; vec4 fogColor; Camera camera; mat4 rotMat; } ubo; struct Sphere { vec3 pos; float radius; vec3 diffuse; float specular; int id; }; struct Plane { vec3 normal; float distance; vec3 diffuse; float specular; int id; }; layout (std140, binding = 2) buffer Spheres { Sphere spheres[ ]; }; layout (std140, binding = 3) buffer Planes { Plane planes[ ]; }; void reflectRay(inout vec3 rayD, in vec3 mormal) { rayD = rayD + 2.0 * -dot(mormal, rayD) * mormal; } // Lighting ========================================================= float lightDiffuse(vec3 normal, vec3 lightDir) { return clamp(dot(normal, lightDir), 0.1, 1.0); } float lightSpecular(vec3 normal, vec3 lightDir, float specularFactor) { vec3 viewVec = normalize(ubo.camera.pos); vec3 halfVec = normalize(lightDir + viewVec); return pow(clamp(dot(normal, halfVec), 0.0, 1.0), specularFactor); } // Sphere =========================================================== float sphereIntersect(in vec3 rayO, in vec3 rayD, in Sphere sphere) { vec3 oc = rayO - sphere.pos; float b = 2.0 * dot(oc, rayD); float c = dot(oc, oc) - sphere.radius*sphere.radius; float h = b*b - 4.0*c; if (h < 0.0) { return -1.0; } float t = (-b - sqrt(h)) / 2.0; return t; } vec3 sphereNormal(in vec3 pos, in Sphere sphere) { return (pos - sphere.pos) / sphere.radius; } // Plane =========================================================== float planeIntersect(vec3 rayO, vec3 rayD, Plane plane) { float d = dot(rayD, plane.normal); if (d == 0.0) return 0.0; float t = -(plane.distance + dot(rayO, plane.normal)) / d; if (t < 0.0) return 0.0; return t; } int intersect(in vec3 rayO, in vec3 rayD, inout float resT) { int id = -1; for (int i = 0; i < spheres.length(); i++) { float tSphere = sphereIntersect(rayO, rayD, spheres[i]); if ((tSphere > EPSILON) && (tSphere < resT)) { id = spheres[i].id; resT = tSphere; } } for (int i = 0; i < planes.length(); i++) { float tplane = planeIntersect(rayO, rayD, planes[i]); if ((tplane > EPSILON) && (tplane < resT)) { id = planes[i].id; resT = tplane; } } return id; } float calcShadow(in vec3 rayO, in vec3 rayD, in int objectId, inout float t) { for (int i = 0; i < spheres.length(); i++) { if (spheres[i].id == objectId) continue; float tSphere = sphereIntersect(rayO, rayD, spheres[i]); if ((tSphere > EPSILON) && (tSphere < t)) { t = tSphere; return SHADOW; } } return 1.0; } vec3 fog(in float t, in vec3 color) { return mix(color, ubo.fogColor.rgb, clamp(sqrt(t*t)/20.0, 0.0, 1.0)); } vec3 renderScene(inout vec3 rayO, inout vec3 rayD, inout int id) { vec3 color = vec3(0.0); float t = MAXLEN; // Get intersected object ID int objectID = intersect(rayO, rayD, t); if (objectID == -1) { return color; } vec3 pos = rayO + t * rayD; vec3 lightVec = normalize(ubo.lightPos - pos); vec3 normal; // Planes // Spheres for (int i = 0; i < planes.length(); i++) { if (objectID == planes[i].id) { normal = planes[i].normal; float diffuse = lightDiffuse(normal, lightVec); float specular = lightSpecular(normal, lightVec, planes[i].specular); color = diffuse * planes[i].diffuse + specular; } } for (int i = 0; i < spheres.length(); i++) { if (objectID == spheres[i].id) { normal = sphereNormal(pos, spheres[i]); float diffuse = lightDiffuse(normal, lightVec); float specular = lightSpecular(normal, lightVec, spheres[i].specular); color = diffuse * spheres[i].diffuse + specular; } } if (id == -1) return color; id = objectID; // Shadows t = length(ubo.lightPos - pos); color *= calcShadow(pos, lightVec, id, t); // Fog color = fog(t, color); // Reflect ray for next render pass reflectRay(rayD, normal); rayO = pos; return color; } void main() { ivec2 dim = imageSize(resultImage); vec2 uv = vec2(gl_GlobalInvocationID.xy) / dim; vec3 rayO = ubo.camera.pos; vec3 rayD = normalize(vec3((-1.0 + 2.0 * uv) * vec2(ubo.aspectRatio, 1.0), -1.0)); // Basic color path int id = 0; vec3 finalColor = renderScene(rayO, rayD, id); // Reflection if (REFLECTIONS) { float reflectionStrength = REFLECTIONSTRENGTH; for (int i = 0; i < RAYBOUNCES; i++) { vec3 reflectionColor = renderScene(rayO, rayD, id); finalColor = (1.0 - reflectionStrength) * finalColor + reflectionStrength * mix(reflectionColor, finalColor, 1.0 - reflectionStrength); reflectionStrength *= REFLECTIONFALLOFF; } } imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), vec4(finalColor, 0.0)); }