#include class light{ public: //need to store which type of light this is, point, directional, area, etc. char type; //p, a, d (area is a point light with area) vec3 point; vec3 direction; double area; vec3 color; vec3 attenuation; light(char t, vec3 position, double a, vec3 c, vec3 atten) { //set white as the default color for the light color = c; //set default attenuation to constant attenuation = atten; type = t; if(type == 'p') point = position; if(type == 'd') { direction = position; direction.normalize(); } if(type == 'a') { point = position; area = a; } } vec3 findDirectionToPoint(vec3 position) { vec3 dir = vec3(point.x - position.x, point.y - position.y, point.z - position.z); dir.normalize(); return dir; } vec3 findDiffuseComponent(intersectionTest test) { //need to check on the types of light, if we have a directional light then that makes things easy, if point or area then compute the point direction if(type == 'd') { double diffuse = test.normalDirection.dotProduct(direction); diffuse = diffuse > 0 ? diffuse : 0; return vec3(test.diffuseColor.x*diffuse, test.diffuseColor.y*diffuse, test.diffuseColor.z*diffuse); } if(type == 'p' || type == 'a') { double diffuse = test.normalDirection.dotProduct(findDirectionToPoint(test.intersectionPosition)); diffuse = diffuse > 0 ? diffuse : 0; return vec3(test.diffuseColor.x*diffuse, test.diffuseColor.y*diffuse, test.diffuseColor.z*diffuse); } } vec3 findSpecularComponent(vec3 eyePoint, intersectionTest test) { //need to check on the types of light, if we have a directional light then that makes things easy, if point or area then compute the point direction if(type == 'd') { direction.normalize(); vec3 viewVec = vec3(eyePoint.x - test.intersectionPosition.x, eyePoint.y - test.intersectionPosition.y, eyePoint.z - test.intersectionPosition.z); viewVec.normalize(); vec3 halfAngle = vec3((viewVec.x + direction.x)/2.0, (viewVec.y + direction.y)/2.0, (viewVec.z + direction.z)/2.0 ); halfAngle.normalize(); double specularity = pow((double)(test.normalDirection.dotProduct(halfAngle)), test.shininess); specularity = specularity > 0 ? specularity : 0; return vec3(test.specularColor.x*specularity, test.specularColor.y*specularity, test.specularColor.z*specularity); } if(type == 'p' || type == 'a') { vec3 dir = findDirectionToPoint(test.intersectionPosition); dir.normalize(); vec3 viewVec = vec3(eyePoint.x - test.intersectionPosition.x, eyePoint.y - test.intersectionPosition.y, eyePoint.z - test.intersectionPosition.z); viewVec.normalize(); vec3 halfAngle = vec3((viewVec.x + dir.x)/2.0, (viewVec.y + dir.y)/2.0, (viewVec.z + dir.z)/2.0 ); halfAngle.normalize(); double specularity = pow((double)(test.normalDirection.dotProduct(halfAngle)), test.shininess); specularity = specularity > 0 ? specularity : 0; return vec3(test.specularColor.x*specularity, test.specularColor.y*specularity, test.specularColor.z*specularity); } } //for a given point, do the shadow test depending on the type of light we have and return the light intensity (between 0 and 1) double findShadow(vec3 position, Scene scene) { //start with easy cases, if we have a directional light then just test the direction with the scene if(type == 'd') { //no attenuation with directional lights (constant attenuation) intersectionTest shadowTest = scene.testScene(position, direction); if(shadowTest.foundPoint == true)//in shadow return shadowTest.translucency; else //otherwise return full light return 1; } //now do second easiest case, in which we have a point light, basically treat it the same as directional but you compute the direction and... attenuation? if(type == 'p') { vec3 dir = vec3(point.x - position.x, point.y - position.y, point.z - position.z); //find attenuation based on the distance of the light point to the surface double distance = sqrt(dir.x*dir.x + dir.y*dir.y + dir.z*dir.z); double atten = 1.0/(attenuation.x + attenuation.y*distance + attenuation.z*distance*distance); atten = min(atten, 1.0); dir.normalize(); intersectionTest shadowTest = scene.testScene(position, dir); if(shadowTest.foundPoint == true)//in shadow return shadowTest.translucency*atten; else //otherwise return full light return 1.0*atten; } //now the difficult case, if we have an area point light source the we need to find the amount of light that is hitting that point if(type == 'a') { //find main direction, using this we'll find the orthogonal plane and thus be able to test a spherical area vec3 dir = vec3(point.x - position.x, point.y - position.y, point.z - position.z); //find attenuation based on the distance of the center of the light source to the surface double distance = sqrt(dir.x*dir.x + dir.y*dir.y + dir.z*dir.z); double atten = 1.0/(attenuation.x + attenuation.y*distance + attenuation.z*distance*distance); atten = min(atten, 1.0); dir.normalize(); //use up direction to find plane, hopefully we won't have any numerical errors with the cross product (avoid having the light directly overhead, otherwise have to test another axis too) vec3 tangent = dir.crossProduct(vec3(1, 0, 0)); tangent.normalize(); vec3 binormal = dir.crossProduct(tangent); binormal.normalize(); //now starting from the center point, make spirals of light points double lightAccumulator = 0; int lightSamples = 50; for(int i = 0; i < lightSamples; i++) { double radius = area/(double)lightSamples; //vec3 tempPoint = vec3( point.x + cos((float)i/(float)5)*tangent.x*radius, // point.y + cos((float)i/(float)5)*tangent.y*radius, // point.z + cos((float)i/(float)5)*tangent.z*radius ); /*vec3 tempPoint = vec3( point.x + sin((float)i/(float)5)*binormal.x*radius, point.y + sin((float)i/(float)5)*binormal.y*radius, point.z + sin((float)i/(float)5)*binormal.z*radius );*/ vec3 tempPoint = vec3( point.x + sin((float)i/(float)10)*tangent.x*radius + cos((float)i/(float)10)*binormal.x*radius, point.y + sin((float)i/(float)10)*tangent.y*radius + cos((float)i/(float)10)*binormal.y*radius, point.z + sin((float)i/(float)10)*tangent.z*radius + cos((float)i/(float)10)*binormal.z*radius ); //for each new point we've calculated start accumulating which ones contribute light dir = vec3(tempPoint.x - position.x, tempPoint.y - position.y, tempPoint.z - position.z); dir.normalize(); intersectionTest shadowTest = scene.testScene(position, dir); if(shadowTest.foundPoint == false)//let there be light! lightAccumulator ++; else lightAccumulator += shadowTest.translucency; } //normalize the light accumulator and return return ((double)lightAccumulator/(double)lightSamples)*atten; } } };