RealTime-Rendering23-Möller–Trumbore Triangle-Ray intersection
射线和三角形快速求交判断方法(Möller–Trumbore)
射线和三角形快速求交判断方法(Möller–Trumbore)
基于深度值重构世界空间位置, 本文提供了三种方法, 适用于不同的场景.
几何着色器需要定义输入的图元以及输出的图元信息:
1 | #version 460 core |
输入的图元可以使用以下任意一种类型:
输出的图元仅可以使用以下三种类型:
几何着色器允许在渲染管线中动态的生成几何体。即使绘制命令只指定了最基础的图元(比如4个点),几何着色器也能将这些点实时扩展为更复杂的形状,从而用非常简单的输入创造出丰富的视觉效果。
glDrawArrays/glDrawElements中传入的图元类型对应了几何着色器中输入的图元类型(in)
详细介绍了compute Graphic中涉及到的各种图元类型
为场景中的每个对象分配一个唯一的颜色,将所有对象渲染到一个FBO中,拾取时基于鼠标指针在屏幕上的位置索引FBO中的颜色值。如果该颜色与某个对象匹配,则该对象被选中。以下是绘制到SelectionBuffer和从SelectionBuffer读取的详细步骤:
首先需要一个单的pass将物体分配的颜色写入单独的FBO,这里需要处理几件事:
| 步骤 | 操作 | 详细说明 |
|---|---|---|
| 1 | 创建帧缓冲区 | 创建一个与渲染屏幕分辨率相同的帧缓冲区 |
| 2 | 分配唯一索引 | 为每个对象分配一个唯一的索引值(通常是从 1 开始的整数) |
| 3 | 索引转颜色 | 将索引值转换为 32 位颜色值 (r, g, b, a) |
| 4 | 渲染 | 用索引转换得到的颜色将对象绘制到帧缓冲区 |
| 步骤 | 操作 | 详细说明 |
|---|---|---|
| 1 | 获取鼠标位置 | 从屏幕获取鼠标坐标 (x, y) |
| 2 | 读取像素 | 使用 glReadPixels() 读取鼠标位置的颜色值 |
| 3 | 颜色转索引 | 将帧缓冲区中的颜色值转换回对象索引值 |
| 4 | 命中检测 | 如果索引值非零,表示选中了对象 |
| 5 | 未命中检测 | 如果索引值为零,表示未选中任何对象 |
GLSL定义了一个叫做gl_PointSize输出变量,它是一个float变量,可以使用它来设置点的大小。在顶点着色器中修改点的大小的话,就可以对每个顶点设置不同的值了。
在顶点着色器中修改点大小的功能默认是禁用的,如果需要使用它的话,需要启用OpenGL的GL_PROGRAM_POINT_SIZE:
1 | glEnable(GL_PROGRAM_POINT_SIZE); |
整型变量gl_VertexID储存了正在绘制顶点的当前ID。当使用glDrawElements命令进行渲染的时候,这个变量会存储正在绘制顶点的当前索引。当使用glDrawArrays命令绘制的时候,这个变量会储存从渲染调用开始的已处理顶点数量。
gl_FragCoord的x和y分量是片段的屏幕坐标,其原点为窗口的左下角.z分量等于对应片段的深度值.
1 | void main() |
1 | GBufferPass(); |
通常情况下,当我们渲染一个复杂光照场景下的片段着色器时,我们会计算场景中每一个光源的贡献,不管它们离这个片段有多远。很大一部分的光源根本就不会到达这个片段,所以为什么我们还要浪费这么多光照运算呢?
隐藏在光体积背后的核心思想就是计算光源的半径,或是体积,也就是光能够到达片段的范围。由于大部分光源都使用了某种形式的衰减(Attenuation),我们可以用它来计算光源能够到达的最远距离,或者说是半径。接下来只需要对那些在一个或多个光体积内的片段进行繁重的光照运算就行了。这可以给我们省下来很可观的计算量,因为我们现在只在需要的情况下计算光照。
这个方法的难点就是需要计算出一个光源光体积的大小,或者是半径。
1 | float constant = 1.0; |
1 | struct Light { |
上面那个片段着色器在实际情况下不能真正地工作,并且它只演示了我们可以不知怎样能使用光体积减少光照运算。然而事实上,你的GPU和GLSL并不擅长优化循环和分支。这一缺陷的原因是GPU中着色器的运行是高度并行的,大部分的架构要求对于一个大的线程集合,GPU需要对它运行完全一样的着色器代码从而获得高效率。这通常意味着一个着色器运行时总是执行一个if语句所有的分支从而保证着色器运行都是一样的,这使得我们之前的半径检测优化完全变得无用,我们仍然在对所有光源计算光照!
使用光体积更好的方法是渲染一个实际的球体,并根据光体积的半径缩放。这些球的中心放置在光源的位置,由于它是根据光体积半径缩放的,这个球体正好覆盖了光的可视体积。这就是我们的技巧:我们使用大体相同的延迟片段着色器来渲染球体。因为球体产生了完全匹配于受影响像素的着色器调用,我们只渲染了受影响的像素而跳过其它的像素。下面这幅图展示了这一技巧:
基于computeShader实现GPU的视锥剔除和遮挡剔除.
GPU提出的第一步先判断包围盒是否完全位于视锥体外部,需要构造frustum的六个平面以及场景包围盒,循环frustum的6个面分别与包围盒求交,如果包围盒在任意一个平面的外部,则直接剔除。
1 | bool frustumCulling(mat4 mvp, BoundingBox bbox) |