这个例子示范了使用浮点纹理的一些HDR光照效果。整数纹理格式具有离散的极限范围,其结果在动态光照条件下会丢失一些色彩信息;相反,浮点格式能储存很大或很小的色彩值,包括超出可显示范围的0.0到1.0。这个灵活性允许动态实现光照效果,就像强光背景中弱照明与光晕条件下的蓝色移位。该范例也使用一种简易的光照适应模式,用摄像机即刻暴光过度或是暴光不足来改变光照环境.
该范例为何有趣
光线在真实世界中有一个介于最高和最低亮度的很高的比值;在我们生活中从太阳到星光的可见光,被称为动态范围的比值大约是1012:1;可是,人类视觉系统只能适应部分暴光在1000:1左右的动态范围。为了看到高动态范围内的光线传播,人类视觉系统会自动调整暴光光线,在进入的光线强度中选择一个小范围亮度,这个过程不是瞬间的,就像你进入一个阳光下的黑暗建筑中所注意到的,反之亦然。这种对光适应性延迟的暴光调整,能被应用程序实时模拟。
计算机显示和打印媒体的动态范围在100:1,所以要在高动态范围下显示场景我们不得不去找折衷的办法;该范例展示了在摄影术中称为色调映射的原开发技术,它描述了一个映射HDR图像到低动态范围空间的方法。色调映射有效地产生出与发生在人类视觉系统上的自动暴光控制同样的效果。
最后,当一个亮光冲击镜头,必然会出现不自然的情况,包括如图片中的光晕和星光现象。如果在渲染过程中高强光值没有被丢弃,这些现象能在场景以真实级别进行渲染的图像上产生。由于这是一个快速处理的效果,所需时间取决于显示它们所唯一依赖的屏幕分辨率,而不是场景或光照复杂度。
概述
这个例子使用了Direct3D 9浮点纹理来存储场景HDR光照信息。整数纹理格式在像素着色器中被限制在了0.0f 到1.0f之间,但是浮点纹理允许容纳更多的值,范围甚至是小数和指数的有效常量。
下面的参考例子描述的数学方法非常接近这个光照效果的实现。
范例如何工作
在这个例子中,场景物体是普通整数格式的纹理,但球形物体用高范围值表现光影,以建立有效的发光光源。所有的物体在场景中使用一个在顶点与光亮度上计算环境光,漫反射,镜面反射的照明模式进行着色;由于这些光被赋予高值,照明物体的结果色彩能远超出1.0f的阈值。如果一个浮点纹理没有用来获得这些HDR值,这个光线的数据将被丢失,场景将出现暴光过度,如图所示。
相反,如果场景渲染到一个浮点表面,HDR信息将被储存,场景于是能色调映射为一个适当的低动态范围来显示,如下图所看到的。
下图展示了这个例子完整的流程图。场景首先在一个有相同尺寸后缓冲的浮点纹理上进行渲染。由于场景将被多次采样以便实现快速处理,一个好的步骤是缩小场景到像素着色器取样的最小值。这个额外的好处是当它们关联的纹理放大到全尺寸时能够实现大量快速处理。按比例缩放场景纹理是测量场景平均亮度的步骤。使用平均亮度值,才能确定场景中的哪个像素在场景色调映射到低动态范围后将被加亮。除了加亮的区域外除去所有场景中的色彩值,从场景中复制加亮过滤以便创建能用快速处理光照效果的资源。
该例子的场景渲染使用了像素着色器来控制环境光,漫反射,镜面反射作用于每个像素上的各个光照。由于这些光对于场景来说是很亮的,大多数像素具有的每个色彩通道在0.0到1.0 的图形卡显示范围之内。因此,这些纹理直接观察时看起来大部分是白的。可是,1.0以上的色彩信息并没有丢失,只不过需要色调映射到0.0 to 1.0 的范围。在整个过程,色调映射场景到期望的亮度范围,快速处理图像效果混合到场景上以生成最终图像。
用户指南
计算亮度
在你的程序能缩放场景强度之前,你需要确定场景在开始时有多亮。目的是为平均场景亮度作个好的估计,以最小化计算所需的时间消耗数量。减少采样纹理可以提升计算速度,但是较少采样会面临丢失亮区域的风险。在这个例子中最接近的方法是首先按1/4 x 1/4比例缩放场景,使用平均每个4 x 4的纹理块减少采样。这个缩放纹理也用来建立光照效果的资源纹理,因此缩放操作的精确度是很重要的,以避免镜头在场景中移动出现闪烁现象。
下面的亮度方程方法用来测量平均场景亮度和完成色调映射的白纸特征Photographic Tone Reproduction for Digital Images。
平均场景亮度用来最后计算所有采样像素平均对数值的逆对数。Delta包含一个小值来操作纯黑纹理情况。这个方程实现了测量亮度方法的四个像素着色过程,如下:
1.样本平均值 log() 值到一个64 x 64 纹理中。
2.按比例缩小到16 x 16。
3.按比例缩小到4 x 4。
4.按比例缩小到1 x 1并在结果上执行一个exp()操作。
保存一个1 x 1的纹理,计算需要平均亮度值,计算工作能被视频卡完全处理而不需要AGP总线传输。
暴光控制
暴光控制是找出一个适当的低动态范围的HDR场景视图的方法。在物理镜头系统中,光圈调整光线进入系统的数量极限。该方法使用计算机图像来模拟通常叫做色调映射的暴光控制。它引用映射一个HDR图像空间到低动态范围空间中以适合于视频显示的方法。不同于暴光控制,色调映射时所有高范围数据是有效的;所以,依赖于操作,图像比使用传统的摄像机可能包含更多的来自亮区和暗区的细节。
在平均场景亮度计算之后,HDR场景纹理能被按照目标平均亮度缩放,表现在下面方程式的alpha上;
这个方程式为最终场景简单的产生一个围绕物体平均亮度的场景亮度中心的线性缩放;然而,这个结果值还尚未被压缩到适合于可视化屏幕的低动态范围0.0 到 1.0。下面的色调映射操作完成期望的压缩。
光适应性
人类视觉系统不能对光线环境亮度的变化作出瞬间调整,这种特性容易被稍微扩展的色调映射计算模拟。通过色调映射公式用使用者当前适当亮度值替代平均场景亮度。这个适当亮度值存储在1 x 1的纹理中并调整每一帧缓慢追踪测量场景的亮度,如下代码片段所示:
float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - pow( 0.98f, 30 * g_fElapsedTime ) );
例如,如果摄像机是固定的,适合的亮度最后将匹配标准的亮度,色调映射的输出和适配器无效时输出的结果一致。可是,如果摄像机焦点移动到不同亮度量级的区域,在整个视野调整到新光照环境过程中图像将出现暴光过度或暴光不足,适应性模式在这个例子中不是预期的人类适应性的真实模式,它至少比一个半小时的全黑适应要好的多。
光亮过滤器
色调映射执行两处代码;一处是在渲染中作为最终着色器的一部分传给合成的耀光纹理到场景中,一处是作为光亮过滤器的一部分。光亮过滤器使用色调映射首先测定哪个最终场景图像区域将被照亮,然后排除剩余数据。由于HDR光照效果与当前暴光相关,这些效果能在色调映射图像上完成并直接到场景输出。这个程序优势在于能用整数纹理产生眩光。
下面的代码片段展示了光亮过滤器在减去黑暗区域之前首先映射场景到希望的中等灰度的亮度的物体。色调映射操作从稍微修改的最终方程中转换了场景的亮度到0.0?/SPAN>1.0范围之间。不是用亮度增量来区分亮度,而是用一个偏移值替换公式中的一个量。当偏移值增加,独立在场景亮区和暗区的部分也相应增加。
// Determine what the pixel's value will be after tone mapping occurs vSample.rgb *= g_fMiddleGray /(fAdaptedLum + 0.001f);// Subtract out dark pixels vSample.rgb -= BRIGHT_PASS_THRESHOLD; // Clamp to 0 vSample = max(vSample, 0.0f); // Map the resulting value into the 0 to 1 range. Higher values for // BRIGHT_PASS_OFFSET will isolate lights from illuminated scene // objects.v Sample.rgb /= (BRIGHT_PASS_OFFSET+vSample);
光晕效果
在完成光晕之前,光亮过滤器图像已被缩小和模糊到原始场景纹理1/8 x 1/8比例,光晕处理只是一个简单的水平线与垂直线的光亮过滤器场景中的高斯模糊的两次分别处理。如图所示,纹理首先被沿着水平线模糊,然后水平模糊后的纹理沿着垂直方向模糊以完成这个处理。作为最终场景的一部分,光晕纹理缩小到使用双线性过滤和场景直接输出的后缓冲尺寸。
星光效果
星光效果可能需要对每个星光光线至少三次以上的渲染。星光模式在这个例子中使用了八个光线,结果可能花费了24次处理来全部渲染。GlareDef.h文件定义了不同星光类型的特征,包括光线数目,光线方向,和色彩偏移。如下图所示,一次渲染全部单独方向的光线,穿过单独光线纹理的平均纹理值用于建立最终的合成的星光纹理。这个合成纹理是缩小的和直接到最终场景的图像。