#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 float smoothing; 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; //bool twoExists = false; 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; uint subtract; uint dummy2; uint dummy3; uint dummy4; }; struct GLSLLight { vec4 position; vec4 diffuse_color; vec4 specular_color; vec4 ambient_color; }; layout(std430, binding = 0) buffer PrimitiveBuffer { GLSLPrimitive primitives[]; }; layout(std430, binding = 1) buffer LightBuffer { GLSLLight lights[]; }; 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 round(vec3 pos, GLSLPrimitive prim, float radius) //{ // return sdf(pos, prim) - radius; //} float sdBox() { vec3 p = vec3(0.0, 0.0, 0.0); vec3 b = vec3(0.0, 0.0, 0.1); vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } 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); //d = round(pos, prim, prim.radius); //d = sdBox(); break; } default: { d = 0.0; break; } } return d; } float smoothMinSDF(float d1, float d2, float k) { if (k == 0.0) return min(d1, d2); float h = max(k - abs(d1 - d2), 0.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) return max(d1, d2); float h = max(k - abs(d1 - d2), 0.0) / k; return max(d1, d2) - h * h * k * 0.25; } // From Inigo Quilez float smoothSubSDF(float d1, float d2, float k) { float h = clamp(0.5 - 0.5 * (d2 + d1) / k, 0.0, 1.0); return mix(d2, -d1, h) + k * h * (1.0 - h); } float calcDist(vec3 pos, inout float dist) { for (int i = 0; i < primitives.length(); i++) { float newDist = sdf(pos, primitives[i]); if (primitives[i].subtract == 0) dist = smoothMinSDF(dist, newDist, smoothing); else dist = smoothSubSDF(newDist, dist, smoothing); } return dist; } float calcDist2(vec3 pos) { float dist = 999999.0; for (int i = 0; i < primitives.length(); i++) { float newDist = sdf(pos, primitives[i]); if (primitives[i].subtract == 0) dist = smoothMinSDF(newDist, dist, smoothing); else dist = smoothSubSDF(newDist, dist, smoothing); } return dist; } vec3 estimateNormals(vec3 p, float epsilon) { vec2 e = vec2(1.0, -1.0) * 0.5773; return normalize(e.xyy * calcDist2(p + e.xyy * epsilon) + e.yyx * calcDist2(p + e.yyx * epsilon) + e.yxy * calcDist2(p + e.yxy * epsilon) + e.xxx * calcDist2(p + e.xxx * epsilon)); } 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; } } vec4 renderLight(vec4 position) { vec4 newPosition = position; vec4 matPos = P * V * newPosition; matPos /= matPos.w; vec2 screenPos = 2.0 * vec2(gl_FragCoord.x / window_width, gl_FragCoord.y / window_height) - 1.0; float aspectRatio = float(window_width) / float(window_height); screenPos.y /= aspectRatio; vec2 centerScreenPos = matPos.xy; float radius = 0.05 / length(newPosition.xyz - cam_pos); vec4 color; if (length(screenPos - centerScreenPos) < radius && length(screenPos - centerScreenPos) > radius * 0.9) { color = vec4(0.957, 0.827, 0.369, 1.0); } else { color = vec4(0.0); } return color; } vec4 RayMarch(vec2 coords, vec2 offset) { vec3 normals; vec4 color = vec4(0.0); vec4 lightColor = vec4(0.0); vec4 ray_pos = vec4(cam_pos, 1.0); float ray_dist = 0.0; float max_dist = 100.0; float epsilon = 0.001; int steps = 0; int max_steps = 1000; float k = smoothing; vec4 cam_dir = inverse(P) * vec4(coords + offset, 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; calcDist(ray_pos.xyz, minDist); vec3 color1 = vec3(0.0), color2 = vec3(0.0); float closestDist = 999999.0, secondClosestDist = 999999.0; int closestIndex = -1, secondClosestIndex = -1; for (int i = 0; i < primitives.length(); i++) { float dist = sdf(ray_pos.xyz, primitives[i]); // Update closest and second closest distances and indices if (dist < closestDist) { secondClosestDist = closestDist; secondClosestIndex = closestIndex; closestDist = dist; closestIndex = i; } else if (dist < secondClosestDist) { secondClosestDist = dist; secondClosestIndex = i; } } if (closestIndex != -1) { color1 = primitives[closestIndex].diffuse_color.rgb; } if (secondClosestIndex != -1) { color2 = primitives[secondClosestIndex].diffuse_color.rgb; } // Calculate weight based on distances float weight = weightFunction(closestDist, secondClosestDist, smoothing); // Blend colors based on weight vec3 finalColor = mix(color1, color2, weight); for (int i = 0; i < primitives.length(); i++) { float weight = weightFunction(minDist, smoothing, epsilon); 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; vec4 ambient; vec4 diffuse; vec4 specular; float distLight; vec3 lw; ambient_color = vec4(accumulatedAmbient.rgb, 1.0); //diffuse_color = vec4(accumulatedDiffuse.rgb, 1.0); diffuse_color.rgb = finalColor; normals = estimateNormals(ray_pos.xyz, epsilon); for (int i = 0; i < lights.length(); i++) { ambient += lights[i].ambient_color * ambient_color; vec3 nw = normalize(normals); lw = normalize(lights[i].position.xyz - ray_pos.xyz); distLight = length(vec4(ray_pos.xyz - lights[i].position.xyz, 1.0)); diffuse += max(dot(nw, lw), 0) * diffuse_color * lights[i].diffuse_color; float spec; vec3 vw = normalize(cam_pos - ray_pos.xyz); vec3 halfwayDir = normalize(lw + vw); spec = pow(max(dot(halfwayDir, nw), 0), int(accumulatedSpecExp)); specular += spec * accumulatedSpecular * lights[i].specular_color; color = ambient + (1.0 / (distLight * distLight) * (diffuse + specular)); } break; } ray_pos += ray_dir * minDist; ray_dist += minDist; } for (int i = 0; i < lights.length(); i++) lightColor += renderLight(lights[i].position); return mix(color, lightColor, lightColor.a); } void main(void) { vec4 finalColor = vec4(0.0); int AA = 1; // Set the level of anti-aliasing float invAA = 1.0 / float(AA*AA); vec2 ndc_pos = 2.0 * vec2(gl_FragCoord.x / window_width, gl_FragCoord.y / window_height) - 1.0; for (int y = 0; y < AA; ++y) { for (int x = 0; x < AA; ++x) { vec2 offset = vec2(float(x) / float(window_width), float(y) / float(window_height)); finalColor += RayMarch(ndc_pos, offset * invAA); } } finalColor.rgb /= float(AA*AA); finalColor.r = pow(finalColor.r, 1.0 / 2.2); finalColor.g = pow(finalColor.g, 1.0 / 2.2); finalColor.b = pow(finalColor.b, 1.0 / 2.2); FragColor = finalColor; }