Cubemap.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Processing/ImageObjectImpl.h>
  9. #include <Processing/ImageToProcess.h>
  10. #include <Processing/PixelFormatInfo.h>
  11. #include <Processing/ImageConvert.h>
  12. #include <Processing/ImageFlags.h>
  13. #include <Compressors/Compressor.h>
  14. #include <Converters/PixelOperation.h>
  15. #include <Converters/Cubemap.h>
  16. #include <CCubeMapProcessor.h>
  17. namespace ImageProcessingAtom
  18. {
  19. CubemapLayoutInfo CubemapLayout::s_layoutList[CubemapLayoutTypeCount];
  20. template <class TInteger>
  21. inline bool IsPowerOfTwo(TInteger x)
  22. {
  23. return (x & (x - 1)) == 0;
  24. }
  25. CubemapLayoutInfo::CubemapLayoutInfo()
  26. : m_type(CubemapLayoutNone)
  27. , m_rows(0)
  28. , m_columns(0)
  29. {
  30. }
  31. void CubemapLayoutInfo::SetFaceInfo(CubemapFace face, AZ::u8 row, AZ::u8 col, CubemapFaceDirection dir)
  32. {
  33. m_faceInfos[face].row = row;
  34. m_faceInfos[face].column = col;
  35. m_faceInfos[face].direction = dir;
  36. }
  37. void CubemapLayout::InitCubemapLayoutInfos()
  38. {
  39. //CubemapLayoutHorizontal
  40. //left , right, front, back, top, bottom;
  41. //NOTE: this layout is widely used in game projects by Jan 2018 since other layouts weren't supported correctly
  42. //but the faces in one has unusual directions compare to other format.
  43. //The direction matters when using it as input for Cubemap generation filter.
  44. //Left: rotated left 90 degree. Right: rotated right 90 degree
  45. //Front: rotated 180 degree. Back: no rotation
  46. //Top: rotate 180 degree. Bottom: no rotation
  47. CubemapLayoutInfo* info = &s_layoutList[CubemapLayoutHorizontal];
  48. info->m_rows = 1;
  49. info->m_columns = 6;
  50. info->m_type = CubemapLayoutHorizontal;
  51. info->SetFaceInfo(FaceLeft, 0, 0, CubemapFaceDirection::DirRotateLeft90);
  52. info->SetFaceInfo(FaceRight, 0, 1, CubemapFaceDirection::DirRotateRight90);
  53. info->SetFaceInfo(FaceFront, 0, 2, CubemapFaceDirection::DirRotate180);
  54. info->SetFaceInfo(FaceBack, 0, 3, CubemapFaceDirection::DirNoRotation);
  55. info->SetFaceInfo(FaceTop, 0, 4, CubemapFaceDirection::DirRotate180);
  56. info->SetFaceInfo(FaceBottom, 0, 5, CubemapFaceDirection::DirNoRotation);
  57. //CubemapLayoutHorizontalCross
  58. // top
  59. // left front right back
  60. // bottom
  61. info = &s_layoutList[CubemapLayoutHorizontalCross];
  62. info->m_rows = 3;
  63. info->m_columns = 4;
  64. info->m_type = CubemapLayoutHorizontalCross;
  65. info->SetFaceInfo(FaceLeft, 1, 0, CubemapFaceDirection::DirNoRotation);
  66. info->SetFaceInfo(FaceRight, 1, 2, CubemapFaceDirection::DirNoRotation);
  67. info->SetFaceInfo(FaceFront, 1, 1, CubemapFaceDirection::DirNoRotation);
  68. info->SetFaceInfo(FaceBack, 1, 3, CubemapFaceDirection::DirNoRotation);
  69. info->SetFaceInfo(FaceTop, 0, 1, CubemapFaceDirection::DirNoRotation);
  70. info->SetFaceInfo(FaceBottom, 2, 1, CubemapFaceDirection::DirNoRotation);
  71. //CubemapLayoutVerticalCross
  72. // top
  73. // left front right
  74. // bottom
  75. // back
  76. info = &s_layoutList[CubemapLayoutVerticalCross];
  77. info->m_rows = 4;
  78. info->m_columns = 3;
  79. info->m_type = CubemapLayoutVerticalCross;
  80. info->SetFaceInfo(FaceLeft, 1, 0, CubemapFaceDirection::DirNoRotation);
  81. info->SetFaceInfo(FaceRight, 1, 2, CubemapFaceDirection::DirNoRotation);
  82. info->SetFaceInfo(FaceFront, 1, 1, CubemapFaceDirection::DirNoRotation);
  83. info->SetFaceInfo(FaceBack, 3, 1, CubemapFaceDirection::DirRotate180);
  84. info->SetFaceInfo(FaceTop, 0, 1, CubemapFaceDirection::DirNoRotation);
  85. info->SetFaceInfo(FaceBottom, 2, 1, CubemapFaceDirection::DirNoRotation);
  86. //CubemapLayoutVertical
  87. // left
  88. // right
  89. // front
  90. // back
  91. // top
  92. // bottom
  93. info = &s_layoutList[CubemapLayoutVertical];
  94. info->m_rows = 6;
  95. info->m_columns = 1;
  96. info->m_type = CubemapLayoutVertical;
  97. info->SetFaceInfo(FaceLeft, 0, 0, CubemapFaceDirection::DirRotateLeft90);
  98. info->SetFaceInfo(FaceRight, 1, 0, CubemapFaceDirection::DirRotateRight90);
  99. info->SetFaceInfo(FaceFront, 2, 0, CubemapFaceDirection::DirRotate180);
  100. info->SetFaceInfo(FaceBack, 3, 0, CubemapFaceDirection::DirNoRotation);
  101. info->SetFaceInfo(FaceTop, 4, 0, CubemapFaceDirection::DirRotate180);
  102. info->SetFaceInfo(FaceBottom, 5, 0, CubemapFaceDirection::DirNoRotation);
  103. //make sure all types were initialized
  104. for (int i = 0; i < CubemapLayoutTypeCount; i++)
  105. {
  106. AZ_Assert(s_layoutList[i].m_type == i, "layout %d is not initialized", i);
  107. }
  108. }
  109. const float* GetTransformMatrix(CubemapFaceDirection dir, bool isInvert)
  110. {
  111. switch (dir)
  112. {
  113. case CubemapFaceDirection::DirNoRotation:
  114. {
  115. static const float mat[] = { 1, 0, 0, 1 };
  116. return mat;
  117. }
  118. case CubemapFaceDirection::DirRotateLeft90:
  119. {
  120. //thelta = 90 degree
  121. //{cos, -sin, sin, cos}
  122. if (isInvert)
  123. {
  124. return GetTransformMatrix(CubemapFaceDirection::DirRotateRight90, false);
  125. }
  126. static const float mat[] = { 0, -1, 1, 0 };
  127. return mat;
  128. }
  129. case CubemapFaceDirection::DirRotateRight90:
  130. {
  131. //thelta = -90 degree
  132. if (isInvert)
  133. {
  134. return GetTransformMatrix(CubemapFaceDirection::DirRotateLeft90, false);
  135. }
  136. static const float mat[] = { 0, 1, -1, 0 };
  137. return mat;
  138. }
  139. case CubemapFaceDirection::DirRotate180:
  140. {
  141. //thelta = 180 degree
  142. static const float mat[] = { -1, 0, 0, -1 };
  143. return mat;
  144. }
  145. case CubemapFaceDirection::DirMirrorHorizontal:
  146. {
  147. static const float mat[] = { 1, 0, 0, -1 };
  148. return mat;
  149. }
  150. default:
  151. {
  152. AZ_Assert(false, "unimplemented direction matrix");
  153. static const float mat[] = { 1, 0, 0, 1 };
  154. return mat;
  155. }
  156. }
  157. }
  158. void TransformImage(CubemapFaceDirection srcDir, CubemapFaceDirection dstDir, const AZ::u8* srcImageBuf,
  159. AZ::u8* dstImageBuf, AZ::u8 bytePerPixel, AZ::u32 rectSize)
  160. {
  161. //get final matrix to transform dst back to src
  162. const float* m1 = GetTransformMatrix(dstDir, true);
  163. const float* m2 = GetTransformMatrix(srcDir, false);
  164. float mtx[4];
  165. mtx[0] = m1[0] * m2[0] + m1[1] * m2[2];
  166. mtx[1] = m1[0] * m2[1] + m1[1] * m2[3];
  167. mtx[2] = m1[2] * m2[0] + m1[3] * m2[2];
  168. mtx[3] = m1[2] * m2[1] + m1[3] * m2[3];
  169. const float* noRotate = GetTransformMatrix(CubemapFaceDirection::DirNoRotation, false);
  170. if (memcmp(noRotate, mtx, 4 * sizeof(float)) == 0)
  171. {
  172. memcpy(dstImageBuf, srcImageBuf, rectSize * rectSize * bytePerPixel);
  173. return;
  174. }
  175. //for each pixel in dst image, find it's location in src and copy the data from there
  176. float halfSize = static_cast<float>(rectSize / 2);
  177. for (AZ::u32 row = 0; row < rectSize; row++)
  178. {
  179. for (AZ::u32 col = 0; col < rectSize; col++)
  180. {
  181. //coordinate in image center as origin and right as positive X, up as positive Y
  182. float dstX = col + 0.5f - halfSize;
  183. float dstY = halfSize - row - 0.5f;
  184. float srcX = dstX * mtx[0] + dstY * mtx[1];
  185. float srcY = dstX * mtx[2] + dstY * mtx[3];
  186. AZ::u32 srcCol = static_cast<AZ::u32>(srcX + halfSize);
  187. AZ::u32 srcRow = static_cast<AZ::u32>(halfSize - srcY);
  188. memcpy(&dstImageBuf[(row * rectSize + col) * bytePerPixel],
  189. &srcImageBuf[(srcRow * rectSize + srcCol) * bytePerPixel], bytePerPixel);
  190. }
  191. }
  192. }
  193. CubemapLayout::CubemapLayout()
  194. : m_info(nullptr)
  195. , m_image(nullptr)
  196. , m_faceSize(256)
  197. {
  198. }
  199. CubemapLayout* CubemapLayout::CreateCubemapLayout(IImageObjectPtr image)
  200. {
  201. //only support uncompressed format.
  202. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(image->GetPixelFormat()))
  203. {
  204. AZ_Assert(false, "CubemapLayout only support uncompressed image");
  205. return nullptr;
  206. }
  207. CubemapLayout* layout = nullptr;
  208. CubemapLayoutInfo* info = GetCubemapLayoutInfo(image);
  209. if (info)
  210. {
  211. layout = new CubemapLayout();
  212. layout->m_info = GetCubemapLayoutInfo(image);
  213. layout->m_image = image;
  214. layout->m_faceSize = image->GetWidth(0) / layout->m_info->m_columns;
  215. }
  216. return layout;
  217. }
  218. CubemapLayoutInfo* CubemapLayout::GetCubemapLayoutInfo(CubemapLayoutType type)
  219. {
  220. if (type == CubemapLayoutNone)
  221. {
  222. return nullptr;
  223. }
  224. //if it's never initialized
  225. if (s_layoutList[0].m_type == CubemapLayoutNone)
  226. {
  227. InitCubemapLayoutInfos();
  228. }
  229. return &s_layoutList[type];
  230. }
  231. CubemapLayoutInfo* CubemapLayout::GetCubemapLayoutInfo(IImageObjectPtr image)
  232. {
  233. //if it's never initialized
  234. if (s_layoutList[0].m_type == CubemapLayoutNone)
  235. {
  236. InitCubemapLayoutInfos();
  237. }
  238. if (image == nullptr)
  239. {
  240. return nullptr;
  241. }
  242. uint32 width, height;
  243. width = image->GetWidth(0);
  244. height = image->GetHeight(0);
  245. CubemapLayoutInfo* info = nullptr;
  246. for (int i = 0; i < CubemapLayoutTypeCount; i++)
  247. {
  248. if (width * s_layoutList[i].m_rows == height * s_layoutList[i].m_columns)
  249. {
  250. info = &s_layoutList[i];
  251. //we require the face size need to be power of two
  252. if (IsPowerOfTwo(width / info->m_columns))
  253. {
  254. return info;
  255. }
  256. else
  257. {
  258. return nullptr;
  259. }
  260. }
  261. }
  262. return nullptr;
  263. }
  264. //public functions to get faces information for associated image
  265. AZ::u32 CubemapLayout::GetFaceSize()
  266. {
  267. return m_faceSize;
  268. }
  269. CubemapLayoutInfo* CubemapLayout::GetLayoutInfo()
  270. {
  271. return m_info;
  272. }
  273. CubemapFaceDirection CubemapLayout::GetFaceDirection(CubemapFace face)
  274. {
  275. return m_info->m_faceInfos[face].direction;
  276. }
  277. void CubemapLayout::GetFaceData(CubemapFace face, void* outBuffer, AZ::u32& outSize)
  278. {
  279. //only valid for uncompressed
  280. AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->GetPixelFormat())->bitsPerBlock / 8;
  281. AZ::u8* imageBuf;
  282. AZ::u32 dwPitch;
  283. m_image->GetImagePointer(0, imageBuf, dwPitch);
  284. AZ::u8* dstBuf = (AZ::u8*)outBuffer;
  285. AZ::u32 startX = m_info->m_faceInfos[face].column * m_faceSize;
  286. AZ::u32 startY = m_info->m_faceInfos[face].row * m_faceSize;
  287. //face size is same as rows for uncompressed format
  288. for (AZ::u32 y = 0; y < m_faceSize; y++)
  289. {
  290. AZ::u32 scanlineSize = m_faceSize * sizePerPixel;
  291. AZ::u8* srcBuf = &imageBuf[(startY + y) * dwPitch + startX * sizePerPixel];
  292. memcpy(dstBuf, srcBuf, scanlineSize);
  293. dstBuf += scanlineSize;
  294. }
  295. outSize = m_faceSize * m_faceSize * sizePerPixel;
  296. }
  297. void CubemapLayout::SetFaceData(CubemapFace face, void* dataBuffer, [[maybe_unused]] AZ::u32 dataSize)
  298. {
  299. //only valid for uncompressed
  300. AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->GetPixelFormat())->bitsPerBlock / 8;
  301. AZ::u8* imageBuf;
  302. AZ::u32 dwPitch;
  303. m_image->GetImagePointer(0, imageBuf, dwPitch);
  304. AZ::u8* srcBuf = (AZ::u8*)dataBuffer;
  305. AZ::u32 startX = m_info->m_faceInfos[face].column * m_faceSize;
  306. AZ::u32 startY = m_info->m_faceInfos[face].row * m_faceSize;
  307. //face size is same as rows for uncompressed format
  308. for (AZ::u32 y = 0; y < m_faceSize; y++)
  309. {
  310. AZ::u32 scanlineSize = m_faceSize * sizePerPixel;
  311. AZ::u8* dstBuf = &imageBuf[(startY + y) * dwPitch + startX * sizePerPixel];
  312. memcpy(dstBuf, srcBuf, scanlineSize);
  313. srcBuf += scanlineSize;
  314. }
  315. }
  316. void* CubemapLayout::GetFaceMemBuffer(AZ::u32 mip, CubemapFace face, AZ::u32& outPitch)
  317. {
  318. if (CubemapLayoutVertical != m_info->m_type)
  319. {
  320. AZ_Assert(false, "this should only be used for CubemapLayoutVertical which has continuous memory for each face");
  321. return nullptr;
  322. }
  323. AZ::u32 faceSize = m_faceSize >> mip;
  324. AZ::u8* imageBuf;
  325. m_image->GetImagePointer(mip, imageBuf, outPitch);
  326. AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
  327. //use startY is same as rows from m_image since the pixel format is uncompressed
  328. return &imageBuf[startY * outPitch];
  329. }
  330. void CubemapLayout::SetToFaceMemBuffer(AZ::u32 mip, CubemapFace face, void* dataBuffer)
  331. {
  332. if (CubemapLayoutVertical != m_info->m_type)
  333. {
  334. AZ_Assert(false, "this should only be used for CubemapLayoutVertical which has continuous memory for each face");
  335. return;
  336. }
  337. AZ::u32 faceSize = m_faceSize >> mip;
  338. AZ::u32 pitch;
  339. AZ::u8* imageBuf;
  340. m_image->GetImagePointer(mip, imageBuf, pitch);
  341. AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
  342. //use startY is same as rows from m_image since the pixel format is uncompressed
  343. memcpy(&imageBuf[startY * pitch], dataBuffer, faceSize * pitch);
  344. }
  345. void CubemapLayout::GetRectForFace(AZ::u32 mip, CubemapFace face, QRect& outRect)
  346. {
  347. AZ::u32 faceSize = m_faceSize >> mip;
  348. AZ::u32 startY = m_info->m_faceInfos[face].row * faceSize;
  349. AZ::u32 startX = m_info->m_faceInfos[face].column * faceSize;
  350. outRect.setRect(startX, startY, faceSize, faceSize);
  351. }
  352. bool ImageToProcess::ConvertCubemapLayout(CubemapLayoutType dstLayoutType)
  353. {
  354. const EPixelFormat srcPixelFormat = m_img->GetPixelFormat();
  355. //it need to be uncompressed format
  356. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcPixelFormat))
  357. {
  358. AZ_Assert(false, "Please convert the image to uncompressed pixel format before calling ConvertCubemapLayout");
  359. return false;
  360. }
  361. // If it's a latitude longitude map, convert it to cube map with vertical layout first.
  362. if (IsValidLatLongMap(m_img))
  363. {
  364. m_img = ConvertLatLongMapToCubemap(m_img);
  365. }
  366. //check if it's valid cubemap size
  367. CubemapLayoutInfo* layoutInfo = CubemapLayout::GetCubemapLayoutInfo(m_img);
  368. if (layoutInfo == nullptr)
  369. {
  370. AZ_Error("Image Processing", false, "The original image doesn't have a valid size (layout) as cubemap");
  371. return false;
  372. }
  373. //if the source is same as output layout, return directly
  374. if (layoutInfo->m_type == dstLayoutType)
  375. {
  376. return true;
  377. }
  378. CubemapLayoutInfo* dstLayoutInfo = CubemapLayout::GetCubemapLayoutInfo(dstLayoutType);
  379. //create cubemap layout for source image for later operation.
  380. CubemapLayout* srcCubemap = CubemapLayout::CreateCubemapLayout(m_img);
  381. AZ::u32 faceSize = srcCubemap->GetFaceSize();
  382. //create new image with same pixel format and copy properties from source image
  383. IImageObjectPtr newImage(IImageObject::CreateImage(faceSize* dstLayoutInfo->m_columns,
  384. faceSize* dstLayoutInfo->m_rows, 1, srcPixelFormat));
  385. CubemapLayout* dstCubemap = CubemapLayout::CreateCubemapLayout(newImage);
  386. newImage->CopyPropertiesFrom(newImage);
  387. //copy data from src cube to dst cube for each face
  388. //temp buf for copy over data
  389. AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(srcPixelFormat)->bitsPerBlock / 8; //only valid for uncompressed
  390. AZ::u8* buf = new AZ::u8[faceSize * faceSize * sizePerPixel];
  391. AZ::u8* tempBuf = new AZ::u8[faceSize * faceSize * sizePerPixel];
  392. for (AZ::u32 faceIdx = 0; faceIdx < FaceCount; faceIdx++)
  393. {
  394. AZ::u32 outSize = 0;
  395. CubemapFace face = (CubemapFace)faceIdx;
  396. srcCubemap->GetFaceData(face, buf, outSize);
  397. CubemapFaceDirection srcDir = srcCubemap->GetFaceDirection(face);
  398. CubemapFaceDirection dstDir = dstCubemap->GetFaceDirection(face);
  399. if (srcDir == dstDir)
  400. {
  401. dstCubemap->SetFaceData(face, buf, outSize);
  402. }
  403. else
  404. {
  405. //transform the image
  406. TransformImage(srcDir, dstDir, buf, tempBuf, static_cast<AZ::u8>(sizePerPixel), faceSize);
  407. dstCubemap->SetFaceData(face, tempBuf, outSize);
  408. }
  409. }
  410. //clean up
  411. delete[] buf;
  412. delete[] tempBuf;
  413. delete srcCubemap;
  414. delete dstCubemap;
  415. newImage->AddImageFlags(EIF_Cubemap);
  416. m_img = newImage;
  417. return true;
  418. }
  419. bool ImageConvertProcess::FillCubemapMipmaps()
  420. {
  421. //this function only works with pixel format rgba32f
  422. const EPixelFormat srcPixelFormat = m_image->Get()->GetPixelFormat();
  423. if (srcPixelFormat != ePixelFormat_R32G32B32A32F)
  424. {
  425. AZ_Assert(false, "%s only works with pixel format rgba32f", __FUNCTION__);
  426. return false;
  427. }
  428. //only if the src image has one mip
  429. if (m_image->Get()->GetMipCount() != 1)
  430. {
  431. AZ_Assert(false, "%s called for a mipmapped image. ", __FUNCTION__);
  432. return false;
  433. }
  434. const PresetSettings& preset = m_input->m_presetSetting;
  435. CubemapLayout* srcCubemap = CubemapLayout::CreateCubemapLayout(m_image->Get());
  436. uint32 outWidth;
  437. uint32 outHeight;
  438. uint32 outReduce = 0;
  439. AZ::u32 srcFaceSize = srcCubemap->GetFaceSize();
  440. //get output face size
  441. AZ::u32 outFaceSize, outFaceHeight;
  442. GetOutputExtent(srcFaceSize, srcFaceSize, outFaceSize, outFaceHeight, outReduce, &m_input->m_textureSetting, &preset);
  443. //get final cubemap image size
  444. outWidth = outFaceSize * srcCubemap->GetLayoutInfo()->m_columns;
  445. outHeight = outFaceSize * srcCubemap->GetLayoutInfo()->m_rows;
  446. //max mipmap count
  447. uint32 maxMipCount;
  448. if (preset.m_mipmapSetting == nullptr || !m_input->m_textureSetting.m_enableMipmap)
  449. {
  450. maxMipCount = 1;
  451. }
  452. else
  453. {
  454. //calculate based on face size, and use final export format which may save some low level mip calculation
  455. maxMipCount = CPixelFormats::GetInstance().ComputeMaxMipCount(preset.m_pixelFormat, outFaceSize, outFaceSize);
  456. //the FilterImage function won't do well with rect size 1. avoiding cubemap with face size 1
  457. if (srcFaceSize >> maxMipCount == 1 && maxMipCount > 1)
  458. {
  459. maxMipCount -= 1;
  460. }
  461. }
  462. if (preset.m_cubemapSetting->m_filter == CubemapFilterType::ggx)
  463. {
  464. //the PBR shader currently requires 6 mip levels (i.e., [0..5])
  465. //[GFX TODO][ATOM-2482] make this data driven per reflection cubemap
  466. static const uint32 ShaderMipCount = 6;
  467. if (maxMipCount < ShaderMipCount)
  468. {
  469. AZ_Assert(false, "Filter type GGX requires a texture size capable of at least %d mip levels", ShaderMipCount);
  470. return false;
  471. }
  472. maxMipCount = ShaderMipCount;
  473. }
  474. //generate box filtered source image mip chain
  475. IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat));
  476. mippedSourceImage->CopyPropertiesFrom(m_image->Get());
  477. for (int iSide = 0; iSide < 6; ++iSide)
  478. {
  479. for (int iMip = 0; iMip < (int)maxMipCount; iMip++)
  480. {
  481. QRect srcRect;
  482. QRect dstRect;
  483. srcRect.setLeft(0);
  484. srcRect.setRight(srcFaceSize);
  485. srcRect.setTop(iSide * srcFaceSize);
  486. srcRect.setBottom((iSide + 1) * srcFaceSize);
  487. AZ::u32 mipFaceSize = outFaceSize >> iMip;
  488. dstRect.setLeft(0);
  489. dstRect.setRight(mipFaceSize);
  490. dstRect.setTop(iSide * mipFaceSize);
  491. dstRect.setBottom((iSide + 1) * mipFaceSize);
  492. MipGenType mipGenType = (iMip == 0 ? MipGenType::point : MipGenType::box);
  493. FilterImage(mipGenType, MipGenEvalType::sum, 0, 0, m_image->Get(), 0, mippedSourceImage, iMip, &srcRect, &dstRect);
  494. }
  495. }
  496. //replace the source cubemap with the mipped version
  497. delete srcCubemap;
  498. srcCubemap = CubemapLayout::CreateCubemapLayout(mippedSourceImage);
  499. //create new new output image with proper face
  500. IImageObjectPtr outImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat));
  501. outImage->CopyPropertiesFrom(m_image->Get());
  502. CubemapLayout* dstCubemap = CubemapLayout::CreateCubemapLayout(outImage);
  503. AZ::u32 dstMipCount = outImage->GetMipCount();
  504. //filter mip 0 from source to destination
  505. for (int iSide = 0; iSide < 6; ++iSide)
  506. {
  507. QRect srcRect;
  508. QRect dstRect;
  509. srcRect.setLeft(0);
  510. srcRect.setRight(srcFaceSize);
  511. srcRect.setTop(iSide * srcFaceSize);
  512. srcRect.setBottom((iSide + 1) * srcFaceSize);
  513. dstRect.setLeft(0);
  514. dstRect.setRight(outFaceSize);
  515. dstRect.setTop(iSide * outFaceSize);
  516. dstRect.setBottom((iSide + 1) * outFaceSize);
  517. FilterImage(m_input->m_textureSetting.m_mipGenType, m_input->m_textureSetting.m_mipGenEval, 0, 0, m_image->Get(), 0,
  518. outImage, 0, &srcRect, &dstRect);
  519. }
  520. CCubeMapProcessor atiCubemanGen;
  521. //ATI's cubemap generator to filter the image edges to avoid seam problem
  522. // https://gpuopen.com/archive/gamescgi/cubemapgen/
  523. //the thread support was done with windows thread function so it's removed for multi-dev platform support
  524. atiCubemanGen.m_NumFilterThreads = 0;
  525. //input and output cubemap set to have save dimensions
  526. atiCubemanGen.Init(outFaceSize, outFaceSize, dstMipCount, 4);
  527. //load the 6 faces of the input cubemap for each mip level into the cubemap processor
  528. void* pMem;
  529. uint32 nPitch;
  530. for (int iFace = 0; iFace < 6; ++iFace)
  531. {
  532. for (int iMip = 0; iMip < (int)maxMipCount; ++iMip)
  533. {
  534. pMem = srcCubemap->GetFaceMemBuffer(iMip, (CubemapFace)iFace, nPitch);
  535. atiCubemanGen.SetInputFaceData(
  536. iFace, // FaceIdx,
  537. iMip, // MipIdx
  538. CP_VAL_FLOAT32, // SrcType,
  539. 4, // SrcNumChannels,
  540. nPitch, // SrcPitch,
  541. pMem, // SrcDataPtr,
  542. 1000000.0f, // MaxClamp,
  543. 1.0f, // Degamma,
  544. 1.0f); // Scale
  545. }
  546. }
  547. //number of rays to use for the GGX importance sampling
  548. //note: more rays reduces artifacts but increases processing time
  549. //[GFX TODO][ATOM-2956] add a sample quality option to the reflection volume to control this per reflection
  550. static const uint32 SampleCountGGX = 256;
  551. //filter cubemap
  552. atiCubemanGen.InitiateFiltering(
  553. preset.m_cubemapSetting->m_angle, //BaseFilterAngle,
  554. preset.m_cubemapSetting->m_mipAngle, //InitialMipAngle,
  555. preset.m_cubemapSetting->m_mipSlope, //MipAnglePerLevelScale,
  556. (int)preset.m_cubemapSetting->m_filter, //FilterType, CP_FILTER_TYPE_COSINE for diffuse cube
  557. preset.m_cubemapSetting->m_edgeFixup > 0 ? CP_FIXUP_PULL_LINEAR : CP_FIXUP_NONE, //FixupType, CP_FIXUP_PULL_LINEAR if FixupWidth> 0
  558. static_cast<int32>(preset.m_cubemapSetting->m_edgeFixup), //FixupWidth,
  559. true, //bUseSolidAngle,
  560. 16, //GlossScale,
  561. 0, //GlossBias
  562. SampleCountGGX);
  563. //copy the convolved cubemap data for each face and mip into the output image
  564. for (int iFace = 0; iFace < 6; ++iFace)
  565. {
  566. for (unsigned int dstMip = 0; dstMip < dstMipCount; ++dstMip)
  567. {
  568. pMem = dstCubemap->GetFaceMemBuffer(dstMip, (CubemapFace)iFace, nPitch);
  569. atiCubemanGen.GetOutputFaceData(iFace, dstMip, CP_VAL_FLOAT32, 4, nPitch, pMem, 1.0f, 1.0f);
  570. }
  571. }
  572. delete srcCubemap;
  573. delete dstCubemap;
  574. //set back to image
  575. m_image->Set(outImage);
  576. return true;
  577. }
  578. // Convert from direction into (u,v) coordinates latitude-longitude map
  579. void NormalToLatLongUV(const AZ::Vector3& dir, float& outU, float& outV)
  580. {
  581. // The normal we calculate from cubemap is Y up. +z is forward
  582. float r = sqrt(dir.GetX() * dir.GetX() + dir.GetY() * dir.GetY());
  583. float latitude = (r < abs(dir.GetZ())) ? acos(r) * (dir.GetZ() >= 0 ? 1 : -1) : asin(dir.GetZ());
  584. float longitude = (dir.GetY() == 0.0f && dir.GetX() == 0.0f) ? 0.0f : atan2(dir.GetX(), dir.GetY());
  585. outU = 1.0f - (longitude * 0.159154943f + 0.5f); // Longitude [-Pi, Pi] -> [0, 1]
  586. outV = 0.5f - latitude * 0.318309886f; // Latitude [Pi/2, -Pi/2] -> [0, 1]
  587. AZ_Assert(outU <= 1 && outU >= 0, "wrong pixel position");
  588. AZ_Assert(outV <= 1 && outV >= 0, "wrong pixel position");
  589. }
  590. // Get normal from a 2d vector and cubemap face index
  591. AZ::Vector3 GetNormalForVerticalLayout(CubemapFace faceIdx, float x, float y)
  592. {
  593. AZ::Vector3 normal;
  594. switch (faceIdx)
  595. {
  596. case FaceLeft:
  597. normal = AZ::Vector3(-1, -x, y);
  598. break;
  599. case FaceRight:
  600. normal = AZ::Vector3(1, x, y);
  601. break;
  602. case FaceFront:
  603. normal = AZ::Vector3(-x, -y, 1);
  604. break;
  605. case FaceBack:
  606. normal = AZ::Vector3(-x, y, -1);
  607. break;
  608. case FaceTop:
  609. normal = AZ::Vector3(-x, 1, y);
  610. break;
  611. case FaceBottom:
  612. normal = AZ::Vector3(x, -1, y);
  613. break;
  614. }
  615. normal.Normalize();
  616. return normal;
  617. }
  618. bool IsValidLatLongMap(IImageObjectPtr latitudeMap)
  619. {
  620. AZ::u32 srcWidth = latitudeMap->GetWidth(0);
  621. AZ::u32 srcHeight = latitudeMap->GetHeight(0);
  622. return (srcWidth == srcHeight * 2 && srcWidth % 4 == 0);
  623. }
  624. IImageObjectPtr ConvertLatLongMapToCubemap(IImageObjectPtr latitudeMap)
  625. {
  626. const EPixelFormat srcPixelFormat = latitudeMap->GetPixelFormat();
  627. // The map need to be uncompressed format
  628. if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(srcPixelFormat))
  629. {
  630. AZ_Assert(false, "The input image should have uncompressed pixel format.");
  631. return nullptr;
  632. }
  633. AZ_Assert(latitudeMap->GetMipCount() == 1, "The mipmap won't be converted");
  634. AZ::u32 srcWidth = latitudeMap->GetWidth(0);
  635. AZ::u32 srcHeight = latitudeMap->GetHeight(0);
  636. AZ::u8* srcBuf;
  637. AZ::u32 outPitch;
  638. latitudeMap->GetImagePointer(0, srcBuf, outPitch);
  639. if (!IsValidLatLongMap(latitudeMap))
  640. {
  641. AZ_Error("Image Processing", false, "Invalid latitude-longitude map resolution [%dx%d]."
  642. "The aspect ratio should be 2:1 and the width should be dividable by 4 ", srcWidth, srcHeight);
  643. return nullptr;
  644. }
  645. AZ::u32 srcfaceSize = srcWidth / 4;
  646. // Convert face size to power of 2 since CreateCubemapLayout doesn't support non-power of 2 face size.
  647. // Find the highest power of 2 less than or equal to source face size
  648. srcfaceSize >>= 1;
  649. AZ::u32 faceSize = 1;
  650. while (srcfaceSize > 0)
  651. {
  652. faceSize <<= 1;
  653. srcfaceSize >>= 1;
  654. }
  655. AZ_Assert(faceSize <= srcWidth / 4, "wrong conversion");
  656. // Create output image
  657. IImageObjectPtr outImage(IImageObject::CreateImage(faceSize, faceSize * FaceCount, 1, srcPixelFormat));
  658. outImage->CopyPropertiesFrom(latitudeMap);
  659. outImage->AddImageFlags(EIF_Cubemap);
  660. CubemapLayout* dstCubemap = CubemapLayout::CreateCubemapLayout(outImage);
  661. IPixelOperationPtr pixelOp = CreatePixelOperation(srcPixelFormat);
  662. AZ::u32 sizePerPixel = CPixelFormats::GetInstance().GetPixelFormatInfo(srcPixelFormat)->bitsPerBlock / 8; //only valid for uncompressed
  663. AZ::u32 facePixelCount = faceSize * faceSize;
  664. float radius = faceSize / 2.0f;
  665. for (AZ::u32 faceIdx = 0; faceIdx < FaceCount; faceIdx++)
  666. {
  667. CubemapFace face = (CubemapFace)faceIdx;
  668. AZ::u8* buf = (AZ::u8*)dstCubemap->GetFaceMemBuffer(0, face, outPitch);
  669. // Get value from the original map and assign it to each pixel on this face
  670. for (AZ::u32 pixelIdx = 0; pixelIdx < facePixelCount; pixelIdx++)
  671. {
  672. float x = ((pixelIdx % faceSize) - radius) / radius;
  673. float y = -((pixelIdx / faceSize) - radius) / radius;
  674. AZ::Vector3 normal = GetNormalForVerticalLayout(face, x, y);
  675. float srcU, srcV;
  676. NormalToLatLongUV(normal, srcU, srcV);
  677. // Get 4 corner pixels' color and use them to interpolate the final color of destination pixel
  678. float px = srcU * (float)(srcWidth - 1);
  679. float py = srcV * (float)(srcHeight - 1);
  680. AZ::u32 px1 = (AZ::u32) px;
  681. AZ::u32 px2 = ((AZ::u32) px + 1) % srcWidth;
  682. AZ::u32 py1 = (AZ::u32) py;
  683. AZ::u32 py2 = ((AZ::u32) py + 1) % srcHeight;
  684. float t1 = px - px1;
  685. float t2 = py - py1;
  686. float p1[4], p2[4], p3[4], p4[4];
  687. pixelOp->GetRGBA(&srcBuf[(px1 + py1 * srcWidth) * sizePerPixel], p1[0], p1[1], p1[2], p1[3]);
  688. pixelOp->GetRGBA(&srcBuf[(px1 + py2 * srcWidth) * sizePerPixel], p2[0], p2[1], p2[2], p2[3]);
  689. pixelOp->GetRGBA(&srcBuf[(px2 + py1 * srcWidth) * sizePerPixel], p3[0], p3[1], p3[2], p3[3]);
  690. pixelOp->GetRGBA(&srcBuf[(px2 + py2 * srcWidth) * sizePerPixel], p4[0], p4[1], p4[2], p4[3]);
  691. float dstP[4];
  692. dstP[0] = (1 - t2) * ((1 - t1) * p1[0] + t1 * p3[0]) + t2 * ((1 - t1) * p2[0] + t1 * p4[0]);
  693. dstP[1] = (1 - t2) * ((1 - t1) * p1[1] + t1 * p3[1]) + t2 * ((1 - t1) * p2[1] + t1 * p4[1]);
  694. dstP[2] = (1 - t2) * ((1 - t1) * p1[2] + t1 * p3[2]) + t2 * ((1 - t1) * p2[2] + t1 * p4[2]);
  695. dstP[3] = (1 - t2) * ((1 - t1) * p1[3] + t1 * p3[3]) + t2 * ((1 - t1) * p2[3] + t1 * p4[3]);
  696. pixelOp->SetRGBA(&buf[pixelIdx * sizePerPixel], dstP[0], dstP[1], dstP[2], dstP[3]);
  697. }
  698. }
  699. delete dstCubemap;
  700. return outImage;
  701. }
  702. } // namespace ImageProcessingAtom