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

@@ -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