diff options
| author | sotech117 <michael_foiani@brown.edu> | 2023-12-07 16:23:20 -0500 |
|---|---|---|
| committer | sotech117 <michael_foiani@brown.edu> | 2023-12-07 16:23:20 -0500 |
| commit | caa765bff49d54217b75aaf0e7acf4e5392a11e4 (patch) | |
| tree | 9b92914dfb88b99599e8e60e4512e9e9ea9a25db /src/illuminate | |
| parent | a9274459443f1d560d7580a162deb581549980cb (diff) | |
upload base code
Diffstat (limited to 'src/illuminate')
| -rw-r--r-- | src/illuminate/illuminate.cpp | 304 | ||||
| -rw-r--r-- | src/illuminate/reflect.cpp | 115 | ||||
| -rw-r--r-- | src/illuminate/shadow.cpp | 58 |
3 files changed, 477 insertions, 0 deletions
diff --git a/src/illuminate/illuminate.cpp b/src/illuminate/illuminate.cpp new file mode 100644 index 0000000..d6d43c8 --- /dev/null +++ b/src/illuminate/illuminate.cpp @@ -0,0 +1,304 @@ +#include "raytracer/raytracer.h" + +glm::vec4 RayTracer::illuminationFromPointLight( + const SceneLightData &light, + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 directionToCamera, + const RenderShapeData &shape, + const RayTraceScene &scene + ) +{ + auto directionFromIntersectionToLight = light.pos.xyz() - intersectionWorld; + directionFromIntersectionToLight = glm::normalize(directionFromIntersectionToLight); + + // check if this light is blocked by an object + auto distanceToLight = glm::distance(light.pos.xyz(), intersectionWorld); + bool isShadow = RayTracer::isShadowed( + light.pos, + distanceToLight, + glm::vec4(directionFromIntersectionToLight, 0.f), + glm::vec4(intersectionWorld, 1.f), + scene); + if (isShadow) + { + // if this is a shadow, then no light contribution + return glm::vec4(0.f); + } + + // calculate attenuation + float c1 = light.function.x; + float c2 = light.function.y; + float c3 = light.function.z; + float attenuation = std::min(1.f, 1.f / (c1 + distanceToLight * c2 + (distanceToLight * distanceToLight) * c3)); + + return phong( + light.color, + attenuation, + directionFromIntersectionToLight, + directionToCamera, + intersectionWorld, + normalWorld, + shape, + scene); +} + +glm::vec4 RayTracer::illuminationFromSpotLight( + const SceneLightData &light, + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 directionToCamera, + const RenderShapeData &shape, + const RayTraceScene &scene +) +{ + auto distance = glm::distance(light.pos.xyz(), intersectionWorld); + + // calculate the angle from the shape to the spot light + auto directionFromIntersectionToLight = glm::normalize(light.pos.xyz() - intersectionWorld); + + // calculate intensity, based on angle. apply falloff if necessary + auto lightDirection = glm::normalize(light.dir.xyz()); + // invert the direction of the intersection to light for dot product to work correctly + auto cosTheta = glm::dot(-directionFromIntersectionToLight, lightDirection); + auto theta = glm::acos(cosTheta); + + // determine intensity, based on location on spot cone + glm::vec4 intensity; + float inner = light.angle - light.penumbra; + if (theta <= inner) + { + intensity = light.color; + } + else if + ( + theta > inner + && theta <= light.angle + ) + { + // inside the penumbra, need to apply falloff + float falloff = -2 * std::pow(theta - inner, 3) / std::pow(light.penumbra, 3) + + 3 * std::pow(theta - inner, 2) / std::pow(light.penumbra, 2); + intensity = light.color * (1 - falloff); + } + else // theta > light.angle + { + return glm::vec4(0.f); + } + + // if the light is within the cone, see if it's a shadow + auto distanceToLight = glm::distance(light.pos.xyz(), intersectionWorld); + bool isShadow = RayTracer::isShadowed( + light.pos, + distanceToLight, + glm::vec4(directionFromIntersectionToLight, 0.f), + glm::vec4(intersectionWorld, 1.f), + scene); + if (isShadow) + { + // if this is a shadow, then no light contribution + return glm::vec4(0.f); + } + + // calculate attenuation + float c1 = light.function.x; + float c2 = light.function.y; + float c3 = light.function.z; + float attenuation = std::min(1.f, 1.f / (c1 + distance * c2 + (distance * distance) * c3)); + + return phong( + intensity, + attenuation, + directionFromIntersectionToLight, + directionToCamera, + intersectionWorld, + normalWorld, + shape, + scene); +} + +glm::vec4 RayTracer::illuminationFromDirectionalLight( + const SceneLightData &light, + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 directionToCamera, + const RenderShapeData &shape, + const RayTraceScene &scene +) +{ + // define direction and distance of directional light + auto directionFromIntersectionToLight = - light.dir; + directionFromIntersectionToLight = glm::normalize(directionFromIntersectionToLight); + float distanceToLight = FINF; // directional light infinitely far away + + // check if an object blocks ours + bool isShadow = RayTracer::isShadowed( + light.pos, + distanceToLight, + directionFromIntersectionToLight, + glm::vec4(intersectionWorld, 1.f), + scene); + if (isShadow) + { + // if this is a shadow, then no light contribution + return glm::vec4(0.f); + } + + float attenuation = 1.f; // directional lights don't attenuate + return phong( + light.color, + attenuation, + directionFromIntersectionToLight, + directionToCamera, + intersectionWorld, + normalWorld, + shape, + scene); +} + + + +// Calculates the RGBA of a pixel from intersection infomation and globally-defined coefficients +glm::vec4 RayTracer::illuminatePixel( + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 directionToCamera, + const RenderShapeData& shape, + const RayTraceScene &scene, + int depth) +{ + // Normalizing directions + normalWorld = glm::normalize(normalWorld); + directionToCamera = glm::normalize(directionToCamera); + + // to be summed then returned + glm::vec4 illumination(0, 0, 0, 1.f); + + // add the ambient term + float ka = scene.getGlobalData().ka; + illumination += ka*shape.primitive.material.cAmbient; + + for (const SceneLightData &light : scene.getLights()) { + switch (light.type) { + case LightType::LIGHT_POINT: + illumination += + illuminationFromPointLight(light, intersectionWorld, normalWorld, directionToCamera, shape, scene); + continue; + case LightType::LIGHT_DIRECTIONAL: + illumination += + illuminationFromDirectionalLight(light, intersectionWorld, normalWorld, directionToCamera, shape, scene); + continue; + case LightType::LIGHT_SPOT: + illumination += + illuminationFromSpotLight(light, intersectionWorld, normalWorld, directionToCamera, shape, scene); + continue; + case LightType::LIGHT_AREA: + illumination += + illuminationFromAreaLight(light, intersectionWorld, normalWorld, directionToCamera, shape, scene); + continue; + default: + continue; + } + } + + auto incidentDir = -directionToCamera; + // recursive raytracing for the reflection and refraction (see reflect.cpp) + illumination += refract(intersectionWorld, normalWorld, incidentDir, shape, scene, depth + 1); + illumination += reflect(intersectionWorld, normalWorld, incidentDir, shape, scene, depth + 1); + + return illumination; +} + +// helper function to handle the diffuse and specular terms +// also handles the texture within that diffuse term +glm::vec4 RayTracer::phong( + glm::vec4 lightColor, + float attenuation, + glm::vec3 directionFromIntersectionToLight, + glm::vec3 directionToCamera, + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + const RenderShapeData &shape, + const RayTraceScene &scene) +{ + float kd = scene.getGlobalData().kd; + float ks = scene.getGlobalData().ks; + auto material = shape.primitive.material; + + glm::vec4 illumination(0.f); + + // calculate diffuse term + auto dotDiffuse = glm::dot(normalWorld, directionFromIntersectionToLight); + if (dotDiffuse > 0) // ensure not facing away + { + auto diffuse = (kd * material.cDiffuse); + if (material.textureMap.isUsed) + { + glm::vec4 pObject = shape.inverseCTM * glm::vec4(intersectionWorld, 1.f); + diffuse = interpolateTexture(pObject, shape, diffuse); + } + illumination += (attenuation * lightColor) * dotDiffuse * diffuse; + } + + // add specular term + auto reflectedDirOverNormal = + 2 * glm::dot(directionFromIntersectionToLight, normalWorld) * normalWorld - + directionFromIntersectionToLight; + auto dotSpecular = glm::dot(reflectedDirOverNormal, directionToCamera); + auto toPow = std::pow(dotSpecular, material.shininess); + if (dotSpecular > 0) { + illumination += (attenuation * lightColor) * toPow * (ks * material.cSpecular); + } + + return illumination; +} + +// EXTRA CREDIT -> AREA LIGHT +glm::vec4 RayTracer::illuminationFromAreaLight( + const SceneLightData &light, + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 directionToCamera, + const RenderShapeData &shape, + const RayTraceScene &scene +) { + // select a random point within the light's height and width + float width = light.width; + float height = light.height; + float x = ((float) rand() / (float) RAND_MAX) * width - width / 2.f; + float y = ((float) rand() / (float) RAND_MAX) * height - height / 2.f; + glm::vec4 lightPosition = light.pos + glm::vec4(x, y, 0.f, 0.f); + + auto directionFromIntersectionToLight = lightPosition.xyz() - intersectionWorld; + directionFromIntersectionToLight = glm::normalize(directionFromIntersectionToLight); + + // check if this light is blocked by an object + auto distanceToLight = glm::distance(lightPosition.xyz(), intersectionWorld); + bool isShadow = RayTracer::isShadowed( + lightPosition, + distanceToLight, + glm::vec4(directionFromIntersectionToLight, 0.f), + glm::vec4(intersectionWorld, 1.f), + scene); + if (isShadow) + { + // if this is a shadow, then shoow a ray to a random point in the light + return glm::vec4(0.f); + } + + // calculate attenuation + float c1 = light.function.x; + float c2 = light.function.y; + float c3 = light.function.z; + float attenuation = std::min(1.f, 1.f / (c1 + distanceToLight * c2 + (distanceToLight * distanceToLight) * c3)); + + return phong( + light.color, + attenuation, + directionFromIntersectionToLight, + directionToCamera, + intersectionWorld, + normalWorld, + shape, + scene); +} diff --git a/src/illuminate/reflect.cpp b/src/illuminate/reflect.cpp new file mode 100644 index 0000000..c7fea98 --- /dev/null +++ b/src/illuminate/reflect.cpp @@ -0,0 +1,115 @@ +// +// Created by Michael Foiani on 11/4/23. +// + +#include "raytracer/raytracer.h" + +// helper that reflects vectors +glm::vec3 reflectVector( + glm::vec3 incidentDir, + glm::vec3 normal) +{ + return incidentDir - 2.f * glm::dot(incidentDir, normal) * normal; +} + +glm::vec4 RayTracer::reflect( + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 incidentDir, + const RenderShapeData &shape, + const RayTraceScene &scene, + int depth) +{ + auto material = shape.primitive.material; + // check if the material is reflective + if (material.cReflective == glm::vec4(0.f)) + { + return glm::vec4(0.f); + } + auto reflectedDir = reflectVector(incidentDir, normalWorld); + + // shoot a ray from the intersection point in the reflected direction + auto reflectColors = getPixelFromRay(glm::vec4(intersectionWorld + .001f * reflectedDir, 1.f), glm::vec4(reflectedDir, 0.f), scene, depth + 1); + return scene.getGlobalData().ks * material.cReflective * reflectColors; +} + +// EXTRA CREDIT -> refracting + +// TRUE REFRACTING +// get the reflection coefficient from fresnel's equations +bool REAL_REFRACTING = false; +float fresnels( + float currentMediumIor, + float otherMediumIor, + float cosAngleIncident, + float cosAngleTransmitted) +{ + float rPerp = (currentMediumIor * cosAngleIncident - otherMediumIor * cosAngleTransmitted) / + (currentMediumIor * cosAngleIncident + otherMediumIor * cosAngleTransmitted); + rPerp *= rPerp; + float rPara = (otherMediumIor * cosAngleIncident - currentMediumIor * cosAngleTransmitted) / + (otherMediumIor * cosAngleIncident + currentMediumIor * cosAngleTransmitted); + rPara *= rPara; + return (rPerp + rPara) / 2.f; +} + +// Your refracting +glm::vec4 RayTracer::refract( + glm::vec3 intersectionWorld, + glm::vec3 normalWorld, + glm::vec3 incidentDir, + const RenderShapeData& shape, + const RayTraceScene &scene, + int depth +) +{ + auto material = shape.primitive.material; + // check if the material is transparent + if (material.cTransparent == glm::vec4(0.f)) + { + return glm::vec4(0.f); + } + + // apply snells law to find the sin of refracted angle (squared) + incidentDir = glm::normalize(incidentDir); + float cosAngleIncident = glm::dot(incidentDir, normalWorld); + float currentMediumIor = mediumIor; + float otherMediumIor = material.ior; + + if (cosAngleIncident < 0) + { + // outside the object + cosAngleIncident = -cosAngleIncident; + } + else + { + // inside the object, invert the normal and swap the Iors + normalWorld = -normalWorld; + std::swap(currentMediumIor, otherMediumIor); + } + + float iorRatio = currentMediumIor / otherMediumIor; + float sinAngleTransmittedSquared = iorRatio * iorRatio * (1 - cosAngleIncident * cosAngleIncident); + if (sinAngleTransmittedSquared > 1.f) // total internal reflection, not considered + { + return glm::vec4(0.f); + } + + auto cosAngleTransmitted = glm::sqrt(1 - sinAngleTransmittedSquared); + + // compute refracted ray according to snell's law + auto refractedDir = glm::normalize( + incidentDir * iorRatio + + (iorRatio * cosAngleIncident - cosAngleTransmitted) * normalWorld); + + // send a ray in the refracted direction to get the colors + auto refractedColors = getPixelFromRay( + glm::vec4(intersectionWorld + .001f * refractedDir, 1.f), + glm::vec4(refractedDir, 0.f), + scene, + depth + 1); + + float fresnel = fresnels(currentMediumIor, otherMediumIor, cosAngleIncident, cosAngleTransmitted); + auto color = scene.getGlobalData().kt * material.cTransparent * refractedColors * (1 - fresnel); + return color; +}
\ No newline at end of file diff --git a/src/illuminate/shadow.cpp b/src/illuminate/shadow.cpp new file mode 100644 index 0000000..99e2b29 --- /dev/null +++ b/src/illuminate/shadow.cpp @@ -0,0 +1,58 @@ +#include "raytracer/raytracer.h" + +bool RayTracer::isShadowed( + glm::vec4 lightPosition, + float distanceToLight, + glm::vec4 directionFromIntersectionToLight, + glm::vec4 intersectionWorld, + const RayTraceScene &scene) +{ + // normalize direction + directionFromIntersectionToLight = glm::normalize(directionFromIntersectionToLight); + + // acceleration causes "bad jaggies" so we disable it for now + if (m_config.enableAcceleration) + { + RenderShapeData shapeData; + auto pBias = intersectionWorld + .001f * directionFromIntersectionToLight; + float t = traverseBVH(pBias, directionFromIntersectionToLight, shapeData, scene.m_bvh); + return t != FINF; + } + + for (const RenderShapeData &s: scene.getShapes()) { + // convert this world ray to object space + glm::vec4 dObject = glm::normalize( + s.inverseCTM * directionFromIntersectionToLight); + glm::vec4 pObject = s.inverseCTM * intersectionWorld; + + // see if there is an intersection + glm::vec4 newIntersectionObj = findIntersection(pObject, dObject, s); + + if (newIntersectionObj.w == 1.f) // hit! + { + // check if the intersection is the same as the pObject + if (floatEquals(glm::distance(newIntersectionObj, pObject), 0.f, 0.001f)) + { + // don't consider self-intersections + continue; + } + + // check if this intersection is closer than the direction to the light + auto newIntersectionWorld = s.ctm * newIntersectionObj; + if (distanceToLight == FINF) + { + // if the light is infinitely far away light, then any non-self intersection is valid + return true; + } + + float newDist = glm::distance(newIntersectionWorld, lightPosition); + if (newDist < distanceToLight - 0.001f) + { + // an object in front of the camera is the way -> shadow + return true; + } + } + } + + return false; +}
\ No newline at end of file |
