Просмотр исходного кода

Added equirectangular projection conversion to cubemap.

Branimir Karadžić 8 лет назад
Родитель
Сommit
0f75efea06
3 измененных файлов с 221 добавлено и 1 удалено
  1. 8 0
      include/bimg/bimg.h
  2. 190 0
      src/image.cpp
  3. 23 1
      tools/texturec/texturec.cpp

+ 8 - 0
include/bimg/bimg.h

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

+ 190 - 0
src/image.cpp

@@ -3759,4 +3759,194 @@ namespace bimg
 		return total;
 		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
 } // namespace bimg

+ 23 - 1
tools/texturec/texturec.cpp

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