games101作业七,计算机图形学作业三,详细知识点总结(附代码)

计算机图形学课程作业总结,同课程慎重Ctrl C/V

文章目录

  • 1. 光线追踪算法
    • 光线追踪的准备工作
    • Möller-Trumbore算法
    • rayTriangleIntersect()函数
  • 2. 光线追踪包围盒加速算法
    • 1. AABB 包围盒又称 轴对齐包围盒
    • 2. 光线与包围盒(AABB)的相交检测算法
    • 3. 三维空间的拓展
    • 4. 包围盒模型的算法
  • 3. 层次包围盒BVH加速
    • 1. 前述问题
    • 2. BVH层次包围盒
  • 4. 最终渲染函数 Render() && castRay()
    • 1. Render()函数
    • 2. castRay()函数
  • 5. Debug处理
    • 1. fopen函数
    • 2. C2039 “optional“: 不是 “std“ 的成员
  • 6. games101的一些前置作业:
    • games101作业五
    • games101作业六
    • games101作业七


下面是一做本次实验的一些算法知识总结:

1. 光线追踪算法

在光线追踪中最重要的操作之一就是找到光线与物体的交点。一旦找到光线与物体的交点,就可以执行着色并返回像素颜色。因此 光线的生成和光线与三角的相交在光线追踪算法 中至关重要!!

光线追踪的准备工作

光线追踪的一些假设:

  • 出发点是一个点
  • 光源是点光源
  • 场景物体的反射为镜面反射

光线投射步骤:

  • 出射点穿过成像平面打出一根光线到场景
  • 找到与场景最近的交点
  • 将交点与光源连接,判断是否在阴影中
  • 计算着色情况写回像素中

递归光线追踪:

  • 光线不仅回反射而且会折射,然后再与其他物体进行反射
  • 每次反射有能量损耗,若没有损耗则无数次后的累加会变成白色
  • 递归设置一个最大次数

确定光线与场景的交点:

  • 光线方程

    • 以o为起点 d为方向(单位向量)t为时间 r(t) = o+ td 0<=t<∞

    • 在隐式几何中,可以直接通过将光线方程带入表达式就可以进行计算;

    • 在显示几何中,需要将几何体中每一个三角形面进行判断具体方法如下

      • 几何面用 一个点 和 一个法线 表示平面 (p-p’)·N = 0;
      • 将光线方程r(t)带入光线方程中;
      • 求出交点,判断交点是在三角形内,还是在三角形外,若在内侧则是三角形的交点;
        • 判断交点在三角形内外侧的方法在前面章节中学习过
      • 在这里插入图片描述

Moller Trumbore算法:

Möller-Trumbore算法

射线三角相交算法:可以更快的求出三角形与射线的交点

  • P0 P1 P2为 三角形的三个顶点
  • 光线方程为 r(t) = o+ td 0<=t<∞
  • 通过解出来参数``E1 E2 S S1 S2`参数的定义解出来交点。
    在这里插入图片描述

rayTriangleIntersect()函数

rayTriangleIntersect(): 判断光线是否与三角形相交。

bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig, const Vector3f& dir, float& tnear, float& u, float& v)

v0, v1, v2 是三角形的三个顶点,orig 是光线的起点,dir 是光线单位化的方向向量。tnear, u, v 使用下述算法推导的 Moller-Trumbore 算法来更新的参数

bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,const Vector3f& dir, float& tnear, float& u, float& v)
{// TODO: Implement this function that tests whether the triangle// that's specified bt v0, v1 and v2 intersects with the ray (whose// origin is *orig* and direction is *dir*)// Also don't forget to update tnear, u and v.Vector3f E1 = v1 - v0;Vector3f E2 = v2 - v0;Vector3f S = orig - v0;Vector3f S1 = crossProduct(dir, E2);Vector3f S2 = crossProduct(S, E1);float n = 1.0f/dotProduct(S1, E1);Vector3f res(dotProduct(S2,E2),dotProduct(S1,S),dotProduct(S2,dir));res = n*res;tnear = res.x;u = res.y;v = res.z;if(tnear > 0.f && 1-u-v>=0.f && u>=0.f && v>=0.f)    return true;else  return false;
}

2. 光线追踪包围盒加速算法

在显式几何计算交点时当面足够多的时候,计算起来是十分耗费时间了,因此如何优化十分关键。AABB模型就是加速计算光线与场景交点的方法

1. AABB 包围盒又称 轴对齐包围盒

AABB 包围盒就是采用一个长方体将物体包裹起来,进行两个物体的相交性检测时仅检测物体对应包围盒(包裹物体的长方体)的相交性

另外,AABB 包围盒有一个重要特性,那就是包围盒对应的长方体每一个面都是与某个坐标轴平面平行的,因此,AABB 包围盒又称了 轴对齐包围盒

有了上述约定后,确定 AABB 包围盒就简单多了,仅需要记录 6 个值即可,这 6 个值分别代表包围盒在每个坐标轴上的最小值与最大值,即 xmin、xmax、ymin、ymax、zmin、zmax。

2. 光线与包围盒(AABB)的相交检测算法

下面是光线与物体相交的三种不同情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

观察上述三幅图可以得出,只要发生区间交叠,光线与平面就能相交

那么区间交叠出现的条件便是:光线==进入平面处的最大t值==小于光线==离开平面处的最小t值==

那么问题就变成了如何求光线进入平面处的最大t值以及光线离开平面处的最小t值

这个问题通过 线与平面相交参数方程求解就可以了。

  • 几何面用 一个点 和 一个法线 表示平面 (p-p’)·N = 0;
  • 将光线方程P = r(t) 代入光线方程中;
  • 求出交点,找出对应的t值;

3. 三维空间的拓展

  • 增加一对平面,判断三对面的相交时间tmin,tmax。

  • 求出tmin最大值,tmax最小值。

  • 若tmax-tmin大于0则说明光线与包围盒相交。

  • AABB有交点当且仅当tmin<tmax且tmax>=0

  • 几种tmin 或 tmax 为负值的特殊说明

    • tmax<0 说明包围盒在光线背面,无交点(我们假设光源是一个点,如果包围盒在背面是不会有光传播到的)。
    • tmax>=0 且tmin<0 说明光源在包围盒中。

  • 在下面的例子中用盒子8个顶点斜对角线的两个点的xyz坐标 作为平面的值 进行判断

4. 包围盒模型的算法

IntersectP()判断光线是否与盒子相交

// 判断光线是否与盒子相交   用盒子8个顶点斜对角线的两个点的xyz坐标 作为平面的值 进行判断
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg) const
{// invDir: ray direction(x,y,z), invDir=(1.0/x,1.0/y,1.0/z), use this because Multiply is faster that Division// dirIsNeg: ray direction(x,y,z), dirIsNeg=[int(x>0),int(y>0),int(z>0)], use this to simplify your logic// TODO test if ray bound intersectsfloat t_Min_x = (pMin.x - ray.origin.x) * invDir[0];     // 乘以每个方向的权重float t_Min_y = (pMin.y - ray.origin.y) * invDir[1];     // 于每一组平面都需要分别计算一个最小时间和最大时间,代入公式即可,// 这里的invDir分别记录了光线传播沿xyz方向的倒数,便于计算公式中的 “÷某一传播方向” 。float t_Min_z = (pMin.z - ray.origin.z) * invDir[2];   // pMix.x  .y  .z都相当于一个平面dfloat t_Max_x = (pMax.x - ray.origin.x) * invDir[0];float t_Max_y = (pMax.y - ray.origin.y) * invDir[1];float t_Max_z = (pMax.z - ray.origin.z) * invDir[2];  if (!dirIsNeg[0])   // 判断如果为负数的话,那就改变方向交换最大最小值。{float t = t_Min_x;t_Min_x = t_Max_x;t_Max_x = t;}if (!dirIsNeg[1]){float t = t_Min_y;t_Min_y = t_Max_y;t_Max_y = t;}if (!dirIsNeg[2]){float t = t_Min_z;t_Min_z = t_Max_z;t_Max_z = t;}float t_enter = std::max(t_Min_x, std::max(t_Min_y, t_Min_z));   //找到进盒子的最大值float t_exit = std::min(t_Max_x, std::min(t_Max_y, t_Max_z));    //找到出玻璃盒的最小值if (t_enter <= t_exit && t_exit >= 0)   // 判断必要条件return true;elsereturn false;
}

3. 层次包围盒BVH加速

1. 前述问题

如果三角形的面特别多,那么用Moller Trumenbore算法进行计算光线与三角形相交的计算就会变慢许多。因此我们采用包围盒的形式,先进行一个初步判断。以用来加速算法

建立网格的目的:

  • 通过网格可以判断有tmin,tmax
  • 若有 则对遍历到的网格测试与其有的交点
  • 网格密度一般认为是27x物体数量
  • 主要分类: Oct-Tree 八叉树 KD-Tree(原来用的多) BSP-Tree

由于KD树的一些问题:

  • 难以判断物体与包围盒边界的相交问题
  • 一个物体容易相对穿过多个包围盒被重复计算影响性能。
  • KD数的应用场景越来越小。
  • 因此我们使用新的更加广泛的包围盒算法(BVH)进行加速

2. BVH层次包围盒

BVH的特点:

  • 按照三角形进行划分,一个三角形只可能出现在一个包围盒内但是不同包围盒之间可能有相交
  • 思想像分组,只对三角形较多的组进行递归分组
  • 每次划分都选择XYZ最长的轴进行划分,这样划分比较均匀
  • 总选择中间的三角形进行划分,因此划分结果接近平衡二叉树
  • 划分到一个比较小的数量后停止继续划分(比如5个三角形)

在看函数实现之前我们可以看一下Intersecton的结构体:

如下结构体,关键的属性值即为happened(是否相交)coords(交点坐标)normal(交点所在平面法线)distance(光线起点到交点的距离)obj(交点所在物体的物体类型)m(交点所在物体的材料类型)

depth类似于“视深”,在后续的代码中对于可以反光的材质如会发生反射、折射或者透明的材质,会继续递归调用castRay并且把depth+1,而在最初又规定了可处理的depth的最大深度,这里认为反映了一个像素已反射其他像素的次数,即当前的光线是第几层次的间接光源,程序设定的最大depth为5,故一个像素点最多能显示五次光线反射的效果。

struct Intersection
{Intersection(){happened=false;coords=Vector3f();normal=Vector3f();distance= std::numeric_limits<double>::max();obj =nullptr;m=nullptr;}bool happened;Vector3f coords;Vector3f normal;double distance;Object* obj;Material* m;
};

实现代码:

  • 思路描述:

  • 先判断是否与当前判断的盒子边界是否与光线相交,若无交点则判断结束返回

  • 若当前节点有物体,则判断是否有物体和光线相交,有相交,则进一步判断物体和光线的交点;

  • 前两个判断完毕: 满足 1. 当前节点的盒子跟光线有相交 2. 当前节点的盒子里面没有物体, 因此需要判断大盒子的细分节点 左右两个小盒子

  • 循环递归判断左右节点重复上述步骤,最终返回与物体交点更近的Intersection

Intersection BVHAccel::Intersect(const Ray& ray) const // in BVH.cpp
{Intersection isect;if (!root)return isect;isect = BVHAccel::getIntersection(root, ray);return isect;
}Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const  // in BVH.cpp
{Intersection isect;// TODO Traverse the BVH to find intersectionif (!node->bounds.IntersectP(ray, ray.direction_inv, std::array<int, 3> {ray.direction.x > 0, ray.direction.y > 0, ray.direction.z > 0}))return isect;if (node->object != nullptr)return node->object->getIntersection(ray);Intersection isect_left, isect_right;isect_left = getIntersection(node->left, ray);isect_right = getIntersection(node->right, ray);return isect_left.distance <= isect_right.distance ? isect_left : isect_right;
}Intersection Scene::intersect(const Ray &ray) const //in Scene.cpp
{// TO DO Use BVH.cpp's Intersect function instead of the current return this->bvh->Intersect(ray);
}

4. 最终渲染函数 Render() && castRay()

1. Render()函数

Render()计算像素着色 生成图像,根据像素点,生成光线,再用生成的光线进行判断,找到交点,返回最近的颜色(然后根据计算出来的交点,通过反射折射继续不断递归计算)

在遍历所有像素的循环里,生成对应的光线并**将返回的颜色保存在帧缓冲区( framebuffer)**中。在渲染过程结束后,帧缓冲区中的信息将被保存为图像
在这里插入图片描述

void Renderer::Render(const Scene& scene)
{std::vector<Vector3f> framebuffer(scene.width * scene.height);// 在遍历所有像素的循环里,生成对应的光线并将返回的颜色保存在帧缓冲区( framebuffer)中float scale = std::tan(deg2rad(scene.fov * 0.5f));float imageAspectRatio = scene.width / (float)scene.height;// imageAspectRatio即宽高比,是原本scene的宽高比,所以要想恢复原始的比例需要用 宽 * 宽高比,即 x * imageAspectRatio。// Use this variable as the eye position to start your rays.Vector3f eye_pos(0);int m = 0;for (int j = 0; j < scene.height; ++j){for (int i = 0; i < scene.width; ++i){// generate primary ray directionfloat x ;x = (2 * ((float)i + 0.5) / scene.width - 1) * imageAspectRatio * scale ;float y;y = (1.0 - 2 * ((float)j + 0.5) / scene.height) * scale;// TODO: Find the x and y positions of the current pixel to get the direction// vector that passes through it.// Also, don't forget to multiply both of them with the variable *scale*, and// x (horizontal) variable with the *imageAspectRatio*       Vector3f dir = normalize(Vector3f(x, y, -1)); // Don't forget to normalize this direction!framebuffer[m++] = castRay(eye_pos, dir, scene, 0);// 该函数调用 trace 来查询光线与场景中最近的对象的交点 castRay返回该像素点的颜色,并存入缓冲区里 }UpdateProgress(j / (float)scene.height);}// save framebuffer to fileFILE * fp = fopen("binary.ppm", "wb");(void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);for (auto i = 0; i < scene.height * scene.width; ++i) {static unsigned char color[3];color[0] = (char)(255 * clamp(0, 1, framebuffer[i].x));color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));fwrite(color, 1, 3, fp);}fclose(fp);    
}

2. castRay()函数

// Implementation of Path Tracing
Vector3f Scene::castRay(const Ray& ray, int depth) const
{// TO DO Implement Path Tracing Algorithm hereIntersection intersection = Scene::intersect(ray);   //先得到相交的点if (intersection.happened) {Vector3f hitPoint = intersection.coords;Vector3f N = intersection.normal; // normalMaterial* m = intersection.m;Vector3f L_dir(0.0), L_indir(0.0);// Uniformly sample the light at x (pdf_light = 1 / A)Intersection intersection_light;float pdf_light;sampleLight(intersection_light, pdf_light);// Shoot a ray from p to xVector3f dir_p_x = (intersection_light.coords - hitPoint).normalized();Ray ray_p_x(hitPoint + EPSILON * N, dir_p_x);Intersection intersection_p_x = Scene::intersect(ray_p_x);if (intersection_p_x.happened && intersection_p_x.m->hasEmission()) {Vector3f NN = intersection_p_x.normal;L_dir = intersection_p_x.m->m_emission * m->eval(ray.direction, dir_p_x, N) * dotProduct(dir_p_x, N) * dotProduct(-dir_p_x, NN) / intersection_p_x.distance / pdf_light;}if (get_random_float() <= RussianRoulette) {// Trace a ray r(p, wi)Vector3f dir_i = m->sample(ray.direction, N).normalized();Ray ray_p_diri(hitPoint, dir_i);Intersection intersection_p_diri = Scene::intersect(ray_p_diri);if (intersection_p_diri.happened && !intersection_p_diri.m->hasEmission()) {L_indir = castRay(ray_p_diri, depth + 1) * m->eval(ray.direction, dir_i, N) * dotProduct(dir_i, N) / m->pdf(ray.direction, dir_i, N) / RussianRoulette;}}return m->getEmission() + L_dir + L_indir;}else {return Vector3f(0, 0, 0);}
}

5. Debug处理

1. fopen函数

3.右键项目------->Properties(属性)------->Configuration Properties(配置属性)------>C/C++ ------>Preprocessor(预处理器)------->Preprocessor Difinitions (预处理器定义) 添加 _CRT_SECURE_NO_WARNINGS 之后点击OK。最后 应用------>确定。具体操作如下。

img
原文链接:https://blog.csdn.net/join_yuan/article/details/105629292

2. C2039 “optional“: 不是 “std“ 的成员

std::optional 需要C++17.

Visual Studio:

项目 -> 属性 -> C/C++ -> 所有选项 -> 附加选项,增加:/std:c++17

gcc/clang:

添加-std=c++17

6. games101的一些前置作业:

下面是我做的一些games101的一些前置作业用以过渡

games101作业五

历史作业可以参考 作业七

在这次作业中,我们需要实现两个部分:光线的生成和光线与三角的相交。

std::optional<hit_payload> trace(const Vector3f &orig, const Vector3f &dir,const std::vector<std::unique_ptr<Object> > &objects)
{float tNear = kInfinity;std::optional<hit_payload> payload;for (const auto & object : objects){float tNearK = kInfinity;uint32_t indexK;Vector2f uvK;if (object->intersect(orig, dir, tNearK, indexK, uvK) && tNearK < tNear){payload.emplace();payload->hit_obj = object.get();payload->tNear = tNearK;payload->index = indexK;payload->uv = uvK;tNear = tNearK;}}return payload;
}Vector3f castRay(const Vector3f &orig, const Vector3f &dir, const Scene& scene,int depth)
{if (depth > scene.maxDepth) {return Vector3f(0.0,0.0,0.0);}Vector3f hitColor = scene.backgroundColor;if (auto payload = trace(orig, dir, scene.get_objects()); payload){Vector3f hitPoint = orig + dir * payload->tNear;Vector3f N; // normalVector2f st; // st coordinatespayload->hit_obj->getSurfaceProperties(hitPoint, dir, payload->index, payload->uv, N, st);switch (payload->hit_obj->materialType) {case REFLECTION_AND_REFRACTION:{Vector3f reflectionDirection = normalize(reflect(dir, N));Vector3f refractionDirection = normalize(refract(dir, N, payload->hit_obj->ior));Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?hitPoint - N * scene.epsilon :hitPoint + N * scene.epsilon;Vector3f refractionRayOrig = (dotProduct(refractionDirection, N) < 0) ?hitPoint - N * scene.epsilon :hitPoint + N * scene.epsilon;Vector3f reflectionColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1);Vector3f refractionColor = castRay(refractionRayOrig, refractionDirection, scene, depth + 1);float kr = fresnel(dir, N, payload->hit_obj->ior);hitColor = reflectionColor * kr + refractionColor * (1 - kr);break;}case REFLECTION:{float kr = fresnel(dir, N, payload->hit_obj->ior);Vector3f reflectionDirection = reflect(dir, N);Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?hitPoint + N * scene.epsilon :hitPoint - N * scene.epsilon;hitColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1) * kr;break;}default:{Vector3f lightAmt = 0, specularColor = 0;Vector3f shadowPointOrig = (dotProduct(dir, N) < 0) ?hitPoint + N * scene.epsilon :hitPoint - N * scene.epsilon;for (auto& light : scene.get_lights()) {Vector3f lightDir = light->position - hitPoint;// square of the distance between hitPoint and the lightfloat lightDistance2 = dotProduct(lightDir, lightDir);lightDir = normalize(lightDir);float LdotN = std::max(0.f, dotProduct(lightDir, N));// is the point in shadow, and is the nearest occluding object closer to the object than the light itself?auto shadow_res = trace(shadowPointOrig, lightDir, scene.get_objects());bool inShadow = shadow_res && (shadow_res->tNear * shadow_res->tNear < lightDistance2);lightAmt += inShadow ? 0 : light->intensity * LdotN;Vector3f reflectionDirection = reflect(-lightDir, N);specularColor += powf(std::max(0.f, -dotProduct(reflectionDirection, dir)),payload->hit_obj->specularExponent) * light->intensity;}hitColor = lightAmt * payload->hit_obj->evalDiffuseColor(st) * payload->hit_obj->Kd + specularColor * payload->hit_obj->Ks;break;}}}return hitColor;
}
void Renderer::Render(const Scene& scene)
{std::vector<Vector3f> framebuffer(scene.width * scene.height);// 在遍历所有像素的循环里,生成对应的光线并将返回的颜色保存在帧缓冲区( framebuffer)中float scale = std::tan(deg2rad(scene.fov * 0.5f));float imageAspectRatio = scene.width / (float)scene.height;// imageAspectRatio即宽高比,是原本scene的宽高比,所以要想恢复原始的比例需要用 宽 * 宽高比,即 x * imageAspectRatio。// Use this variable as the eye position to start your rays.Vector3f eye_pos(0);int m = 0;for (int j = 0; j < scene.height; ++j){for (int i = 0; i < scene.width; ++i){// generate primary ray directionfloat x ;x = (2 * ((float)i + 0.5) / scene.width - 1) * imageAspectRatio * scale ;float y;y = (1.0 - 2 * ((float)j + 0.5) / scene.height) * scale;// TODO: Find the x and y positions of the current pixel to get the direction// vector that passes through it.// Also, don't forget to multiply both of them with the variable *scale*, and// x (horizontal) variable with the *imageAspectRatio*            Vector3f dir = normalize(Vector3f(x, y, -1)); // Don't forget to normalize this direction!framebuffer[m++] = castRay(eye_pos, dir, scene, 0);}UpdateProgress(j / (float)scene.height);}// save framebuffer to fileFILE * fp = fopen("binary.ppm", "wb");(void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);for (auto i = 0; i < scene.height * scene.width; ++i) {static unsigned char color[3];color[0] = (char)(255 * clamp(0, 1, framebuffer[i].x));color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));fwrite(color, 1, 3, fp);}fclose(fp);    
}

三角形库

#pragma once#include "Object.hpp"#include <cstring>bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,const Vector3f& dir, float& tnear, float& u, float& v)// : v0, v1, v2 是三角形的三个// 顶点,orig 是光线的起点,    dir 是光线单位化的方向向量。tnear, u, v 是你需// 要使用我们课上推导的 Moller - Trumbore 算法来更新的参数
{// TODO: Implement this function that tests whether the triangle// that's specified bt v0, v1 and v2 intersects with the ray (whose// origin is *orig* and direction is *dir*)// Also don't forget to update tnear, u and v.Vector3f E1 = v1 - v0, E2 = v2 - v0, S = orig - v0;Vector3f S1 = crossProduct(dir, E2), S2 = crossProduct(S, E1);if (dotProduct(S1, E1) <= 0) return false;// 更新了tnear 和 u, v tnear = dotProduct(S2, E2) / dotProduct(S1, E1);u = dotProduct(S1, S) / dotProduct(S1, E1);v = dotProduct(S2, dir) / dotProduct(S1, E1);if (tnear >= 0 && u >= 0 && v >= 0 && (1 - u - v) >= 0) {return true;}return false;
}class MeshTriangle : public Object
{
public:MeshTriangle(const Vector3f* verts, const uint32_t* vertsIndex, const uint32_t& numTris, const Vector2f* st){uint32_t maxIndex = 0;for (uint32_t i = 0; i < numTris * 3; ++i)if (vertsIndex[i] > maxIndex)maxIndex = vertsIndex[i];maxIndex += 1;vertices = std::unique_ptr<Vector3f[]>(new Vector3f[maxIndex]);memcpy(vertices.get(), verts, sizeof(Vector3f) * maxIndex);vertexIndex = std::unique_ptr<uint32_t[]>(new uint32_t[numTris * 3]);memcpy(vertexIndex.get(), vertsIndex, sizeof(uint32_t) * numTris * 3);numTriangles = numTris;stCoordinates = std::unique_ptr<Vector2f[]>(new Vector2f[maxIndex]);memcpy(stCoordinates.get(), st, sizeof(Vector2f) * maxIndex);}bool intersect(const Vector3f& orig, const Vector3f& dir, float& tnear, uint32_t& index,Vector2f& uv) const override{bool intersect = false;for (uint32_t k = 0; k < numTriangles; ++k){const Vector3f& v0 = vertices[vertexIndex[k * 3]];const Vector3f& v1 = vertices[vertexIndex[k * 3 + 1]];const Vector3f& v2 = vertices[vertexIndex[k * 3 + 2]];float t, u, v;if (rayTriangleIntersect(v0, v1, v2, orig, dir, t, u, v) && t < tnear){tnear = t;uv.x = u;uv.y = v;index = k;intersect |= true;}}return intersect;}void getSurfaceProperties(const Vector3f&, const Vector3f&, const uint32_t& index, const Vector2f& uv, Vector3f& N,Vector2f& st) const override{const Vector3f& v0 = vertices[vertexIndex[index * 3]];const Vector3f& v1 = vertices[vertexIndex[index * 3 + 1]];const Vector3f& v2 = vertices[vertexIndex[index * 3 + 2]];Vector3f e0 = normalize(v1 - v0);Vector3f e1 = normalize(v2 - v1);N = normalize(crossProduct(e0, e1));const Vector2f& st0 = stCoordinates[vertexIndex[index * 3]];const Vector2f& st1 = stCoordinates[vertexIndex[index * 3 + 1]];const Vector2f& st2 = stCoordinates[vertexIndex[index * 3 + 2]];st = st0 * (1 - uv.x - uv.y) + st1 * uv.x + st2 * uv.y;}Vector3f evalDiffuseColor(const Vector2f& st) const override{float scale = 5;float pattern = (fmodf(st.x * scale, 1) > 0.5) ^ (fmodf(st.y * scale, 1) > 0.5);return lerp(Vector3f(0.815, 0.235, 0.031), Vector3f(0.937, 0.937, 0.231), pattern);}std::unique_ptr<Vector3f[]> vertices;uint32_t numTriangles;std::unique_ptr<uint32_t[]> vertexIndex;std::unique_ptr<Vector2f[]> stCoordinates;
};

games101作业六

重点关注物体 划分算法 Bounding Volume Hierarchy (BVH)。

实现 Ray-Bounding Volume 求交与 BVH 查找。

depth类似于“视深”,在后续的代码中对于可以反光的材质如会发生反射、折射或者透明的材质,会继续递归调用castRay并且把depth+1,而在最初又规定了可处理的depth的最大深度,这里认为反映了一个像素已反射其他像素的次数,即当前的光线是第几层次的间接光源,程序设定的最大depth为5,故一个像素点最多能显示五次光线反射的效果。

struct Intersection
{Intersection(){happened=false;coords=Vector3f();normal=Vector3f();distance= std::numeric_limits<double>::max();obj =nullptr;m=nullptr;}bool happened;Vector3f coords;Vector3f normal;double distance;Object* obj;Material* m;
};

如上,关键的属性值即为happened(是否相交),coords(交点坐标),normal(交点所在平面法线),distance(光线起点到交点的距离),obj(交点所在物体的物体类型),m(交点所在物体的材料类型)。

games101作业七

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117728.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对GRUB和initramfs的小探究

竞赛时对操作系统启动过程产生了些疑问&#xff0c;于是问题导向地浅浅探究了下GRUB和initramfs相关机制&#xff0c;相关笔记先放在这里了。 内核启动流程 在传统的BIOS系统中&#xff0c;计算机具体的启动流程如下&#xff1a; 电源启动&#xff1a;当计算机的电源打开时&…

『heqingchun-Qt的艺术-优雅界面设计开发』

Qt的艺术-优雅界面设计开发 效果图 一、新建Qt窗口工程 二、准备资源文件 1.图标资源 链接: 图标资源 2.Qss资源 链接: Qss资源 三、设计开发 项目源码链接: CSDN资源

JS中面向对象的程序设计

面向对象&#xff08;Object-Oriented&#xff0c;OO&#xff09;的语言有一个标志&#xff0c;那就是它们都有类的概念&#xff0c;而通过类可以创建任意多个具有相同属性和方法的对象。但在ECMAScript 中没有类的概念&#xff0c;因此它的对象也与基于类的语言中的对象有所不…

基于大数据的社交平台数据爬虫舆情分析可视化系统 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果**实现功能****可视化统计****web模块界面展示**3 LDA模型 4 情感分析方法**预处理**特征提取特征选择分类器选择实验 5 部分核心代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大数据…

Go语言入门心法(十五):Go微服务实战

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(七): 并发与通道 Go语言入门心法(八): mysql驱动安装报错o…

Redis主从模式(二)---拓扑结构及复制过程

目录 一, Redis主从模式下的复制拓扑结构 1.1 一主一从结构 1.2 一主多从结构 1.3 树形主从结构 二, 主从复制过程 2.1 主从复制建立复制流程图 2.2 数据同步(psyc) 1.replicationid/replid (复制id) 2.offset(偏移量) 2.3 psync运行流程 2.4 全量复制 2.5 部分复制…

操作教程|如何注册成为Moonbeam社区代表参与治理

社区代表是高度参与社区治理的社区成员&#xff0c;其主要职责是将社区成员委托给他们的投票权参与社区投票&#xff0c;并确保链上治理稳健发展和活跃参与度。本文将向您展示如何快速注册成为社区代表。 首先&#xff0c;前往Moonbeam委托网站&#xff0c;点击网页右上角的“…

老师们都在用的办公好物

现在还有老师不知道班级查询系统吗?各位老师们&#xff0c;向大家推荐一款超级实用的班级查询系统&#xff0c;帮你轻松管理学生信息&#xff0c;省去繁琐的手动操作&#xff0c;还能让学生们自主查询&#xff0c;简直是老师的福音&#xff01; 如果你在编程方面感到有些吃力&…

【vue3】依赖注 provide、inject(父组件与儿子、孙子、曾孙子组件之间的传值)

一、基本用法&#xff1a; //父组件 import { ref, provide } from vue const radio ref<string>(red) provide(myColor,radio) //注入依赖//儿子组件、孙子组件、曾孙子组件 import { inject } from vue import type { Ref } from vue; const myColor inject<Ref&l…

CICD 流程学习(五)Jenkins后端工程构建

案例1&#xff1a;数据库服务部署 MySQL部署 #安装MySQL服务 [rootServices ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootServices ~]# yum -y install mysql.x86_64 mysql-server.x86_64 mysql-devel.x86_64 ... Complete! [rootServices ~]# #启动…

SpringCloud复习:(3)LoadBalancerInterceptor

使用Ribbon时&#xff0c;execute方法会由RibbonLoadBalancerClient类来实现 它会调用重载的execute方法 getLoadBalancer默认会返回ZoneAwareLoadBalancer&#xff08;基类是BaseLoadBalancer).此处调用的getServer方法就会根据负载均衡策略选择适当的服务器来为下一步的htt…

计算机网络第3章-运输层(2)

可靠数据传输原理 可靠数据传输依靠数据在一条可靠信道上进行传输。 TCP也正是依靠可靠信道进行传数据&#xff0c;从而数据不会被丢失。 而实现这种可靠数据传输服务是可靠数据传输协议的责任 构造可靠数据传输协议 1.经完全可靠信道的可靠数据传输&#xff1a;rdt1.0 在…

使用性能监控软件的主要措施

性能监控软件是管理和维护计算机系统的不可或缺的工具。它们提供了实时性能数据&#xff0c;帮助用户优化资源利用、提高系统稳定性和提供更好的用户体验。选择合适的性能监控软件并正确使用它们可以帮助你在竞争激烈的数字时代脱颖而出&#xff0c;确保你的系统始终在最佳状态…

51单片机练习(04)

eg1:使用定时器的方式实现单片机流水灯 #include <REGX52.H> #include <INTRINS.H> #define uchar unsigned char #define uint unsigned int uchar temp,t0;// 初始化函数 void init(){temp 0xfe;//第一个发光二级管点亮P1 temp;// 初始化定时器TMOD 0x11;TH0…

抽丝剥茧,Redis使用事件总线EventBus或AOP优化健康检测

目录 前言 Lettuce 什么是事件总线EventBus&#xff1f; Connected Connection activated Disconnected Connection deactivated Reconnect failed 使用 一种另类方法—AOP 具体实现 前言 在上一篇深入浅出&#xff0c;SpringBoot整合Quartz实现定时任务与Redis健康…

openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户

文章目录 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户108.1 创建、修改和删除用户108.2 私有用户108.3 永久用户108.4 用户认证优先规则 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户 使用CREATE USER和ALTER USER可以创建和管理数据…

百度文心一言4.0——使用及API测试

登录百度智能云&#xff1a;百度智能云 文心一言4.0使用 开通付费&#xff1a; 创建应用&#xff1a; 自行创建应用名称&#xff1a; 对话测试&#xff1a; API测试 ERNIE-Bot-4 API&#xff1a;ERNIE-Bot-4 打开链接查看自己的API Key&#xff0c;Secret Key。 可参…

高速下载b站视频的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

JavaWeb——Servlet原理、生命周期、IDEA中实现一个Servlet(全过程)

6、servlet 6.1、什么是servlet 在JavaWeb中&#xff0c;Servlet是基于Java编写的服务器端组件&#xff0c;用于处理客户端&#xff08;通常是Web浏览器&#xff09;发送的HTTP请求并生成相应的HTTP响应。Servlet运行在Web服务器上&#xff0c;与Web容器&#xff08;如Tomcat&…

如何平衡 Wi-Fi 7 与未来 5G/6G 的发展?

随着社会进步&#xff0c;人们对信息技术的需求不断提升。当前互联网、大数据、云计算、人工智能、区块链等新技术深刻演变&#xff0c;产业数字化、智能化、绿色化转型不断加速&#xff0c;智能产业、数字经济蓬勃发展&#xff0c;极大改变全球要素资源配置方式、产业发展模式…