Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include "EngineMath.h"
- #include "Defines.h"
- #include <vector>
- struct Sphere {
- TVECTOR color = { 0.0f, 0.0f, 0.0f, 0.0f };
- TVECTOR center = { 0.0f, 0.0f, 0.0f, 1.0f };
- float radius = 0.0f;
- // material
- float specular_exponent = 0.0f;
- float reflective = 0.0f;
- };
- enum class ELightType : uint8_t {
- Point,
- Spot,
- Directional,
- Ambient
- };
- struct Light {
- TVECTOR postion = { 0.0f, 0.0f, 0.0f, 1.0f };
- TVECTOR direction = { 1.0f, 1.0f, 1.0f, 0.0f };
- TVECTOR color = { 1.0f, 1.0f, 1.0f, 1.0f };
- float intensity = 1.0f;
- ELightType type = ELightType::Point;
- };
- struct Scene {
- std::vector<Sphere> spheres;
- std::vector<Light> lights;
- } scene;
- TVECTOR canvas_to_viewport(int x, int y)
- {
- TVECTOR v;
- v.x = x * 1.77777f / static_cast<float>(canvas_width);
- v.y = y * viewport_height / static_cast<float>(canvas_height);
- v.z = static_cast<float>(projection_plane_z);
- v.w = 0.0f;
- return v;
- }
- // Determines if a ray from the origin location intersects the given sphere using the quadratic formula
- bool ray_intersect_sphere(const TVECTOR origin, const TVECTOR ray_direction, const Sphere& sphere, float& t1, float& t2)
- {
- float r = sphere.radius;
- TVECTOR CO_v = Vector_Sub(origin, sphere.center);
- // Quadratic formula
- float a = Vector_Dot(ray_direction, ray_direction);
- float b = 2.0f * Vector_Dot(CO_v, ray_direction);
- float c = Vector_Dot(CO_v, CO_v) - r * r;
- float discriminant = b * b - 4 * a * c;
- if (discriminant < 0.0f) // no intersection/no solution for t
- {
- t1 = t2 = INFINITY;
- return false;
- }
- t1 = (-b + sqrt(discriminant)) / (2.0f * a);
- t2 = (-b - sqrt(discriminant)) / (2.0f * a);
- return true;
- }
- // returns the closest intersection on the given ray and stores the closest sphere intersected by the ray
- float closest_intersection(const TVECTOR ray_origin, const TVECTOR ray_direction, const float near_plane, const float far_plane, Sphere& closest_sphere)
- {
- float t1, t2, closest_t = far_plane;
- for (const Sphere& s : scene.spheres)
- {
- if (ray_intersect_sphere(ray_origin, ray_direction, s, t1, t2))
- {
- if (near_plane < t1 && t1 < far_plane && t1 < closest_t)
- {
- closest_t = t1;
- closest_sphere = s;
- }
- if (near_plane < t2 && t2 < far_plane && t2 < closest_t)
- {
- closest_t = t2;
- closest_sphere = s;
- }
- }
- }
- return closest_t;
- }
- float compute_lighting(TVECTOR point, TVECTOR normal, TVECTOR eye, float specular)
- {
- float illumination = 0.0f;
- for (const Light& light : scene.lights)
- {
- if (light.type == ELightType::Ambient)
- {
- illumination += light.intensity;
- }
- else
- {
- TVECTOR light_v = { 0.0f, 0.0f, 0.0f, 0.0f };
- float t_max = 0.0f;
- if (light.type == ELightType::Point)
- {
- light_v = Vector_Sub(light.postion, point);
- t_max = 1.0f;
- }
- else // if directional light
- {
- light_v = light.direction;
- t_max = INFINITY;
- }
- //Shadow check
- Sphere shadow_sphere;
- float shadow_t = closest_intersection(point, light_v, 0.001f, t_max, shadow_sphere);
- if (shadow_t != t_max) // if there was an intersection/there is something blocking the light
- continue;
- // Diffuse Reflection
- float n_dot_l = Vector_Dot(normal, light_v);
- if (n_dot_l > 0.0f)
- illumination += light.intensity * n_dot_l / (Vector_Length(normal) * Vector_Length(light_v));
- //Specular Reflection
- if (specular != -1.0f)
- {
- TVECTOR reflection_v = Vector_Reflect(light_v, normal);
- float r_dot_v = Vector_Dot(reflection_v, eye);
- if (r_dot_v > 0.0f)
- illumination += light.intensity * powf(r_dot_v / (Vector_Length(reflection_v) * Vector_Length(eye)), specular);
- }
- }
- }
- return illumination;
- }
- // Computes the intersection of the ray with every sphere
- // and returns the color of the sphere at the nearest intersection inside the requested range of t
- TVECTOR trace_ray(const TVECTOR ray_origin, const TVECTOR ray_direction, const float near_plane, const float far_plane, const int recursion_depth = 0)
- {
- Sphere closest_sphere;
- float closest_t = closest_intersection(ray_origin, ray_direction, near_plane, far_plane, closest_sphere);
- if (closest_t == far_plane) // if there was no sphere intersection
- return BACKGROUND_COLOR;
- TVECTOR point = Vector_Add(ray_origin, Vector_Scalar_Multiply(ray_direction, closest_t));// Compute Intersection (O + t*D)
- TVECTOR normal = Vector_Normalize(Vector_Sub(point, closest_sphere.center)); // Compute Sphere Normal
- TVECTOR eye = Vector_Negate(ray_direction);
- float lighting = saturate(compute_lighting(point, normal, eye, closest_sphere.specular_exponent));
- TVECTOR local_color = Vector_Scalar_Multiply(closest_sphere.color, lighting);
- // If we hit the recursion limit or the object is not reflective, we’ re done
- float reflective = closest_sphere.reflective;
- if (recursion_depth <= 0 || reflective <= 0.0f)
- return /*Vector_Saturate*/(local_color);
- // Compute the reflected color
- TVECTOR reflection_v = Vector_Reflect(eye, normal);
- TVECTOR reflected_color = trace_ray(point, reflection_v, 0.001f, INFINITY, recursion_depth - 1);
- TVECTOR out_color = Vector_Add(
- Vector_Scalar_Multiply(local_color, (1.0f - reflective)),
- Vector_Scalar_Multiply(reflected_color, reflective));
- return Vector_Saturate(out_color);
- }
- int main()
- {
- TVECTOR cam_pos = { 0.0f, 0.0f, 0.0f, 1.0f };
- const int32_t canvas_size = canvas_width * canvas_height;
- std::vector<Pixel> pixels(canvas_size + 1);
- // define the scene
- {
- Sphere s1, s2, s3, s4;
- // red sphere
- s1.color = { 1.0f, 0.0f, 0.0f, 1.0f };
- s1.center = { 0.0f, -1.0f, 3.0f, 1.0f };
- s1.radius = 1.0f;
- s1.specular_exponent = 500.0f; // shiny
- s1.reflective = 0.2f;
- // green sphere
- s2.color = { 0.0f, 1.0f, 0.0f, 1.0f };
- s2.center = { -2.0f, 0.0f, 4.0f, 1.0f};
- s2.radius = 1.0f;
- s2.specular_exponent = 10.0f; // not so shiny
- s2.reflective = 0.4f;
- // blue sphere
- s3.color = { 0.0f, 0.0f, 1.0f, 1.0f };
- s3.center = { 2.0f, 0.0f, 4.0f, 1.0f};
- s3.radius = 1.0f;
- s3.specular_exponent = 500.0f; // shiny
- s3.reflective = 0.3f;
- // yellow sphere
- s4.color = { 1.0f, 1.0f, 0.0f, 1.0f };
- s4.center = { 0.0f, -5001.0f, 0.0f, 1.0f };
- s4.radius = 5000.0f;
- s4.specular_exponent = 1000.0f; // very shiny
- s4.reflective = 0.5f;
- scene.spheres.push_back(s1);
- scene.spheres.push_back(s2);
- scene.spheres.push_back(s3);
- scene.spheres.push_back(s4);
- Light amb_l, point_l, dir_l;
- // Ambient light
- amb_l.type = ELightType::Ambient;
- amb_l.intensity = 0.2f;
- // Point light
- point_l.type = ELightType::Point;
- point_l.intensity = 0.6f;
- point_l.postion = { 1.0f, 0.0f, 1.0f, 1.0f };
- // Directional light
- dir_l.type = ELightType::Directional;
- dir_l.intensity = 0.2f;
- dir_l.direction = { 1.0f, 4.0f, 4.0f, 0.0f };
- scene.lights.push_back(amb_l);
- scene.lights.push_back(point_l);
- scene.lights.push_back(dir_l);
- }
- int width = canvas_width >> 1;
- int height = canvas_height >> 1;
- // for every pixel on the canvas
- for (int y = -height; y < height; ++y)
- {
- for (int x = -width; x < width; ++x)
- {
- // convert 2d coordinate to 3d space
- TVECTOR ray = canvas_to_viewport(x, y);
- TVECTOR color = trace_ray(cam_pos, ray, 1.0f, INFINITY, 3);
- int p_index = (y + height) * canvas_width + (x + width);
- pixels[p_index].red = static_cast<uint8_t>(color.x * 255);
- pixels[p_index].green = static_cast<uint8_t>(color.y * 255);
- pixels[p_index].blue = static_cast<uint8_t>(color.z * 255);
- }
- }
- // PutPixel
- std::ofstream file("RayTrace.bmp", std::ios::trunc | std::ios::out);
- assert(file.is_open());
- if (file.is_open())
- {
- bmpHeader.write_to_file(file);
- bmpInfoHeader.write_to_file(file);
- for (size_t i = 0; i < canvas_size; ++i)
- {
- pixels[i].write_to_file(file);
- }
- }
- file.close();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement