Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <cstdlib>
- #include <cstdio>
- #include <cmath>
- #include <fstream>
- #include <vector>
- #include <iostream>
- #include <cassert>
- #include <random>
- #include <algorithm>
- #include <Eigen>
- using namespace Eigen;
- // image background color
- Vector3f bgcolor(1.0f, 1.0f, 1.0f);
- // lights in the scene
- std::vector<Vector3f> lightPositions = { Vector3f( 0.0, 60, 60)
- , Vector3f(-60.0, 60, 60)
- , Vector3f( 60.0, 60, 60) };
- class Sphere
- {
- public:
- Vector3f center; // position of the sphere
- float radius; // sphere radius
- Vector3f surfaceColor; // surface color
- Sphere(
- const Vector3f &c,
- const float &r,
- const Vector3f &sc) :
- center(c), radius(r), surfaceColor(sc)
- {
- }
- // line vs. sphere intersection (note: this is slightly different from ray vs. sphere intersection!)
- bool intersect(const Vector3f &rayOrigin, const Vector3f &rayDirection, float &t0, float &t1) const
- {
- Vector3f l = center - rayOrigin;
- float tca = l.dot(rayDirection);
- if (tca < 0) return false;
- float d2 = l.dot(l) - tca * tca;
- if (d2 > (radius * radius)) return false;
- float thc = sqrt(radius * radius - d2);
- t0 = tca - thc;
- t1 = tca + thc;
- return true;
- }
- };
- // diffuse reflection model
- Vector3f diffuse(const Vector3f &L, // direction vector from the point on the surface towards a light source
- const Vector3f &N, // normal at this point on the surface
- const Vector3f &diffuseColor,
- const float kd // diffuse reflection constant
- )
- {
- Vector3f resColor = Vector3f::Zero();
- float lightIntensity = 1.0f / (float)lightPositions.size();
- resColor = diffuseColor * lightIntensity * kd * std::max(L.dot(N), 0.0f);
- return resColor;
- }
- // Phong reflection model
- Vector3f phong(const Vector3f &L, // direction vector from the point on the surface towards a light source
- const Vector3f &N, // normal at this point on the surface
- const Vector3f &V, // direction pointing towards the viewer
- const Vector3f &diffuseColor,
- const Vector3f &specularColor,
- const float kd, // diffuse reflection constant
- const float ks, // specular reflection constant
- const float alpha) // shininess constant
- {
- Vector3f resColor = Vector3f::Zero();
- Vector3f specularComponent = Vector3f::Zero();
- float lightIntensity = 1.0f / (float)lightPositions.size();
- Vector3f reflectionVector = 2 * N * N.dot(L) - L;
- specularComponent = specularColor * lightIntensity * ks * std::pow(std::max(reflectionVector.dot(V), 0.0f), alpha);
- resColor = diffuse(L, N, diffuseColor, kd) + specularComponent;
- return resColor;
- }
- Vector3f trace(
- const Vector3f &rayOrigin,
- const Vector3f &rayDirection,
- const std::vector<Sphere> &spheres)
- {
- Vector3f pixelColor = Vector3f::Zero();
- Vector3f A, R;
- float closestDist = INFINITY;
- float t0, t1;
- pixelColor = bgcolor;
- for(int sphereIndex = 0; sphereIndex < spheres.size(); sphereIndex++){
- if(spheres[sphereIndex].intersect(rayOrigin, rayDirection, t0, t1))
- {
- // We check if we're closer than the last sphere painted here
- // We check t0 and t1 since we have no knowledge of which is closer
- float visibleDist = t0 < t1 ? t0 : t1;
- if(visibleDist < closestDist){
- closestDist = visibleDist;
- Vector3f closestPoint = rayOrigin + rayDirection*visibleDist;
- // Calculate lighting
- pixelColor = Vector3f(0.0f, 0.0f, 0.0f);
- // Iterate across all lights
- for(int lightIndex = 0; lightIndex < lightPositions.size(); lightIndex++){
- // Calculate the direction of the light ray
- Vector3f lightRayDir = lightPositions[lightIndex] - closestPoint;
- lightRayDir.normalize();
- bool didCollide = false;
- // Iterate over all spheres and check if the ray from the visible point to the line intercepts with that sphere
- for(int otherSphereIndex = 0; otherSphereIndex < spheres.size(); otherSphereIndex++){
- if(spheres[otherSphereIndex].intersect(closestPoint, lightRayDir, t0, t1)){
- didCollide = true;
- }
- }
- // If there is no interception, add the color value for this light
- if(!didCollide){
- Vector3f sphereNormal = closestPoint - spheres[sphereIndex].center;
- sphereNormal.normalize();
- // pixelColor += diffuse(lightRayDir, sphereNormal, spheres[sphereIndex].surfaceColor, 1.0f);
- pixelColor += phong(lightRayDir, sphereNormal, -rayDirection, spheres[sphereIndex].surfaceColor, Vector3f::Ones(), 1.0f, 3.0f, 100.0f);
- }
- }
- }
- }
- }
- return pixelColor;
- }
- void render(const std::vector<Sphere> &spheres)
- {
- unsigned width = 640;
- unsigned height = 480;
- Vector3f *image = new Vector3f[width * height];
- Vector3f *pixel = image;
- float invWidth = 1 / float(width);
- float invHeight = 1 / float(height);
- float fov = 30;
- float aspectratio = width / float(height);
- float angle = tan(M_PI * 0.5f * fov / 180.f);
- // Trace rays
- for (unsigned y = 0; y < height; ++y)
- {
- for (unsigned x = 0; x < width; ++x)
- {
- float rayX = (2 * ((x + 0.5f) * invWidth) - 1) * angle * aspectratio;
- float rayY = (1 - 2 * ((y + 0.5f) * invHeight)) * angle;
- Vector3f rayDirection(rayX, rayY, -1);
- rayDirection.normalize();
- *(pixel++) = trace(Vector3f::Zero(), rayDirection, spheres);
- }
- }
- // Save result to a PPM image
- std::ofstream ofs("./render.ppm", std::ios::out | std::ios::binary);
- ofs << "P6\n" << width << " " << height << "\n255\n";
- for (unsigned i = 0; i < width * height; ++i)
- {
- const float x = image[i](0);
- const float y = image[i](1);
- const float z = image[i](2);
- ofs << (unsigned char)(std::min(float(1), x) * 255)
- << (unsigned char)(std::min(float(1), y) * 255)
- << (unsigned char)(std::min(float(1), z) * 255);
- }
- ofs.close();
- delete[] image;
- }
- int main(int argc, char **argv)
- {
- std::vector<Sphere> spheres;
- // position, radius, surface color
- spheres.push_back(Sphere(Vector3f(0.0, -10004, -20), 10000, Vector3f(0.50, 0.50, 0.50)));
- spheres.push_back(Sphere(Vector3f(0.0, 0, -20), 4, Vector3f(1.00, 0.32, 0.36)));
- spheres.push_back(Sphere(Vector3f(5.0, -1, -15), 2, Vector3f(0.90, 0.76, 0.46)));
- spheres.push_back(Sphere(Vector3f(5.0, 0, -25), 3, Vector3f(0.65, 0.77, 0.97)));
- spheres.push_back(Sphere(Vector3f(-5.5, 0, -13), 3, Vector3f(0.90, 0.90, 0.90)));
- render(spheres);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment