Added equirectangular projection conversion to cubemap.

This commit is contained in:
Branimir Karadžić
2018-01-05 15:09:33 -08:00
parent b38c892289
commit 0f75efea06
3 changed files with 221 additions and 1 deletions

View File

@@ -601,6 +601,14 @@ namespace bimg
, ImageMip& _mip
);
///
ImageContainer* imageCubemapFromLatLongRgba32F(
bx::AllocatorI* _allocator
, const ImageContainer& _input
, bool _useBilinearInterpolation
, bx::Error* _err
);
} // namespace bimg
#endif // BIMG_IMAGE_H_HEADER_GUARD

View File

@@ -3759,4 +3759,194 @@ namespace bimg
return total;
}
// +----------+
// |-z 2|
// | ^ +y |
// | | |
// | +---->+x |
// +----------+----------+----------+----------+
// |+y 1|+y 4|+y 0|+y 5|
// | ^ -x | ^ +z | ^ +x | ^ -z |
// | | | | | | | | |
// | +---->+z | +---->+x | +---->-z | +---->-x |
// +----------+----------+----------+----------+
// |+z 3|
// | ^ -y |
// | | |
// | +---->+x |
// +----------+
//
struct CubeMapFace
{
float uv[3][3];
};
static const CubeMapFace s_cubeMapFace[] =
{
{{ // +x face
{ 0.0f, 0.0f, -1.0f }, // u -> -z
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 1.0f, 0.0f, 0.0f }, // +x face
}},
{{ // -x face
{ 0.0f, 0.0f, 1.0f }, // u -> +z
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ -1.0f, 0.0f, 0.0f }, // -x face
}},
{{ // +y face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, 0.0f, 1.0f }, // v -> +z
{ 0.0f, 1.0f, 0.0f }, // +y face
}},
{{ // -y face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, 0.0f, -1.0f }, // v -> -z
{ 0.0f, -1.0f, 0.0f }, // -y face
}},
{{ // +z face
{ 1.0f, 0.0f, 0.0f }, // u -> +x
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 0.0f, 0.0f, 1.0f }, // +z face
}},
{{ // -z face
{ -1.0f, 0.0f, 0.0f }, // u -> -x
{ 0.0f, -1.0f, 0.0f }, // v -> -y
{ 0.0f, 0.0f, -1.0f }, // -z face
}},
};
/// _u and _v should be center addressing and in [-1.0+invSize..1.0-invSize] range.
void texelUvToDir(float* _result, uint8_t _side, float _u, float _v)
{
const CubeMapFace& face = s_cubeMapFace[_side];
float tmp[3];
tmp[0] = face.uv[0][0] * _u + face.uv[1][0] * _v + face.uv[2][0];
tmp[1] = face.uv[0][1] * _u + face.uv[1][1] * _v + face.uv[2][1];
tmp[2] = face.uv[0][2] * _u + face.uv[1][2] * _v + face.uv[2][2];
bx::vec3Norm(_result, tmp);
}
void latLongFromDir(float* _outU, float* _outV, const float* _in)
{
const float phi = bx::fatan2(_in[0], _in[2]);
const float theta = bx::facos(_in[1]);
*_outU = (bx::kPi + phi)/bx::kPi2;
*_outV = theta*bx::kInvPi;
}
ImageContainer* imageCubemapFromLatLongRgba32F(bx::AllocatorI* _allocator, const ImageContainer& _input, bool _useBilinearInterpolation, bx::Error* _err)
{
BX_ERROR_SCOPE(_err);
if (_input.m_depth != 1
&& _input.m_numLayers != 1
&& _input.m_format != TextureFormat::RGBA32F
&& _input.m_width/2 != _input.m_height)
{
BX_ERROR_SET(_err, BIMG_ERROR, "Input image format is not equirectangular projection.");
return NULL;
}
const uint32_t srcWidthMinusOne = _input.m_width-1;
const uint32_t srcHeightMinusOne = _input.m_height-1;
const uint32_t srcPitch = _input.m_width*16;
const uint32_t dstWidth = _input.m_height/2;
const uint32_t dstPitch = dstWidth*16;
const float invDstWidth = 1.0f / float(dstWidth);
ImageContainer* output = imageAlloc(_allocator
, _input.m_format
, uint16_t(dstWidth)
, uint16_t(dstWidth)
, uint16_t(1)
, 1
, true
, false
);
const uint8_t* srcData = (const uint8_t*)_input.m_data;
for (uint8_t side = 0; side < 6 && _err->isOk(); ++side)
{
ImageMip mip;
imageGetRawData(*output, side, 0, output->m_data, output->m_size, mip);
for (uint32_t yy = 0; yy < dstWidth; ++yy)
{
for (uint32_t xx = 0; xx < dstWidth; ++xx)
{
float* dstData = (float*)&mip.m_data[yy*dstPitch+xx*16];
const float uu = 2.0f*xx*invDstWidth - 1.0f;
const float vv = 2.0f*yy*invDstWidth - 1.0f;
float dir[3];
texelUvToDir(dir, side, uu, vv);
float srcU, srcV;
latLongFromDir(&srcU, &srcV, dir);
srcU *= srcWidthMinusOne;
srcV *= srcHeightMinusOne;
if (_useBilinearInterpolation)
{
const uint32_t x0 = uint32_t(srcU);
const uint32_t y0 = uint32_t(srcV);
const uint32_t x1 = bx::min(x0 + 1, srcWidthMinusOne);
const uint32_t y1 = bx::min(y0 + 1, srcHeightMinusOne);
const float* src0 = (const float*)&srcData[y0*srcPitch + x0*16];
const float* src1 = (const float*)&srcData[y0*srcPitch + x1*16];
const float* src2 = (const float*)&srcData[y1*srcPitch + x0*16];
const float* src3 = (const float*)&srcData[y1*srcPitch + x1*16];
const float tx = srcU - float(int32_t(x0) );
const float ty = srcV - float(int32_t(y0) );
const float omtx = 1.0f - tx;
const float omty = 1.0f - ty;
float p0[4];
bx::vec4Mul(p0, src0, omtx*omty);
float p1[4];
bx::vec4Mul(p1, src1, tx*omty);
float p2[4];
bx::vec4Mul(p2, src2, omtx*ty);
float p3[4];
bx::vec4Mul(p3, src3, tx*ty);
const float rr = p0[0] + p1[0] + p2[0] + p3[0];
const float gg = p0[1] + p1[1] + p2[1] + p3[1];
const float bb = p0[2] + p1[2] + p2[2] + p3[2];
const float aa = p0[3] + p1[3] + p2[3] + p3[3];
dstData[0] = rr;
dstData[1] = gg;
dstData[2] = bb;
dstData[3] = aa;
}
else
{
const uint32_t x0 = uint32_t(srcU);
const uint32_t y0 = uint32_t(srcV);
const float* src0 = (const float*)&srcData[y0*srcPitch + x0*16];
dstData[0] = src0[0];
dstData[1] = src0[1];
dstData[2] = src0[2];
dstData[3] = src0[3];
}
}
}
}
return output;
}
} // namespace bimg

View File

@@ -37,6 +37,7 @@ struct Options
, quality(bimg::Quality::Default)
, mips(false)
, normalMap(false)
, equirect(false)
, iqa(false)
, sdf(false)
, alphaTest(false)
@@ -69,6 +70,7 @@ struct Options
bimg::Quality::Enum quality;
bool mips;
bool normalMap;
bool equirect;
bool iqa;
bool sdf;
bool alphaTest;
@@ -158,6 +160,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
&& !_options.sdf
&& !_options.alphaTest
&& !_options.normalMap
&& !_options.equirect
&& !_options.iqa
;
@@ -201,6 +204,23 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData
return output;
}
if (_options.equirect)
{
bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input);
bimg::imageFree(input);
bimg::ImageContainer* dst = bimg::imageCubemapFromLatLongRgba32F(_allocator, *src, true, _err);
bimg::imageFree(src);
if (!_err->isOk() )
{
return NULL;
}
input = bimg::imageConvert(_allocator, outputFormat, *dst);
bimg::imageFree(dst);
}
output = bimg::imageAlloc(
_allocator
, outputFormat
@@ -630,6 +650,7 @@ void help(const char* _error = NULL, bool _showHelp = true)
" -q <quality> Encoding quality (default, fastest, highest).\n"
" -m, --mips Generate mip-maps.\n"
" -n, --normalmap Input texture is normal map.\n"
" --equirect Input texture equirectangular projection of cubemap.\n"
" --sdf <edge> Compute SDF texture.\n"
" --ref <alpha> Alpha reference value.\n"
" --iqa Image Quality Assessment\n"
@@ -728,7 +749,8 @@ int main(int _argc, const char* _argv[])
options.mips = cmdLine.hasArg('m', "mips");
options.normalMap = cmdLine.hasArg('n', "normalmap");
options.iqa = cmdLine.hasArg('\0', "iqa");
options.equirect = cmdLine.hasArg("equirect");
options.iqa = cmdLine.hasArg("iqa");
const char* maxSize = cmdLine.findOption("max");
if (NULL != maxSize)