您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

477 行
20 KiB

using UnityEngine;
using UnityEditor.Experimental.VFX.Toolbox;
// MiniEXR 2013 by Aras Pranckevicius / Unity Technologies.
//
// C# conversion by Ilya Suzdalnitski.
// Slightly Modified by Thomas Ich�
//
// Writes OpenEXR RGB files out of half-precision RGBA or RGB data.
//
namespace VFXToolbox.MiniEXR {
//Based on source-forge project: http://sourceforge.net/projects/csharp-half/
internal static class HalfHelper
{
private static uint[] mantissaTable = GenerateMantissaTable();
private static uint[] exponentTable = GenerateExponentTable();
private static ushort[] offsetTable = GenerateOffsetTable();
private static ushort[] baseTable = GenerateBaseTable();
private static sbyte[] shiftTable = GenerateShiftTable();
// Transforms the subnormal representation to a normalized one.
private static uint ConvertMantissa(int i)
{
uint m = (uint)(i << 13); // Zero pad mantissa bits
uint e = 0; // Zero exponent
// While not normalized
while ((m & 0x00800000) == 0)
{
e -= 0x00800000; // Decrement exponent (1<<23)
m <<= 1; // Shift mantissa
}
m &= unchecked((uint)~0x00800000); // Clear leading 1 bit
e += 0x38800000; // Adjust bias ((127-14)<<23)
return m | e; // Return combined number
}
private static uint[] GenerateMantissaTable()
{
uint[] mantissaTable = new uint[2048];
mantissaTable[0] = 0;
for (int i = 1; i < 1024; i++)
{
mantissaTable[i] = ConvertMantissa(i);
}
for (int i = 1024; i < 2048; i++)
{
mantissaTable[i] = (uint)(0x38000000 + ((i - 1024) << 13));
}
return mantissaTable;
}
private static uint[] GenerateExponentTable()
{
uint[] exponentTable = new uint[64];
exponentTable[0] = 0;
for (int i = 1; i < 31; i++)
{
exponentTable[i] = (uint)(i << 23);
}
exponentTable[31] = 0x47800000;
exponentTable[32] = 0x80000000;
for (int i = 33; i < 63; i++)
{
exponentTable[i] = (uint)(0x80000000 + ((i - 32) << 23));
}
exponentTable[63] = 0xc7800000;
return exponentTable;
}
private static ushort[] GenerateOffsetTable()
{
ushort[] offsetTable = new ushort[64];
offsetTable[0] = 0;
for (int i = 1; i < 32; i++)
{
offsetTable[i] = 1024;
}
offsetTable[32] = 0;
for (int i = 33; i < 64; i++)
{
offsetTable[i] = 1024;
}
return offsetTable;
}
private static ushort[] GenerateBaseTable()
{
ushort[] baseTable = new ushort[512];
for (int i = 0; i < 256; ++i)
{
sbyte e = (sbyte)(127 - i);
if (e > 24)
{ // Very small numbers map to zero
baseTable[i | 0x000] = 0x0000;
baseTable[i | 0x100] = 0x8000;
}
else if (e > 14)
{ // Small numbers map to denorms
baseTable[i | 0x000] = (ushort)(0x0400 >> (18 + e));
baseTable[i | 0x100] = (ushort)((0x0400 >> (18 + e)) | 0x8000);
}
else if (e >= -15)
{ // Normal numbers just lose precision
baseTable[i | 0x000] = (ushort)((15 - e) << 10);
baseTable[i | 0x100] = (ushort)(((15 - e) << 10) | 0x8000);
}
else if (e > -128)
{ // Large numbers map to Infinity
baseTable[i | 0x000] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
}
else
{ // Infinity and NaN's stay Infinity and NaN's
baseTable[i | 0x000] = 0x7c00;
baseTable[i | 0x100] = 0xfc00;
}
}
return baseTable;
}
private static sbyte[] GenerateShiftTable()
{
sbyte[] shiftTable = new sbyte[512];
for (int i = 0; i < 256; ++i)
{
sbyte e = (sbyte)(127 - i);
if (e > 24)
{ // Very small numbers map to zero
shiftTable[i | 0x000] = 24;
shiftTable[i | 0x100] = 24;
}
else if (e > 14)
{ // Small numbers map to denorms
shiftTable[i | 0x000] = (sbyte)(e - 1);
shiftTable[i | 0x100] = (sbyte)(e - 1);
}
else if (e >= -15)
{ // Normal numbers just lose precision
shiftTable[i | 0x000] = 13;
shiftTable[i | 0x100] = 13;
}
else if (e > -128)
{ // Large numbers map to Infinity
shiftTable[i | 0x000] = 24;
shiftTable[i | 0x100] = 24;
}
else
{ // Infinity and NaN's stay Infinity and NaN's
shiftTable[i | 0x000] = 13;
shiftTable[i | 0x100] = 13;
}
}
return shiftTable;
}
public static float HalfToSingle(ushort half)
{
uint result = mantissaTable[offsetTable[half >> 10] + (half & 0x3ff)] + exponentTable[half >> 10];
return System.BitConverter.ToSingle( System.BitConverter.GetBytes( result ), 0 );
//return *((float*)&result);
}
public static ushort SingleToHalf(float single)
{
//uint value = *((uint*)&single);
uint value = System.BitConverter.ToUInt32( System.BitConverter.GetBytes( single ), 0 );
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return result;
}
}
internal static class MiniEXR {
// Writes EXR into a memory buffer.
// Input:
// - (width) x (height) image,
// - channels=4: 8 bytes per pixel (R,G,B,A order, 16 bit float per channel; alpha ignored), or
// - channels=3: 6 bytes per pixel (R,G,B order, 16 bit float per channel).
// Returns memory buffer with .EXR contents and buffer size in outSize. free() the buffer when done with it.
public static void MiniEXRWrite (string _filePath, uint _width, uint _height, bool _ExportAlpha, Color[] _colorArray, bool bFlipVertical) {
byte[] bytes = MiniEXRWrite(_width, _height, _ExportAlpha, _colorArray, bFlipVertical);
System.IO.File.WriteAllBytes(_filePath, bytes );
}
public static byte[] MiniEXRWrite (uint _width, uint _height, bool _ExportAlpha, Color[] _colorArray, bool bFlipVertical = false)
{
if (bFlipVertical)
_colorArray = FlipVertical(_colorArray, _width, _height);
byte stride = (byte)(_ExportAlpha ? 4 : 3);
float[] rgbaArray = new float[ _colorArray.Length * stride ];
for (int i = 0; i < _colorArray.Length; i++)
{
rgbaArray[i * stride] = _colorArray[i].r;
rgbaArray[i * stride + 1] = _colorArray[i].g;
rgbaArray[i * stride + 2] = _colorArray[i].b;
if(_ExportAlpha)
rgbaArray[i * stride + 3] = _colorArray[i].a;
}
return MiniEXRWrite(_width, _height, stride, rgbaArray);
}
private static Color[] FlipVertical(Color[] input, uint _width, uint _height)
{
Color[] output = new Color[input.Length];
uint k = 0;
for(int j = (int)_height-1; j >= 0; j--)
{
for (int i = 0; i < _width; i++)
{
int idx = i + (j * (int)_width);
output[k] = input[idx];
k++;
}
}
return output;
}
public static byte[] MiniEXRWrite (uint _width, uint _height, uint _channels, float[] _rgbaArray)
{
//const void* rgba16f
uint ww = _width-1;
uint hh = _height-1;
byte[] kHeader;
if(_channels == 3)
{
kHeader = new byte[] {
0x76, 0x2f, 0x31, 0x01, // magic
2, 0, 0, 0, // version, scanline
// channels
(byte)'c',(byte)'h',(byte)'a',(byte)'n',(byte)'n',(byte)'e',(byte)'l',(byte)'s',0,
(byte)'c',(byte)'h',(byte)'l',(byte)'i',(byte)'s',(byte)'t',0,
55,0,0,0,
(byte)'B',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // R, half
(byte)'G',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // G, half
(byte)'R',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // B, half
0,
// compression
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0,
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0,
1,0,0,0,
0, // no compression
// dataWindow
(byte)'d',(byte)'a',(byte)'t',(byte)'a',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0,
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0,
16,0,0,0,
0,0,0,0,0,0,0,0,
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF),
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF),
// displayWindow
(byte)'d',(byte)'i',(byte)'s',(byte)'p',(byte)'l',(byte)'a',(byte)'y',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0,
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0,
16,0,0,0,
0,0,0,0,0,0,0,0,
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF),
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF),
// lineOrder
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0,
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0,
1,0,0,0,
0, // increasing Y
// pixelAspectRatio
(byte)'p',(byte)'i',(byte)'x',(byte)'e',(byte)'l',(byte)'A',(byte)'s',(byte)'p',(byte)'e',(byte)'c',(byte)'t',(byte)'R',(byte)'a',(byte)'t',(byte)'i',(byte)'o',0,
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0,
4,0,0,0,
0,0,0x80,0x3f, // 1.0f
// screenWindowCenter
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'C',(byte)'e',(byte)'n',(byte)'t',(byte)'e',(byte)'r',0,
(byte)'v',(byte)'2',(byte)'f',0,
8,0,0,0,
0,0,0,0, 0,0,0,0,
// screenWindowWidth
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'W',(byte)'i',(byte)'d',(byte)'t',(byte)'h',0,
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0,
4,0,0,0,
0,0,0x80,0x3f, // 1.0f
// end of header
0,
};
}
else
{
kHeader = new byte[] {
0x76, 0x2f, 0x31, 0x01, // magic
2, 0, 0, 0, // version, scanline
// channels
(byte)'c',(byte)'h',(byte)'a',(byte)'n',(byte)'n',(byte)'e',(byte)'l',(byte)'s',0,
(byte)'c',(byte)'h',(byte)'l',(byte)'i',(byte)'s',(byte)'t',0,
55,0,0,0,
(byte)'A',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // A, half
(byte)'B',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // R, half
(byte)'G',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // G, half
(byte)'R',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // B, half
0,
// compression
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0,
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0,
1,0,0,0,
0, // no compression
// dataWindow
(byte)'d',(byte)'a',(byte)'t',(byte)'a',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0,
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0,
16,0,0,0,
0,0,0,0,0,0,0,0,
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF),
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF),
// displayWindow
(byte)'d',(byte)'i',(byte)'s',(byte)'p',(byte)'l',(byte)'a',(byte)'y',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0,
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0,
16,0,0,0,
0,0,0,0,0,0,0,0,
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF),
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF),
// lineOrder
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0,
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0,
1,0,0,0,
0, // increasing Y
// pixelAspectRatio
(byte)'p',(byte)'i',(byte)'x',(byte)'e',(byte)'l',(byte)'A',(byte)'s',(byte)'p',(byte)'e',(byte)'c',(byte)'t',(byte)'R',(byte)'a',(byte)'t',(byte)'i',(byte)'o',0,
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0,
4,0,0,0,
0,0,0x80,0x3f, // 1.0f
// screenWindowCenter
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'C',(byte)'e',(byte)'n',(byte)'t',(byte)'e',(byte)'r',0,
(byte)'v',(byte)'2',(byte)'f',0,
8,0,0,0,
0,0,0,0, 0,0,0,0,
// screenWindowWidth
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'W',(byte)'i',(byte)'d',(byte)'t',(byte)'h',0,
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0,
4,0,0,0,
0,0,0x80,0x3f, // 1.0f
// end of header
0,
};
}
uint kHeaderSize = (uint)kHeader.Length;
uint kScanlineTableSize = 8 * _height;
uint pixelRowSize = _width * _channels * 2;
uint fullRowSize = pixelRowSize + 8;
uint bufSize = kHeaderSize + kScanlineTableSize + _height * fullRowSize;
byte[] buf = new byte[bufSize];
// copy in header
int bufI = 0;
for (int i = 0; i < kHeaderSize; i++) {
buf[ bufI ] = kHeader[i];
bufI++;
}
// line offset table
uint ofs = kHeaderSize + kScanlineTableSize;
for (int y = 0; y < _height; ++y)
{
buf[ bufI++ ] = (byte)(ofs & 0xFF);
buf[ bufI++ ] = (byte)((ofs >> 8) & 0xFF);
buf[ bufI++ ] = (byte)((ofs >> 16) & 0xFF);
buf[ bufI++ ] = (byte)((ofs >> 24) & 0xFF);
buf[ bufI++ ] = 0;
buf[ bufI++ ] = 0;
buf[ bufI++ ] = 0;
buf[ bufI++ ] = 0;
ofs += fullRowSize;
}
//Convert float to half float
ushort[] srcHalf = new ushort[_rgbaArray.Length];
for (int i = 0; i < _rgbaArray.Length; i++) {
//Gamma encode before converting : no
//_rgbaArray[i] = Mathf.Pow(_rgbaArray[i], 2.2f);
srcHalf[i] = HalfHelper.SingleToHalf( _rgbaArray[i] );
}
uint srcDataI = 0;
for (int y = 0; y < _height; y++)
{
// coordinate
buf[ bufI++ ] = (byte)(y & 0xFF);
buf[ bufI++ ] = (byte)((y >> 8) & 0xFF);
buf[ bufI++ ] = (byte)((y >> 16) & 0xFF);
buf[ bufI++ ] = (byte)((y >> 24) & 0xFF);
// data size
buf[ bufI++ ] = (byte)(pixelRowSize & 0xFF);
buf[ bufI++ ] = (byte)((pixelRowSize >> 8) & 0xFF);
buf[ bufI++ ] = (byte)((pixelRowSize >> 16) & 0xFF);
buf[ bufI++ ] = (byte)((pixelRowSize >> 24) & 0xFF);
// B, G, R
//memcpy (ptr, src, width*6); //Copy first line - 6 bits, 2 bits per channel
uint tempSrcI;
//If _channels == 4, write Alpha
if(_channels == 4)
{
tempSrcI = srcDataI;
for (int x = 0; x < _width; ++x)
{
//Blue
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 3 ] );
buf[ bufI++ ] = halfBytes[0];
buf[ bufI++ ] = halfBytes[1];
tempSrcI += _channels;
}
}
//First copy a line of B
tempSrcI = srcDataI;
for (int x = 0; x < _width; ++x)
{
//Blue
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 2 ] );
buf[ bufI++ ] = halfBytes[0];
buf[ bufI++ ] = halfBytes[1];
tempSrcI += _channels;
}
//Then copy a line of G
tempSrcI = srcDataI;
for (int x = 0; x < _width; ++x)
{
//Blue
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 1 ] );
buf[ bufI++ ] = halfBytes[0];
buf[ bufI++ ] = halfBytes[1];
tempSrcI += _channels;
}
//Finally copy a line of R
tempSrcI = srcDataI;
for (int x = 0; x < _width; ++x)
{
//Blue
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI ] );
buf[ bufI++ ] = halfBytes[0];
buf[ bufI++ ] = halfBytes[1];
tempSrcI += _channels;
}
srcDataI += _width * _channels;
}
return buf;
}
}
}