Games101 作业7

虚拟机代码:

路径追踪部分

路径追踪步骤:
1,摄像机对每个像素多次取样,每次取样射出一个射线;(在此代码中采样的变量为spp,采样率越大处理的效果越好,时间越久)
2,如果该射线直接打到的光源上,则返回交点处的能量
3,如果没打到任何物体上就返回空
4,如果打到的是物体上的一点P
(1) 在所有光源上,按光源面积均匀采样一次
(2) 连接光源采样点和P点
(3) 如果中间没有障碍物,则说明是直接光照,则计算其radiance
(4) 如果有障碍物,说明是间接光照,对P点处进行半圆采样,获得一个采样的出射光线
(5) 以该出射光线为新的射线重复2之后的步骤,进行递归
5,根据俄罗斯转盘的思想去结束递归,防止递归爆炸

涉及到的公式
BRDF
强调的是从某个dw方向入射进来的一束强调为L的光线,打在了单位面积中的单位点上,然后该单位点反射(漫反射或者折射)出多少能量到某个方向。

在这里插入图片描述

Monte Carlo(蒙特卡洛积分)
蒙特卡洛:给任何一个函数,计算其定积分的方法
在这里插入图片描述

蒙特卡洛的思想就是,在函数f(x)上进行随机取样,该取样函数是p(X), X为随机变量,然后按照上面的公式进行计算即可,就是一个取平均的思想
在这里插入图片描述
光源的采样
为了解决光源过小,需要对x所处的半圆进行极大的均匀采样,才能采样到光源(与其让x找光源,不如光源找x,直接对光源采样节约资源)
在这里插入图片描述
步骤:
1,先将光源分成A份,每份的概率为1/A, dw就是dA在w方向的映射,为了计算出dw,就将dA所表示的面积先转到w方向,
2,然后根据公式dw = (dA* cos(sita))/r^2 的式子,求得dw
在这里插入图片描述

// 路径追踪
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
   
    // TO DO Implement Path Tracing Algorithm here
	// 判断ray是否和场景有交点
	Intersection inter = intersect(ray);
	// 如果不存在交点则直接返回空
	if (!inter.happened) return {
   };
	// 如果存在交点,判断交点是不是刚好是光源
	// m_emission 是交点处的能量,如果交点是光源,就直接返回光源处的能量,作为该点的radiance
	if (inter.m->hasEmission()) return inter.m->getEmission();

	//如果交点是物体上某点,并且是直接光照

	Vector3f dirLight(0.0, 0.0, 0.0); // 定义直接光照 
	// 获得对光源的采样,包括光源的位置和采样的pdf
	Intersection lightInter; 
	float lightPDF = 0.0;// 光源的概率密度,待会使用蒙特卡洛解渲染方程的时候要使用
	sampleLight(lightInter,lightPDF);

	// 物体表面的法线
	auto objNorm = inter.normal;
	// 光源的法线
	auto lightNorm = lightInter.normal;

	// 物体交点的位置
	auto objPos = inter.coords;
	// 光源的位置
	auto lightPos = lightInter.coords;

	// 指向光源的方向向量
	auto Obj2Light = (lightPos - objPos);
	// 光源到物体的距离
	float obj2light_distance = dotProduct(Obj2Light, Obj2Light);
	// 单位向量
	Obj2Light = Obj2Light.normalized();
	// 为了防止物体上的点到光源的路径中没有其他的遮挡物,需要再次判断下
	Ray tempLight(objPos, Obj2Light);
	Intersection obj2light_inter = intersect(tempLight);
	// 如果和场景有交点,并且该交点距离光源极其的近,则说明肯定是直接光照,额可以放心的使用蒙特卡洛解渲染方程
	if (obj2light_inter.happened && (obj2light_inter.coords - lightPos).norm() < 1e-2) {
   
		// 获得最初和物体相交的交点处的BRDF(和材料有关)
		// 需要的参数是,交点处的入射光线,出射光线,法线
		auto fr = inter.m->eval(ray.direction, Obj2Light, objNorm);
		// 蒙特卡洛公式,解渲染方程
		// 直接光照 = 光源的能量 * BRDF * cos(sita) * cos(sita)pie/ 交点到光源距离平方 / pdf
		dirLight = lightInter.emit * fr * dotProduct(objNorm, Obj2Light) *dotProduct(lightNorm, -Obj2Light)/lightPDF/obj2light_distance;
	}

	// 和物体相交,求间接光照,视频里面说为了防止反射无限递归,导致递归爆炸,就使用了随机数的思想去结束递归
	// 比RussianRoulette小的认为有可能反射出来光
	Vector3f indirLight(0.0, 0.0, 0.0); // 间接光照的能量
	if (get_random_float() < RussianRoulette) {
   
		// 按照该材质的性质,给定入射方向与法向量,用某种分布采样一个出射方向
		auto newRayDirect = inter.m->sample(ray.direction, objNorm).normalized();
		Ray newRay(objPos, newRayDirect);
		// 判断新的光线和场景的相交情况
		Intersection newInter = intersect(newRay);
		// 和场景有新的交点,并且该交点不是光源(因为是光源的情况就是直接光照了,上面已经处理过了)
		if (newInter.happened && !newInter.m->hasEmission()) {
   
			// 获得与新的出射光之间的BRDF
			auto fr = inter.m->eval(ray.direction, newRayDirect,objNorm);
			// 获得该点处的pdf
			auto pdf = inter.m->pdf(ray.direction, newRayDirect, objNorm);
			// 递归计算,注意点乘的是  dotProduct(objNorm, newRay.direction) ,因为此时能量来自于newRayDirect指向位置处的能量
			indirLight = castRay(newRay, depth + 1) * fr * dotProduct(objNorm, newRayDirect) / pdf / RussianRoulette;
		}
	}
	return dirLight + indirLight;
	
}

耗时:31分钟
结果:
在这里插入图片描述

多线程的使用

先说一个坑:因为在Ubuntu系统下跑的代码,要在该系统下使用thread需要添加一个调用库的操作;
在Assignment7->CmakeLists.txt->添加 link_libraries(pthread)
在这里插入图片描述
多线程的知识点:
使用lambda表达式和auto去创建匿名函数
语法
auto 函数名 = labmda表达式

auto castRayMultiThreading = [&](uint32_t rowStart, uint32_t rowEnd, uint32_t colStart, uint32_t colEnd)
{
   
};

lamda表达式
这种写法表示的是以引用的方式去调用父函数的所有变量,它能保证这个匿名函数能够使用上一级函数中的变量,也能改变其值,并返回回去,即匿名函数可以改变了父函数中的变量的值

[&](int a,int b ){
   
...
}

互斥锁:
因为设计到多线程,该匿名函数会被多个线程同时调用,为防止进度条更新出现冲突,需设置一个互斥锁

std::lock_guard<std::mutex> g1(mutex_ins);

Join() 函数
当涉及到多线程的时候,从主线程中去开启多个子线程,如果主线程的部分内容需要等待子线程完成后才能进行,则使用join()函数,让主线程进入阻塞态,等待子线程执行完后,恢复主线程的执行。

for (int i = 0; i < bx*by; i++) th[i].join();

代码思想:
1,将整个像素平面分为N块
2,每块都单独存在一个线程中
3,用for循环开启所有的子线程,让程序并行的处理整个像素平面
4&#

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值