代码改变世界

如何从真实照片中分离高光和漫反射信息

2016-04-16 10:30  风恋残雪  阅读(3631)  评论(0编辑  收藏  举报

原文地址:http://filmicgames.com/archives/233

上篇文章讲到所有物体都是有光泽的,那么这篇文章作者将展示如何从图片中分离漫反射和高光信息。

你可能以为接下来的这些东西会比较困难,但是它实际上并没有你想你的困难。我在家做的一件事情就是使用偏振光来把漫反射和高光从真实图像中分离出来。美工(Artists)会花很多时间在看参照图片,但是通过分离漫反射和高光能帮助你理解什么是材质。在上面的图中,左边是只有漫反射光照,中间是带有漫反射和高光,右边的是只有高光的。顺便说一句,我并不是这方面的专家,但是这就是我如何理解它的。

  1. 原始图像:

  2. 漫反射:

  3. 高光:

    如果你想要自己去做分离这件事,那么你需要:

  4. 一个可以手动拍照的相机。比如单反。
  5. 一个远程快门控制器,这样你就可以不用碰相机就来拍摄多张照片。
  6. 一个光源,我使用了一个宜家的灯。
  7. 一些偏振片。我从polarization.com购买。你需要"完全层叠线性偏振片"。确保你用的是线性偏振器,而不是圆偏振器。

    在最基础的以层面,在图形学中我们假设物体都有漫反射的高光反射。下图 是一个光源照射表面,旁边有一个相机 。顺便说一句,这个模型是一个非常大的简化。

    其中到达表面的一些光会离开表面。如果有一个白色光源和一个蓝色的表面,那么反射的光还是白色的,我们称它为高光。

    其它的一些光会被 表面吸收,一些电子会被激活,并且一个新的光子会沿一个随机方向发射。如果 我们有一个白色的光源和一个蓝色的表面,那么出射的光会是蓝色的,我们称它为漫反射。

    希望你已经理解这些。如果先前不了解,那么你现在应该了解了。特别酷的一件事情是光可以被偏振。你可以从你的好朋友wikipedia(国内请自行百度或者谷歌偏振)另外,高光会保持它进入时的偏振,但是漫反射不会。因为漫反射光在随机方向重新传播的,并且伴随随机偏振,我们可以说它是非偏振的。

    下面是所需的设置:

    很简单,我把相机所有的全部调到手动模式,这意味着:

  1. 手动曝光
  2. 手动调节光圈
  3. 手动调节白平衡
  4. 手动调节ISO
  5. 关闭自动对焦
  6. 确保颜色配置是sRGB而不是Adobe 98

    你还需要有一个远程快门控制器。这样就可以不碰相机来拍摄多张照片。如果 你必须碰相机来拍摄,那么我确信出现不对准的情况。并且我们需要一个光源,让我们来瞧瞧它。

    它就是一个宜家卤素灯,但是前面放了一个偏振片。我使用磁带来固定它。作为警告,灯会变得非常热,因此偏振处已经有点变形了。

    我们要做的就是拍摄两张照片。但是我将需要把一个偏振片放到相机前面。注意我们的相机和光源上都有一个偏振片。如果你觉得我是一个从洛杉矶来的游戏程序员,那么你就错了。它只是 我用来付房租的,在我追求成为一个手部模特梦想的时候(玩笑)。

    注意到偏振片上的橙色胶带没有?我现在将要把它旋转90度再拍另外一张。看这个橙色的胶带已经移动了。

    下面是我们第一张拍摄的照片的样子。如果你的对齐完美的话,那么应该没有高光信息。当然,多少会有一点,因为通过手动对齐偏振片是基本不可能的。

    它是如何工作的?偏振光达到 表面,高光从表面反射并且射向相机。它到达跟偏振光垂直的偏振器,因此它完全被吸收。同时 ,漫被吸收并且重新传输的反射光是非偏振的。偏振光吸收了一半的漫反射光,余下的部分进入了相机。因此图片中有一半的漫反射但没有高光信息。现在来看第二图。

    在上面这张图中,偏振的高光到达跟它平行的偏振器,因此所有的光都通过了。同时漫反射不是非偏振光,所有 它其中的一半被吸收。它有一半的漫反射和所有的高光信息。

    如果 我们把第一张图约定为A,第二张图约定为B,那么漫反射就是2*A,而高光就是B-A。当然这些图片是存储在sRGB空间的。因此这里我们需要使用Shader代码来比较这两幅图片,并且把它们 分开,并且把结果再存储为一张sRGB图片。跟往常一样,代码没有被测试过。

      

 1 float LinearToSrgb(float val)
 2 {
 3    float ret;
 4    if (val <= 0.0)
 5       ret = 0.0f;
 6    else if (val <= 0.0031308f)
 7       ret = 12.92f*val;
 8    else if (val <= 1.0f)
 9       ret = (pow(val, 0.41666)*1.055f)-0.055f;
10    else
11       ret = 1.0f;
12    return ret;
13 }
14 
15 float SrgbToLinear(float val)
16 {
17    float ret;
18    if (val <= 0.0f)
19       ret = 0;
20    else if (val <= 0.04045f)
21       ret = val / 12.92f;
22    else if (val <= 1.0f)
23       ret = pow((val + 0.055f)/1.055f,2.4f);
24    else
25       ret = 1.0f;
26    return ret;
27 }
28 
29 int g_bSpecOrDiff;
30 
31 float4 ps_main( float2 texCoord  : TEXCOORD0 ) : COLOR
32 {
33    float3 srcA = tex2D(Texture0, texCoord ).rgb;
34    float3 srcB = tex2D(Texture1, texCoord ).rgb;
35    float3 linA = SrgbToLinear(srcA);
36    float3 linB = SrgbToLinear(srcB);
37    float3 linDiff = linA*2;
38    float3 linSpec = linB-linA;
39    float3 texDiff = LinearToSrgb(linDiff);
40    float3 texSpec = LinearToSrgb(linSpec);
41    float3 ret = g_bSpecOrDiff ? texDiff : texSpec;
42    return ret;
43 }

 

如果你上面做的全部正确的族,那么漫反射图片应该是看起来这个样子的

高光图片应该是这样的:

正如你所看到的,这个过程并不完美。

  1. 在漫反射图片中多少会有一些高光信息。
  2. 一些物体表现得不是很好 。在只有高光的图片中,手柄处有一些偏蓝。我觉得是手柄是非导体导致频率有一些扰乱。
  3. 如果 高光或者漫反射被截断到1.0,那么这些像素就不对了。最好是曝光不足而不是曝光过度。
  4. 确保你使用一个远程快门控制器,否则你会有对齐的问题。

希望不是太麻烦,你可以自己来分离高光和漫反射了。