为什么标准化的EXR渲染表面法线看起来与PNG渲染不同?

时间:2018-04-12 02:44:58

标签: python graphics 3d rendering blender

我使用Cycles渲染表面法线并以EXR格式存储结果。 EXR格式的值在[-1, 1]范围内,这意味着我无法直接将值存储在PNG文件中。以下是我如何阅读.exr文件并将值存储在numpy数组中:

import OpenEXR, array, Imath
exrFile = OpenEXR.InputFile('normal.exr')
FLOAT = Imath.PixelType(Imath.PixelType.FLOAT)
(RGB) = [array.array('f', exrFile.channel(Chan, FLOAT)).tolist() for Chan in ("R", "G", "B") ]
normalNPArray = np.array(RGB)
normalNPArray = normalNPArray.reshape((3, resolution, resolution))

我将值标准化为如下,并将曲面法线存储为PNG图像:

normalNPArray += 1
normalNPArray /= 2
normalNPArray *= 255
normalNPArray = normalNPArray.astype(np.uint8)
im = Image.fromarray(normalNPArray.transpose(1, 2, 0), mode='RGB')
im.save('temp.png')

这是打开存储图像后得到的结果: enter image description here

如果我只是忽略零以下的值(删除/注释前两行),我会得到以下结果: enter image description here

但是,如果我直接将曲面Normal存储为PNG图像而不是EXR,我会得到以下结果:

enter image description here

我想知道,如何才能获得看起来像PNG渲染的可视化效果? Blender如何规范化这些值?

2 个答案:

答案 0 :(得分:0)

我有一位朋友帮我这个,希望对你有用:

为渲染器或着色器制作法线贴图时,需要考虑以下因素:

  1. "空间"其中定义了法线。这可以是世界空间,对象空间或切线空间。
  2. 渲染器期望[x,y,z]与[x,-y,z]等轴的排序。
  3. 范围可以是[0,1]或[-1,1]。
  4. 可能还有Alpha的处理方式。
  5. 人们可以阅读有关格式的信息并弄清楚。或者简单地获得现有的法线贴图,并直观地计算出格式。例如,如果工作地图看起来是浅蓝色,那么它们很可能在切线空间中,而世界空间则有更纯净的红色,绿色和蓝色。

    大多数人使用切线空间法线贴图,因为它允许变形。我相信Blender会为您提供所有三个HERE

    的选项

    注意: 我检查您存储的图像(图像1),背景不是0.如果原始图像具有-1背景,则应为零。所以你在代码中做错了。如果您发布EXR图像以便我可以进行一些测试,那将非常有用。

答案 1 :(得分:0)

在其中一条评论中提及keltar,结果表明,在将数组类型转换为0时,Numpy不会自动将负值设置为uint8。例如,假设我有一个numpy数组如下:

array([  46.73017823, -250.31618571, -247.44416527,   97.52841554,
       -204.92988386,  191.09452493,  103.15708521,  -86.10470495,
         46.211924  , -195.30653599,  240.44499889, -169.42729244,
        210.44996545, -182.04892973, -166.20581924, -221.11524425,
        164.79367242, -199.80888341,   94.1786936 ,   43.45477102])

我原以为normalNPArray = normalNPArray.astype(np.uint8)会自动将负值设置为0,因为uint8自然不支持负值。但是,将数组转换为np.uint8会给我以下内容:

array([ 46,   6,   9,  97,  52, 191, 103, 170,  46,  61, 240,  87, 210,
            74,  90,  35, 164,  57,  94,  43], dtype=uint8)

执行normalNPArray[normalNPArray < 0] = 0然后将数组转换为np.uint8将解决此问题。