#version 430 #extension GL_NV_uniform_buffer_std430_layout : enable uniform mat4 P; uniform mat4 V; uniform mat4 M; uniform vec3 cam_pos; uniform int window_width; uniform int window_height; uniform vec3 la; uniform vec3 ld; uniform vec3 ls; uniform float smoothing; uniform vec3 sphere_center; const uint P_SPHERE = 0x00; const uint P_TORUS = 0x01; const uint P_CONE = 0x02; const uint P_CUBE = 0x03; const uint P_ROUNDED_CUBE = 0x04; struct GLSLPrimitive { vec4 position; vec4 diffuse_color; vec4 specular_color; vec4 ambient_color; int specular_exponent; uint type; float radius; float height; float inner_radius; float outer_radius; float size; uint dummy; }; layout(std430, binding = 0) buffer PrimitiveBuffer { GLSLPrimitive primitives[]; }; out vec4 FragColor; struct NormalEstimationData { vec3 normal; float minDistance; float secondMinDistance; float smoothMinDistance; GLSLPrimitive closestPrimitive; GLSLPrimitive secondClosestPrimitive; }; float sphereSDF(vec3 p, vec3 center, float r) { return length(p - center) - r; } float torusSDF(vec3 p, vec3 center, float R, float r) { vec2 q = vec2(length(p.xz - center.xz) - R, p.y - center.y); return length(q) - r; } float coneSDF(vec3 p, vec3 center, float h, float r) { vec2 d = abs(vec2(length(p.xz), p.y)) - vec2(r, h); return length(d) < 0.0 ? max(d.x, -d.y) : length(max(d, vec2(0))); } float cubeSDF(vec3 p, vec3 c, float s) { float x = max(p.x - c.x - (s / 2.0), c.x - p.x - (s / 2.0)); float y = max(p.y - c.y - (s / 2.0),c.y - p.y - (s / 2.0)); float z = max(p.z - c.z - (s / 2.0),c.z - p.z - (s / 2.0)); float d = x; d = max(d, y); d = max(d, z); return d; } float roundedCubeSDF(vec3 p, vec3 center, float s, float r) { return length(max(abs(p - center) - vec3(s), vec3(0))) - r; } float sdf(vec3 pos, GLSLPrimitive prim) { float d; switch (prim.type) { case P_SPHERE: { d = sphereSDF(pos, prim.position.xyz, prim.radius); break; } case P_TORUS: { d = torusSDF(pos, prim.position.xyz, prim.outer_radius, prim.inner_radius); break; } case P_CONE: { d = coneSDF(pos, prim.position.xyz, prim.height, prim.radius); break; } case P_CUBE: { d = cubeSDF(pos, prim.position.xyz, prim.size); break; } case P_ROUNDED_CUBE: { d = roundedCubeSDF(pos, prim.position.xyz, prim.size, prim.radius); break; } default: { d = 0.0; break; } } return d; } float smoothMinSDF(float d1, float d2, float k) { if (k == 0.0) k = 0.000001; float h = max(k - abs(d1 - d2), 0) / k; return min(d1, d2) - h * h * h * k * 1.0 / 6.0; } float smoothMaxSDF(float d1, float d2, float k) { if (k == 0.0) k = 0.000001; float h = min(k - abs(d1 - d2), 0) / k; return min(d1, d2) - h * h * h * k * 1.0 / 6.0; } void estimateSmoothNormals(vec3 p, float smoothness, float epsilon, inout vec3 normals, inout vec4 color) { float d = sdf(p, primitives[0]); float nx = sdf(vec3(p.x + epsilon, p.y, p.z), primitives[0]); float ny = sdf(vec3(p.x, p.y + epsilon, p.z), primitives[0]); float nz = sdf(vec3(p.x, p.y, p.z + epsilon), primitives[0]); float blendFactor = clamp(0.5 + 0.5 * (sdf(p, primitives[0]) - sdf(p, primitives[1])) / smoothness, 0.0, 1.0); vec4 blendedColor = mix(primitives[0].diffuse_color, primitives[1].diffuse_color, blendFactor); blendFactor = clamp(0.5 + 0.5 * (sdf(p, primitives[1]) - sdf(p, primitives[2])) / smoothness, 0.0, 1.0); vec4 blendedColor1 = mix(primitives[1].diffuse_color, primitives[2].diffuse_color, blendFactor); blendFactor = clamp(0.5 + 0.5 * (sdf(p, primitives[2]) - sdf(p, primitives[0])) / smoothness, 0.0, 1.0); vec4 blendedColor2 = mix(primitives[2].diffuse_color, primitives[0].diffuse_color, blendFactor); vec4 blendedColor3 = mix(blendedColor, blendedColor1, blendFactor); vec4 blendedColor4 = mix(blendedColor3, blendedColor2, blendFactor); for (int i = 1; i < primitives.length(); i++) { d = smoothMinSDF(d, sdf(p, primitives[i]), smoothness); nx = smoothMinSDF(nx, sdf(vec3(p.x + epsilon, p.y, p.z), primitives[i]), smoothness); ny = smoothMinSDF(ny, sdf(vec3(p.x, p.y + epsilon, p.z), primitives[i]), smoothness); nz = smoothMinSDF(nz, sdf(vec3(p.x, p.y, p.z + epsilon), primitives[i]), smoothness); } nx -= d; ny -= d; nz -= d; normals = normalize(vec3(nx, ny, nz)); } vec3 estimateNormals(vec3 p, GLSLPrimitive prim, float epsilon) { float x = sdf(vec3(p.x + epsilon, p.y, p.z), prim) - sdf(vec3(p.x - epsilon, p.y, p.z), prim); float y = sdf(vec3(p.x, p.y + epsilon, p.z), prim) - sdf(vec3(p.x, p.y - epsilon, p.z), prim); float z = sdf(vec3(p.x, p.y, p.z + epsilon), prim) - sdf(vec3(p.x, p.y, p.z - epsilon), prim); return normalize(vec3(x, y, z)); } //vec3 estimateNormalsMax(vec3 p, float smoothness) { // float epsilon = 0.001; // float d = max(torusSDF(p, torus.center, torus.R, torus.r), sphereSDF(p, sphere.center, sphere.r)); // float nx = max(torusSDF(vec3(p.x + epsilon, p.y, p.z), torus.center, torus.R, torus.r), sphereSDF(vec3(p.x + epsilon, p.y, p.z), sphere.center, sphere.r)) - d; // float ny = max(torusSDF(vec3(p.x, p.y + epsilon, p.z), torus.center, torus.R, torus.r), sphereSDF(vec3(p.x, p.y + epsilon, p.z), sphere.center, sphere.r)) - d; // float nz = max(torusSDF(vec3(p.x, p.y, p.z + epsilon), torus.center, torus.R, torus.r), sphereSDF(vec3(p.x, p.y, p.z + epsilon), sphere.center, sphere.r)) - d; // return normalize(vec3(nx, ny, nz)); //} vec4 blendColors(vec4 color1, vec4 color2, float blendFactor) { return mix(color1, color2, blendFactor); } float weightFunction(float distance, float k, float epsilon) { if (k <= 0.0) { // When k is zero, return 1 if the distance is zero (object is closest), else return 0. return distance <= 0.001 ? 1.0 : 0.0; } else { // As k increases, the transition becomes smoother. return (k * k * k) / distance; } } void main(void) { vec3 normals; vec3 color = la; vec4 ray_pos = vec4(cam_pos, 1.0); float ray_dist = 0.0; float max_dist = 100.0; float epsilon = 0.0001; int steps = 0; int max_steps = 1000; vec3 light_pos = vec3(0.0, 1.0, 0.0); int spec_exponent = 40; float k = smoothing; vec2 ndc_pos = 2.0 * vec2(gl_FragCoord.x / window_width, gl_FragCoord.y / window_height) - 1.0; vec4 cam_dir = inverse(P) * vec4(ndc_pos, 1.0, 1.0); cam_dir /= cam_dir.w; cam_dir = vec4(cam_dir.xyz, 0.0); vec4 ray_dir = normalize(inverse(V) * cam_dir); vec4 accumulatedDiffuse = vec4(0.0); vec4 accumulatedSpecular = vec4(0.0); vec4 accumulatedAmbient = vec4(0.0); float accumulatedSpecExp = 0.0; float totalWeight = 0.0; int closestIndex = -1; while (ray_dist < max_dist && steps < max_steps && primitives.length() != 0) { steps++; float minDist = 999999.0; for (int i = 0; i < primitives.length(); i++) { float dist = sdf(ray_pos.xyz, primitives[i]); float weight = weightFunction(dist, smoothing, epsilon); if (dist < sdf(ray_pos.xyz, primitives[closestIndex])) { closestIndex = i; } minDist = smoothMinSDF(minDist, dist, k); accumulatedDiffuse += primitives[i].diffuse_color * weight; accumulatedSpecular += primitives[i].specular_color * weight; accumulatedAmbient += primitives[i].ambient_color * weight; accumulatedSpecExp += primitives[i].specular_exponent * weight; totalWeight += weight; } if (minDist <= epsilon) { if (totalWeight > 0.0) { accumulatedDiffuse /= totalWeight; accumulatedSpecular /= totalWeight; accumulatedAmbient /= totalWeight; accumulatedSpecExp /= totalWeight; } vec4 ambient_color, diffuse_color; ambient_color = vec4(accumulatedAmbient.rgb, 1.0); diffuse_color = vec4(accumulatedDiffuse.rgb, 1.0); vec4 ambient = vec4(la, 1.0) * ambient_color; //normals = estimateNormals(ray_pos.xyz, primitives[closestIndex], epsilon); estimateSmoothNormals(ray_pos.xyz, k, epsilon, normals, diffuse_color); vec3 nw = normalize(normals); vec3 lw = normalize(light_pos - ray_pos.xyz); float dist = length(vec4(ray_pos.xyz - light_pos, 1.0)); vec4 diffuse = max(dot(nw, lw), 0) * diffuse_color * vec4(ld, 1.0); float spec; vec3 vw = normalize(cam_pos - ray_pos.xyz); vec3 halfwayDir = normalize(lw + vw); spec = pow(max(dot(halfwayDir, nw), 0), int(accumulatedSpecExp)); vec4 specular = spec * vec4(accumulatedSpecular.rgb * ls, 1.0); color = (ambient + (1.0 / (dist * dist) * (diffuse + specular))).xyz; break; } ray_pos += ray_dir * minDist; ray_dist += minDist; } FragColor = vec4(color, 1.0); }