0

0

c++如何从零实现一个光线追踪器_c++ Ray Tracing in One Weekend实践【项目】

冰火之心

冰火之心

发布时间:2025-12-12 21:59:31

|

897人浏览过

|

来源于php中文网

原创

实现光线追踪器的核心是理解光与物体交互的物理过程,并用C++将其转化为数学逻辑;RTOW系列以vec3、ray、hittable为支柱,通过相机模型、抗锯齿、材质系统和递归追踪,用极简代码实现从光线发射到像素着色的完整流程。

c++如何从零实现一个光线追踪器_c++ ray tracing in one weekend实践【项目】

从零实现一个光线追踪器,核心不在于写多少代码,而在于理解“光如何与物体交互”这一物理过程,并用 C++ 把它一步步翻译成可计算的数学逻辑。《Ray Tracing in One Weekend》(RTOW)系列正是为此设计:它跳过图形学大部头理论,用极简的 C++ 代码带你亲手发出第一条光线、算出第一个像素颜色。下面是以该项目为蓝本、面向初学者的实践路径——不堆概念,只讲你敲代码时真正需要知道的事。

用最简结构启动:Vec3 + Ray + Hittable 是三大支柱

RTOW 的起点不是 OpenGL 或 Vulkan,而是一个三维向量类 vec3(封装加减乘除、点积、叉积、归一化),一条射线 ray(原点 + 方向),以及一个抽象接口 hittable(表示“能被光线打中的东西”,比如球)。这三者构成整个渲染器的骨架:

  • vec3 不要手写除法重载:除以标量容易出错,建议统一用 /= s* (1.0/s);归一化前务必检查长度是否为 0
  • ray 的方向不必单位化:后续求交时用参数 t 判断远近,方向向量是否单位化只影响 t 的物理意义,不影响结果。但若做余弦加权采样(如 diffuse 材质),方向就得单位化
  • hittable 接口只需一个 hit() 函数:输入 ray 和 t 的搜索区间 [t_min, t_max],输出是否相交、交点位置、法向、材质等。球体是最简单的实现——解一元二次方程即可

逐像素发射光线:从相机到图像缓冲区

没有“场景管理器”或“渲染管线”,只有一台针孔相机(camera)和一块二维图像缓冲区(vector)。关键步骤是把每个像素中心映射成一条世界空间中的射线:

  • 相机模型用 three-vector 表达:origin(镜头位置)、lower_left_corner(成像平面左下角)、horizontal(一行像素宽度)、vertical(一列像素高度)。这样每条射线就是 ray(origin, lower_left_corner + u*horizontal + v*vertical),其中 u,v ∈ [0,1]
  • 抗锯齿靠多采样:每个像素不只发 1 条光,而是随机生成多个 (u,v) 偏移(如 4×4 子像素),对每条光计算颜色后取平均。别用规则网格,用随机或分层采样(halton 序列更优)
  • 颜色用 RGB 三元组直接存:不用 float[3] 或 struct color —— RTOW 里 vec3 就是 color。记得伽马校正:输出前对每个通道做 sqrt(color.x)(或 pow(x, 1/2.2))

材质与光照:让物体“看起来不一样”

纯色球体太单调。RTOW 引入了最简材质系统:每个 hittable 持有一个 shared_ptr,material 定义两个行为——是否散射(scatter)、是否发光(emitted)。典型例子:

CoCo
CoCo

智谱AI推出的首个有记忆的企业自主Agent智能体

下载

立即学习C++免费学习笔记(深入)”;

  • lambertian(漫反射):hit 后生成一条新方向(在法向半球内均匀或余弦加权随机),衰减系数 = albedo × cos(θ),这是能量守恒的关键
  • metal(金属):方向 = 反射向量 + 小扰动(模拟粗糙度),衰减 = albedo;若反射方向指向物体内部(dot(反射, 法向) ≤ 0),则不散射(吸收)
  • dielectric(玻璃):需判断是否全反射(用斯涅尔定律+临界角),否则按 Fresnel 公式混合反射/折射比例。注意折射方向需根据入射侧调整法向符号

加速收敛:递归深度 + 蒙特卡洛降噪

每条光线反弹一次就停?那只有直接光照。真实效果需要递归追踪(但不能无限):

  • 设最大递归深度(如 50):超过即返回黑色(或背景色)。更稳的做法是俄罗斯轮盘(Russian Roulette):每层以概率 p 继续追踪,否则终止并把当前 radiance 除以 p 补偿期望值
  • 每像素多条光线 + 多帧累积:单帧 100 样本仍噪,但保存中间结果(如 PPM 格式支持增量写入),跑几百帧后导出最终图。OpenEXR 格式更适合保存高动态范围中间结果
  • 别急着优化 BVH:前几章完全用 list 暴力遍历。100 个球体下每帧仍秒出。等你加上 10000 个三角面再考虑包围盒层次结构

基本上就这些。RTOW 的魔力在于:它不教你“工业级渲染器怎么写”,而是让你在 300 行核心代码里,亲眼看见光线如何从相机出发、弹跳、衰减、最终变成屏幕上的一个像素。写完第一版后,你会自然想加阴影、纹理、HDR 环境光、甚至简易 BRDF——那些不再是黑箱,而是你亲手调过的参数和公式。

相关专题

更多
css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

571

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

100

2025.10.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1049

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

86

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

456

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

11

2026.01.19

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

392

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

572

2023.08.10

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 7.2万人学习

C 教程
C 教程

共75课时 | 4.2万人学习

C++教程
C++教程

共115课时 | 13.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号