直接上伪代码:
castRay (){
//Vector 值1 值2;
//生成 镜头射线于物体的交点
//pdf
If 镜头射线正好交于光源 则:
阿巴阿巴阿巴
If 镜头射线于物体的交点 则:
交点和光源之间的距离
交点到光源方向 和交点坐标作为参数 再生成一次镜头射线于物体的交点 查找物体 然后对比深度;
If( 物体距离 >光源距离):
值1 += L_烈度 *BRDF *cos(入射线于,交点法线) * cos(光源射线,光源面法线) / (两点距离^2) / pdf
否则:
pass
设定一个枪毙函数的概率值 p 例5/6
If ( 随机值1~6整数 > p ) 则{
值2 = 0;
否则:
生成随机射入光线wi.
//If wi.这个射线如果能照射到光源则:
Pass
Else if wi. 这个射线如果能照射到 物体则:
值2 = 调用函数castRay() *BRDF *cos法线 /pdf / p
镜头射线于物体的交点 没有则:
Return {}
Return 值1+值2;
} 大致代码逻辑这样子; 视频里P16 63m 时候有说
需要之前作业的粘贴和修改内容:
inline Intersection Triangle::getIntersection(Ray ray)
{
Intersection inter;
if (dotProduct(ray.direction, normal) > 0)
return inter;
double u, v, t_tmp = 0;
Vector3f s1 = crossProduct(ray.direction, e2);
double det = dotProduct(e1, s1);
if (fabs(det) < EPSILON)
return inter;
double det_inv = 1. / det;
Vector3f s = ray.origin - v0;
u = dotProduct(s, s1) * det_inv;
if (u < 0 || u > 1)
return inter;
Vector3f s2 = crossProduct(s, e1);
v = dotProduct(ray.direction, s2) * det_inv;
if (v < 0 || u + v > 1)
return inter;
t_tmp = dotProduct(e2, s2) * det_inv;
if(t_tmp < 0)
{
return inter;
}
inter.happened = true;
inter.coords = ray.origin+ t_tmp* ray.direction;
inter.normal = normal;
//Vector3f p_inter = ray.origin-inter.coords;
//inter.distance = sqrt( (double)(p_inter.x*p_inter.x + p_inter.y*p_inter.y+ p_inter.z*p_inter.z) );
inter.distance = t_tmp;
inter.obj = this;
inter.m = m;
return inter;
}
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
const std::array<int, 3>& dirIsNeg) const
{ float t_inX,t_outX,t_inY,t_outY,t_inZ,t_outZ;
if (ray.direction.x >0 ){
t_inX = (pMin.x - ray.origin.x) * invDir.x;
t_outX = (pMax.x - ray.origin.x) * invDir.x;
}else if (ray.direction.x ==0.0 ){
t_inX = __FLT_MAX__;
t_outX = __FLT_MAX__;
}else {
t_outX = (pMin.x - ray.origin.x) * invDir.x;
t_inX = (pMax.x - ray.origin.x) * invDir.x;
}
if (ray.direction.y >0){
t_inY = (pMin.y - ray.origin.y) * invDir.y;
t_outY = (pMax.y - ray.origin.y) * invDir.y;
}else if (ray.direction.y ==0.0 ){
t_inY = __FLT_MAX__;
t_outY = __FLT_MAX__;
}else{
t_outY = (pMin.y - ray.origin.y) * invDir.y;
t_inY = (pMax.y - ray.origin.y) * invDir.y;
}
if (ray.direction.z >0 ){
t_inZ = (pMin.z - ray.origin.z) * invDir.z;
t_outZ = (pMax.z - ray.origin.z) * invDir.z;
}else if (ray.direction.z ==0.0 ){
t_inZ = __FLT_MAX__;
t_outZ = __FLT_MAX__;
}else{
t_outZ = (pMin.z - ray.origin.z) * invDir.z;
t_inZ = (pMax.z - ray.origin.z) * invDir.z;
}
float t_min = std::max(std::max(t_inX,t_inY),t_inZ);
float t_max = std::min(std::min(t_outX,t_outY),t_outZ);
if ( t_max >= t_min &&t_max >= 0 )
return true;
return false;
}
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
Vector3f invDir (1.0/ray.direction.x, 1.0/ray.direction.y, 1.0/ray.direction.z );
std::array<int,3> int3arr = {0,0,0};
if (node->bounds.IntersectP(ray, invDir, int3arr ) ){
if( node->left==nullptr && node->right==nullptr){
return node->object->getIntersection(ray);
}else{
Intersection inter1 = BVHAccel::getIntersection( node->left, ray);
Intersection inter2 = BVHAccel::getIntersection( node->right, ray);
return inter1.distance > inter2.distance ? inter2 : inter1;
}
}else{
return {};
}
} 要写内容:Scene::castRay 实现:
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
float pdf;
Vector3f color1 ={.0,.0,.0},color2={.0,.0,.0};
Intersection intersection = Scene::intersect(ray);
//找不到这个投射点.
if (!intersection.happened ){
return {};
}
//这里想不通如何判断是否是光的看的别人
//如果射线打到光 //hasEmission函数m->m_emission向量的距离是否大于0.00001}
//在main中定义了光材质Material* light = new Material(DIFFUSE, Vector3f(0.747f+0.058f, 0.747f+0.258f, 0.747f)....);
//其他材质创建时 第二个传参值都是Vector3f(0.0f);
// 第二传参值定义了m->m_emission也就是说除了光其他 调用 hasEmission函数都是false
if ( intersection.m->hasEmission() ){
return intersection.m->m_emission;
}//之后的都是 有投射点且不是光
Material *mater = intersection.m;
Intersection light_inter;
sampleLight(light_inter, pdf);
Vector3f light_point_v3 = light_inter.coords - intersection.coords;
Vector3f point_light_dir = light_point_v3.normalized();
float point_light_dis_pow2 = light_point_v3.x *light_point_v3.x + light_point_v3.y * light_point_v3.y + light_point_v3.z *light_point_v3. z ;
float point_light_dis = std::sqrt(point_light_dis_pow2);
//光比物体近 还要考虑物体和光距离一样的情况 这里还是看别人写的才发现的 的确考虑不周
//进度问题 这里写成了>= -0.00005f 写小了就会有横条
if ( (intersect( Ray( intersection.coords, point_light_dir) ).distance - point_light_dis )
>= -0.00005f){
//需知 光源烈度 * BRDF * cos(交点法线,交点到光方向) * cos(光源法线,-交点到光方向) / PDF / 距离^2
color1 = light_inter.emit
* mater->eval(ray.direction, point_light_dir, intersection.normal)
* dotProduct(intersection.normal,point_light_dir)
* dotProduct(light_inter.normal, -point_light_dir)
/ pdf
/ point_light_dis_pow2 ;
}
float p = (float)(rand() % 100) /100;
if(p > RussianRoulette){
return color1;
}
Vector3f l_exit_dir = mater->sample(ray.direction, intersection.normal);
Ray l_exit = Ray( intersection.coords, l_exit_dir);
Intersection inter_sequel = intersect( l_exit);
//如何判断这个射线射没射到光源?
if( inter_sequel.happened && !inter_sequel.m->hasEmission() ){
color2 = castRay( l_exit, depth+1)
* mater->eval(ray.direction, l_exit_dir, intersection.normal)
* dotProduct(intersection.normal,l_exit_dir)
/ mater->pdf(ray.direction, l_exit_dir, intersection.normal)
/ RussianRoulette;
}
return color1 + color2;
}
写的时候想半天急躁了 结果写错好几个地方 检查n遍 我吐了.越急写错概率越高,别学我

如果你图形是这样 恭喜你Scene::castRay 内容有错
不要怀疑黑屏是程序问题 绝对是代码有问题
注: 如果长时间启动了ide 试着重新打开. 或许能好 (极小部分情况)

两个错误问题的解决方法

Cpp3 RussianRoulette=0.8
手动修改 Renderer::Render 里spp 参数(也就是每个像素采样次数) 我直接设置成3 递归枪毙率RussianRoulette可改 要不然太慢了….. 就这样一张图2m…
Cpp=20 一张图直接37m 搞不起


修复后大致效果