1
Fork 0

Add indirect sampling, and update README

This commit is contained in:
redstrate 2020-05-29 21:31:56 -04:00
parent 35159a93b2
commit efd466fc78
4 changed files with 107 additions and 38 deletions

View file

@ -19,7 +19,7 @@ public:
const float h2 = height / 2.0f;
const float w2 = width / 2.0f;
glm::vec3 ray_dir = position + (h2 / tan(glm::radians(fov) / 2)) * direction + (y - h2) * up + (float)(x - w2) * right;
const glm::vec3 ray_dir = position + (h2 / tan(glm::radians(fov) / 2)) * direction + (y - h2) * up + static_cast<float>(x - w2) * right;
return Ray(position, ray_dir);
}

View file

@ -6,6 +6,7 @@
struct Object {
glm::vec3 position = glm::vec3(0);
glm::vec3 color = glm::vec3(1);
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
@ -25,42 +26,42 @@ struct Scene {
};
inline glm::vec3 fetch_position(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
tinyobj::index_t idx = mesh.indices[(index * 3) +vertex];
const tinyobj::index_t idx = mesh.indices[(index * 3) +vertex];
tinyobj::real_t vx = object.attrib.vertices[3*idx.vertex_index+0];
tinyobj::real_t vy = object.attrib.vertices[3*idx.vertex_index+1];
tinyobj::real_t vz = object.attrib.vertices[3*idx.vertex_index+2];
const auto vx = object.attrib.vertices[3*idx.vertex_index+0];
const auto vy = object.attrib.vertices[3*idx.vertex_index+1];
const auto vz = object.attrib.vertices[3*idx.vertex_index+2];
return glm::vec3(vx, vy, vz);
}
inline glm::vec3 fetch_normal(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
const tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
tinyobj::real_t nx = object.attrib.normals[3*idx.normal_index+0];
tinyobj::real_t ny = object.attrib.normals[3*idx.normal_index+1];
tinyobj::real_t nz = object.attrib.normals[3*idx.normal_index+2];
const auto nx = object.attrib.normals[3*idx.normal_index+0];
const auto ny = object.attrib.normals[3*idx.normal_index+1];
const auto nz = object.attrib.normals[3*idx.normal_index+2];
return glm::vec3(nx, ny, nz);
}
struct HitResult {
glm::vec3 position, normal;
tinyobj::mesh_t* mesh = nullptr;
Object object;
};
std::optional<HitResult> test_mesh(const Ray ray, const Object& object, const tinyobj::mesh_t& mesh, float& tClosest) {
bool intersection = false;
HitResult result = {};
for (size_t i = 0; i < mesh.num_face_vertices.size(); i++) {
for(size_t i = 0; i < mesh.num_face_vertices.size(); i++) {
const glm::vec3 v0 = fetch_position(object, mesh, i, 0) + object.position;
const glm::vec3 v1 = fetch_position(object, mesh, i, 1) + object.position;
const glm::vec3 v2 = fetch_position(object, mesh, i, 2) + object.position;
float t = std::numeric_limits<float>::infinity(), u, v;
if (intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if (t < tClosest && t > epsilon) {
if(intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if(t < tClosest && t > epsilon) {
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
@ -86,13 +87,13 @@ std::optional<HitResult> test_scene(const Ray ray, const Scene& scene, float tCl
HitResult result = {};
for(auto& object : scene.objects) {
for (uint32_t i = 0; i < object.shapes.size(); i++) {
for(uint32_t i = 0; i < object.shapes.size(); i++) {
auto mesh = object.shapes[i].mesh;
if(auto hit = test_mesh(ray, object, mesh, tClosest)) {
if(const auto hit = test_mesh(ray, object, mesh, tClosest)) {
intersection = true;
result = hit.value();
result.mesh = &mesh;
result.object = object;
}
}
}
@ -102,3 +103,86 @@ std::optional<HitResult> test_scene(const Ray ray, const Scene& scene, float tCl
else
return {};
}
constexpr glm::vec3 light_position = glm::vec3(5);
constexpr float light_bias = 0.01f;
constexpr int max_depth = 2;
constexpr int num_indirect_samples = 4;
struct SceneResult {
HitResult hit;
glm::vec3 color, indirect;
};
// methods adapated from https://users.cg.tuwien.ac.at/zsolnai/gfx/smallpaint/
inline std::tuple<glm::vec3, glm::vec3> orthogonal_system(const glm::vec3& v1) {
glm::vec3 v2;
if(glm::abs(v1.x) > glm::abs(v1.y)) {
// project to the y = 0 plane and construct a normalized orthogonal vector in this plane
const float inverse_length = 1.0f / sqrtf(v1.x * v1.x + v1.z * v1.z);
v2 = glm::vec3(-v1.z * inverse_length, 0.0f, v1.x * inverse_length);
} else {
// project to the x = 0 plane and construct a normalized orthogonal vector in this plane
const float inverse_length = 1.0f / sqrtf(v1.y * v1.y + v1.z * v1.z);
v2 = glm::vec3(0.0f, v1.z * inverse_length, -v1.y * inverse_length);
}
return {v2, glm::cross(v1, v2)};
}
glm::vec3 hemisphere(const double u1, const double u2) {
const double r = sqrt(1.0 - u1 * u1);
const double phi = 2 * M_PI * u2;
return glm::vec3(cos(phi) * r, sin(phi) * r, u1);
}
std::optional<SceneResult> cast_scene(const Ray ray, const Scene& scene, const int depth = 0) {
if(depth > max_depth)
return {};
if(auto hit = test_scene(ray, scene)) {
const float diffuse = lighting::point_light(hit->position, light_position, hit->normal);
//shadow calculation
glm::vec3 direct(0);
if(glm::dot(light_position - hit->position, hit->normal) > 0) {
const glm::vec3 light_dir = glm::normalize(light_position - hit->position);
const Ray shadow_ray(hit->position + (hit->normal * light_bias), light_dir);
const float shadow = test_scene(shadow_ray, scene) ? 0.0f : 1.0f;
direct = diffuse * shadow * glm::vec3(1);
}
glm::vec3 indirect(0);
for(int i = 0; i < num_indirect_samples; i++) {
const float theta = drand48() * M_PI;
const float cos_theta = cos(theta);
const float sin_theta = sin(theta);
const auto [rotX, rotY] = orthogonal_system(hit->normal);
const glm::vec3 sampled_dir = hemisphere(cos_theta, sin_theta);
const glm::vec3 rotated_dir = {
glm::dot({rotX.x, rotY.x, hit->normal.x}, sampled_dir),
glm::dot({rotX.y, rotY.y, hit->normal.y}, sampled_dir),
glm::dot({rotX.z, rotY.z, hit->normal.z}, sampled_dir)
};
if(const auto indirect_result = cast_scene(Ray(ray.origin, rotated_dir), scene, depth + 1))
indirect += indirect_result->color * cos_theta;
}
indirect /= num_indirect_samples;
SceneResult result = {};
result.hit = *hit;
result.color = (indirect + direct) * hit->object.color;
result.indirect = indirect;
return result;
} else {
return {};
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -23,18 +23,16 @@
#include <tiny_obj_loader.h>
// scene information
constexpr int32_t width = 512, height = 512;
constexpr glm::vec3 light_position = glm::vec3(5);
constexpr int32_t width = 128, height = 128;
const Camera camera = [] {
Camera camera;
camera.look_at(glm::vec3(0, 0, 4), glm::vec3(0));
return camera;
}();
constexpr glm::vec3 model_color = glm::vec3(1.0f, 1.0f, 1.0f);
// internal variables
constexpr float light_bias = 0.01f;
constexpr int32_t tile_size = 32;
constexpr int32_t num_tiles_x = width / tile_size;
constexpr int32_t num_tiles_y = height / tile_size;
@ -49,22 +47,8 @@ bool calculate_tile(const int32_t from_x, const int32_t to_width, const int32_t
for (int32_t x = from_x; x < (from_x + to_width); x++) {
Ray ray_camera = camera.get_ray(x, y, width, height);
if (auto hit = test_scene(ray_camera, scene)) {
const float diffuse = lighting::point_light(hit->position, light_position, hit->normal);
//shadow calculation
float shadow = 0.0f;
if(glm::dot(light_position - hit->position, hit->normal) > 0) {
const glm::vec3 light_dir = glm::normalize(light_position - hit->position);
const Ray shadow_ray(hit->position + (hit->normal * light_bias), light_dir);
if(test_scene(shadow_ray, scene))
shadow = 1.0f;
}
const glm::vec3 finalColor = model_color * diffuse * (1.0f - shadow);
colors.get(x, y) = glm::vec4(finalColor, 1.0f);
if(auto result = cast_scene(ray_camera, scene)) {
colors.get(x, y) = glm::vec4(result->color, 1.0f);
image_dirty = true;
}
@ -182,8 +166,8 @@ void render() {
void dump_to_file() {
uint8_t pixels[width * height * 3] = {};
int i = 0;
for (int32_t y = height - 1; y >= 0; y--) {
for (int32_t x = 0; x < width; x++) {
for(int32_t y = height - 1; y >= 0; y--) {
for(int32_t x = 0; x < width; x++) {
const glm::ivec4 c = colors.get(x, y);
pixels[i++] = c.r;
pixels[i++] = c.g;
@ -239,6 +223,7 @@ int main(int, char*[]) {
auto& plane = scene.load_from_file("plane.obj");
plane.position.y = -1;
plane.color = {1, 0, 0};
}
ImGui::EndMenu();