【GAMES101】作业5——光线与三角形相交
作业内容:实现的内容为求光线与物体的交点,需要实现的函数如下
Renderer.cpp 中的 Render():需要为每个像素生成一条对应的光线,然后调用函数 castRay() 来得到颜色,最后将颜色存储在帧缓冲区的相应像素中
Triangle.hpp 中的 rayTriangleIntersect(): v0, v1, v2 是三角形的三个顶点,orig 是光线的起点,dir 是光线单位化的方向向量。tnear, u, v 是需要使用我们课上推导的 Moller-Trumbore 算法来更新的参数
main函数整体框架
int main() { //创建一个1280*960的场景 Scene scene(1280, 960); //make_unique智能指针 创建一个中心为(-1,0,-12)半径为2的球 并返回该指针 auto sph1 = std::make_unique<Sphere>(Vector3f(-1, 0, -12), 2); //设定Sphere类继承object类的材质类型:漫反射和高光 sph1->materialType = DIFFUSE_AND_GLOSSY; //设定漫反射系数 sph1->diffuseColor = Vector3f(0.6, 0.7, 0.8); //另一个球 auto sph2 = std::make_unique<Sphere>(Vector3f(0.5, -0.5, -8), 1.5); //设定折射率 sph2->ior = 1.5; //设定材质类型:反射与折射 sph2->materialType = REFLECTION_AND_REFRACTION; //将物体加入scene 强制右值引用 scene.Add(std::move(sph1)); scene.Add(std::move(sph2)); //加入地板 //地板的四个顶点 Vector3f verts[4] = {{-5,-3,-6}, {5,-3,-6}, {5,-3,-16}, {-5,-3,-16}}; //unsigned int uint32_t vertIndex[6] = {0, 1, 3, 1, 2, 3}; Vector2f st[4] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; auto mesh = std::make_unique<MeshTriangle>(verts, vertIndex, 2, st); //设置材质为漫反射与高光 mesh->materialType = DIFFUSE_AND_GLOSSY; //加入地板 scene.Add(std::move(mesh)); //加入光源 scene.Add(std::make_unique<Light>(Vector3f(-20, 70, 20), 0.5)); scene.Add(std::make_unique<Light>(Vector3f(30, 50, -12), 0.5)); Renderer r; //渲染 r.Render(scene); return 0; }
Render()实现
需要实现屏幕空间对于scene上的映射,分为两步
第一步:将栅格空间映射到[-1,1]^2范围
1. 先对i,j加上0.5获取像素点的坐标,且由于像素点的范围为(width-1,height-1),故除以其使得目前空间范围在x∈[0,1],y∈[0,1]上,即此时x=(i+0.5)/(width-1) y=(j+0.5)/(height-1)
2.将x∈[0,1],y∈[0,1]转换到x∈[-1,1],y∈[-1,1]上,对x,y乘以2减1即可,此时x=2*(i+0.5)/(width-1)-1 y=2*(j+0.5)/(height-1)-1
3.由于给的框架中y的初始坐标是在上方,需要对y取-1,不然图像是上下颠倒的,此时x=2*(i+0.5)/(width-1)-1 y=1-2*(j+0.5)/(height-1)
第二步:投影
从Vector3f dir = Vector3f(x, y, -1)可知,我们视角到成像的平面距离为1,利用forY(框架中为scale)与AspectRatio算出最终位置
x=tan(forY/2)*AspectRatio*[2*(i+0.5)/(width-1)-1] y=[1-2*(j+0.5)/(height-1)]*tan(forY/2)
float scale = std::tan(deg2rad(scene.fov * 0.5f));//for=90 float imageAspectRatio = scene.width / (float)scene.height; // 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 direction float x; float y; //查找栅格空间在屏幕像素点上的映射 x = scale * imageAspectRatio * (2 * ((i + 0.5f) / (float)scene.width - 1) - 1); y = scale * (2 * (j + 0.5f) / (float)height - 1) - 1); Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction! dir = normalize(dir); framebuffer[m++] = castRay(eye_pos, dir, scene, 0); } UpdateProgress(j / (float)scene.height); }
rayTriangleIntersect()实现
P0,P1,P2为三角形顶点 O为射线顶点 D为射线方向,直接套公式就可以了
bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig, const Vector3f& dir, float& tnear, float& u, float& v) { Vector3f e1 = v1 - v0; Vector3f e2 = v2 - v0; Vector3f s = orig - v0; Vector3f s1 = crossProduct(dir, e2); Vector3f s2 = crossProduct(s, e1); float t = dotProduct(s2, e2) / dotProduct(s1, e1); float b1 = dotProduct(s1, s) / dotProduct(s1, e1); float b2 = dotProduct(s2, dir) / dotProduct(s1, e1); if (t > 0.0 && b1 > 0.0 && b2 > 0.0 && (1 - b1 - b2) > 0.0) { tnear = t; u = b1; v = b2; return true; } // 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. return false; }
结果图片:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了