R"raw_string( #version 120 struct intersection { bool intersected; vec3 point; vec3 normal; vec3 color; float reflection; int idx; //todo different types }; varying vec2 outvertex; //settings uniform float aspectratio; uniform float time; uniform int supersamples; uniform int reflections; uniform bool shadows; uniform vec3 backgroundcolor; //light uniform vec3 l_position; uniform float l_radius; //spheres //todo ubo? uniform int s_length; uniform vec3 s_positions[100]; uniform float s_radii[100]; uniform vec4 s_colors[100]; uniform float s_reflections[100]; const float PI = 3.14159265359; //din = german industrial normal float light_diffuse(vec3 pos, vec3 din) { vec3 l_dir = normalize(l_position - pos); return max(dot(l_dir, din), 0.0f); } //adapted from https://wiki.delphigl.com/index.php/shader_ConeVolumeShadow float light_shadow(vec3 pos) { float s = 1.0f; for(int l = 0; l < s_length; l++) { vec3 s_pos = s_positions[l]; float s_rad = s_radii[l]; // project fragment (pos) on the cone axis => F_ vec3 nvLO = s_pos - l_position; float dLO = length(nvLO); nvLO /= dLO; vec3 vLF = pos - l_position; float dLF_ = dot(vLF, nvLO); if (dLF_ < dLO) { // fragment before occluder => no shadow continue; } vec3 F_ = l_position + dLF_ * nvLO; float rF = distance(F_, pos); // compute outer and inner radius at F_ float rF_outer = (s_rad + l_radius) * (dLF_ / dLO) - l_radius; if (rF >= rF_outer) { // outside the outer cone => no shadow continue; } float rF_inner = (s_rad - l_radius) * (dLF_ / dLO) + l_radius; if (rF_inner >= rF) { // inside the inner cone => full shadow return 0.0; } else if (rF_inner >= 0.0 || rF >= -rF_inner) { // soft shadow, linear interpolation s *= (rF - rF_inner) / (rF_outer - rF_inner); } else { // light from both sides of the occluder s *= (-2.0*rF_inner) / (rF_outer - rF_inner); } } return s; } intersection intersect_sphere(vec3 r_pos, vec3 r_dir, vec3 s_pos, float s_rad, inout float minimum) { intersection result = intersection(false,vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), 0.0f, -1); vec3 v = r_pos - s_pos; float a = dot(r_pos, r_pos); float b = dot(v, r_dir); float c = dot(v, v) - (s_rad * s_rad); float d = b * b - c; if(d >= 0) { float w1 = -b - sqrt(d); float w2 = -b + sqrt(d); if(w1 > 0.0f || w2 > 0.0f) { float w = min(w1, w2); float o = max(w1, w2); if(w1 <= 0.0f) w = w2; else if(w2 <= 0.0f) w = w1; if(w < minimum) { minimum = w; } else { return intersection(false,vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), 0.0f, -1); } vec3 p = r_pos + (w * r_dir); result.intersected = true; result.point = p; result.normal = normalize((p - s_pos) / s_rad); return result; } } return result; } intersection intersect(vec3 r_pos, vec3 r_dir, int idx) { intersection result = intersection(false,vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), 0.0f, -1); float minimum = 1000.0f; for(int l = 0; l < s_length; l++) { if(idx == l) continue; float maximum; intersection r = intersect_sphere(r_pos, r_dir, s_positions[l], s_radii[l], minimum); if(r.intersected) { r.color = s_colors[l].rgb; r.idx = l; r.reflection = s_reflections[l]; result = r; } //todo other geometric objects //todo handle min with other geometric objects } return result; } vec3 cast_ray(vec3 r_pos, vec3 r_pixel) { vec3 r_dir = normalize(r_pixel - r_pos); vec3 color = backgroundcolor; intersection i = intersection(false,vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), 0.0f, -1); vec3 p = r_pos; vec3 d = r_dir; int idx = -1; for(int bounce = 0; bounce < reflections; bounce++) { intersection i = intersect(p,d, idx); if(i.intersected) { float l_factor = light_diffuse(i.point, i.normal); if(shadows && l_factor > 0.004f) { l_factor *= light_shadow(i.point); } if(bounce == 0) { color = i.color * l_factor; } else { color = mix(color, color * i.color * l_factor, i.reflection); } //d = 2*(dot(p, i.normal))*i.normal - p; d = reflect(-p, i.normal); p = i.point; idx = i.idx; } else break; } return color; } void main() { vec2 rp = outvertex; rp.x = rp.x * aspectratio; vec4 outcolor = vec4(cast_ray(vec3(0.0f, 0.0f, 1.0f), vec3(rp, 0.0f)), 0.0f); for(int supersample = 1; supersample < supersamples; supersample++) { outcolor = mix(outcolor, vec4(cast_ray(vec3(sin(supersample * PI/supersamples)/200, cos(supersample * PI/supersamples)/200, 1.0f), vec3(rp, 0.0f)), 0.0f), 0.5f); } gl_FragColor = outcolor; } )raw_string"