nori/src/perspective.cpp

154 lines
5.2 KiB
C++

/*
This file is part of Nori, a simple educational ray tracer
Copyright (c) 2015 by Wenzel Jakob
Nori is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Nori is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <nori/camera.h>
#include <nori/rfilter.h>
#include <nori/warp.h>
#include <Eigen/Geometry>
NORI_NAMESPACE_BEGIN
/**
* \brief Perspective camera with depth of field
*
* This class implements a simple perspective camera model. It uses an
* infinitesimally small aperture, creating an infinite depth of field.
*/
class PerspectiveCamera : public Camera {
public:
PerspectiveCamera(const PropertyList &propList) {
/* Width and height in pixels. Default: 720p */
m_outputSize.x() = propList.getInteger("width", 1280);
m_outputSize.y() = propList.getInteger("height", 720);
m_invOutputSize = m_outputSize.cast<float>().cwiseInverse();
/* Specifies an optional camera-to-world transformation. Default: none */
m_cameraToWorld = propList.getTransform("toWorld", Transform());
/* Horizontal field of view in degrees */
m_fov = propList.getFloat("fov", 30.0f);
/* Near and far clipping planes in world-space units */
m_nearClip = propList.getFloat("nearClip", 1e-4f);
m_farClip = propList.getFloat("farClip", 1e4f);
m_rfilter = NULL;
}
void activate() {
float aspect = m_outputSize.x() / (float) m_outputSize.y();
/* Project vectors in camera space onto a plane at z=1:
*
* xProj = cot * x / z
* yProj = cot * y / z
* zProj = (far * (z - near)) / (z * (far-near))
* The cotangent factor ensures that the field of view is
* mapped to the interval [-1, 1].
*/
float recip = 1.0f / (m_farClip - m_nearClip),
cot = 1.0f / std::tan(degToRad(m_fov / 2.0f));
Eigen::Matrix4f perspective;
perspective <<
cot, 0, 0, 0,
0, cot, 0, 0,
0, 0, m_farClip * recip, -m_nearClip * m_farClip * recip,
0, 0, 1, 0;
/**
* Translation and scaling to shift the clip coordinates into the
* range from zero to one. Also takes the aspect ratio into account.
*/
m_sampleToCamera = Transform(
Eigen::DiagonalMatrix<float, 3>(Vector3f(-0.5f, -0.5f * aspect, 1.0f)) *
Eigen::Translation<float, 3>(-1.0f, -1.0f/aspect, 0.0f) * perspective).inverse();
/* If no reconstruction filter was assigned, instantiate a Gaussian filter */
if (!m_rfilter)
m_rfilter = static_cast<ReconstructionFilter *>(
NoriObjectFactory::createInstance("gaussian", PropertyList()));
}
Color3f sampleRay(Ray3f &ray,
const Point2f &samplePosition,
const Point2f &apertureSample) const {
/* Compute the corresponding position on the
near plane (in local camera space) */
Point3f nearP = m_sampleToCamera * Point3f(
samplePosition.x() * m_invOutputSize.x(),
samplePosition.y() * m_invOutputSize.y(), 0.0f);
/* Turn into a normalized ray direction, and
adjust the ray interval accordingly */
Vector3f d = nearP.normalized();
float invZ = 1.0f / d.z();
ray.o = m_cameraToWorld * Point3f(0, 0, 0);
ray.d = m_cameraToWorld * d;
ray.mint = m_nearClip * invZ;
ray.maxt = m_farClip * invZ;
ray.update();
return Color3f(1.0f);
}
void addChild(NoriObject *obj) {
switch (obj->getClassType()) {
case EReconstructionFilter:
if (m_rfilter)
throw NoriException("Camera: tried to register multiple reconstruction filters!");
m_rfilter = static_cast<ReconstructionFilter *>(obj);
break;
default:
throw NoriException("Camera::addChild(<%s>) is not supported!",
classTypeName(obj->getClassType()));
}
}
/// Return a human-readable summary
std::string toString() const {
return tfm::format(
"PerspectiveCamera[\n"
" cameraToWorld = %s,\n"
" outputSize = %s,\n"
" fov = %f,\n"
" clip = [%f, %f],\n"
" rfilter = %s\n"
"]",
indent(m_cameraToWorld.toString(), 18),
m_outputSize.toString(),
m_fov,
m_nearClip,
m_farClip,
indent(m_rfilter->toString())
);
}
private:
Vector2f m_invOutputSize;
Transform m_sampleToCamera;
Transform m_cameraToWorld;
float m_fov;
float m_nearClip;
float m_farClip;
};
NORI_REGISTER_CLASS(PerspectiveCamera, "perspective");
NORI_NAMESPACE_END