aobench - Siv3D/Reference-JP GitHub Wiki
ベンチマークプログラム aobench のSiv3D/C++実装
# include <Siv3D.hpp>
const int32 WIDTH = 480;
const int32 HEIGHT = 480;
const int32 NSUBSAMPLES = 2;
const int32 NAO_SAMPLES = 8;
namespace ao
{
struct Isect
{
double t;
Vec3 p;
Vec3 n;
int32 hit;
};
struct Sphere
{
Vec3 center;
double radius;
};
struct Plane
{
Vec3 p;
Vec3 n;
};
struct Ray
{
Vec3 org;
Vec3 dir;
};
Sphere spheres[3];
Plane plane;
void ray_sphere_intersect(Isect* isect, const Ray& ray, const Sphere& sphere)
{
const Vec3 rs = ray.org - sphere.center;
const double B = rs.dot(ray.dir);
const double C = rs.dot(rs) - sphere.radius * sphere.radius;
const double D = B * B - C;
if (D > 0.0)
{
const double t = -B - Sqrt(D);
if ((t > 0.0) && (t < isect->t))
{
isect->t = t;
isect->hit = 1;
isect->p = ray.org + ray.dir * t;
isect->n = (isect->p - sphere.center).normalized();
}
}
}
void ray_plane_intersect(Isect* isect, const Ray& ray, const Plane& _plane)
{
const double d = -_plane.p.dot(_plane.n);
const double v = ray.dir.dot(_plane.n);
if (Abs(v) < 1.0e-17)
{
return;
}
const double t = -(ray.org.dot(_plane.n) + d) / v;
if ((t > 0.0) && (t < isect->t))
{
isect->t = t;
isect->hit = 1;
isect->p = ray.org + ray.dir * t;
isect->n = _plane.n;
}
}
std::array<Vec3, 3> orthoBasis(const Vec3& n)
{
std::array<Vec3, 3> basis;
basis[2] = n;
basis[1].set(0.0, 0.0, 0.0);
if ((n.x < 0.6) && (n.x > -0.6))
{
basis[1].x = 1.0;
}
else if ((n.y < 0.6) && (n.y > -0.6))
{
basis[1].y = 1.0;
}
else if ((n.z < 0.6) && (n.z > -0.6))
{
basis[1].z = 1.0;
}
else
{
basis[1].x = 1.0;
}
basis[0] = basis[1].cross(basis[2]).normalized();
basis[1] = basis[2].cross(basis[0]).normalized();
return basis;
}
Vec3 ambient_occlusion(const Isect& isect)
{
const int32 ntheta = NAO_SAMPLES;
const int32 nphi = NAO_SAMPLES;
const double eps = 0.0001;
const Vec3 p = isect.p + eps * isect.n;
const std::array<Vec3, 3> basis = orthoBasis(isect.n);
double occlusion = 0.0;
for (int32 j = 0; j < ntheta; ++j)
{
for (int32 i = 0; i < nphi; ++i)
{
const double theta = Sqrt(Random());
const double phi = TwoPi * Random();
const double x = Cos(phi) * theta;
const double y = Sin(phi) * theta;
const double z = Sqrt(1.0 - theta * theta);
const double rx = x * basis[0].x + y * basis[1].x + z * basis[2].x;
const double ry = x * basis[0].y + y * basis[1].y + z * basis[2].y;
const double rz = x * basis[0].z + y * basis[1].z + z * basis[2].z;
const Ray ray = { p,{ rx, ry, rz } };
Isect occIsect;
occIsect.t = 1.0e+17;
occIsect.hit = 0;
ray_sphere_intersect(&occIsect, ray, spheres[0]);
ray_sphere_intersect(&occIsect, ray, spheres[1]);
ray_sphere_intersect(&occIsect, ray, spheres[2]);
ray_plane_intersect(&occIsect, ray, plane);
if (occIsect.hit)
{
occlusion += 1.0;
}
}
}
occlusion = (ntheta * nphi - occlusion) / (ntheta * nphi);
return{ occlusion, occlusion, occlusion };
}
void render(Image& image, const int32 w, const int32 h, const int32 nsubsamples)
{
for (int32 y = 0; y < h; ++y)
{
for (int32 x = 0; x < w; ++x)
{
Vec3 color{ 0.0, 0.0, 0.0 };
for (int32 v = 0; v < nsubsamples; ++v)
{
for (int32 u = 0; u < nsubsamples; ++u)
{
const double px = (x + (u / static_cast<double>(nsubsamples)) - (w / 2.0)) / (w / 2.0);
const double py = -(y + (v / static_cast<double>(nsubsamples)) - (h / 2.0)) / (h / 2.0);
const Ray ray = { { 0.0, 0.0, 0.0 }, Vec3{ px, py, -1.0 }.normalized() };
Isect isect;
isect.t = 1.0e+17;
isect.hit = 0;
ray_sphere_intersect(&isect, ray, spheres[0]);
ray_sphere_intersect(&isect, ray, spheres[1]);
ray_sphere_intersect(&isect, ray, spheres[2]);
ray_plane_intersect(&isect, ray, plane);
if (isect.hit)
{
color += ambient_occlusion(isect);
}
}
}
color /= nsubsamples * nsubsamples;
image[y][x] = ColorF{ color.x, color.y, color.z };
}
}
}
void init_scene()
{
spheres[0].center.set(-2.0, 0.0, -3.5);
spheres[0].radius = 0.5;
spheres[1].center.set(-0.5, 0.0, -3.0);
spheres[1].radius = 0.5;
spheres[2].center.set(1.0, 0.0, -2.2);
spheres[2].radius = 0.5;
plane.p.set(0.0, -0.5, 0.0);
plane.n.set(0.0, 1.0, 0.0);
}
}
void Main()
{
ao::init_scene();
Image image(WIDTH, HEIGHT);
Stopwatch stopwatch(true);
ao::render(image, WIDTH, HEIGHT, NSUBSAMPLES);
Println(stopwatch.ms(), L"ms");
Texture(image).draw();
WaitKey();
}