83 lines
2.3 KiB
GLSL
83 lines
2.3 KiB
GLSL
#version 430 core
|
|
|
|
in layout(location = 0) vec3 normal;
|
|
in layout(location = 1) vec2 textureCoordinates;
|
|
in layout(location = 2) vec3 worldPosition;
|
|
|
|
struct LightSource {
|
|
vec3 position;
|
|
vec3 color;
|
|
};
|
|
|
|
uniform LightSource lights[3];
|
|
uniform vec3 cameraPosition;
|
|
uniform vec3 ballPosition;
|
|
|
|
out vec4 fragColor;
|
|
|
|
const vec3 baseColor = vec3(1.0);
|
|
const float ambientStrength = 0.1;
|
|
const float specularStrength = 0.5;
|
|
const float shininess = 32.0;
|
|
|
|
// attenuation: 1 / (la + lb*d + lc*d²)
|
|
const float la = 0.1;
|
|
const float lb = 0.01;
|
|
const float lc = 0.001;
|
|
|
|
// soft shadow radii
|
|
const float ballRadius = 1.0;
|
|
const float hardRadius = ballRadius;
|
|
const float softRadius = ballRadius * 2.0;
|
|
|
|
float rand(vec2 co) {
|
|
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
|
}
|
|
float dither(vec2 uv) {
|
|
return (rand(uv) * 2.0 - 1.0) / 256.0;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec3 N = normalize(normal);
|
|
vec3 V = normalize(cameraPosition - worldPosition);
|
|
|
|
// start with ambience
|
|
vec3 result = ambientStrength * baseColor;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
vec3 toLight = lights[i].position - worldPosition;
|
|
vec3 L = normalize(toLight);
|
|
float dist = length(toLight);
|
|
|
|
float attenuation = 1.0 / (la + lb * dist + lc * dist * dist);
|
|
|
|
float diff = max(dot(N, L), 0.0);
|
|
vec3 diffuse = diff * lights[i].color;
|
|
|
|
// blinn-phong half-vector
|
|
vec3 H = normalize(L + V);
|
|
float spec = pow(max(dot(N, H), 0.0), shininess);
|
|
vec3 specular = specularStrength * spec * lights[i].color;
|
|
|
|
vec3 toBall = ballPosition - worldPosition;
|
|
float projDist = dot(toBall, L); // distance along light ray
|
|
float perpDist = length(toBall - projDist * L); // perpendicular distance to ray
|
|
bool inShadowCone = (projDist > 0.0) && (projDist < dist);
|
|
|
|
float shadow = 1.0;
|
|
// skip ball's own light (index 2) to avoid self-shadowing the whole scene
|
|
if (inShadowCone) {
|
|
if (perpDist <= hardRadius) {
|
|
shadow = 0.0;
|
|
} else if (perpDist < softRadius) {
|
|
shadow = (perpDist - hardRadius) / (softRadius - hardRadius);
|
|
}
|
|
}
|
|
|
|
result += shadow * attenuation * (diffuse + specular) * baseColor;
|
|
}
|
|
|
|
fragColor = vec4(result + dither(textureCoordinates), 1.0);
|
|
}
|