Created
May 25, 2018 10:04
-
-
Save 0b5vr/d93f143a2886f66c12b35f0bdf80d7eb to your computer and use it in GitHub Desktop.
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
#define STB_IMAGE_WRITE_IMPLEMENTATION | |
#define TINYOBJLOADER_IMPLEMENTATION | |
#define _CRT_SECURE_NO_WARNINGS | |
#include <algorithm> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <vector> | |
#include <stb_image.h> | |
#include <stb_image_write.h> | |
#include <OpenEXR/ImathVec.h> | |
#include <OpenEXR/ImathLine.h> | |
#include <OpenEXR/ImathLineAlgo.h> | |
#include <tiny_obj_loader.h> | |
int main(int argc, char** argv) | |
{ | |
if (argc <= 1) { return 0; } | |
const int w = 256; | |
const int h = 256; | |
// 画像真ん中の後ろのほうをposとした適当なレイを作る. | |
Imath::Line3d ray; | |
ray.pos = Imath::V3d(w / 2.0, h / 2.0, 100); | |
// 三角形. | |
struct Triangle { | |
Imath::V3d p[3]; | |
Imath::V3d n[3]; | |
}; | |
// OBJファイルの読み込み | |
tinyobj::attrib_t attrib; | |
std::vector<tinyobj::shape_t> shapes; | |
std::vector<tinyobj::material_t> materials; | |
std::string err; | |
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, argv[1]); | |
if (!err.empty()) | |
{ | |
std::cerr << err << std::endl; | |
exit(1); | |
} | |
// OBJファイルから三角形リストを読み込む. | |
// カメラが適当なので、適当にスケールかけます. | |
// 画像はy座標が下向きなので、モデルのy座標を反転します. | |
const Imath::V3f scale(100.0, -100.0, 100.0); | |
// 三角形リスト. | |
std::vector<Triangle> triangles; | |
for (size_t i = 0; i < shapes.size(); i++) | |
{ | |
const size_t face_count = shapes[i].mesh.indices.size() / 3; | |
for (size_t f = 0; f < face_count; f++) | |
{ | |
Triangle tri; | |
for (int k = 0; k < 3; k++) | |
{ | |
tinyobj::index_t idx = shapes[i].mesh.indices[f * 3 + k]; | |
// 頂点. | |
int fp = idx.vertex_index; | |
tri.p[k] = Imath::V3d( | |
attrib.vertices[3 * fp + 0], | |
attrib.vertices[3 * fp + 1], | |
attrib.vertices[3 * fp + 2] | |
) * scale; | |
// 法線. | |
int fn = idx.normal_index; | |
tri.n[k] = Imath::V3d( | |
attrib.normals[3 * fn + 0], | |
attrib.normals[3 * fn + 1], | |
attrib.normals[3 * fn + 2] | |
); | |
} | |
triangles.push_back(tri); | |
} | |
} | |
std::vector<unsigned char> image_buffer(w * h * 4); | |
for (int y = 0; y < h; ++y) | |
{ | |
for (int x = 0; x < w; ++x) | |
{ | |
int pos = (y*w + x) * 4; | |
image_buffer[pos + 0] = x; | |
image_buffer[pos + 1] = y; | |
image_buffer[pos + 2] = 0xFF; | |
image_buffer[pos + 3] = 0xFF; | |
// レイの方向. | |
ray.dir = Imath::V3d(x, y, 0) - ray.pos; | |
// レイが当たったかどうか. | |
bool is_hit = false; | |
// 一番近くの当たった点までの距離. | |
double closest_distance = std::numeric_limits<double>::max(); | |
// 一番近くの当たった点の法線. | |
Imath::V3d closest_normal; | |
for (size_t t = 0; t < triangles.size(); ++t) | |
{ | |
Triangle& triangle = triangles[t]; | |
// 三角形とレイの交差判定. | |
Imath::V3d hit_point; | |
Imath::V3d uvw; | |
bool is_front = false; | |
if (Imath::intersect(ray, triangle.p[0], triangle.p[1], triangle.p[2], hit_point, uvw, is_front)) | |
{ | |
if (is_front) | |
{ | |
is_hit = true; | |
// 前当たったとこより近いかどうか記憶しておく. | |
const double distance = (hit_point - ray.pos).length(); | |
if (closest_distance > distance) | |
{ | |
closest_distance = distance; | |
closest_normal = (triangle.n[0]*uvw.x + triangle.n[1]*uvw.y + triangle.n[2]*uvw.z).normalized(); | |
} | |
} | |
} | |
} | |
if (is_hit) | |
{ | |
// レイが三角形に当たった. | |
Imath::V3d light(1, 1, 1); | |
double nl = std::max(0.0, closest_normal.dot(light.normalized())); | |
image_buffer[pos + 0] = static_cast<unsigned char>(nl * 0xFF); | |
image_buffer[pos + 1] = static_cast<unsigned char>(nl * 0xFF); | |
image_buffer[pos + 2] = static_cast<unsigned char>(nl * 0xFF); | |
image_buffer[pos + 3] = 0xFF; | |
} | |
} | |
} | |
stbi_write_png("out.png", w, h, STBI_rgb_alpha, &(*image_buffer.begin()), 0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment