c++如何从零实现一个光线追踪器_c++ Ray Tracing in One Weekend实践【项目】
技术百科
冰火之心
发布时间:2025-12-12
浏览: 次 实现光线追踪器的核心是理解光与物体交互的物理过程,并用C++将其转化为数学逻辑;RTOW系列以vec3、ray、hittable为支柱,通过相机模型、抗锯齿、材质系统和递归追踪,用极简代码实现从光线发射到像素着色的完整流程。
从零实现一个光线追踪器,核心不在于写多少代码,而在于理解“光如何与物体交互”这一物理过程,并用 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,其中 u,v ∈ [0,1]
+ v*vertical) - 抗锯齿靠多采样:每个像素不只发 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)。典型例子:
- 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——那些不再是黑箱,而是你亲手调过的参数和公式。
# 这是
# 这一
# 多个
# 第一个
# 你会
# 每条
# 递归
# cos
# 堆
# c++
# 接口
# red
# 封装
# Struct
# Float
# 加减乘除
# 渲染器
# 抗锯齿
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何使用Golang实现路由参数绑定_使用Mux和
- Win11怎么设置虚拟内存最佳大小_Windows
- 如何使用Golang实现路由分组管理_Golang
- MySQL 中使用 IF 和 CASE 实现查询字
- Win11怎么打开注册表_Windows 11注册
- 如何在 PHP 单元测试中正确模拟带方法的图像处理
- Windows10怎样设置家长控制_Windows
- Avalonia如何实现跨窗口通信 Avaloni
- Win11如何卸载OneDrive_Win11卸载
- Win11怎样安装搜狗输入法_Win11安装搜狗输
- Win11怎么关闭自动维护 Win11禁用系统自动
- Win11怎么设置桌面图标间距_Windows11
- MAC怎么用连续互通相机里的“桌上视角”_MAC在
- 如何在 Go 中判断变量是否为函数类型
- c++ namespace命名空间用法_c++避免
- Win10系统更新错误0x80240034怎么办
- 获取 PHP 文件最后修改时间的正确方法
- Windows怎样拦截WPS弹窗广告_Window
- Windows10系统怎么查看运行时间_Win10
- Win11怎么关闭OneDrive同步_Win11
- php订单日志权限怎么设_php订单日志文件权限设
- php转mp4怎么设置帧率_调整php生成mp4视
- Win11怎么清理C盘OneDrive缓存_Win
- Win10怎样清理C盘浏览器缓存_Win10清理浏
- Win11怎么设置DNS服务器_Windows11
- 如何使用Golang安装依赖库_管理模块和第三方包
- Windows 11怎么更改锁屏超时时间_Wind
- c++中如何使用auto关键字_c++11类型推导
- Win11怎么关闭系统透明度_Windows11个
- Win11怎么设置开机自动连接宽带_Windows
- c++如何实现一个高性能的环形队列(Ring Bu
- 如何在Golang中实现服务熔断与限流_Golan
- Win11怎么设置系统还原_Windows11系统
- C#如何使用XPathNavigator高效查询X
- c++的mutex和lock_guard如何使用
- Win11怎么查看显卡显存_查询Win11显卡详细
- Win11怎么关闭自动调节亮度 Win11禁用内容
- Win11怎么设置虚拟内存_Windows 11优
- Windows10如何删除恢复分区_Win10 D
- 如何在 Laravel 中通过嵌套关联关系进行 o
- 如何使用Golang实现基本类型比较_Golang
- 如何解决Windows字体显示模糊的问题?(Cle
- MAC如何修改默认应用程序_MAC文件后缀关联设置
- Win10如何更改开机密码_Windows10登录
- Win11怎么查看已连接wifi密码 Win11查
- windows如何禁用驱动程序强制签名_windo
- Win11 C盘满了怎么清理 Win11磁盘清理和
- Win10怎么卸载鲁大师_Win10彻底卸载鲁大师
- 如何使用Golang实现文件追加操作_向已有文件追
- c++ try_emplace用法_c++ map

+ v*vertical)
QQ客服