GLSLでレイトレーシング

最終更新: 2017-07-31 11:13

http://glslsandbox.com/e#41730.6
http://glslsandbox.com/e#41762.0

#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

const float PI = 3.1416;

vec3 cameraPosition;

const int n_sphere = 2;
const int n_triangle = 1;
const int n_plane = 6;
const int n_light = 2;


float EPS = 1e-5;

struct Ray{
    vec3 origin;
    vec3 direction;
};

struct Surface {
    int type;
    vec3 color;
};

struct Hit {
    bool valid;
    vec3 position;
    vec3 direction;
    Ray ray;
    Surface surface;
};		

struct Sphere{
    vec3 position;
    float radius;
    Surface surface;
};
struct Plane{
    vec3 position;
    vec3 normal;
    Surface surface;
};

struct Triangle {
    vec3 position;
    vec3 edge1;
    vec3 edge2;
    Surface surface;
};

struct Light {
    vec3 position;
    float irrad;
};

Sphere spheres[n_sphere];
Plane planes[n_plane];
Triangle triangles[n_triangle];
Light lights[n_light];

Ray generate_camera_ray() {
    vec2 pos = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
    vec3 dir = normalize(vec3(pos.x, pos.y, -1.0));
    return Ray(cameraPosition, dir);
}

float det(vec3 a, vec3 b, vec3 c) {
    return (a.x * b.y * c.z)
        + (a.y * b.z * c.x)
        + (a.z * b.x * c.y)
        - (a.x * b.z * c.y)
        - (a.y * b.x * c.z)
        - (a.z * b.y * c.x);
}

Hit trace(Ray ray) {
    Hit min_hit;
    float len = 0.0;
    min_hit.valid = false;

    //trace Spheres
    for (int i = 0; i < n_sphere; ++i) {
        vec3 a = ray.origin - spheres[i].position;
        float b = dot(a, ray.direction);
        float c = dot(a, a) - spheres[i].radius * spheres[i].radius;
        float d = b * b - c;

        vec3 origin_to_dir = ray.direction * dot(a, ray.direction);
        if (d > 0.001) {
            float t = -b - sqrt(d);
            if (t > EPS && (!min_hit.valid || len > t)) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = normalize(min_hit.position - spheres[i].position);
                min_hit.surface = spheres[i].surface;
            }
        }
    }

    for (int i=0; i<n_plane; ++i) {
        //trace Plane
        float d = -dot(planes[i].position, planes[i].normal);
        float v = dot(ray.direction, planes[i].normal);
        float t = -(dot(ray.origin, planes[i].normal) + d) / v;
        if (t > EPS) {
            if (!min_hit.valid || len > t) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = planes[i].normal;
                min_hit.surface = planes[i].surface;
            }
        }
    }
    for (int i=0; i<n_triangle; ++i) {
        Triangle T = triangles[i];
        vec3 invRay = -ray.direction;
        vec3 e1 = T.edge1;
        vec3 e2 = T.edge2;

        float denominator =  det(e1, e2, invRay );
        if ( denominator == 0.0 ) continue;

        float invDenominator = 1.0 / denominator;
        vec3 d = ray.origin - T.position;

        float u = det(d, e2, invRay ) * invDenominator;
        if (u < 0.0 || u > 1.0) continue;

        float v = det( e1, d, invRay ) * invDenominator;
        if (v < 0.0 || u + v > 1.0) continue;

        float t = det(e1, e2, d) * invDenominator;
        if (t > EPS) {
            if (!min_hit.valid || len > t) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = normalize(cross(e1, e2)) * sign(invDenominator);
                min_hit.surface = T.surface;
            }
        }
    }

    return min_hit;
}

// Phongの反射モデルの実装
vec3 shade(Hit hit) {
    for (int r = 0; r < 20; ++r) {
        if (!hit.valid) { return vec3(0.0); }
        if (hit.surface.type == 0) { // normal surface
            vec3 Rr = vec3(0.0);
            for(int l=0; l<n_light; l++) {
                Ray ray = Ray(hit.position, normalize(lights[l].position - hit.position));
                Hit hit2 = trace(ray);
                if (!hit2.valid || length(hit2.position - hit.position) > length(lights[l].position - hit.position)) {
                    vec3 lightDirection = normalize(lights[l].position - hit.position);
                    vec3 ka = vec3(0.01);
                    vec3 kd = hit.surface.color;
                    vec3 ks = vec3(0.6);
                    float alpha = 8.0;
                    float Ia = 0.5;
                    float Ii = 0.5;
                    vec3 Ra = ka * Ia;
                    vec3 Rd = kd * Ii * clamp(dot(lightDirection, hit.direction), 0.0, 1.0);
                    vec3 Rs = vec3(0.0);
                    if(dot(hit.direction, lightDirection) > 0.0) {
                        float r = length(lightDirection);
                        float d = dot(hit.direction, lightDirection);
                        vec3 reflectDirection = 2.0 * dot(hit.direction, lightDirection) * hit.direction - lightDirection;
                        Rs =  ks * Ii * pow(clamp(dot(-hit.ray.direction, reflectDirection), 0.0, 1.0), alpha);
                    }
                    Rr += clamp(Ra + Rd + Rs, 0.0, 1.0);
                }
            }
            return Rr;
        } else if (hit.surface.type == 1) { // complete mirror
            hit = trace(Ray(hit.position, reflect(hit.ray.direction, hit.direction)));
        }
    }
    return vec3(0.0);
}

void main(void){
    cameraPosition = vec3(-7.0*cos(PI*mouse.x), 7.0*sin(0.5 * PI*(2.0*mouse.y-1.0)), 16.0) ;
    
    spheres[0] = Sphere(vec3(6.0+4.0*sin(3.0*sin(time)), 5.0*cos(time), -8.0+3.0*cos(cos(time))), 5.0+sin(time), Surface(1, vec3(0.3, 0.3, 1.0)));
    spheres[1] = Sphere(vec3(-7.0+6.0*sin(time+sin(time)), 10.0+4.0*cos(3.0*time), -10.0+cos(time)), 3.0+cos(time), 
			Surface(0, vec3(0.5+sin(time)*0.5, 1.0, 0.5+0.5*cos(2.0*time))));

    triangles[0] = Triangle(vec3(-6.0, -7.0, -0.0+sin(sin(time))), 
            vec3(10.0, 0.0+2.0*sin(time), 0.5), 
            vec3(1.0, -2.0+2.0*cos(2.0*time), -10.0), 
            Surface(0, vec3(0.5+0.5*sin(time), 0.5+0.5*cos(2.0*time), 1.0)));

    planes[0] = Plane(vec3(0.0, -11.0, 0.0), vec3(0.0, 1.0, 0.0), Surface(0, vec3(1.0, 1.0, 1.0)));
    
    planes[1] = Plane(vec3(0.0, 35.0, 0.0), vec3(0.0, -1.0, 0.0), Surface(0, vec3(1.0, 1.0, 1.0)));
    planes[2] = Plane(vec3(25.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), Surface(0, vec3(0.3, 1.0, 0.3)));
    planes[3] = Plane(vec3(-25.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), Surface(0, vec3(1.0, 0.0, 0.0)));
    planes[4] = Plane(vec3(0.0, 0.0, -20.0), vec3(0.0, 0.0, 1.0), Surface(1, vec3(1.0, 1.0, 1.0)));
    planes[5] = Plane(vec3(0.0, 0.0, 18.0), vec3(0.0, 0.1, 1.0), Surface(1, vec3(1.0, 1.0, 1.0)));
    
    lights[0].position = vec3(14.0, 15.0, 7.0);
    lights[1].position = vec3(-14.0, 12.0, 5.0);
    
    Ray ray = generate_camera_ray();
    Hit hit = trace(ray);
    vec3 pixel = shade(hit);
    gl_FragColor = vec4(pixel, 1.0);
}
#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

vec3 cameraPosition = vec3(0.0, 0.0, 5.0);

const int n_sphere = 2;
const int n_triangle = 1;
const int n_plane = 6;
const int n_light = 2;


float EPS = 1e-5;

struct Ray{
    vec3 origin;
    vec3 direction;
};

struct Surface {
    int type;
    vec3 color;
};

struct Hit {
    bool valid;
    vec3 position;
    vec3 direction;
    Ray ray;
    Surface surface;
};		

struct Sphere{
    vec3 position;
    float radius;
    Surface surface;
};
struct Plane{
    vec3 position;
    vec3 normal;
    Surface surface;
};

struct Triangle {
    vec3 position;
    vec3 edge1;
    vec3 edge2;
    Surface surface;
};

struct Light {
    vec3 position;
    float irrad;
};

Sphere spheres[n_sphere];
Plane planes[n_plane];
Triangle triangles[n_triangle];
Light lights[n_light];

Ray generate_camera_ray() {
    vec2 pos = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
    vec3 dir = normalize(vec3(pos.x, pos.y, -1.0));
    return Ray(cameraPosition, dir);
}

float det(vec3 a, vec3 b, vec3 c) {
    return (a.x * b.y * c.z)
        + (a.y * b.z * c.x)
        + (a.z * b.x * c.y)
        - (a.x * b.z * c.y)
        - (a.y * b.x * c.z)
        - (a.z * b.y * c.x);
}

Hit trace(Ray ray) {
    Hit min_hit;
    float len = 0.0;
    min_hit.valid = false;

    //trace Spheres
    for (int i = 0; i < n_sphere; ++i) {
        vec3 a = ray.origin - spheres[i].position;
        float b = dot(a, ray.direction);
        float c = dot(a, a) - spheres[i].radius * spheres[i].radius;
        float d = b * b - c;

        vec3 origin_to_dir = ray.direction * dot(a, ray.direction);
        if (d > 0.001) {
            float t = -b - sqrt(d);
            if (t > EPS && (!min_hit.valid || len > t)) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = normalize(min_hit.position - spheres[i].position);
                min_hit.surface = spheres[i].surface;
            }
        }
    }

    for (int i=0; i<n_plane; ++i) {
        //trace Plane
        float d = -dot(planes[i].position, planes[i].normal);
        float v = dot(ray.direction, planes[i].normal);
        float t = -(dot(ray.origin, planes[i].normal) + d) / v;
        if (t > EPS) {
            if (!min_hit.valid || len > t) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = planes[i].normal;
                min_hit.surface = planes[i].surface;
            }
        }
    }
    for (int i=0; i<n_triangle; ++i) {
        Triangle T = triangles[i];
        vec3 invRay = -ray.direction;
        vec3 e1 = T.edge1;
        vec3 e2 = T.edge2;

        float denominator =  det(e1, e2, invRay );
        if ( denominator == 0.0 ) continue;

        float invDenominator = 1.0 / denominator;
        vec3 d = ray.origin - T.position;

        float u = det(d, e2, invRay ) * invDenominator;
        if (u < 0.0 || u > 1.0) continue;

        float v = det( e1, d, invRay ) * invDenominator;
        if (v < 0.0 || u + v > 1.0) continue;

        float t = det(e1, e2, d) * invDenominator;
        if (t > EPS) {
            if (!min_hit.valid || len > t) {
                len = t;
                min_hit.valid = true;
                min_hit.position = ray.origin + t * ray.direction;
                min_hit.ray = ray;
                min_hit.direction = normalize(cross(e1, e2)) * sign(invDenominator);
                min_hit.surface = T.surface;
            }
        }
    }

    return min_hit;
}

vec3 shade(Hit hit) {
    for (int r = 0; r < 20; ++r) {
        if (!hit.valid) { return vec3(0.0); }
        if (hit.surface.type == 0) { // normal surface
            vec3 Rr = vec3(0.0);
            for(int l=0; l<n_light; l++) {
                Ray ray = Ray(hit.position, normalize(lights[l].position - hit.position));
                Hit hit2 = trace(ray);
                if (!hit2.valid || length(hit2.position - hit.position) > length(lights[l].position - hit.position)) {
                    vec3 lightDirection = normalize(lights[l].position - hit.position);
                    vec3 ka = vec3(0.01);
                    vec3 kd = hit.surface.color;
                    vec3 ks = vec3(0.3);
                    float alpha = 8.0;
                    float Ia = 0.5;
                    float Ii = 0.5;
                    vec3 Ra = ka * Ia;
                    vec3 Rd = kd * Ii * clamp(dot(lightDirection, hit.direction), 0.0, 1.0);
                    vec3 Rs = vec3(0.0);
                    if(dot(hit.direction, lightDirection) > 0.0) {
                        float r = length(lightDirection);
                        float d = dot(hit.direction, lightDirection);
                        vec3 reflectDirection = 2.0 * dot(hit.direction, lightDirection) * hit.direction - lightDirection;
                        Rs =  ks * Ii * pow(clamp(dot(-hit.ray.direction, reflectDirection), 0.0, 1.0), alpha);
                    }
                    Rr += clamp(Ra + Rd + Rs, 0.0, 1.0);
                }
            }
            return Rr;
        } else if (hit.surface.type == 1) { // complete mirror
            hit = trace(Ray(hit.position, reflect(hit.ray.direction, hit.direction)));
        }
    }
    return vec3(0.0);
}

void main(void){
    
    spheres[0] = Sphere(vec3(6.0, -2.0, -10.0), 4.0, Surface(1, vec3(0.3, 0.3, 1.0)));
    spheres[1] = Sphere(vec3(-6.0, 3.0, -12.0), 4.0, Surface(0, vec3(0.0, 1.0, 0.5)));

    triangles[0] = Triangle(vec3(-6.0, -8.0, -5.0), 
            vec3(10.0, 0.0, 0.5), 
            vec3(1.0, -2.0, -10.0), 
            Surface(0, vec3(0.3, 0.3, 1.0)));

    planes[0] = Plane(vec3(0.0, -10.0, 0.0), vec3(0.0, 1.0, 0.0), Surface(0, vec3(1.0, 1.0, 1.0)));
    planes[1] = Plane(vec3(0.0, 16.0, 0.0), vec3(0.0, -1.0, 0.0), Surface(0, vec3(1.0, 1.0, 1.0)));
    planes[2] = Plane(vec3(20.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), Surface(0, vec3(0.3, 1.0, 0.3)));
    planes[3] = Plane(vec3(-20.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), Surface(0, vec3(1.0, 0.0, 0.0)));
    planes[4] = Plane(vec3(0.0, 0.0, -20.0), vec3(0.0, 0.0, 1.0), Surface(1, vec3(1.0, 1.0, 1.0)));
    planes[5] = Plane(vec3(0.0, 0.0, 10.0), vec3(0.0, 0.1, 1.0), Surface(1, vec3(1.0, 1.0, 1.0)));
    
    lights[0].position = vec3(10.0, 12.0, 7.0);
    lights[1].position = vec3(-10.0, 10.0, 5.0);
    Ray ray = generate_camera_ray();
    Hit hit = trace(ray);
    vec3 pixel = shade(hit);
    gl_FragColor = vec4(pixel, 1.0);
}
#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

vec3 cameraPosition = vec3(0.0, 3.0, -5.0);
vec3 lightPosition = vec3(-10.0, 10.0, -10.0);

float Ia = 0.5;
float Ii = 1.0;
float alpha = 3.0;
float EPS = 1e-5;

struct Ray{
	vec3 origin;
	vec3 direction;
};

struct Hit {
	int count;
	vec3 position;
	vec3 normal;
	vec3 color;
	float dist;
	vec3 rayDirection;
};		

struct Sphere{
	float radius;
	vec3  position;
	vec3 ka, kd, ks;
};

void getColor(Ray R, inout Hit hit, vec3 ka, vec3 kd, vec3 ks) {
	vec3 lightDirection = normalize(lightPosition - hit.position);
	vec3 Ra = ka * Ia;
	vec3 Rd = kd * Ii * clamp(dot(lightDirection, hit.normal), 0.0, 1.0);
	vec3 Rs = vec3(0.0);
	if(dot(hit.normal, lightDirection) > 0.0) {
		vec3 reflectDirection = 2.0 * dot(hit.normal, lightDirection) * hit.normal - lightDirection;
		Rs =  ks * Ii * pow(clamp(dot(-R.direction, reflectDirection), 0.0, 1.0), alpha);
	}
	vec3 Rr = clamp(Ra + Rd + Rs, 0.0, 1.0);
        hit.color = Rr;
}
void intersectSphere(Ray R, Sphere S, inout Hit hit) {
	vec3  a = R.origin - S.position;
	float b = dot(a, R.direction);
	float c = dot(a, a) - (S.radius * S.radius);
	float d = b * b - c;
	if(d > 0.0) {
		float t = -b - sqrt(d);
		if(t > EPS && t < hit.dist){
			if(t > 0.0) hit.count++;
			hit.dist = t;
			hit.position = R.origin + R.direction * t;
			hit.normal = normalize(hit.position - S.position);
			hit.rayDirection = R.direction;
			getColor(R, hit, S.ka, S.kd, S.ks);
		}
	}
}

struct Triangle {
	vec3 origin;
	vec3 edge1;
	vec3 edge2;
	vec3 ka, kd, ks;
};

float det(vec3 a, vec3 b, vec3 c) {
	return (a.x * b.y * c.z)
		+ (a.y * b.z * c.x)
		+ (a.z * b.x * c.y)
		- (a.x * b.z * c.y)
		- (a.y * b.x * c.z)
		- (a.z * b.y * c.x);
}

void intersectTriangle(Ray R, Triangle T, inout Hit hit) {
	vec3 invRay = -1.0 * R.direction;
	vec3 e1 = T.edge1;
	vec3 e2 = T.edge2;

	float denominator =  det(e1, e2, invRay );
	if ( denominator == 0.0 ) return;

	float invDenominator = 1.0 / denominator;
	vec3 d = R.origin - T.origin;

	float u = det(d, e2, invRay ) * invDenominator;
	if (u < 0.0 || u > 1.0) return;

	float v = det( e1, d, invRay ) * invDenominator;
	if (v < 0.0 || u + v > 1.0) return;

	float t = det(e1, e2, d) * invDenominator;
	if(t > EPS && t < hit.dist) {
		hit.count++;
		hit.position = R.origin + R.direction * t;
		hit.dist = t;
		hit.normal   = normalize(cross(e1, e2)) * sign(invDenominator);
        	getColor(R, hit, T.ka, T.kd, T.ks);
		hit.rayDirection = R.direction;
	}
}

struct Plane{
	vec3 position;
	vec3 normal;
	vec3 ka, kd, ks;
};

void intersectPlane(Ray R, Plane P, inout Hit hit){
	float d = -dot(P.position, P.normal);
	float v = dot(R.direction, P.normal);
	float t = -(dot(R.origin, P.normal) + d) / v;
	if(t > EPS && t < hit.dist){
		hit.count++;
		hit.dist = t;
		hit.position = R.origin + R.direction * t;
		hit.normal = P.normal;
		getColor(R, hit, P.ka, P.kd, P.ks);
		float m = mod(hit.position.x, 2.0);
		float n = mod(hit.position.z, 2.0);
		if((m > 1.0 && n > 1.0) || (m < 1.0 && n < 1.0)){
			hit.color *= 0.8;
		}
		
		float f = 1.0 - min(abs(hit.position.z), 25.0) *0.04;
		hit.rayDirection = R.direction;
	}
}

Sphere sphere[12];
Triangle triangle;
Plane plane;

void checkHit(Ray ray, inout Hit hit) {
	for(int i=0; i<12; i++) {
		intersectSphere(ray, sphere[i], hit);
	}
	intersectTriangle(ray, triangle, hit);
	intersectPlane(ray, plane, hit);
}

void main(void){
	for(int i=0; i<12; i++) {
		sphere[i].radius = 0.3;
		sphere[i].position = vec3(2.0*sin(float(i)+0.0), 0.5+0.8*float(i), 2.0);
		sphere[i].ka = vec3(0.01);
		sphere[i].ks = vec3(0.3);
	}
	sphere[0].kd = vec3(1.0, 0.0, 0.0);
	sphere[1].kd = vec3(1.0, 0.0, 0.5);
	sphere[2].kd = vec3(1.0, 0.0, 1.0);
	sphere[3].kd = vec3(0.5, 0.0, 1.0);
	sphere[4].kd = vec3(0.0, 0.0, 1.0);
	sphere[5].kd = vec3(0.0, 0.5, 1.0);
	sphere[6].kd = vec3(0.0, 1.0, 1.0);
	sphere[7].kd = vec3(0.0, 1.0, 0.5);
	sphere[8].kd = vec3(0.0, 1.0, 0.0);
	sphere[9].kd = vec3(0.5, 1.0, 0.0);
	sphere[10].kd = vec3(1.0, 1.0, 0.0);
	sphere[11].kd = vec3(1.0, 0.5, 0.0);
	
	
	// draw
	// fragment position
	vec2 pos = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);

	// ray init
	Ray ray;
	ray.origin = cameraPosition;
	ray.direction = normalize(vec3(pos.x, pos.y, 1.0));
	
	// triangle init
	triangle.origin = vec3(-1.0, 1.0, 2.0);
	triangle.edge1 = vec3(6.0, 2.0, -0.5);
	triangle.edge2 = vec3(1.0, 6.0, 3.0);
	triangle.ka = vec3(0.1);
	triangle.kd = vec3(0.7, 0.0, 0.0);
	triangle.ks = vec3(0.3);

	plane.position = vec3(0.0, 0.0, 0.0);
	plane.normal = vec3(0.0, 1.0, 0.0);
	plane.ka = vec3(0.5);
	plane.kd = vec3(1.0, 1.0, 1.0);
	plane.ks = vec3(0.3);

	Hit hit;
	hit.count = 0;
	hit.dist = 1e+30;
	
	vec3 tempColor = vec3(1.0);
	checkHit(ray, hit);
	vec3 pixel = vec3(0.5, 0.7, 1.0);
	if(hit.count > 0) {
		pixel = hit.color;
		/*Hit isShadow;
		isShadow.count = 0;
		isShadow.dist = 1e+30;
		Ray ray2;
		ray2.direction = normalize(lightPosition - hit.position);
		ray2.origin = hit.position + ray2.direction * EPS;
		checkHit(ray2, isShadow);
		if(isShadow.count ==  0) pixel = hit.color;
		else pixel = hit.color * Ia * 0.2;*/
	}
	gl_FragColor = vec4(pixel, 1.0);
}
#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

vec3 cameraPosition = vec3(0.0, 3.0, -5.0);
vec3 lightPosition = vec3(-10.0, 10.0, -10.0);

float Ia = 0.5;
float Ii = 1.0;
float alpha = 3.0;
float EPS = 1e-5;

struct Ray{
	vec3 origin;
	vec3 direction;
};

struct Hit {
	int count;
	vec3 position;
	vec3 normal;
	vec3 color;
	float dist;
	vec3 rayDirection;
};		

struct Sphere{
	float radius;
	vec3  position;
	vec3 ka, kd, ks;
};

void getColor(Ray R, inout Hit hit, vec3 ka, vec3 kd, vec3 ks) {
	vec3 lightDirection = normalize(lightPosition - hit.position);
	vec3 Ra = ka * Ia;
	vec3 Rd = kd * Ii * clamp(dot(lightDirection, hit.normal), 0.0, 1.0);
	vec3 Rs = vec3(0.0);
	if(dot(hit.normal, lightDirection) > 0.0) {
		vec3 reflectDirection = 2.0 * dot(hit.normal, lightDirection) * hit.normal - lightDirection;
		Rs =  ks * Ii * pow(clamp(dot(-R.direction, reflectDirection), 0.0, 1.0), alpha);
	}
	vec3 Rr = clamp(Ra + Rd + Rs, 0.0, 1.0);
        hit.color = Rr;
}
void intersectSphere(Ray R, Sphere S, inout Hit hit) {
	vec3  a = R.origin - S.position;
	float b = dot(a, R.direction);
	float c = dot(a, a) - (S.radius * S.radius);
	float d = b * b - c;
	if(d > 0.0) {
		float t = -b - sqrt(d);
		if(t > EPS && t < hit.dist){
			if(t > 0.0) hit.count++;
			hit.dist = t;
			hit.position = R.origin + R.direction * t;
			hit.normal = normalize(hit.position - S.position);
			hit.rayDirection = R.direction;
			getColor(R, hit, S.ka, S.kd, S.ks);
		}
	}
}

struct Triangle {
	vec3 origin;
	vec3 edge1;
	vec3 edge2;
	vec3 ka, kd, ks;
};

float det(vec3 a, vec3 b, vec3 c) {
	return (a.x * b.y * c.z)
		+ (a.y * b.z * c.x)
		+ (a.z * b.x * c.y)
		- (a.x * b.z * c.y)
		- (a.y * b.x * c.z)
		- (a.z * b.y * c.x);
}

void intersectTriangle(Ray R, Triangle T, inout Hit hit) {
	vec3 invRay = -1.0 * R.direction;
	vec3 e1 = T.edge1;
	vec3 e2 = T.edge2;

	float denominator =  det(e1, e2, invRay );
	if ( denominator == 0.0 ) return;

	float invDenominator = 1.0 / denominator;
	vec3 d = R.origin - T.origin;

	float u = det(d, e2, invRay ) * invDenominator;
	if (u < 0.0 || u > 1.0) return;

	float v = det( e1, d, invRay ) * invDenominator;
	if (v < 0.0 || u + v > 1.0) return;

	float t = det(e1, e2, d) * invDenominator;
	if(t > EPS && t < hit.dist) {
		hit.count++;
		hit.position = R.origin + R.direction * t;
		hit.dist = t;
		hit.normal   = normalize(cross(e1, e2)) * sign(invDenominator);
        	getColor(R, hit, T.ka, T.kd, T.ks);
		hit.rayDirection = R.direction;
	}
}

struct Plane{
	vec3 position;
	vec3 normal;
	vec3 ka, kd, ks;
};

void intersectPlane(Ray R, Plane P, inout Hit hit){
	float d = -dot(P.position, P.normal);
	float v = dot(R.direction, P.normal);
	float t = -(dot(R.origin, P.normal) + d) / v;
	if(t > EPS && t < hit.dist){
		hit.count++;
		hit.dist = t;
		hit.position = R.origin + R.direction * t;
		hit.normal = P.normal;
		getColor(R, hit, P.ka, P.kd, P.ks);
		float m = mod(hit.position.x, 2.0);
		float n = mod(hit.position.z, 2.0);
		/*if((m > 1.0 && n > 1.0) || (m < 1.0 && n < 1.0)){
			diff *= 0.5;
		}*/
		
		float f = 1.0 - min(abs(hit.position.z), 25.0) *0.04;
		hit.rayDirection = R.direction;
	}
}
Triangle triangle[2];
Sphere sphere[2];
Plane plane;

void checkHit(Ray ray, inout Hit hit) {
	for(int i=0; i<2; i++) {
		intersectSphere(ray, sphere[i], hit);
	}
	for(int i=0; i<1; i++) {
		intersectTriangle(ray, triangle[i], hit);
	}
	intersectPlane(ray, plane, hit);
}

void main(void){
	// fragment position
	vec2 pos = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);

	// ray init
	Ray ray;
	ray.origin = cameraPosition;
	ray.direction = normalize(vec3(pos.x, pos.y, 1.0));
	
	// triangle init
	triangle[0].origin = vec3(-1.0, 1.0, 2.0);
	triangle[0].edge1 = vec3(6.0, 2.0, -0.5);
	triangle[0].edge2 = vec3(1.0, 6.0, 3.0);
	triangle[0].ka = vec3(0.1);
	triangle[0].kd = vec3(0.7, 0.0, 0.0);
	triangle[0].ks = vec3(0.3);
	
	// sphere init
	sphere[0].radius = 0.5;
	sphere[0].position = vec3(0.5, 3.0, 0.0);
	sphere[0].ka = vec3(0.1);
	sphere[0].kd = vec3(0.0, 0.7, 0.0);
	sphere[0].ks = vec3(0.5);
	
	sphere[1].radius = 0.5;
	sphere[1].position = vec3(-0.5, 5.0, 0.5);
	sphere[1].ka = vec3(0.01);
	sphere[1].kd = vec3(0.0, 0.0, 0.7);
	sphere[1].ks = vec3(0.3);

	plane.position = vec3(0.0, 0.0, 0.0);
	plane.normal = vec3(0.0, 1.0, 0.0);
	plane.ka = vec3(0.5);
	plane.kd = vec3(1.0, 1.0, 1.0);
	plane.ks = vec3(0.3);

	Hit hit;
	hit.count = 0;
	hit.dist = 1e+30;
	
	vec3 tempColor = vec3(1.0);
	checkHit(ray, hit);
	vec3 pixel = vec3(0.5, 0.7, 1.0);
	Ray ray2;
	if(hit.count > 0) {
		Hit isShadow;
		isShadow.count = 0;
		isShadow.dist = 1e+30;
		ray2.direction = normalize(lightPosition - hit.position);
		ray2.origin = hit.position + ray2.direction * EPS;
		checkHit(ray2, isShadow);
		if(isShadow.count ==  0) pixel = hit.color;
		else pixel = hit.color * Ia * 0.2;
	}
	gl_FragColor = vec4(pixel, 1.0);
}