From 0f75efea06a4da36907abc3d1fd1ffcb060594ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Fri, 5 Jan 2018 15:09:33 -0800 Subject: [PATCH] Added equirectangular projection conversion to cubemap. --- include/bimg/bimg.h | 8 ++ src/image.cpp | 190 ++++++++++++++++++++++++++++++++++++ tools/texturec/texturec.cpp | 24 ++++- 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/include/bimg/bimg.h b/include/bimg/bimg.h index 323e416..5dc5adf 100644 --- a/include/bimg/bimg.h +++ b/include/bimg/bimg.h @@ -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 diff --git a/src/image.cpp b/src/image.cpp index 2a66d94..22ea54c 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -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 diff --git a/tools/texturec/texturec.cpp b/tools/texturec/texturec.cpp index 0dbdc50..d86ca90 100644 --- a/tools/texturec/texturec.cpp +++ b/tools/texturec/texturec.cpp @@ -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 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 Compute SDF texture.\n" " --ref 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)