原理
相机在光线追踪系统中,负责在图像的取样位置,生成一束光线。
相机和人眼类似,实现的是透视投影(perspective projection). 透视投影从视点(view point, eye position), 向某个方向观察场景,观察的角度范围称为视野(field of view, FOV)。 出了定义观察的前向(forward)是哪个方向之外,还需要定义在成像平面上,何谓上下和左右。
上图显示的是一个相机模型。
forward 和 right 分别是前向和右向的单位向量
由于像平面是大小可变的(分辨率不同), 为了计算方便,我们在这里设定同一的取样坐标 (sx,sy), 以左下角为 (0,0), 以右上角为 (1,1).
因为视点是固定的,所以光线的起点不变。要生成光线,只需要用采样坐标(sx,sy) 计算其方向 d. d 可以分解为 right x forward x up 这三个正交单位向量上的投影
- forward 方向投影就是 单位 1
- 以上图水平左右方向为例, tan(fov/2)=s/forward, 而 forward 是单位向量,所以 s=tan(fov/2). 通过 (sx−0.5)×2 将 sx:(0,1)→(−1,1), 再乘以水平方向的长度s, 就是在 right 方向的投影r。
- 竖直上下方向同上。
实现
简化版, 方形成像,水平和竖直方向的 fov 相同
perspectiveCamera.h
#ifndef PERSPECTIVECAMERA_H #define PERSPECTIVECAMERA_H #include "cray.h" class perspectiveCamera{ public: perspectiveCamera(); ~perspectiveCamera(); perspectiveCamera(const GVector3& _eye,const GVector3& _front,const GVector3& _refUp,float _fov); CRay generateRay(float x,float y); private: GVector3 eye; GVector3 front; GVector3 refUp; float fov; GVector3 right; GVector3 up; float fovScale; }; #endif
perspectiveCamera.cpp
#include"perspectiveCamera.h" perspectiveCamera::perspectiveCamera() { } perspectiveCamera::~perspectiveCamera() { } perspectiveCamera::perspectiveCamera(const GVector3& _eye,const GVector3& _front,const GVector3& _refUp,float _fov) { eye=_eye; front=_front; refUp=_refUp; fov=_fov; right=front.crossMul(refUp); up = right.crossMul(front); fovScale = tan(fov* (PI * 0.5f / 180)) * 2; } CRay perspectiveCamera::generateRay(float x,float y) { GVector3 r = right*((x - 0.5f) * fovScale); GVector3 u = up*((y - 0.5f) * fovScale); GVector3 tmp=front+r+u; tmp.normalize(); return CRay(eye,tmp); }
实践
在实际应用中,通常水平和竖直方向的 fov 是不同的,需要分别计算 fovx 和 fovy. 这和最终成像的图像分辨率是相关的。
在实际应用中,视点到成像平面的距离是焦距 f, 通常不是单位距离 1. 这时候,tan(fov/2)=s/f→s=ftan(fov/2), d 在 forward 方向的投影距离是 f,而不再是默认的单位 1。
版权声明:本文为CSDN博主「北境の守卫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baishuo8/article/details/81664720