123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- #ifndef UNITY_PACKING_INCLUDED
- #define UNITY_PACKING_INCLUDED
- //-----------------------------------------------------------------------------
- // Normal packing
- //-----------------------------------------------------------------------------
- real3 PackNormalMaxComponent(real3 n)
- {
- return (n / Max3(abs(n.x), abs(n.y), abs(n.z))) * 0.5 + 0.5;
- }
- real3 UnpackNormalMaxComponent(real3 n)
- {
- return normalize(n * 2.0 - 1.0);
- }
- // Ref: http://www.vis.uni-stuttgart.de/~engelhts/paper/vmvOctaMaps.pdf
- // Encode with Oct, this function work with any size of output
- // return real between [-1, 1]
- real2 PackNormalOctRectEncode(real3 n)
- {
- // Perform planar projection.
- real3 p = n * rcp(dot(abs(n), 1.0));
- real x = p.x, y = p.y, z = p.z;
- // Unfold the octahedron.
- // Also correct the aspect ratio from 2:1 to 1:1.
- real r = saturate(0.5 - 0.5 * x + 0.5 * y);
- real g = x + y;
- // Negative hemisphere on the left, positive on the right.
- return real2(CopySign(r, z), g);
- }
- real3 UnpackNormalOctRectEncode(real2 f)
- {
- real r = f.r, g = f.g;
- // Solve for {x, y, z} given {r, g}.
- real x = 0.5 + 0.5 * g - abs(r);
- real y = g - x;
- real z = max(1.0 - abs(x) - abs(y), REAL_EPS); // EPS is absolutely crucial for anisotropy
- real3 p = real3(x, y, CopySign(z, r));
- return normalize(p);
- }
- // Ref: http://jcgt.org/published/0003/02/01/paper.pdf
- // Encode with Oct, this function work with any size of output
- // return float between [-1, 1]
- float2 PackNormalOctQuadEncode(float3 n)
- {
- //float l1norm = dot(abs(n), 1.0);
- //float2 res0 = n.xy * (1.0 / l1norm);
- //float2 val = 1.0 - abs(res0.yx);
- //return (n.zz < float2(0.0, 0.0) ? (res0 >= 0.0 ? val : -val) : res0);
- // Optimized version of above code:
- n *= rcp(dot(abs(n), 1.0));
- float t = saturate(-n.z);
- return n.xy + (n.xy >= 0.0 ? t : -t);
- }
- float3 UnpackNormalOctQuadEncode(float2 f)
- {
- float3 n = float3(f.x, f.y, 1.0 - abs(f.x) - abs(f.y));
- //float2 val = 1.0 - abs(n.yx);
- //n.xy = (n.zz < float2(0.0, 0.0) ? (n.xy >= 0.0 ? val : -val) : n.xy);
- // Optimized version of above code:
- float t = max(-n.z, 0.0);
- n.xy += n.xy >= 0.0 ? -t.xx : t.xx;
- return normalize(n);
- }
- real2 PackNormalHemiOctEncode(real3 n)
- {
- real l1norm = dot(abs(n), 1.0);
- real2 res = n.xy * (1.0 / l1norm);
- return real2(res.x + res.y, res.x - res.y);
- }
- real3 UnpackNormalHemiOctEncode(real2 f)
- {
- real2 val = real2(f.x + f.y, f.x - f.y) * 0.5;
- real3 n = real3(val, 1.0 - dot(abs(val), 1.0));
- return normalize(n);
- }
- // Tetrahedral encoding - Looks like Tetra encoding 10:10 + 2 is similar to oct 11:11, as oct is cheaper prefer it
- // To generate the basisNormal below we use these 4 vertex of a regular tetrahedron
- // v0 = float3(1.0, 0.0, -1.0 / sqrt(2.0));
- // v1 = float3(-1.0, 0.0, -1.0 / sqrt(2.0));
- // v2 = float3(0.0, 1.0, 1.0 / sqrt(2.0));
- // v3 = float3(0.0, -1.0, 1.0 / sqrt(2.0));
- // Then we normalize the average of each face's vertices
- // normalize(v0 + v1 + v2), etc...
- static const real3 tetraBasisNormal[4] =
- {
- real3(0., 0.816497, -0.57735),
- real3(-0.816497, 0., 0.57735),
- real3(0.816497, 0., 0.57735),
- real3(0., -0.816497, -0.57735)
- };
- // Then to get the local matrix (with z axis rotate to basisNormal) use GetLocalFrame(basisNormal[xxx])
- static const real3x3 tetraBasisArray[4] =
- {
- real3x3(-1., 0., 0.,0., 0.57735, 0.816497,0., 0.816497, -0.57735),
- real3x3(0., -1., 0.,0.57735, 0., 0.816497,-0.816497, 0., 0.57735),
- real3x3(0., 1., 0.,-0.57735, 0., 0.816497,0.816497, 0., 0.57735),
- real3x3(1., 0., 0.,0., -0.57735, 0.816497,0., -0.816497, -0.57735)
- };
- // Return [-1..1] vector2 oriented in plane of the faceIndex of a regular tetrahedron
- real2 PackNormalTetraEncode(float3 n, out uint faceIndex)
- {
- // Retrieve the tetrahedra's face for the normal direction
- // It is the one with the greatest dot value with face normal
- real dot0 = dot(n, tetraBasisNormal[0]);
- real dot1 = dot(n, tetraBasisNormal[1]);
- real dot2 = dot(n, tetraBasisNormal[2]);
- real dot3 = dot(n, tetraBasisNormal[3]);
- real maxi0 = max(dot0, dot1);
- real maxi1 = max(dot2, dot3);
- real maxi = max(maxi0, maxi1);
- // Get the index from the greatest dot
- if (maxi == dot0)
- faceIndex = 0;
- else if (maxi == dot1)
- faceIndex = 1;
- else if (maxi == dot2)
- faceIndex = 2;
- else //(maxi == dot3)
- faceIndex = 3;
- // Rotate n into this local basis
- n = mul(tetraBasisArray[faceIndex], n);
- // Project n onto the local plane
- return n.xy;
- }
- // Assume f [-1..1]
- real3 UnpackNormalTetraEncode(real2 f, uint faceIndex)
- {
- // Recover n from local plane
- real3 n = real3(f.xy, sqrt(1.0 - dot(f.xy, f.xy)));
- // Inverse of transform PackNormalTetraEncode (just swap order in mul as we have a rotation)
- return mul(n, tetraBasisArray[faceIndex]);
- }
- // Unpack from normal map
- real3 UnpackNormalRGB(real4 packedNormal, real scale = 1.0)
- {
- real3 normal;
- normal.xyz = packedNormal.rgb * 2.0 - 1.0;
- normal.xy *= scale;
- return normal;
- }
- real3 UnpackNormalRGBNoScale(real4 packedNormal)
- {
- return packedNormal.rgb * 2.0 - 1.0;
- }
- real3 UnpackNormalAG(real4 packedNormal, real scale = 1.0)
- {
- real3 normal;
- normal.xy = packedNormal.ag * 2.0 - 1.0;
- normal.xy *= scale;
- normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
- return normal;
- }
- // Unpack normal as DXT5nm (1, y, 0, x) or BC5 (x, y, 0, 1)
- real3 UnpackNormalmapRGorAG(real4 packedNormal, real scale = 1.0)
- {
- // Convert to (?, y, 0, x)
- packedNormal.a *= packedNormal.r;
- return UnpackNormalAG(packedNormal, scale);
- }
- real3 UnpackNormal(real4 packedNormal)
- {
- #if defined(UNITY_NO_DXT5nm)
- return UnpackNormalRGBNoScale(packedNormal);
- #else
- // Compiler will optimize the scale away
- return UnpackNormalmapRGorAG(packedNormal, 1.0);
- #endif
- }
- real3 UnpackNormalScale(real4 packedNormal, real bumpScale)
- {
- #if defined(UNITY_NO_DXT5nm)
- return UnpackNormalRGB(packedNormal, bumpScale);
- #else
- return UnpackNormalmapRGorAG(packedNormal, bumpScale);
- #endif
- }
- //-----------------------------------------------------------------------------
- // HDR packing
- //-----------------------------------------------------------------------------
- // HDR Packing not defined in GLES2
- #if !defined(SHADER_API_GLES)
- // Ref: http://realtimecollisiondetection.net/blog/?p=15
- real4 PackToLogLuv(real3 vRGB)
- {
- // M matrix, for encoding
- const real3x3 M = real3x3(
- 0.2209, 0.3390, 0.4184,
- 0.1138, 0.6780, 0.7319,
- 0.0102, 0.1130, 0.2969);
- real4 vResult;
- real3 Xp_Y_XYZp = mul(vRGB, M);
- Xp_Y_XYZp = max(Xp_Y_XYZp, real3(1e-6, 1e-6, 1e-6));
- vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
- real Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
- vResult.w = frac(Le);
- vResult.z = (Le - (floor(vResult.w * 255.0)) / 255.0) / 255.0;
- return vResult;
- }
- real3 UnpackFromLogLuv(real4 vLogLuv)
- {
- // Inverse M matrix, for decoding
- const real3x3 InverseM = real3x3(
- 6.0014, -2.7008, -1.7996,
- -1.3320, 3.1029, -5.7721,
- 0.3008, -1.0882, 5.6268);
- real Le = vLogLuv.z * 255.0 + vLogLuv.w;
- real3 Xp_Y_XYZp;
- Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);
- Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
- Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
- real3 vRGB = mul(Xp_Y_XYZp, InverseM);
- return max(vRGB, real3(0.0, 0.0, 0.0));
- }
- // The standard 32-bit HDR color format
- uint PackToR11G11B10f(float3 rgb)
- {
- uint r = (f32tof16(rgb.x) << 17) & 0xFFE00000;
- uint g = (f32tof16(rgb.y) << 6) & 0x001FFC00;
- uint b = (f32tof16(rgb.z) >> 5) & 0x000003FF;
- return r | g | b;
- }
- float3 UnpackFromR11G11B10f(uint rgb)
- {
- float r = f16tof32((rgb >> 17) & 0x7FF0);
- float g = f16tof32((rgb >> 6) & 0x7FF0);
- float b = f16tof32((rgb << 5) & 0x7FE0);
- return float3(r, g, b);
- }
- #endif // SHADER_API_GLES
- //-----------------------------------------------------------------------------
- // Quaternion packing
- //-----------------------------------------------------------------------------
- // Ref: https://cedec.cesa.or.jp/2015/session/ENG/14698.html The Rendering Materials of Far Cry 4
- /*
- // This is GCN intrinsic
- uint FindBiggestComponent(real4 q)
- {
- uint xyzIndex = CubeMapFaceID(q.x, q.y, q.z) * 0.5f;
- uint wIndex = 3;
- bool wBiggest = abs(q.w) > max3(abs(q.x), qbs(q.y), qbs(q.z));
- return wBiggest ? wIndex : xyzIndex;
- }
- // Pack a quaternion into a 10:10:10:2
- real4 PackQuat(real4 quat)
- {
- uint index = FindBiggestComponent(quat);
- if (index == 0) quat = quat.yzwx;
- if (index == 1) quat = quat.xzwy;
- if (index == 2) quat = quat.xywz;
- real4 packedQuat;
- packedQuat.xyz = quat.xyz * FastSign(quat.w) * sqrt(0.5) + 0.5;
- packedQuat.w = index / 3.0;
- return packedQuat;
- }
- */
- // Unpack a quaternion from a 10:10:10:2
- real4 UnpackQuat(real4 packedQuat)
- {
- uint index = (uint)(packedQuat.w * 3.0);
- real4 quat;
- quat.xyz = packedQuat.xyz * sqrt(2.0) - (1.0 / sqrt(2.0));
- quat.w = sqrt(1.0 - saturate(dot(quat.xyz, quat.xyz)));
- if (index == 0) quat = quat.wxyz;
- if (index == 1) quat = quat.xwyz;
- if (index == 2) quat = quat.xywz;
- return quat;
- }
- // Integer and Float packing not defined in GLES2
- #if !defined(SHADER_API_GLES)
- //-----------------------------------------------------------------------------
- // Integer packing
- //-----------------------------------------------------------------------------
- // Packs an integer stored using at most 'numBits' into a [0..1] real.
- real PackInt(uint i, uint numBits)
- {
- uint maxInt = (1u << numBits) - 1u;
- return saturate(i * rcp(maxInt));
- }
- // Unpacks a [0..1] real into an integer of size 'numBits'.
- uint UnpackInt(real f, uint numBits)
- {
- uint maxInt = (1u << numBits) - 1u;
- return (uint)(f * maxInt + 0.5); // Round instead of truncating
- }
- // Packs a [0..255] integer into a [0..1] real.
- real PackByte(uint i)
- {
- return PackInt(i, 8);
- }
- // Unpacks a [0..1] real into a [0..255] integer.
- uint UnpackByte(real f)
- {
- return UnpackInt(f, 8);
- }
- // Packs a [0..65535] integer into a [0..1] real.
- real PackShort(uint i)
- {
- return PackInt(i, 16);
- }
- // Unpacks a [0..1] real into a [0..65535] integer.
- uint UnpackShort(real f)
- {
- return UnpackInt(f, 16);
- }
- // Packs 8 lowermost bits of a [0..65535] integer into a [0..1] real.
- real PackShortLo(uint i)
- {
- uint lo = BitFieldExtract(i, 0u, 8u);
- return PackInt(lo, 8);
- }
- // Packs 8 uppermost bits of a [0..65535] integer into a [0..1] real.
- real PackShortHi(uint i)
- {
- uint hi = BitFieldExtract(i, 8u, 8u);
- return PackInt(hi, 8);
- }
- real Pack2Byte(real2 inputs)
- {
- real2 temp = inputs * real2(255.0, 255.0);
- temp.x *= 256.0;
- temp = round(temp);
- real combined = temp.x + temp.y;
- return combined * (1.0 / 65535.0);
- }
- real2 Unpack2Byte(real inputs)
- {
- real temp = round(inputs * 65535.0);
- real ipart;
- real fpart = modf(temp / 256.0, ipart);
- real2 result = real2(ipart, round(256.0 * fpart));
- return result * (1.0 / real2(255.0, 255.0));
- }
- // Encode a real in [0..1] and an int in [0..maxi - 1] as a real [0..1] to be store in log2(precision) bit
- // maxi must be a power of two and define the number of bit dedicated 0..1 to the int part (log2(maxi))
- // Example: precision is 256.0, maxi is 2, i is [0..1] encode on 1 bit. f is [0..1] encode on 7 bit.
- // Example: precision is 256.0, maxi is 4, i is [0..3] encode on 2 bit. f is [0..1] encode on 6 bit.
- // Example: precision is 256.0, maxi is 8, i is [0..7] encode on 3 bit. f is [0..1] encode on 5 bit.
- // ...
- // Example: precision is 1024.0, maxi is 8, i is [0..7] encode on 3 bit. f is [0..1] encode on 7 bit.
- //...
- real PackFloatInt(real f, uint i, real maxi, real precision)
- {
- // Constant
- real precisionMinusOne = precision - 1.0;
- real t1 = ((precision / maxi) - 1.0) / precisionMinusOne;
- real t2 = (precision / maxi) / precisionMinusOne;
- return t1 * f + t2 * real(i);
- }
- void UnpackFloatInt(real val, real maxi, real precision, out real f, out uint i)
- {
- // Constant
- real precisionMinusOne = precision - 1.0;
- real t1 = ((precision / maxi) - 1.0) / precisionMinusOne;
- real t2 = (precision / maxi) / precisionMinusOne;
- // extract integer part
- i = int((val / t2) + rcp(precisionMinusOne)); // + rcp(precisionMinusOne) to deal with precision issue (can't use round() as val contain the floating number
- // Now that we have i, solve formula in PackFloatInt for f
- //f = (val - t2 * real(i)) / t1 => convert in mads form
- f = saturate((-t2 * real(i) + val) / t1); // Saturate in case of precision issue
- }
- // Define various variante for ease of read
- real PackFloatInt8bit(real f, uint i, real maxi)
- {
- return PackFloatInt(f, i, maxi, 256.0);
- }
- void UnpackFloatInt8bit(real val, real maxi, out real f, out uint i)
- {
- UnpackFloatInt(val, maxi, 256.0, f, i);
- }
- real PackFloatInt10bit(real f, uint i, real maxi)
- {
- return PackFloatInt(f, i, maxi, 1024.0);
- }
- void UnpackFloatInt10bit(real val, real maxi, out real f, out uint i)
- {
- UnpackFloatInt(val, maxi, 1024.0, f, i);
- }
- real PackFloatInt16bit(real f, uint i, real maxi)
- {
- return PackFloatInt(f, i, maxi, 65536.0);
- }
- void UnpackFloatInt16bit(real val, real maxi, out real f, out uint i)
- {
- UnpackFloatInt(val, maxi, 65536.0, f, i);
- }
- //-----------------------------------------------------------------------------
- // Float packing
- //-----------------------------------------------------------------------------
- // src must be between 0.0 and 1.0
- uint PackFloatToUInt(real src, uint offset, uint numBits)
- {
- return UnpackInt(src, numBits) << offset;
- }
- real UnpackUIntToFloat(uint src, uint offset, uint numBits)
- {
- uint maxInt = (1u << numBits) - 1u;
- return real(BitFieldExtract(src, offset, numBits)) * rcp(maxInt);
- }
- uint PackToR10G10B10A2(real4 rgba)
- {
- return (PackFloatToUInt(rgba.x, 0, 10) |
- PackFloatToUInt(rgba.y, 10, 10) |
- PackFloatToUInt(rgba.z, 20, 10) |
- PackFloatToUInt(rgba.w, 30, 2));
- }
- real4 UnpackFromR10G10B10A2(uint rgba)
- {
- real4 output;
- output.x = UnpackUIntToFloat(rgba, 0, 10);
- output.y = UnpackUIntToFloat(rgba, 10, 10);
- output.z = UnpackUIntToFloat(rgba, 20, 10);
- output.w = UnpackUIntToFloat(rgba, 30, 2);
- return output;
- }
- // Both the input and the output are in the [0, 1] range.
- real2 PackFloatToR8G8(real f)
- {
- uint i = UnpackShort(f);
- return real2(PackShortLo(i), PackShortHi(i));
- }
- // Both the input and the output are in the [0, 1] range.
- real UnpackFloatFromR8G8(real2 f)
- {
- uint lo = UnpackByte(f.x);
- uint hi = UnpackByte(f.y);
- uint cb = (hi << 8) + lo;
- return PackShort(cb);
- }
- // Pack float2 (each of 12 bit) in 888
- float3 PackFloat2To888(float2 f)
- {
- uint2 i = (uint2)(f * 4095.5);
- uint2 hi = i >> 8;
- uint2 lo = i & 255;
- // 8 bit in lo, 4 bit in hi
- uint3 cb = uint3(lo, hi.x | (hi.y << 4));
- return cb / 255.0;
- }
- // Unpack 2 float of 12bit packed into a 888
- float2 Unpack888ToFloat2(float3 x)
- {
- uint3 i = (uint3)(x * 255.0);
- // 8 bit in lo, 4 bit in hi
- uint hi = i.z >> 4;
- uint lo = i.z & 15;
- uint2 cb = i.xy | uint2(lo << 8, hi << 8);
- return cb / 4095.0;
- }
- #endif // SHADER_API_GLES
- #endif // UNITY_PACKING_INCLUDED
|