光线追踪:原理与实现

cover.jpg

版权声明

  • 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明
  • 文章内容不得删减、修改、演绎
  • 本文视频版本:见文末

幻灯片41.JPG

  • 各位同学大家好,今天我要给大家分享的是光线追踪的原理和实现
  • 大家知道在过往很多年里面,光线追踪技术一般只能用于离线渲染
  • 但是由于硬件发展的速度非常快,大家知道摩尔定律是每一年半硬件的性能会增长一倍
  • 但是现在像英伟达显卡,可能半年或者一年就会推出性能翻倍的显卡
  • 所以已经完全打破了摩尔定律
  • 那按照这个规律,可能也过不了多久,光线追踪技术就可以用在实时游戏当中
  • 实际上现在有一些游戏像赛博朋克 2077 已经用上了支持光追技术
  • 所以今天我就要给大家分享光线追踪的原理和实现,帮助大家能跟上未来图形学的发展

幻灯片2.JPG
今天我们课程的议题主要有这样一些:

  1. 基本光线追踪的实现方式
  • 包括基本光锥代码架构
  1. 光锥性能优化
  • 光追的性能开销主要来源于跟踪的射线数量以及射线反弹的次数,那么如何对光锥进行性能优化呢?
  1. 光锥效果优化
  • 光锥在实现效果的时候,算法特别简单,不需要去用各种各样的trick来做优化。但是由于光追的性能限制,如何在保证性能的前提下去优化效果实现,是一个问题
  1. 基于基本形体的光锥介绍
  • 在前面,我会以一些简单的基本形体,比如:球形立方体去介绍基本光锥算法,但是如何基于任意物体表面来进行光线追踪呢?
  1. 光锥的镜面反射和后处理
  • 我会讲到如何给光锥去添加镜面反射效果,而不仅仅是拥有自发光或者是漫反射
  1. 光追后处理
  • 最后一步我们会讲到如果要做后处理,那么运用光锥应该怎么做。光锥做后处理跟前面我们讲的一样,就是它做起来特别的简单,不需要特殊的trick。

幻灯片3.JPG

光追基本原理

幻灯片4.JPG

  • 光线追踪认为:我们屏幕上面每一个的像素,是我们在光线追踪时的目标方向
  • 我们从摄像机所在的位置向屏幕的每一个像素点发射一条射线,并且沿着这条射线的方向一直行进
  • 然后根据这个射线碰到了哪些物体以及碰到的这个物体反弹的光线信息,去决定最终我们在这个像素点的位置究竟应该如何着色

幻灯片5.JPG

  • 下面我们先从一个最物理的角度去理解一下光照的情形
  • 大家看现在这个图,其实从原理上来说,从物理正确的角度来说,光是由于光源向360度角,没有特定方向性发射光线照射在物体表面
  • 比如说这边这个光源,那么我从光源位置发射出一束光线
  • 然后光线经过无数次的反弹,比如说它可能反弹到这个位置,然后就反弹到这个位置
  • 经过无数次的反弹,最后假设有一束的光线最终射入到了我们的眼睛,那么我们就看见了光
  • 大致的原理是这样
  • 但是实际上如果我们这样去进行光线追踪的话,那么它的性能实在是太低了
  • 因为你想一下,你的这个灯光要往360度角无死角的去发出光线
  • 然后去追踪每一束光线它的一个反射再反射再反射的情况
  • 即使不考虑反弹,由于它是向全方向去发射光线,它的计算开销仍然很大

幻灯片6.JPG

  • 所以实际上图形学当中,我们并不是这样去进行光对的
  • 而是从我们的摄像机,也就是我们的眼睛去发射光线,然后计算光线经过的物体表面的颜色
  • 比如说我们的眼睛看到了这样一块地板,这个地板是紫色的,那么好,我就知道在这个位置是可以获得一个紫色的光
  • 那么类似的假设我的眼睛是照在这个位置,但是由于这个光线它的来源可能是另外一个发光的物体,所以我还需要把这个发光物体也考虑进去
  • 我需要考虑灯光的多次反弹的组合的结果
  • 这种时候地图图形学当中去计算光的方式,把这些光照信息记录在摄像机的投影面上面
  • 这就是我们图形学当中进行光追的方式

幻灯片7.JPG

  • 好,现在我们具体分析一下光锥的技术实现
  • 当我们从摄像机发射出一束光线,在进行光锥的时候,怎么样去获得光呢?
  • 其实有 3 种可能性
    • 第一种可能性是从光源位置发射出一束光,这束光没有照到任何物体的表面
      • 这种情况就不要考虑,因为这个时候没有获得光照,也不需要去计算
    • 第二种情况是射线刚好命中物体的表面,留在我们图上面这个红点
      • 那这个时候我们的射线跟我们的物体表面会有一个焦点,我需要获取这个焦点的光照颜色信息,把它记录在这一条射线路径的光照的结果上面
    • 第三种情况更普遍,就是从光源位置发出的射线经过物体,但是它不是刚好是照在物体的边缘上面,而是命中物体的内部
      • 这个时候会有两个命中点,一个点叫入点,另外一个叫输点
      • 这个时候光照计算的结果就有两个点在这里,我们不需要入点和出点 2 个解
        • 为什么呢?因为在光追的时候,我追踪到入点的时候,由于我们的触点是不能够被我们的眼睛所看见的,所以触点对我来说是没有计算价值的
  • 到这里,大家就已经完成了光追的第一步

幻灯片8.JPG

  • 在这里我们只绘制一个球体,不考虑光线的反弹
  • 那么我们的代码结构大致上会是这样子的
    • 首先我会去获得摄像机的试点,这个地方是获得我们观察的射线
      • 假设我们的眼睛在这儿,我要观察球体的表面的像素
      • 通过这句话我会把这个像素转换到 3D 空间
    • 然后根据这个目标点以及我们的摄像机的原点,我们构造射线的方向
    • 最后一行是我用来判断射线是不是跟球体发生了碰撞
      • 我是把它封装到这个函数里面,叫做 CalculateRayCollision
      • 如果要是发生了碰撞,那么我就取得发生碰撞的这个小球的颜色
    • 如果是绿色,最后我们在这个像素点位置就渲染一个绿色出来
    • 对于我们的蓝球和红球是同理的

光线反弹方向的确定

幻灯片9.JPG

  • 现在我们如果仅仅把一个小球以一种无光照或者说以一种固定颜色的方式去渲染出来,那很显然不是光追希望获得的结果
  • 我们希望获得的结果是计算一束射线,就是白色这个射线照射在一个物体表面以后所获得的一个漫反射的信息,diffuse reflection
  • 那么怎么样去获得这个漫反射信息呢?在这里是有一个难点的
  • 对于我们知道漫反射是在物理上来说,它是朝各个方向不规则的光照反射结果的叠加
  • 所以,如果你想真正的物理真实的去计算一个光锥漫反射,那么你需要把照射在物体的某一个点上面的所有反射光全部计算一遍,这样的话显然是不现实,它的计算代价太高了!
  • 在这里,我们的光锥是运用了一个技巧,就是我随机的选择某一个方向,用这个反射的结果来代表漫反射的结果,那这样的话性能开销就会低得多!
  • 虽然它的结果可能并不是那么令人信服,但是至少它是一个合理的解决方案
  • 并且我在后面还会讲到如何对它进行优化

幻灯片10.JPG

  • 好,那么我们如何随机生成一个光照反射的方向呢?
    • 在这里我们就需要在 shader 里面去编写一个产生随机数的算法
    • 这个算法细节我们先忽略
  • 那么来看一下,假设这个算法我已经写好了,那么我的算法大概会生成一个什么样的图像
    • 那么我就会在屏幕上面的每一个像素点上面都去生成一个随机值
    • 大家注意在这里因为反射的向量是一个单位向量,所以这个随机值介于 0 到 1 之间
    • 如果每一个像素点是随机值用颜色输出,那么对应的颜色就说是在黑白灰之间
    • 并且当我们随机生成反射方向的时候,我们可以传入不同的随机数种子
    • 不同的随机数种子就会生成不同的反射图案
  • 大家知道随机数种子是什么意思吗?
    • 通俗来说,我们在计算机当中生成的随机数,其实并不是真正的随机的,它是通过一个表达式去计算出来的
    • 你可以把随机数种子理解成数学函数 Y 等于 F(X) 里面的 X,你传入不同的 X 就得到不同的随机数 Y,这个 X 就是种子
    • 因此比如说当种子值是1的时候,那么产生的序列可能是 ABCDEFG
    • 而种子如果是3的时候,它会产生另外一个序列,可能是 CDGZRTX
    • 这就是随机数种子的意义所在,就是输入不同的种子产生不同的结果
  • 所以如果你想让光线追踪的结果是随机的,那么你可以输入不同的随机数种子
    • 大家看左边和右边这两幅图像就是用不同的种子所生成的图像
    • 但是大家知道光线反射的一个方向应该是一个三维向量
    • 而我们这个图里面只是为每一个像素生成了一个值
    • 所以实际上我们通常需要一次生成三个随机数,并且要对它进行单位化
    • 就像我们下面这个图表示的一样,在这里每一个像素其实就代表了每一束光线照在物体表面以后的反射方向

幻灯片11.JPG

  • 左边这个图是在分辨率比较低的情况下生成的
    • 这个像素的分辨率比较低,其实就是反射的方向比较少
  • 右边这个图是在整个场景的分辨率调高以后生成的
    • 你会注意到它输出结果就是一个密密麻麻的彩色的噪点图
  • 每一个噪点就代表了每一个像素点的反射方向
  • 这个就是我们在随机生成漫反射方向时候所需要的结果
  • 这个图就是我们生成的小球反射方向的可视化表示

幻灯片12.JPG

  • 右边这个图我等一下解释,我们先看左边截图里这个算法:
    • 刚才我们说因为需要介于 0 到 1 之间的值,并且需要三个,所以在这里我生成第一个值
    • 这个地方是生成第二个值作为反射方向,X 方向这是 Y 方向,还有一个 Z 方向
    • 但是因为这个随机值它是介于 0 到 1 之间的,而我们的反射方向应该是在-1到+1之间
    • 所以在这里有一个乘以 2 减 1 的操作
    • 并且虽然三个分量都是介于-1到+1之间,但是它们合起来以后形成的向量并不一定是一个单位向量
    • 所以在这里我还要需要进行单位化
  • 右边这个图输出的是反射向量的分布信息:
    • 我前面讲过了,漫反射是随机选择一个反射方向
    • 所以你生成反射向量的那个方向就会能获得更多的光线
    • 因为你会去跟踪这个方向,结果就是这个位置比较亮,因为它照射的这个方向的光线反弹次数比较多
    • 在这里 diffuse reflection 是随机的,这张图实际上就描述了反射向量生成的随机值的分布信息
  • 大家会注意到这个分布是不均匀分布的
    • 你可以在这里看到一个星星的标志,或者是一个奔驰的一个车标
    • 这就说明我生成的反射向量并不是完全随机的
  • 这也是光锥需要考虑的问题:
    • 如果生成的反射向量不是随机的,刚才那么更多的获得反射向量的位置,就会产生更多的光照,就会更明亮
    • 这个结果显然不是我们所希望的
    • 所以在这里我们就需要对这个随机数的生成方式进行调整

幻灯片13.JPG

  • 那么怎么调整呢?
  • 常见的随机数分布概率有两种:
    • 一种是:均匀分布
      • 像我们之前之所以生成了一个像外星人标志的反射方向的力度,是因为我们采用的随机数的生成方式是均匀零分布
      • 当它作为球体的反射方向的时候,就得到了上面这种分布不均的结果
    • 另一种是:正态分布
      • 正态分布是说大部分的随机数都是分布在这个随机数范围的中心区域,而随机数范围的边缘区域获得的结果比较少
      • 这个是我们要去做的一个调整
  • 限于时间关系,我就不去一点点的去展示这个正态分布的算法了
    • 相关内容的学习可以参考文末资料

幻灯片14.JPG
好,我们来看一下这个最终的效果。如果你采用的是正态分布,那么你生成的结果是一个没有接缝的结果。那么学到这里,大家掌握光线追踪算法的基本框架已经有 50%了!
幻灯片15.JPG

  • 在这里咱们还有一个问题要处理:
    • 就是假设我有一束光照在物体的表面的这个点上面,那么它反弹的方向不应该指向物体内部
    • 除非这是一个半透明物体,它对光照具有一定的穿透性
    • 对于一个普通材质的物体,如果不考虑透明性,光照就应该不会反弹到物体的内部
  • 所以这个时候我们就要对这个光线反弹的方向进行校正,那怎么校正呢?
    • 在这里告诉大家一个 trick,就是你直接取反取向量的赋值就可以了
    • 如左图所示,白色表示入射光,绿色表示物体正面方向,黄色半球表示可反弹区域,如果随机生成的反弹向量为红色,则应调整为绿色方向
    • 这里是我们在进行光线追踪的时候的一个算法的坑点
    • 也希望大家通过我们这节课的学习能够有所了解,避免在具体实现这个光锥算法的时候遇到困难
  • 恭喜大家又近了一步!

光追基本代码框架

幻灯片16.JPG

  • 最后我们来看一下光锥的基本代码框架,它代码框架大概是这样子的:(文字表现力有限,需要实时讲解请参考文末视频版本)
    • 假设现在有一束入射光作为我们光锥函数的参数,传入到 Trace 函数里面去了(Trace 是跟踪的意思)
    • 然后我们再传入一个随机数,这个随机数用来生成反弹向量
    • 然后在这里我们会写一个 for 循环
      • 为什么是一个循环呢?
        • 因为刚才我讲过,一束光照射到一个物体的表面,它可能会一次反弹以后又碰到一个物体,然后还需要进行二次反弹
      • 但是我们在真实的计算机世界当中,我不可能这样一个物体去无限的反弹下去,无限的去计算反弹,那这个性能开销是无限大的
      • 所以说在这里我们可以设置一个最大计算的反弹次数
      • 在这里如果是实时渲染,不是离线渲染的话,不会把这个值设的很高
    • 在for循环里,我们通过调用下面这行 CalculateRayCollision 去传入射线,获得跟小球的碰撞信息
      • 如果我们碰撞到某一个物体了,那么我就去计算它的下一次的碰撞
        • 这个时候我就去构造我的这个反弹向量
        • 在生成反弹向量的时候,我不会去直接找他碰撞了哪个东西,而是随机的去瞎碰
        • 也就是函数 RandomHemisphereDirection(随机方向生成请参考《TA全栈》工程)
    • 然后我们会进入到下一次循环
      • 下一次循环的时候,如果没有碰到任何物体,也就是if条件不成立以后,那这次光锥就结束了
      • 如果反射向量继续与场景物体碰撞,那就会再次进入if语句,计算下一次反弹
        • 当然前提是它在你的设定的反弹次数以内
    • 所以大家会发现光追的一个坑点:
      • 如果反弹向量刚好命中物体,那在这个点上面你计算出来的反射就会更强烈
      • 如果没有,则反射就会更弱
      • 所以,光锥为什么产生的结果是随机的,是有瑕疵的!
  • 下面我们来看下渲染效果(详细版看文末视频参考)

幻灯片17.JPG
这是场景的基本设置(非光追渲染效果)
幻灯片18.JPG
这是根据基本设置,将中间的小球设置为发光体的光追渲染效果
幻灯片19.JPG
这是不同光线反弹次数情况下,光追效果的变化
可以看到,反弹次数越多,小球之间互相收到光照影响就越明显,但性能开销也越大

光追的8个进阶要点

幻灯片42.JPG
上面主要分享的是“小结”中的第一点“基本光线追踪》
文字表现力较弱,其余内容可以看我的视频版本分享
这里也给大家概括了一些光追进阶点,供大家进一步参考:
幻灯片43.JPG
幻灯片41.JPG

参考

  • 完整视频请点击本链接观看:【TA技术美术进阶】光线追踪:原理和实现_哔哩哔哩_bilibili
  • 更多技术干货请加本人主页头像

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/37675.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超简单的nodejs使用log4js保存日志到本地(可直接复制使用)

引入依赖 npm install log4js 新建配置文件logUtil.js const log4js require(log4js);// 日志配置 log4js.configure({appenders: {// 控制台输出consoleAppender: { type: console },// 文件输出fileAppender: {type: dateFile,filename: ./logs/default, //日志文件的存…

如何从0构建一款类似pytest的工具

Pytest主要模块 Pytest 是一个强大且灵活的测试框架,它通过一系列步骤来发现和运行测试。其核心工作原理包括以下几个方面:测试发现:Pytest 会遍历指定目录下的所有文件,找到以 test_ 开头或 _test.py 结尾的文件,并且…

MS-Net: A Multi-Path Sparse Model for Motion Prediction in Multi-Scenes

MS-Net: A Multi-Path Sparse Model for Motion Prediction in Multi-Scenes 基本信息 期刊:IEEE ROBOTICS AND AUTOMATION LETTERS (IF 4.6 SCI3区)单位:同济大学,上海人工智能实验室时间:2023年12月数据…

架构师必知的绝活-JVM调优

前言 为什么要学JVM? 首先:面试需要 了解JVM能帮助回答面试中的复杂问题。面试中涉及到的JVM相关问题层出不穷,难道每次面试都靠背几百上千条面试八股? 其次:基础知识决定上层建筑 自己写的代码都不知道是怎么回事&a…

C++中的虚函数表结构框架

一.虚函数表介绍 Virtual Table虚函数表是实现多态的 每个有虚函数的类的实现,都有个指向虚函数的指针表(不管是父类还是子类) 指向虚表的指针是作为数据成员存在实例对象中 当调用虚函数时,就去查找对象的虚表中指向整顿派生类函…

Bayes分类器设计

本篇文章是博主在人工智能等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在AI学习笔记&#…

每日一题——Python实现PAT乙级1059 C语言竞赛(举一反三+思想解读+逐步优化)四千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 时间复杂度分析 空间复杂度分析 代码优化建议 总结 我要更强 优化方法…

.NET周刊【6月第4期 2024-06-23】

国内文章 C#.Net筑基-集合知识全解 https://www.cnblogs.com/anding/p/18229596 .Net中提供了数组、列表、字典等多种集合类型,分为泛型和非泛型集合。泛型集合具有更好的性能和类型安全性。集合的基础接口包括IEnumerator、IEnumerable、ICollection、IList、ID…

Gradio 4.37.1官方教程二:Blocks

文章目录 一、Blocks及事件监听器1.1 Blocks结构1.2 事件监听器的类型1.3 多数据流1.4 多输入组件1.5 多输出组件1.6 更新组件配置1.7 添加示例1.8 连续运行事件1.9 持续运行事件1.9.1 every参数1.9.2 load方法1.9.3 change方法 1.10 收集事件数据1.11 绑定多个触发器到同一函数…

基于线调频小波变换的一维时间序列时频分析方法(MATLAB)

在机械故障诊断领域,振动信号的处理常采用以快速傅立叶变换为基础的相关分析、幅值分析、频谱分析等时域和频域分析方法。但经典的FFT存在固有缺点,即它虽然在频域范围内是完全局部化的,但是它不包含任何时域信息,因而不适于分析非平稳信号。近年来涌现的各种时频分析方法(短时…

【刷题】初步认识深搜(DFS)

送给大家一句话: 拥有希望的人,和漫天的星星一样,是永远不会孤独的。 -- 《星游记》 初步认识深搜(DFS) dfs算法二叉树中的深搜Leetcode 129. 求根节点到叶节点数字之和题目描述算法思路 Leetcode 814. 二叉树剪枝题…

Redis-实战篇-缓存更新策略(内存淘汰、超时剔除、主动更新)

文章目录 1、缓存更新策略1.1、内存淘汰1.2、超时剔除1.3、主动更新 2、业务场景:3、主动更新在企业中业务实现有三种方式3.1、Cache Aside Pattern3.1.1、操作缓存和数据库时有三个问题需要考虑:3.1.1.1、删除缓存还是更新缓存?3.1.1.2、如何…

数据同步软件有哪些

数据同步软件有哪些呢?随着企业规模的扩大,企业数据也积累得越来越多,万一发生宕机风险,那么这个损失将不可估量。所以为了容灾备用,我们往往需要将数据同步到另一台备胎服务器上,进行冗余。 那么需要同步的…

centos7.9 python3环境(virtualenv)搭建及所遇错误

人望山,鱼窥荷,真正喜欢想要的,没有一样可以轻易得到。 目录 # 1. 解决版本冲突问题--建议不要跳过(一定要查看软链接是否链接正确) # 2. python3(virtualenv)环境搭建 # 3. virtualenv常用命令 # 4. 所遇错误解析 ## 4.1 遇到 No modul…

惠海 H6246低功耗DC/DC降压型恒压芯片60V降3.3V5V12V 蓝牙模块 单片机供电

1.产品描述 H6246是一种内置60V耐压MOS,支持输入高达48V的高压降压开关控制器,可以向负载提供0.3A的连续电流。H6246支持输出恒定电压,可以通过调节VFB采样电阻来设置输出电压,同时支持最大电流限制,可以通过修改CS采…

操作系统期末复习考题二

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、前言🚀🚀🚀二、正文☀️☀️☀️三、总结🍓🍓🍓 一、前言🚀🚀&am…

【资源调度】1-何为调度?

导读:本期是全网最全【资源调度】系列推文的第1期(共50期左右)。我们将对调度的定义与作用、计划与调度的关系、调度问题的拆解做出详细介绍,使大家对【资源调度】问题有了一个整体的认识,为后续的内容奠定基础。 作者1:张哲铭&am…

个人搭建cppreference网站

近日,由于购买的腾讯云服务器要过期了,之前在服务器搭建的cppreference也要重新搭建,故写下此文章 cppreference的访问速度也慢,故自己WSL子系统简单搭键一下是个不错的选择 环境准备 首先,自己先安装Nginx,在网上找安装教程即可下载cppreference网站资源包:https://pan.baidu…

RFID固定资产管理系统在企业中的应用与优势

随着企业资产规模的不断扩大和管理复杂性的增加,传统的资产管理方式已无法满足企业高效管理的需求。RFID固定资产管理系统凭借其高效、准确、实时的特点,成为企业固定资产管理的新宠。 一、什么是RFID固定资产管理系统 RFID(无线射频识别&…

使用AI工具 Baidu Comate 辅助编码 快速定位修改Bug

一、Baidu Comate 概述 Baidu Comate(百度智能编码助手)是一款基于文心大模型的新一代编码辅助工具。它结合了百度多年积累的编程现场大数据和外部优秀开源数据,旨在为用户提供高质量的编程代码生成和优化服务。Comate的主要目标是提升编码效…