0ebf47e0 |
/// Includes
#include <array>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#define GLM_FORCE_SWIZZLE
#include <glm/glm.hpp>
#include <glm/fwd.hpp>
#include <glm/ext/matrix_transform.hpp> // https://glm.g-truc.net/0.9.9/api/a00247.html
#include <glm/ext/matrix_clip_space.hpp> // https://glm.g-truc.net/0.9.9/api/a00243.html
#include <glm/ext/matrix_projection.hpp> // https://glm.g-truc.net/0.9.9/api/a00245.html
#include <glm/gtc/color_space.hpp> // https://glm.g-truc.net/0.9.9/api/a00289.html
// #include <glm/gtx/io.hpp> // https://glm.g-truc.net/0.9.9/api/a00332.html
/// Namespaces
using namespace glm;
/// Constants
auto const infinity = std::numeric_limits<float>::infinity();
|
cf699a09 |
auto const delta = 0.001F;
|
0ebf47e0 |
/// Types
//// Ray
struct Ray
{
vec3 origin;
vec3 direction;
|
cf699a09 |
vec3 point(float distance) const
{
return origin + direction * distance;
}
Ray resumed(float distance)
{
return {point(distance), direction};
}
|
0ebf47e0 |
};
|
5d4183a9 |
//// Material
struct Material
{
|
cf699a09 |
vec4 color;
|
5d4183a9 |
};
|
0ebf47e0 |
//// Trace
struct Trace
{
|
5d4183a9 |
float distance;
Material material;
|
178b953d |
vec3 miss;
|
0ebf47e0 |
};
//// Circle
struct Circle
{
|
5d4183a9 |
vec3 center;
float radius;
Material material;
|
0ebf47e0 |
Trace trace(Ray const & ray) const
{
auto const offs = center - ray.origin;
auto const proj = dot(offs, ray.direction);
if (proj < 0.0F) // Past.
|
178b953d |
return {infinity, {}, {}};
|
0ebf47e0 |
auto const perp2 = dot(offs, offs) - proj * proj;
auto const pene2 = radius * radius - perp2;
if (pene2 < 0.0F) // Miss.
|
178b953d |
{
auto const poin = ray.point(proj);
auto const rati = 1.0F - radius * inversesqrt(perp2);
auto const miss = rati * (center - poin);
return {proj, material, miss};
}
|
0ebf47e0 |
auto const dist = proj - sqrt(pene2);
if (dist < 0.0F) // Inside.
|
178b953d |
return {infinity, {}, {}};
return {dist, material, vec3(0.0F)};
|
0ebf47e0 |
}
};
//// Shapes
using Shapes = std::vector<Circle>;
//// Scene
struct Scene
{
|
5d4183a9 |
Material background;
Shapes shapes;
|
0ebf47e0 |
Trace trace(Ray const & ray) const
{
|
178b953d |
auto nearest = Trace{infinity, background, {}};
|
0ebf47e0 |
for (auto const & shape : shapes)
{
auto const trace = shape.trace(ray);
if (trace.distance < nearest.distance)
nearest = trace;
}
return nearest;
}
};
//// Camera
struct Camera
{
vec3 position;
vec3 target;
float fovy;
float near;
float far;
Ray ray(uvec2 const & size, uvec2 const & pixel) const
{
auto const aspect = (float)size.x / (float)size.y;
auto const viewport = uvec4(0, 0, size);
auto const window = vec3(vec2(pixel) + 0.5F, 0.0F);
auto const up = vec3(0.0F, 1.0F, 0.0F);
auto const view = glm::lookAt(position, target, up);
auto const proj = glm::perspective(fovy, aspect, near, far);
auto const world = glm::unProject(window, view, proj, viewport);
auto const direction = normalize(world.xyz() - position);
return Ray{position, direction};
}
|
178b953d |
vec2 project(uvec2 const & size, vec3 const & point) const
{
auto const aspect = (float)size.x / (float)size.y;
auto const viewport = uvec4(0, 0, size);
auto const up = vec3(0.0F, 1.0F, 0.0F);
auto const view = glm::lookAt(position, target, up);
auto const proj = glm::perspective(fovy, aspect, near, far);
auto const window = glm::project(point, view, proj, viewport);
return window.xy();
}
|
0ebf47e0 |
};
//// ACES
struct ACES
{
vec3 whitepoint;
vec3 transform(vec3 color) const
{
color /= whitepoint;
return
(color * (2.51F * color + 0.03F)) /
(color * (2.43F * color + 0.59F) + 0.14F);
}
};
//// TGA
struct TGA
{
uvec2 size;
std::string path;
template<typename ColorFunc>
void render(ColorFunc const & color_func) const
{
auto frame_size = size.x * size.y;
auto frame = std::vector<u8vec4>(frame_size);
auto header = std::array<u8, 18>{
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(u8)(size.x >> 0U),
(u8)(size.x >> 8U),
(u8)(size.y >> 0U),
(u8)(size.y >> 8U),
32, 0,
};
for (auto y = 0U; y < size.y; ++y)
{
for (auto x = 0U; x < size.x; ++x)
{
auto const color = color_func(size, uvec2(x, y));
auto const index = y * size.x + x;
frame[index] = clamp(color.bgra(), 0.0F, 1.0F) * 255.0F + 0.5F;
}
}
auto ostream = std::ofstream(path, std::ios::binary);
write(ostream, header);
write(ostream, frame);
}
template<typename Collection>
void write(std::ostream & ostream, Collection const & collection) const
{
ostream.write(
(char const *)collection.data(),
(std::streamsize)(sizeof(*collection.data()) * collection.size())
);
}
};
/// Main
int main(int argc, char const * argv[])
{
//// Arguments
if (argc != 2)
{
std::cerr << "Usage: raytrace <path>" << std::endl;
return 0;
}
auto const * path = argv[1]; // NOLINT
//// Configure
auto const camera = Camera{
vec3(0.0F, 0.0F, 0.0F), // position
vec3(0.0F, 0.0F, -1.0F), // target
radians(45.0F), // fovy
0.01F, // near
100.0F, // far
};
auto const scene = Scene{
|
5d4183a9 |
// background
|
cf699a09 |
{vec4(0.2F, 0.2F, 0.5F, 0.5F)},
|
0ebf47e0 |
// shapes
{
|
cf699a09 |
{vec3( 2.0F, -1.0F, -10.0F), 3.0F, {vec4(1.0F, 0.0F, 0.0F, 1.0F)}},
{vec3( 1.0F, 2.0F, -7.0F), 2.0F, {vec4(0.0F, 1.0F, 0.0F, 1.0F)}},
{vec3(-1.0F, 0.0F, -4.0F), 1.0F, {vec4(0.0F, 0.0F, 1.0F, 1.0F)}},
{vec3( 0.0F, -1.0F, -3.0F), 0.2F, {vec4(0.0F, 1.0F, 1.0F, 0.5F)}},
|
0ebf47e0 |
},
};
auto const tonemap = ACES{vec3(1.0F)};
auto const output = TGA{uvec2(640, 480), path};
//// Render
output.render([&](uvec2 const & size, uvec2 const & pixel) {
|
cf699a09 |
auto rgb = vec3(0.0F); // Pre-multiplied alpha (opacity).
auto trans = 1.0F; // 1 - alpha (opacity).
auto ray = camera.ray(size, pixel);
while (true)
{
auto trace = scene.trace(ray);
auto color = trace.material.color;
|
178b953d |
if (trace.miss != vec3(0.0F))
{
auto const point = ray.point(trace.distance);
auto const dist = distance(
camera.project(size, point),
camera.project(size, point + trace.miss)
);
color.a *= 1.0F - clamp(dist, 0.0F, 1.0F);
}
|
cf699a09 |
rgb += color.rgb() * color.a * trans;
trans *= 1.0F - color.a;
if (trace.distance == infinity || trans == 0.0F)
break;
ray = ray.resumed(trace.distance + delta);
}
auto const alpha = 1.0F - trans;
auto const srgb = convertLinearToSRGB(tonemap.transform(rgb / alpha));
|
0ebf47e0 |
return vec4(srgb, alpha);
});
}
|