Skip to content

Instantly share code, notes, and snippets.

@0b5vr
Created May 25, 2018 10:04
Show Gist options
  • Save 0b5vr/d93f143a2886f66c12b35f0bdf80d7eb to your computer and use it in GitHub Desktop.
Save 0b5vr/d93f143a2886f66c12b35f0bdf80d7eb to your computer and use it in GitHub Desktop.
#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