0.简介
前面做了那么多准备,我们有了相机类模拟相机,有了球类,有了光线类模拟光线,现在就用这些东西来显示一下,算是一次调试。
1.效果
先来一张效果图。
2.解析
这次主要是将之前写过的功能整合了一下,并且对之前的类做了一些小修改。
光线类中添加了一下东西。
class Ray
{
public:
//光线出发点
void* polygon = nullptr;//出发元素指针
float distance;//光线长度
vec3 normal;//所在点法向量
//光线起点
vec3 position;
//光线方向
vec3 direction;
//光线颜色
vec3 color;
//光的强度
float intensity;
//获取光线打到的位置
vec3 getEndPoint(float distance);
Ray();
Ray(vec3 _direction, vec3 _position, float _intensity, vec3 _color,void * _polygon) :direction(_direction), position(_position), intensity(_intensity), color(_color),polygon(_polygon) {}
~Ray();
};
添加了光线经过碰撞计算后得到的信息,碰撞物体,其实就是光线发出点所在物体,还有光线长度,出发点法向量。
多边形类也添加了一个功能,判断指定光线是否与其相交。
class Polygon
{
public:
//位置
vec3 position;
//世界坐标矩阵
mat3 transforms;
//材质
Material m;
virtual Ray intersect(Ray ray) { return Ray(vec3(0, 0, 0), vec3(0, 0, 0), 0, vec3(0, 0, 0), nullptr); }
Polygon();
~Polygon();
};
基类的intersect什么都没有,就是一个返回值,球中的对应函数实现如下。
Ray Sphere::intersect(Ray ray)
{
Ray result(ray.direction,ray.position,ray.intensity, ray.color, nullptr);
//计算球和光线的向量
vec3 v = ray.position - center;
//如果光源在球体表面或者内部,必然与球面相交,并且光源方向指向球心
if (abs(v.length() - radius) < 0.001 && dot(v , ray.direction) <= 0)
{
result.polygon = this;
float cosa = glm::dot(normalize(abs(center - ray.position)), normalize(ray.direction));//normalize(abs((center - ray.position))*normalize(ray.direction));
result.distance = 2 * radius * cosa;
result.position = ray.getEndPoint(result.distance);
result.normal = -normalize(result.position - center);
return result;
}
float disSubR = dot(v , v) - (radius * radius);
float ray_v_dot = dot(ray.direction , v);
if (ray_v_dot <= 0)
{
float discr = ray_v_dot * ray_v_dot - disSubR;
if (discr >= 0)
{
result.polygon = this;
result.distance = -ray_v_dot - sqrt(discr);
result.position = ray.getEndPoint(result.distance);
result.normal = normalize(result.position - center);
return result;
}
}
return result;
}
上面的代码参考了一个JS写的教程中的算法,我在其中添加了折射所需要的代码。
之后在主函数中实现光线追踪,目前还不算是追踪,我们只是显示出了深度信息。
Ray rayTrac(Ray ray,vector<Polygon*> s,int times)
{
Ray r;
for (auto obj : s)
{
r = obj->intersect(ray);
}
return r;
}
Ray render(int i, int j, int rows, int cols, vector<Polygon*> s, Camera cam)
{
float sy = 1.0 - (i * 1.0) / rows;
float sx = (j * 1.0) / cols;
Ray r = rayTrac(cam.generateRay(sx, sy), s, 0);
float depth = 0;
if(r.polygon != nullptr)
depth = 255 - std::min(int((r.distance / 35)*255), 255);
return Ray(r.direction,r.position,0,vec3(depth, depth, depth),nullptr);
}
int main()
{
Camera camera(vec3(0, 10, 10), vec3(0, 0, -1), vec3(0, 1, 0), 90);
Mat img = Mat::zeros(Size(512, 512), CV_8UC3);
Sphere s(vec3(12, 10, -20), 10);
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
vec3 color = render(i, j, img.rows, img.cols, {&s},camera).color;
if (color.z > 255)
color.z = 255;
if (color.y > 255)
color.y = 255;
if (color.x > 255)
color.x = 255;
img.at<Vec3b>(i, j) = Vec3b(color.z, color.y, color.x);
}
}
imshow("img",img);
imwrite("1.jpg",img);
waitKey(0);
return 0;
}
3.拓展
当然还可以显示法向量信息,顺便检查一下算法实现的正确与否。
将代码做简单的修改即可。
Ray render(int i, int j, int rows, int cols, vector<Polygon*> s, Camera cam)
{
float sy = 1.0 - (i * 1.0) / rows;
float sx = (j * 1.0) / cols;
Ray r = rayTrac(cam.generateRay(sx, sy), s, 0);
/*float depth = 0;
if(r.polygon != nullptr)
depth = 255 - std::min(int((r.distance / 35)*255), 255);
return Ray(r.direction, r.position, 0, vec3(depth, depth, depth), nullptr);*/
//float normal = r.normal;
return Ray(r.direction, r.position, 0, vec3((r.normal.x+1)*128, (r.normal.y + 1) * 128, (r.normal.z + 1) * 128), nullptr);
}
4.注意事项
opencv的颜色是BGR,所以显示的时候要注意下。
5.源代码
release中的0.01
GLM库和opencv库需要自行配置好