学习光线追踪(6)---第一次显示

泄露秘密 提交于 2019-12-27 04:12:38

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库需要自行配置好

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!