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

告别‘纸片感’!用C++手撸一个带虚焦模糊的光线追踪相机(附完整代码)

告别‘纸片感’!用C++手撸一个带虚焦模糊的光线追踪相机(附完整代码)

你是否曾经觉得自己的光线追踪渲染图缺少一丝真实感?那些完美聚焦的物体虽然清晰,却总给人一种"纸片"般的扁平感。今天,我们将一起探索如何通过实现虚焦模糊(Defocus Blur)来为你的渲染图注入电影级的景深效果。这不仅能让你的作品更加接近真实摄影效果,还能显著提升场景的立体感和艺术表现力。

1. 虚焦模糊背后的光学原理

在真实世界中,没有任何相机能够同时让所有距离的物体都保持完美聚焦。这种选择性聚焦的效果正是我们所说的"景深",而在光线追踪领域,我们更倾向于使用"虚焦模糊"这个术语来描述这种现象。

薄透镜模型是我们实现虚焦模糊的基础。想象一下:

  • 光线从场景中的一点发出
  • 通过透镜的不同位置
  • 汇聚到传感器上的一个点

这个过程中有三个关键参数影响着最终的模糊效果:

  1. 光圈大小:控制透镜的有效开口直径
  2. 焦距:从透镜到完美聚焦平面的距离
  3. 聚焦距离:相机到需要清晰呈现的物体的距离

提示:在真实相机中,改变聚焦距离通常需要物理移动镜头位置,而在我们的虚拟相机中,这一切都可以通过数学计算来实现。

2. 相机类的关键扩展

为了实现虚焦模糊效果,我们需要对传统的针孔相机模型进行扩展。以下是改造后的camera类核心新增成员:

class camera { public: // ...原有成员... double defocus_angle = 0; // 虚化圆锥的角度 double focus_dist = 10; // 完美聚焦平面的距离 private: // ...其他私有成员... vec3 defocus_disk_u; // 虚化圆盘的水平半径向量 vec3 defocus_disk_v; // 虚化圆盘的垂直半径向量 point3 defocus_disk_sample() const { auto p = random_in_unit_disk(); return center + (p[0] * defocus_disk_u) + (p[1] * defocus_disk_v); } };

初始化过程中需要计算的关键步骤:

  1. 根据defocus_anglefocus_dist计算虚化圆盘半径
  2. 确定圆盘在相机坐标系中的方向向量
  3. 为后续随机采样做好准备
void camera::initialize() { // ...其他初始化代码... // 计算虚化圆盘的基础向量 auto defocus_radius = focus_dist * tan(degrees_to_radians(defocus_angle / 2)); defocus_disk_u = u * defocus_radius; defocus_disk_v = v * defocus_radius; }

3. 光线生成算法的改造

传统的光线追踪相机所有光线都从单一的相机中心点发出。要实现虚焦模糊,我们需要让光线从虚化圆盘上的随机点发出。

改造后的get_ray函数

ray camera::get_ray(int i, int j) const { auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v); auto pixel_sample = pixel_center + pixel_sample_square(); auto ray_origin = (defocus_angle <= 0) ? center : defocus_disk_sample(); auto ray_direction = pixel_sample - ray_origin; return ray(ray_origin, ray_direction); }

这个改造引入了几个重要概念:

  1. 虚化圆盘采样:当defocus_angle大于0时,光线从圆盘上的随机点发出
  2. 聚焦逻辑:所有光线都指向焦平面上的同一点,确保聚焦区域清晰
  3. 模糊程度控制defocus_angle越大,圆盘半径越大,模糊效果越明显

4. 随机采样与单位圆盘生成

为了实现从虚化圆盘的均匀采样,我们需要一个能在单位圆盘内生成随机点的函数。这与常见的单位球体采样不同,需要特别注意。

高效的单位圆盘采样实现

vec3 random_in_unit_disk() { while (true) { auto p = vec3(random_double(-1,1), random_double(-1,1), 0); if (p.length_squared() < 1) return p; } }

这个函数采用拒绝采样法:

  1. 在[-1,1]范围内随机生成x和y坐标
  2. 检查点是否落在单位圆内
  3. 如果不在圆内,则重新生成

注意:虽然看起来有无限循环的风险,但实际上数学上保证了一定会在有限次尝试后返回有效点。

5. 参数调优与效果对比

正确设置相机参数对获得理想的虚焦模糊效果至关重要。下面是一个参数配置示例及其对应的视觉效果:

参数视觉效果描述
defocus_angle0无模糊,所有物体都清晰
defocus_angle5.0轻微模糊,背景略有虚化
defocus_angle10.0明显模糊,电影级景深效果
focus_dist3.4聚焦在近处物体
focus_dist10.0聚焦在远处物体

典型场景配置

camera cam; cam.aspect_ratio = 16.0 / 9.0; cam.image_width = 400; cam.samples_per_pixel = 100; cam.max_depth = 50; cam.vfov = 20; cam.lookfrom = point3(-2,2,1); cam.lookat = point3(0,0,-1); cam.vup = vec3(0,1,0); cam.defocus_angle = 10.0; // 明显的虚焦效果 cam.focus_dist = 3.4; // 聚焦在距离相机3.4单位的位置

6. 性能考量与优化技巧

虚焦模糊虽然能显著提升视觉效果,但也会增加渲染时间。以下是几个优化建议:

  1. 采样数平衡

    • 增加defocus_angle时需要相应增加samples_per_pixel
    • 过大模糊角度会导致噪点增加
  2. 聚焦距离选择

    • 聚焦在场景中的主要兴趣点上
    • 次要元素可以适当模糊以减少计算量
  3. 渐进式渲染

    • 先使用低采样数预览效果
    • 确定参数后再进行高质量渲染
// 快速预览配置 camera preview_cam; preview_cam.samples_per_pixel = 10; preview_cam.max_depth = 5; preview_cam.defocus_angle = 10.0; // 最终渲染配置 camera final_cam; final_cam.samples_per_pixel = 100; final_cam.max_depth = 50; final_cam.defocus_angle = 10.0;

7. 艺术化应用与创意效果

虚焦模糊不仅是物理准确的模拟,更是强大的艺术表达工具。你可以尝试:

  • 选择性聚焦:引导观众视线到场景关键元素
  • 极浅景深:创造微缩模型般的特殊效果
  • 动态模糊:结合相机移动模拟运动模糊

创意参数组合

// 极浅景深效果 cam.defocus_angle = 15.0; cam.focus_dist = 1.5; // 全景清晰效果 cam.defocus_angle = 0.5; cam.focus_dist = 20.0;

在实际项目中,我发现defocus_angle在5-15度之间最能平衡真实感和艺术效果。过大的角度虽然戏剧性强,但会失去太多细节;而过小的角度又难以产生明显的景深效果。

http://www.jsqmd.com/news/519557/

相关文章:

  • 深入理解 synchronized:到底锁的是谁?
  • 2026冲刺用!全场景通用降AIGC平台 千笔·专业降AIGC智能体 VS 灵感ai
  • 【WebRTC】Webrtc-streamer实战:从RTSP到WebRTC的低延迟流媒体转发
  • IGMP V2
  • 随笔3
  • COMSOL锂枝晶应力模型:到手即用
  • 移远EC20模组TCP/IP通信实战:从AT指令到数据透传的完整流程(附常见错误排查)
  • 深度解析EEGNet中的可分离卷积:原理剖析与PyTorch实现技巧
  • 实测对比后 8个降AI率平台:毕业论文全流程必备测评与推荐
  • JavaWeb ——HttpServletRequest 请求对象(附代码)
  • OpenCloudOS 8实战:从零构建高性能WordPress企业官网
  • 高效SRT字幕转Word解决方案:一键批量处理doc与docx格式
  • Excel二维查表插值计算:从INCA到Excel的完整迁移指南(附工具下载)
  • 看完就会:全学科适配的降AI率网站 千笔·降AI率助手 VS Checkjie
  • DDR5内存排错指南:利用EpRC计数器定位故障内存条的物理位置
  • 这份榜单够用!10个降AIGC软件测评:开源免费必看,帮你高效降AI率
  • JavaWeb —— 过滤器 (Filter) 与监听器 (Listener) 全解析(附代码)
  • 别再只用pretrained=True了!timm库加载模型权重的5种实战姿势(附避坑清单)
  • 深入解析UDS(ISO14229) 0x34服务:RequestDownload的数据传输机制与工程实践
  • 3DSlicer实战:从零开始完成冠脉精准分割
  • 告别低效繁琐!普遍认可的降AI率平台 —— 千笔·专业降AIGC智能体
  • 单相并网逆变器MATLAB仿真:离网仿真与PLL锁相环下的电感电流谐波含量THD分析
  • 手把手教你用金蝶云苍穹插件搞定单据列表与动态表单的交互难题
  • PIM Sparse 模式
  • 论文省心了!10个降AIGC工具测评:开源免费,轻松降AI率过关
  • BLDC直流无刷电机FOC控制:Matlab/Simulink中的磁场定向控制实现
  • 豆包上怎么出现自己的公司?2026企业AI获客实操手册 - 品牌2026
  • WordPress中销售倒计时html小部件
  • 【手把手教程】阿里云OpenClaw一键部署指南,两步解锁龙虾AI助理!
  • 【节点】[SampleTexture3D节点]原理解析与实际应用