« popo 的语音通话 | 返回首页 | Windows 下以非阻塞方式读取标准输入 »

HDR 究竟做了些什么

去年初开始,就一直有朋友在我耳边唠叨 HDR 。听的多了,也自然觉得这会是 3d engine 的一大发展方向;自然我们的 engine 也是非支持不可的了。

这几天同事在实现 HDR 的时候,发现目前大多数显卡对浮点表面支持的都不算太好。典型的一点就是,浮点表面上不支持带 alpha 通道的贴图。而像树叶这样的贴图不用通道也不可能。结果就是,树林里的图象惨不忍睹。

从目前我们手头的显卡看,只有几款高端显卡没有这个问题。所以,还不能指望用户的显卡,而要换个思路拐着弯让 HDR 的效果出来了。

我们设想了许多方法,比如用 RGB 渲两遍再处理成 HDR 效果,或者是把 HDR 部分与普通 RGB 部分分开渲染再合成等等。不过,要找到更合理的解决方案,还是得把 HDR 到底是什么搞清楚。

最终,我打开了 openexr 的网站仔细的把找到的文档全读了一遍,算是有了一些了解。

通俗点说,我们看到的世界的景像是经过了人眼的处理,把物体表面反射的光线以色彩的形式映射到我们的视网膜上。相机和摄影机是采用了同样的原理,模拟人眼的方式记录这些色彩。

但是,一旦我们希望改变周遭的光线,直接从已经定型的照片上做处理却会有很大的失真。这是因为,那些色彩已经是经过了人眼瞳孔的处理。如果改变光线,得到新的景象,正确的方法只能是还原原来生成图片时的光线信息,再重新暴光。

最好的方式当然是直接记录物体本身对光线的反射强度,也就是某种原始景象。可惜,我们只能用成像的结果去推导物体的"原始景象"。

那么从一张照片中推算它的"原始景象" 可行吗?事实上,几乎无法办到。因为,信息在那一次成像时已经损失了太多。

损失是怎样造成的?我们成像时一定有使用一个固定的光圈。我们知道,用相机拍摄黑暗处的景物的时候需要用大光圈,这样可以得到暗处的景物的层次,但是,因为入光量的增加,使得亮处景物的层次失去了。而反之,在光亮处拍摄,就需要减小光圈,得到更多了光亮处景物的层次。

最终,一次拍摄的结果是,暗处和亮处的信息不可兼得。一般人工制作带有完整原始景象信息的图片,就需要对静物做不同光圈下的多次暴光,再用数学方法处理这些图片。

采用 HDR 的游戏中,我们通常要制作 HDR 贴图。这 HDR 贴图中的信息就是那"原始景象"。HDR 里的色彩信息是用浮点数表示。这样可以拥有足够大的色彩分辨率,以方便后期处理。

最终游戏显示的过程,就是一个模拟暴光的过程。我们可以从 openexr 网站上找到详细的说明。

按我自己的理解,其中关键的一步就是,在色彩域中找到需要刻意表现的一段区域,把它映射到屏幕显示的色彩范围内。屏幕最终显示的色阶只有 256 级,我们可以留出最亮的几级和最暗的几级来分别表示那段区域的前面和后面。最后的效果就是,特别亮的地方呈现一片白色,而暗的地方一篇漆黑。在做映射的时候,不能采用线性映射,而应该采用符合人眼色彩感觉的 log 曲线来做,这样得到真实感的效果。

当我们选择不同的颜色区域来表现的时候,就可以模拟出人眼在暗处和亮处瞳孔大小之不同的感觉。让黑暗处和明亮处同样色彩分明。

ps. 显卡的硬件处理浮点表面的时候,往往不用浪费巨大储存空间的 32bit float 表示,而采用 16bit 的 half 格式。这个 half 格式并无什么特殊之处,无非是把指数位缩小到了 5bit 而尾数精度给了 10bit 。其表示规则于 float 以及 double 无异,所以和 float 格式互转也是非常方便的。其精度在表现颜色的层次方面,16bit 在现今的显示器上已经绰绰有余了,毕竟最终的显示色阶只有 8bit 而已。

TrackBack

如果你想引用这篇文章,请复制下面的链接发送引用通告(GBK)
http://blog.codingnow.com/mt/mt-tb.cgi/168

Comments

Humh...You are not the only one being overwhealmed by HDR things....but HDR sucks....if not useless at all, and I see plenty of gamma+contract+bloom kind of HDR implementation, they are actually tricks... For a MMOG, we will have open areas, and using HDR means the sky is SIMPLY white as hell, I HATE that idea.....But for the tree stuff, you dont have to stick on alpha, mask is another choice

HDR , High Dynamic Range 表示了高动态范围亮度这一点上,是没有问题的。

但是模拟人眼的感觉这一步,理论上的最佳效果直接截断低于一个亮度的像素并非最佳选择。只是因为 gpu 运算的方便,我们在实际应用时才直接做 clamp 处理。

另外使用浮点表示也并非为了可以表示大到无穷大的数值。half 这个格式对表示较大整数的能力也极其有限。

analyst的理解是完全正确的!

to analyst: 难道讨论可以脱离问题之外的东西而独立存在吗?思维方式难道不影响对问题的讨论吗?除非讨论可以单独存在而不牵扯问题之外的任何事物,否则就不可能不牵扯问题之外的东西。遗憾的是,客观事物的本来面目与你的想像大相径庭。

HDR 的贴图文件不知道有没有什么标准。如果 openexr 定义的是一个准标准的话,今天考察了一下,1.0 在里面并没有什么特殊的含义。只是定义了 middle gray 为 0.18 而已。

哈哈,每个人都会坚持自己认为对的东西,但有时候确实只是因为双方表述的方法不同而造成了误解,求同存异吧。

不过讨论归讨论,牵扯到问题之外的东西就不好了。

骂吧骂吧, 有积怨骂出来好受点。

analyst 跟我这么多年的朋友,不至于也因为我随便说几句有啥怨恨的 :)

这次讨论再次证明云风的思维方式的确有重大问题而不是小问题。我想这是这里无数次重复的历史证明的唯一一件事吧。云风思维方式中最具标志性的也是最惯用的笑话可以从这句话里看出“而你应该去看看论文,而不应该去读那些辗转而来的说明”,这句话隐含一个假定,那就是对方没有去看过论文,而是去读辗转来的说明。且不说事实是否真的如此,作出这个假定就意味着其理性思维能力在小学水平,再联系到云风经常使用这样的假定,所以他的理性思维能力最多也就在幼稚园水平。当然,他的笑话还很多,比如“理解没错表述错误”,一个没能力正确表达自己的理解的人怎么可能有能力去理解事物?这不过是他给自己下台阶的托辞而已。所以回过头来想,那句“应该...而不应该”反倒是可以用来呵斥云风,因为他即使读过千遍也等于没读,他没有思维能力去理解读过的任何东西,所以即使呵斥了也无济于事。

小问题不再争了 :)

其实目前最主要的问题是怎么让 HDR 在现存的显卡上正确的跑的问题。

在目标表面是浮点表面的时候,不支持 alpha test 和 alpha blend 的卡大把的在。

"颜色就是光线对人眼的作用" 在这个基本点上我想没有人会有异议,但对这里的问题怎么解释就是表述方式了。

我昨天的说法确实有问题,而"记录场景原始光强信息" 这个说法的确好一些,我把文章里的改一下。

浮点数并不能记录整个(0,∞)区间。half 格式能记录的最大正整数也只是 65504 而已。我怀疑,在 HDR 的图象文件中,仅仅使用了 [0,1] 这个区间而已。

另外,HDR 在处理暗处的色彩的时候,也并不是直接将范围之外的部分直接 clamp 到 0 。而是减少暗处的层次。而超出范围的则是直接 clamp 到 255 。

无数次重复的历史证明,想让 analyst 承认自己写出来的东西有错误是不可能的 :D

最终画面都是记录每个点出射到视点的光线,而不是记录这点的入射光线。比方一个完全镜面反射物体,如果反射光线没有进入视点,那么不管入射光线有多强这个物体反映到最终画面还是一片黑。


"Making an image scene-referred removes the "film look" (or video look) from the image. The pixel data tell you what was in the original scene, not what would be projected on the screen."

其实这段话的意思是,记录场景原始光强信息,而不是在屏幕上经过转换的结果。不管是屏幕、胶片、CCD还是人眼,它们的动态范围都是有限的,不能完整覆盖自然界的光强范围,太亮的地方clamp到1,太暗的为0。经过clamp以后的场景就会在亮部或者暗步丢失细节。用浮点数去记录以后就可以表示出整个(0,∞)的亮度区间,最终再根据场景中实际的亮度范围,映射到低动态范围的屏幕上去。

to analyst:
不是我的理解问题,或许我的文字表述有问题,但是我认为这种表达会是一种通俗可以接受的方式。而你应该去看看论文,而不应该去读那些辗转而来的说明。

没有环境光的情况下,所有物体当然是一片漆黑;但是有一定的环境光的情况下,景象中的不同位置的地方光强并不相同。而不同物体表面受到的光强不同时,却有可能表现出最终相同的颜色。这就导致了最终的画面不可能完整反映每个点接受到的光强。

可以去看原始的 siggraph 2004 上的论文。

"Making an image scene-referred removes the "film look" (or video look) from the image. The pixel data tell you what
was in the original scene, not what would be projected on the screen."

另外, HDR 图中采用浮点数也不是为了"把亮度区间范围就扩展到了[0,∞)" , 有一点我还没有去确认。但是阅读了个部分代码后,我感觉 HDR 贴图中对图象的描述只用到了 [0,1] 的范围。至少,对于 "middle gray" 的取值,一般情况下只相当于 0.18 而已。

另外,在显示的时候,高于2 ^kneeHigh 的亮度部分的确会被 Clamp 到 255 。 但是,低端部分却不是直接变成黑色,它是由 kneeLow 控制的暗处的色阶的。


理解的有问题。不存在什么物体表面的特质和环境的光线叠加,我们能看见物体是因为物体自身反射环境光线,如果没有任何环境光照射物体那么所有物体都是一片漆黑(自发光物体除外),所谓表面的特质其实就是反射属性。
HDR不是要记录什么“物体表面的特质”,而是用高动态范围记录光强信息,所谓动态范围是指所能表示的最大光强和最小光强之间的数值范围。原本8位整数表示中,亮度只能从0到255,我们可以除以255,将其规一化为[0,1]的区间范围内,这个区间只能表示自然界中一小段亮度范围,要人为的规定0和1所表示最小光强和最大光强,超出这个范围的光都会被钳制。假如整个场景的光都比1还要亮,那么就会变成一片白,反之如果都比0还要暗,那么就会变成一片黑。当我们用浮点数表示以后,亮度区间范围就扩展到了[0,∞),这样诸如太阳光比烛光亮10000倍的光强都可以表示出来了。当然,HDR只是一个中间结果,最终到显示器的时候还能重新规一化到显示器亮度范围中,选用合理的规一化参数就可以得到最多的层次细节。另外浮点数表示还有一个好处是亮度不是均匀增长的,在数值小的地方精度高,数值大的地方精度低,这更符合人眼的习惯。

Post a comment

非这个主题相关的留言请到:留言本