当前位置: 首页 > article >正文

3.27【A】cv homework

void Renderer::Render(const Scene& scene) {
    // ... 其他代码

    for (int j = 0; j < scene.height; ++j) {
        for (int i = 0; i < scene.width; ++i) {
            // 计算当前像素的 x 和 y
            float x = (2.0f * (i + 0.5f) / scene.width - 1.0f) * imageAspectRatio * scale;
            float y = (1.0f - 2.0f * (j + 0.5f) / scene.height) * scale;

            // 生成光线方向并归一化
            Vector3f dir = Vector3f(x, y, -1).normalized();

            framebuffer[m++] = castRay(eye_pos, dir, scene, 0);
        }
        // 更新进度
    }

    // 保存图像...
}bool rayTriangleIntersect(
    const Vector3f& v0, const Vector3f& v1, const Vector3f& v2,
    const Vector3f& orig, const Vector3f& dir,
    float& tnear, float& u, float& v
) {
    Vector3f edge1 = v1 - v0;
    Vector3f edge2 = v2 - v0;
    Vector3f pvec = crossProduct(dir, edge2);
    float det = dotProduct(edge1, pvec);

    // 行列式接近零时无解(光线与三角形平行)
    if (std::fabs(det) < 1e-8)
        return false;

    float inv_det = 1.0f / det;
    Vector3f tvec = orig - v0;
    u = dotProduct(tvec, pvec) * inv_det;
    if (u < 0.0f || u > 1.0f)
        return false;

    Vector3f qvec = crossProduct(tvec, edge1);
    v = dotProduct(dir, qvec) * inv_det;
    if (v < 0.0f || u + v > 1.0f)
        return false;

    float t = dotProduct(edge2, qvec) * inv_det;

    // 检查 t 是否有效且是更近的交点
    if (t > 0.0f && t < tnear) {
        tnear = t;
        return true;
    }

    return false;
}在 Render() 函数中,需要为每个像素生成对应的光线方向。关键步骤如下:

像素坐标到相机空间坐标的转换:

计算归一化设备坐标 (NDC)。

考虑图像的宽高比 (imageAspectRatio) 和视场角 (scale)。

光线方向的计算:

使用透视投影模型,光线起点为相机原点 eye_pos。

对生成的光线方向进行归一化。使用 Möller-Trumbore 算法 判断光线是否与三角形相交,并计算交点参数 tnear、u、v。

算法步骤:

计算三角形的两条边向量 edge1 和 edge2。

计算行列式 det,判断光线是否与三角形平行。

计算重心坐标 u 和 v,并判断是否在合法范围内。

计算交点距离 t,更新最近交点参数。

MeshTriangle 的本质与定义

  • 与单个Triangle的区别
    • 单个Triangle:仅表示一个孤立的三维平面,无法描述曲面或复杂几何体。
    • MeshTriangle:由顶点(Vertices)​三角形索引(Triangles Index)​拓扑关系组成的网格结构,通过大量三角形逼近物体表面

在render过程中,首先会依据场景大小创建一个帧缓冲区,其用于存储渲染结果,接着计算一些场景参数,然后遍历场景中的每个位置,确定其像素x,y的值,对于x,先使其归一化到[-1,1]区间上(+0.5是为了使其处于像素中心),对于y同理,只不过是归一化到[1,-1]区间上,因为屏幕坐标系通常原点在左上角,此处需翻转;然后对x乘imageAspectRatio,即修正宽高比,确保为正方形像素,对x和y都要根据视场角缩放成像平面大小。

然后就是在该点计算光线追踪(此处需将光线方向归一化,因为dir 必须是单位向量,否则 tNear的物理意义会被破坏),调用castRay函数,并将结果保存进帧缓冲区中

接下来对于castRay函数,其实际是一个递归函数,其返回值为当前像素在经过光线追踪后的颜色值Vector3f hitColo,其首先被默认为scene的背景颜色,然后开始由trace函数遍历场景中的所有物体,找到与光线相交的最近交点hitPoint,接着就是由getSurfaceProperties来提取这个点的表面属性,最主要的是获取其材质类型,后面依据材质类型来决定光照计算的方式

对于反射与折射材质REFLECTION_AND_REFRACTION,其需要计算(产生)两类光线,一种是反射,一种是折射,其方向分别由reflect与refract函数确定,最后和render开始时一样进行一次归一,接着为了解决自交问题,对两种新光线起点进行了一次微调,然后就是再次调用castRay函数去递归追踪新的反射与折射光线对场景内物体的影响。最后,确定好反射光与折射光对颜色的影响后,由fresnel函数计算出反射系数kr,并完成反射光与折射光颜色的混合,即为该材质的最终颜色hitcolor。

对于纯反射材质REFLECTION,和上面一种唯一区别就是少了折射,对反射的计算过程一摸一样,就不再赘述,

对于剩下的默认情况,也即漫反射与Phong材质,和镜面反射材质不同,其需要遍历场景中的所有光源,考虑所有光源对其颜色产生的影响。对于每个光源,计算出该点到光源的方向向量与距离,然后同样是借由trace函数来检测该点到光源的路径上是否有物体,即是否发生了遮挡,若存在遮挡,则该光源不贡献,否则按Lambert定律计算光,然后依据Phong模型模拟该光源的镜面反射效果。遍历完所有光源后,依据漫反射系数kd和镜面反射系数Ks来合成最后的漫反射颜色。

这就是castRay函数的逻辑,接下来说明一下里面关键的trace函数:

其作用是遍历场景中的所有物体,找到与给定光线(起点orig,方向为dir)相交的最近交点,若存在交点会返回包含交点信息的hit_payload(包含物体指针,距离tnear,和纹理坐标等),不然就是Nullptr;在遍历时,就是依据各个物体的相交检测方法intersect计算出一个tNearK,然后比较当前的tNear来决定存在物体层叠时最近的交点。

object是一个父虚类,继承自object的物体都需要实现自己的intersect检测方法。先读球类Sphere的,其主要就是求解将光线方程带入球体方程后的一个式子,若无解则说明不存在交点,否则得到两解t0,t1,t0为较小的近解,t1为较远的;若t0<0说明光源在球体内部,采用t1解,若t1仍小于0,则判为无解。接下来就是需要自己实现的三角形判别,不过这里的三角形和前几次作业的三角形不太一样,这个是MeshTriangle,它主要是由顶点,三角形索引组成的,就是给定空间中一堆散点然后由三角形索引来指示如何组成三角形,从而构成复杂的几何体。那么它的intersect方法,就需要不断遍历所有的三角形,使用rayTriangleIntersect函数来决定光线到底离几何体meshTriangle的哪个三角形最近。在rayTriangleIntersect函数中,主要就是依据克莱姆法则和向量混合积推导出的方程来求解。

射线和三角形的相交检测(ray triangle intersection test)【转】 - CC影子 - 博客园

以上就是光线追踪整体的代码框架,render最后会将帧缓冲区里的像素写入到一个ppm格式的文件中,经工具转换为png文件后与文档对比,可发现实现正确。


http://www.kler.cn/a/611608.html

相关文章:

  • 手撕LRU缓存Java版(带输入输出)
  • 《基于机器学习发电数据电量预测》开题报告
  • 学习本地部署DeepSeek的过程(基于LM Studio)
  • 自然语言处理|金融舆情解析:智能事件抽取与风险预警之道
  • 算法-动态规划三
  • 学习threejs,使用Sprite精灵、SpriteMaterial精灵材质
  • 企业内训|DeepSeek技术革命、算力范式重构与场景落地洞察-某头部券商
  • 精选10个好用的WordPress免费主题
  • ELK stack基础架构
  • Android TextView实现跑马灯效果性能优化
  • 用shell脚本,批量备份MySQL中所有数据库,并批量还原!
  • asp.net进销存软件WEB进销存ERP软件库存玻璃行业
  • SQLite优化实践
  • 【设计模式】策略模式(Strategy Pattern)详解
  • 5分钟快速上手Docker容器化部署:从零到实践
  • 带你刷题—公因子的数目(leetcode2427)
  • docker-操作实战
  • Visual Studio 使用 IntelliCode AI 辅助代码开发
  • 【CUDA】mnist_cuda
  • 模块学习篇#2:解析常用于YOLO等深度学习模型的注意力机制CBAM