# 光线追踪(RayTracing)算法理论与实践(一)入门

## 向量类

gvector3.h

#ifndef GVECTOR3_H
#define GVECTOR3_H
#include <iostream>
#include <cmath>
#define MIN(x,y) (x)>(y)?(y):(x);
#define MAX(x,y) (x)>(y)?(x):(y);
using namespace std;

class GVector3
{

public:
float x;
float y;
float z;
// 缺省构造函数
GVector3();
~GVector3();
// 用户构造函数
GVector3(float posX, float posY, float posZ);
//输出向量信息
void getInfo();
//矢量加法
GVector3 operator+(GVector3 v);
//矢量减法
GVector3 operator-(GVector3 v);
//数乘
GVector3 operator*(float n);
//数除
GVector3 operator/(float n);
//向量点积
float dotMul(GVector3 v2);
//向量叉乘
GVector3 crossMul(GVector3 v2);
//绝对值化
GVector3 abs();
//获取分量中的最大值
float max();
//获取分量的最小值
float min();
//获取矢量长度
float getLength();
//向量单位化
GVector3 normalize();
//求两点之间的距离
float getDist(GVector3 v);
//返回零向量
static inline GVector3 zero(){ return GVector3(0,0,0); }
//打印向量的分量值
void show();

};

#endif // GVECTOR3_H


gvector3.cpp

#include "gvector3.h"

GVector3::GVector3()
{
}
GVector3::~GVector3()
{
}
GVector3::GVector3(float posX, float posY, float posZ)
{
x=posX;
y=posY;
z=posZ;
}

GVector3 GVector3::operator+(GVector3 v)
{
return GVector3(x+v.x,v.y+y,v.z+z);
}
GVector3 GVector3::operator-(GVector3 v)
{
return GVector3(x-v.x,y-v.y,z-v.z);
}
GVector3 GVector3::operator*(float n)
{
return GVector3(x*n,y*n,z*n);
}
GVector3 GVector3::operator/(float n)
{
return GVector3(x/n,y/n,z/n);
}
void GVector3::getInfo()
{
cout<<"x:"<<x<<" y:"<<y<<" z:"<<z<<endl;
}
GVector3 GVector3::abs()
{
if(x<0) x*=-1;
if(y<0) y*=-1;
if(z<0) z*=-1;
return GVector3(x,y,z);
}
float GVector3::dotMul(GVector3 v2)
{
return (x*v2.x+y*v2.y+z*v2.z);
}
GVector3 GVector3::crossMul(GVector3 v2)
{
GVector3 vNormal;
// 计算垂直矢量
vNormal.x = ((y * v2.z) - (z * v2.y));
vNormal.y = ((z * v2.x) - (x * v2.z));
vNormal.z = ((x * v2.y) - (y * v2.x));
return vNormal;
}
float GVector3::getLength()
{
return  (float)sqrt(x*x+y*y+z*z);
}
GVector3 GVector3::normalize()
{
float length=getLength();
x=x/length;
y=y/length;
z=z/length;
return GVector3(x,y,z);
}
void GVector3::show()
{
cout<<"x:"<<x<<"  y:"<<y<<"  z"<<z<<endl;
}
float GVector3::max()
{
float tmp=MAX(y,z);
return MAX(x,tmp);
}
float GVector3::min()
{
float tmp=MIN(y,z);
return MIN(x,tmp);
}
float GVector3::getDist(GVector3 v)
{
float tmp=(x-v.x)*(x-v.x)+(y-v.y)*(y-v.y)+(z-v.z)*(z-v.z);
return sqrt(tmp);
}


点乘，也叫向量的内积、数量积。顾名思义，求下来的结果是一个数。

叉乘，也叫向量的外积、向量积。顾名思义，求下来的结果是一个向量，记这个向量为c。

在物理学中，已知力与力臂求力矩，就是向量的外积，即叉乘。
数值上的计算，将向量用坐标表示（三维向量），

| i j k|
|a1 b1 c1|
|a2 b2 c2|
=(b1c2-b2c1,c1a2-a1c2,a1b2-a2b1)
（i、j、k分别为空间中相互垂直的三条坐标轴的单位向量）。

## 光线类

首先来看一下光线的表示方法。
当t=0时，p=e, 当t=1时，p=s.0<t1<t2则t1更接近眼睛。
这个结论在求交的时候会用到。

#ifndef CRAY_H
#define CRAY_H
#include <iostream>
#include "gvector3.h"
#define PI 3.14159
using namespace std;
class CRay
{
private:
GVector3 origin;
GVector3 direction;
public:
CRay();
CRay(GVector3 o,GVector3 d);
~CRay();
void setOrigin(GVector3 o);
void setDirection(GVector3 d);
GVector3 getOrigin();
GVector3 getDirection();
//通过向射线的参数方程传入参数t而获得在射线上的点
GVector3 getPoint(double t);

};
#endif


cray.cpp
#include "cray.h"
CRay::CRay()
{
}
CRay::~CRay()
{
}
CRay::CRay(GVector3 o,GVector3 d)
{
origin=o;
direction=d;
}
void CRay::setDirection(GVector3 d)
{
direction=d;
}
void CRay::setOrigin(GVector3 o)
{
origin=o;
}
GVector3 CRay::getDirection()
{
return direction;
}
GVector3 CRay::getOrigin()
{
return origin;
}
GVector3 CRay::getPoint(double t)
{
return origin+direction*t;
}


## 初试画板

GLFW是一个自由，开源，多平台的图形库，可用于创建窗口，渲染OpenGL，管理输入。
GLFW的配置见《 GLFW入门学习》.这里主要要做的就是将我们窗口映射成像素的点阵，然后填充颜色。

#include <GL/glfw.h>
#include <stdlib.h>
#include<stdio.h>
#define WINDOW_WIDTH  600
#define WINDOW_HEIGHT 600
void initScene(int w,int h)
{
// 启用阴影平滑
// 黑色背景
glClearColor( 0.0, 0.0, 0.0, 0.0 );
// 设置深度缓存
glClearDepth( 1.0 );
// 启用深度测试
glEnable( GL_DEPTH_TEST );
// 所作深度测试的类型
glDepthFunc( GL_LEQUAL );
// 告诉系统对透视进行修正
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
}
//这里进行所有的绘图工作
void  drawScene() {
float colorSpan=0.0005f;
float color=0.0f;
float pixelSize=2.0f;
float posY=-1.0f;
float posX=-1.0f;
long maxDepth=20;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//将原点移动到左下角
glTranslatef(-0.5f,-0.5f,-1.0f);
glPointSize(2.0);
glBegin(GL_POINTS);
double dx=1.0f/WINDOW_WIDTH;
double dy=1.0f/WINDOW_HEIGHT;
float dD=255.0f/maxDepth;
glBegin(GL_POINTS);

for (long y = 0; y < WINDOW_HEIGHT; ++y)
{
double sy = 1-dy*y;
for (long x = 0; x < WINDOW_WIDTH; ++x)
{
double sx =dx*x;
float colorR=x*1.0/WINDOW_WIDTH*255;
float colorB=y*1.0/WINDOW_HEIGHT*255;
glColor3ub(colorR,0,colorB);
glVertex2f(sx,sy);
}
}
// 交换缓冲区
glfwSwapBuffers();
}
//重置窗口大小后的回调函数
void GLFWCALL resizeGL(int width, int height )
{
// 防止窗口大小变为0
if ( height == 0 )
{
height = 1;
}
// 重置当前的视口
glViewport( 0, 0, (GLint)width, (GLint)height );
// 选择投影矩阵
glMatrixMode( GL_PROJECTION );
// 重置投影矩阵
// 设置视口的大小
gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );
// 选择模型观察矩阵
glMatrixMode( GL_MODELVIEW );
}

int main( void )
{
//记录程序运行状态
int running = GL_TRUE;
//初始化 GLFW
if( !glfwInit() )
{
exit( EXIT_FAILURE );
}
// 创建一个OpenGL 窗口
if( !glfwOpenWindow( WINDOW_WIDTH,WINDOW_HEIGHT,6,6,6,0,32,0,GLFW_WINDOW) )
{
glfwTerminate();
exit( EXIT_FAILURE );
}
//初始化OpenGL窗口
initScene(WINDOW_WIDTH, WINDOW_HEIGHT);
//设置窗口大小发生变化时的回调函数
glfwSetWindowSizeCallback(resizeGL);
//主循环
while( running )
{
// OpenGL rendering goes here...
glClear( GL_COLOR_BUFFER_BIT );
// 当按下ESC键的时候触发
running = !glfwGetKey( GLFW_KEY_ESC ) &&glfwGetWindowParam( GLFW_OPENED );
drawScene();
//延时0.05秒
glfwSleep(0.05 );
}
glfwTerminate();
//退出程序
exit( EXIT_SUCCESS );
}


## 球体

csphere.h

#ifndef CSPHERE_H
#define CSPHERE_H
#include "gvector3.h"
#include "intersectresult.h"
#include "cray.h"
class CSphere
{
public:
CSphere();
CSphere(CSphere& s);
void setCenter(GVector3& c);
GVector3 getCenter();
//获取物体表面一点的法线
virtual GVector3 getNormal(GVector3 point);
//用于判断射线和该物体的交点
virtual IntersectResult isIntersected(CRay RAY);
virtual ~CSphere();
protected:
private:
GVector3 center;
};

#endif // CSPHERE_H


csphere.cpp

#include "csphere.h"
#include "intersectresult.h"
CSphere::CSphere()
{
//ctor
}
CSphere::CSphere(GVector3 c,double r)
{
center=c;
}
CSphere::CSphere(CSphere& s)
{
center=s.getCenter();
}
CSphere::~CSphere()
{
//dtor
}
void CSphere::setCenter(GVector3& c)
{
center=c;
}
{
}
GVector3 CSphere::getCenter()
{
return center;
}
{
}
GVector3 CSphere::getNormal(GVector3 p)
{
return p-center;
}
IntersectResult CSphere::isIntersected(CRay _ray)
{
IntersectResult result = IntersectResult::noHit();
GVector3 v = _ray.getOrigin() - center;
float DdotV = _ray.getDirection().dotMul(v);

if (DdotV <= 0) {
float discr = DdotV * DdotV - a0;
if (discr >= 0) {
//
result.isHit=1;
result.distance=-DdotV - sqrt(discr);
result.position=_ray.getPoint(result.distance);
result.normal = result.position-center;
result.normal.normalize();
}
}
return result;
}


#ifndef INTERSECTRESULT_H_INCLUDED
#define INTERSECTRESULT_H_INCLUDED
#include "gvector3.h"
struct IntersectResult{
float distance;
bool isHit;
GVector3 position;
GVector3 normal;
static inline IntersectResult noHit() { return IntersectResult(); }
};

#endif // INTERSECTRESULT_H_INCLUDED


## 摄像机

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);
}


## 渲染一个球

void renderDepth()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(-0.5f,-0.5f,-1.0f);
glPointSize(2.0);
float horiz=0.0;
float dep=10;
PerspectiveCamera camera( GVector3(horiz, 10, dep),GVector3(0, 0, -1),GVector3(0, 1, 0), 90);
long maxDepth=18;
CSphere* sphere1 = new CSphere(GVector3(0, 10, -10), 10.0);
float dx=1.0f/WINDOW_WIDTH;
float dy=1.0f/WINDOW_HEIGHT;
float dD=255.0f/maxDepth;
glBegin(GL_POINTS);
for (long y = 0; y < WINDOW_HEIGHT; ++y)
{
float sy = 1 - dy*y;
for (long x = 0; x < WINDOW_WIDTH; ++x)
{
float sx =dx*x;
CRay ray(camera.generateRay(sx, sy));
IntersectResult result = sphere1->isIntersected(ray);
if (result.isHit)
{
double t=MIN(result.distance*dD,255.0f);
int depth = (int)(255 -t);
glColor3ub(depth,depth,depth);
glVertex2f(sx,sy);
}

}
}

glEnd();
// 交换缓冲区
glfwSwapBuffers();
}

void renderDepth()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(-0.5f,-0.5f,-1.0f);
glPointSize(2.0);
PerspectiveCamera camera( GVector3(0, 10, 10),GVector3(0, 0, -1),GVector3(0, 1, 0), 90);
long maxDepth=20;
CSphere* sphere1 = new CSphere(GVector3(0, 10, -10), 10.0);
camera.initialize();
float dx=1.0f/WINDOW_WIDTH;
float dy=1.0f/WINDOW_HEIGHT;
float dD=255.0f/maxDepth;
glBegin(GL_POINTS);
for (long y = 0; y < WINDOW_HEIGHT; ++y)
{
float sy = 1 - dy*y;
for (long x = 0; x < WINDOW_WIDTH; ++x)
{
float sx =dx*x;
CRay ray(camera.generateRay(sx, sy));
IntersectResult result = sphere1->isIntersected(ray);
if (result.isHit)
{
//double t=MIN(result.distance*dD,255.0f);
//int depth = (int)(255 -t);
//xuanranshengdu
//glColor3ub(depth,depth,depth);
//xuanran normal
glColor3ub(128*(result.normal.x+1),128*(result.normal.y+1),128*(result.normal.z+1));
glVertex2f(sx,sy);
}
}
}
glEnd();
// 交换缓冲区
glfwSwapBuffers();
}

## 结语

Wikipedia, Ray Tracing