Last active
January 24, 2020 17:07
-
-
Save bwedding/749603db22453ef232738a7686e573d2 to your computer and use it in GitHub Desktop.
Raytrace a sphere
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Raytrace.cpp : This file contains the 'main' function. Program execution begins and ends there. | |
// | |
// Viewer here: https://www.kylepaulsen.com/stuff/NetpbmViewer/ | |
#include <iostream> | |
#include <fstream> | |
#include <vector> | |
struct vec3 | |
{ | |
float x; | |
float y; | |
float z; | |
vec3 operator+(const vec3& other) const | |
{ | |
return { x + other.x, y + other.y, z + other.z }; | |
} | |
vec3 operator-(const vec3& other) const | |
{ | |
return { x - other.x, y - other.y, z - other.z }; | |
} | |
vec3 operator-() const | |
{ | |
return { -x, -y, -z }; | |
} | |
vec3 operator*(const float& scalar) const | |
{ | |
return { x * scalar, y * scalar, z * scalar }; | |
} | |
vec3 operator/(const float& scalar) const | |
{ | |
return { x / scalar, y / scalar, z / scalar }; | |
} | |
float dot(const vec3& other) const | |
{ | |
return x * other.x + y * other.y + z * other.z; | |
} | |
float len() const | |
{ | |
return sqrtf(this->dot(*this)); | |
} | |
void normalize() | |
{ | |
float len = this->len(); | |
x /= len; | |
y /= len; | |
z /= len; | |
} | |
}; | |
float intersectSphere(vec3 origin, vec3 dir, vec3 center, float radius) | |
{ | |
vec3 oc = origin - center; | |
float a = dir.dot(dir); | |
float b = 2.0f * oc.dot(dir); | |
float c = oc.dot(oc) - radius * radius; | |
float discriminant = b * b - 4 * a * c; | |
if (discriminant < 0.0f) | |
{ | |
return -1.0f; | |
} | |
return (-b - sqrtf(discriminant)) / (2.0f * a); | |
} | |
void FillColors(std::vector<std::string>& colors) | |
{ | |
for (int i = 0; i < 256; i++) | |
{ | |
char buffer[64]; | |
sprintf_s(buffer, sizeof(buffer), "%d ", i); | |
colors.push_back(buffer); | |
} | |
}; | |
int main() | |
{ | |
std::ofstream outfile; | |
outfile.open("c:/temp/raytrace.txt"); | |
// Write the header | |
// P2 is grayscale next is image size in pixels, finally 63 represents brightest | |
// color (white) | |
outfile << "P2" << std::endl << "1000 1000" << std::endl << "255" << std::endl; | |
// Image width and height. | |
const int width = 1000; | |
const int height = 1000; | |
// For square pixels, e.g. PPM file, set to 1.0f | |
const float pixelAspect = 1.0f; | |
// Sphere position and radius. | |
// Adjust the 3rd parameter to move the camera back. Too close | |
// and you will have a black hole in the sphere. If you have a black | |
// hole, just increase the value. It should be close, just under, the radius | |
const vec3 center{ 0.0f, 0.0f, 360.0f }; | |
// Adjust this to make the spere bigger | |
const float radius = 382.0f; | |
// Light shines from top left | |
vec3 light{ 1.0f, 1.0f, 2.0f }; | |
light.normalize(); | |
// Darkest to lightest | |
std::vector<std::string> colors; | |
FillColors(colors); | |
const size_t numColors = colors.size(); | |
// Ray direction - all parallel, down Z axis. | |
const vec3 dir{ 0.0f, 0.0f, 1.0f }; | |
// For each pixel... | |
for (int y = 0; y < height; y++) | |
{ | |
for (int x = 0; x < width; x++) | |
{ | |
float xWorld = x - (width / 2) + 0.5f; | |
float yWorld = (y - (height / 2) + 0.5f) * pixelAspect; | |
vec3 origin = vec3{ xWorld, yWorld, -30.0f }; | |
float t = intersectSphere(origin, dir, center, radius); | |
if (t > 0.0f) | |
{ | |
// Ray hit. | |
vec3 intersection = origin + (dir * t); | |
vec3 normal = intersection - center; | |
normal.normalize(); | |
float directional = normal.dot(-light) * 0.95f; | |
if (directional < 0.0f) | |
{ | |
directional = 0.0f; | |
} | |
float ambient = 0.05f; | |
float lum = directional + ambient; | |
if (lum < 0) | |
{ | |
// Give some brightness even if pointing | |
// completely away from the light. | |
// With 64 shades, 1 isn't enough | |
outfile << colors[3]; | |
} | |
else | |
{ | |
// Pick character based on light value. | |
outfile << colors[(int)(numColors * lum)]; | |
} | |
} | |
else | |
{ | |
// Ray miss. Make it solid black | |
outfile << colors[0]; | |
} | |
} | |
outfile << std::endl; | |
} | |
outfile.close(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment