image_cubemap_filter.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. /*
  2. * Copyright 2011-2022 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bimg/blob/master/LICENSE
  4. */
  5. #include "bimg_p.h"
  6. #include <bimg/encode.h>
  7. #include <bx/rng.h>
  8. namespace bimg
  9. {
  10. /*
  11. * Copyright 2014-2015 Dario Manesku. All rights reserved.
  12. * License: http://www.opensource.org/licenses/BSD-2-Clause
  13. */
  14. // +----------+
  15. // |-z 2|
  16. // | ^ +y |
  17. // | | |
  18. // | +---->+x |
  19. // +----------+----------+----------+----------+
  20. // |+y 1|+y 4|+y 0|+y 5|
  21. // | ^ -x | ^ +z | ^ +x | ^ -z |
  22. // | | | | | | | | |
  23. // | +---->+z | +---->+x | +---->-z | +---->-x |
  24. // +----------+----------+----------+----------+
  25. // |+z 3|
  26. // | ^ -y |
  27. // | | |
  28. // | +---->+x |
  29. // +----------+
  30. //
  31. struct CubeMapFace
  32. {
  33. enum Enum
  34. {
  35. PositiveX,
  36. NegativeX,
  37. PositiveY,
  38. NegativeY,
  39. PositiveZ,
  40. NegativeZ,
  41. Count
  42. };
  43. struct Edge
  44. {
  45. enum Enum
  46. {
  47. Left,
  48. Right,
  49. Top,
  50. Bottom,
  51. Count
  52. };
  53. };
  54. // --> U _____
  55. // | | |
  56. // v | +Y |
  57. // V _____|_____|_____ _____
  58. // | | | | |
  59. // | -X | +Z | +X | -Z |
  60. // |_____|_____|_____|_____|
  61. // | |
  62. // | -Y |
  63. // |_____|
  64. //
  65. // Neighbour faces in order: left, right, top, bottom.
  66. // FaceEdge is the edge that belongs to the neighbour face.
  67. struct Neighbour
  68. {
  69. uint8_t m_faceIdx;
  70. uint8_t m_faceEdge;
  71. };
  72. bx::Vec3 uv[3];
  73. };
  74. static const CubeMapFace s_cubeMapFace[] =
  75. {
  76. {{ // +x face
  77. { 0.0f, 0.0f, -1.0f }, // u -> -z
  78. { 0.0f, -1.0f, 0.0f }, // v -> -y
  79. { 1.0f, 0.0f, 0.0f }, // +x face
  80. }},
  81. {{ // -x face
  82. { 0.0f, 0.0f, 1.0f }, // u -> +z
  83. { 0.0f, -1.0f, 0.0f }, // v -> -y
  84. { -1.0f, 0.0f, 0.0f }, // -x face
  85. }},
  86. {{ // +y face
  87. { 1.0f, 0.0f, 0.0f }, // u -> +x
  88. { 0.0f, 0.0f, 1.0f }, // v -> +z
  89. { 0.0f, 1.0f, 0.0f }, // +y face
  90. }},
  91. {{ // -y face
  92. { 1.0f, 0.0f, 0.0f }, // u -> +x
  93. { 0.0f, 0.0f, -1.0f }, // v -> -z
  94. { 0.0f, -1.0f, 0.0f }, // -y face
  95. }},
  96. {{ // +z face
  97. { 1.0f, 0.0f, 0.0f }, // u -> +x
  98. { 0.0f, -1.0f, 0.0f }, // v -> -y
  99. { 0.0f, 0.0f, 1.0f }, // +z face
  100. }},
  101. {{ // -z face
  102. { -1.0f, 0.0f, 0.0f }, // u -> -x
  103. { 0.0f, -1.0f, 0.0f }, // v -> -y
  104. { 0.0f, 0.0f, -1.0f }, // -z face
  105. }},
  106. };
  107. static const CubeMapFace::Neighbour s_cubeMapFaceNeighbours[6][4] =
  108. {
  109. { // +X
  110. { CubeMapFace::PositiveZ, CubeMapFace::Edge::Right },
  111. { CubeMapFace::NegativeZ, CubeMapFace::Edge::Left },
  112. { CubeMapFace::PositiveY, CubeMapFace::Edge::Right },
  113. { CubeMapFace::NegativeY, CubeMapFace::Edge::Right },
  114. },
  115. { // -X
  116. { CubeMapFace::NegativeZ, CubeMapFace::Edge::Right },
  117. { CubeMapFace::PositiveZ, CubeMapFace::Edge::Left },
  118. { CubeMapFace::PositiveY, CubeMapFace::Edge::Left },
  119. { CubeMapFace::NegativeY, CubeMapFace::Edge::Left },
  120. },
  121. { // +Y
  122. { CubeMapFace::NegativeX, CubeMapFace::Edge::Top },
  123. { CubeMapFace::PositiveX, CubeMapFace::Edge::Top },
  124. { CubeMapFace::NegativeZ, CubeMapFace::Edge::Top },
  125. { CubeMapFace::PositiveZ, CubeMapFace::Edge::Top },
  126. },
  127. { // -Y
  128. { CubeMapFace::NegativeX, CubeMapFace::Edge::Bottom },
  129. { CubeMapFace::PositiveX, CubeMapFace::Edge::Bottom },
  130. { CubeMapFace::PositiveZ, CubeMapFace::Edge::Bottom },
  131. { CubeMapFace::NegativeZ, CubeMapFace::Edge::Bottom },
  132. },
  133. { // +Z
  134. { CubeMapFace::NegativeX, CubeMapFace::Edge::Right },
  135. { CubeMapFace::PositiveX, CubeMapFace::Edge::Left },
  136. { CubeMapFace::PositiveY, CubeMapFace::Edge::Bottom },
  137. { CubeMapFace::NegativeY, CubeMapFace::Edge::Top },
  138. },
  139. { // -Z
  140. { CubeMapFace::PositiveX, CubeMapFace::Edge::Right },
  141. { CubeMapFace::NegativeX, CubeMapFace::Edge::Left },
  142. { CubeMapFace::PositiveY, CubeMapFace::Edge::Top },
  143. { CubeMapFace::NegativeY, CubeMapFace::Edge::Bottom },
  144. },
  145. };
  146. /// _u and _v should be center addressing and in [-1.0+invSize..1.0-invSize] range.
  147. bx::Vec3 texelUvToDir(uint8_t _side, float _u, float _v)
  148. {
  149. const CubeMapFace& face = s_cubeMapFace[_side];
  150. const bx::Vec3 tmp =
  151. {
  152. face.uv[0].x * _u + face.uv[1].x * _v + face.uv[2].x,
  153. face.uv[0].y * _u + face.uv[1].y * _v + face.uv[2].y,
  154. face.uv[0].z * _u + face.uv[1].z * _v + face.uv[2].z,
  155. };
  156. return bx::normalize(tmp);
  157. }
  158. void dirToTexelUv(float& _outU, float& _outV, uint8_t& _outSide, const bx::Vec3& _dir)
  159. {
  160. const bx::Vec3 absVec = bx::abs(_dir);
  161. const float max = bx::max(absVec.x, absVec.y, absVec.z);
  162. if (max == absVec.x)
  163. {
  164. _outSide = _dir.x >= 0.0f ? uint8_t(CubeMapFace::PositiveX) : uint8_t(CubeMapFace::NegativeX);
  165. }
  166. else if (max == absVec.y)
  167. {
  168. _outSide = _dir.y >= 0.0f ? uint8_t(CubeMapFace::PositiveY) : uint8_t(CubeMapFace::NegativeY);
  169. }
  170. else
  171. {
  172. _outSide = _dir.z >= 0.0f ? uint8_t(CubeMapFace::PositiveZ) : uint8_t(CubeMapFace::NegativeZ);
  173. }
  174. const bx::Vec3 faceVec = bx::mul(_dir, 1.0f/max);
  175. _outU = (bx::dot(s_cubeMapFace[_outSide].uv[0], faceVec) + 1.0f) * 0.5f;
  176. _outV = (bx::dot(s_cubeMapFace[_outSide].uv[1], faceVec) + 1.0f) * 0.5f;
  177. }
  178. ImageContainer* imageCubemapFromLatLongRgba32F(bx::AllocatorI* _allocator, const ImageContainer& _input, bool _useBilinearInterpolation, bx::Error* _err)
  179. {
  180. BX_ERROR_SCOPE(_err);
  181. if (_input.m_depth != 1
  182. && _input.m_numLayers != 1
  183. && _input.m_format != TextureFormat::RGBA32F
  184. && _input.m_width/2 != _input.m_height)
  185. {
  186. BX_ERROR_SET(_err, BIMG_ERROR, "Input image format is not equirectangular projection.");
  187. return NULL;
  188. }
  189. const uint32_t srcWidthMinusOne = _input.m_width-1;
  190. const uint32_t srcHeightMinusOne = _input.m_height-1;
  191. const uint32_t srcPitch = _input.m_width*16;
  192. const uint32_t dstWidth = _input.m_height/2;
  193. const uint32_t dstPitch = dstWidth*16;
  194. const float invDstWidth = 1.0f / float(dstWidth);
  195. ImageContainer* output = imageAlloc(_allocator
  196. , _input.m_format
  197. , uint16_t(dstWidth)
  198. , uint16_t(dstWidth)
  199. , uint16_t(1)
  200. , 1
  201. , true
  202. , false
  203. );
  204. const uint8_t* srcData = (const uint8_t*)_input.m_data;
  205. for (uint8_t side = 0; side < 6 && _err->isOk(); ++side)
  206. {
  207. ImageMip mip;
  208. imageGetRawData(*output, side, 0, output->m_data, output->m_size, mip);
  209. for (uint32_t yy = 0; yy < dstWidth; ++yy)
  210. {
  211. for (uint32_t xx = 0; xx < dstWidth; ++xx)
  212. {
  213. float* dstData = (float*)&mip.m_data[yy*dstPitch+xx*16];
  214. const float uu = 2.0f*xx*invDstWidth - 1.0f;
  215. const float vv = 2.0f*yy*invDstWidth - 1.0f;
  216. const bx::Vec3 dir = texelUvToDir(side, uu, vv);
  217. float srcU, srcV;
  218. bx::toLatLong(&srcU, &srcV, dir);
  219. srcU *= srcWidthMinusOne;
  220. srcV *= srcHeightMinusOne;
  221. if (_useBilinearInterpolation)
  222. {
  223. const uint32_t x0 = uint32_t(srcU);
  224. const uint32_t y0 = uint32_t(srcV);
  225. const uint32_t x1 = bx::min(x0 + 1, srcWidthMinusOne);
  226. const uint32_t y1 = bx::min(y0 + 1, srcHeightMinusOne);
  227. const float* src0 = (const float*)&srcData[y0*srcPitch + x0*16];
  228. const float* src1 = (const float*)&srcData[y0*srcPitch + x1*16];
  229. const float* src2 = (const float*)&srcData[y1*srcPitch + x0*16];
  230. const float* src3 = (const float*)&srcData[y1*srcPitch + x1*16];
  231. const float tx = srcU - float(int32_t(x0) );
  232. const float ty = srcV - float(int32_t(y0) );
  233. const float omtx = 1.0f - tx;
  234. const float omty = 1.0f - ty;
  235. const float p0x = omtx*omty;
  236. const float p0[4] =
  237. {
  238. src0[0] * p0x,
  239. src0[1] * p0x,
  240. src0[2] * p0x,
  241. src0[3] * p0x,
  242. };
  243. const float p1x = tx*omty;
  244. const float p1[4] =
  245. {
  246. src1[0] * p1x,
  247. src1[1] * p1x,
  248. src1[2] * p1x,
  249. src1[3] * p1x,
  250. };
  251. const float p2x = omtx*ty;
  252. const float p2[4] =
  253. {
  254. src2[0] * p2x,
  255. src2[1] * p2x,
  256. src2[2] * p2x,
  257. src2[3] * p2x,
  258. };
  259. const float p3x = tx*ty;
  260. const float p3[4] =
  261. {
  262. src3[0] * p3x,
  263. src3[1] * p3x,
  264. src3[2] * p3x,
  265. src3[3] * p3x,
  266. };
  267. const float rr = p0[0] + p1[0] + p2[0] + p3[0];
  268. const float gg = p0[1] + p1[1] + p2[1] + p3[1];
  269. const float bb = p0[2] + p1[2] + p2[2] + p3[2];
  270. const float aa = p0[3] + p1[3] + p2[3] + p3[3];
  271. dstData[0] = rr;
  272. dstData[1] = gg;
  273. dstData[2] = bb;
  274. dstData[3] = aa;
  275. }
  276. else
  277. {
  278. const uint32_t x0 = uint32_t(srcU);
  279. const uint32_t y0 = uint32_t(srcV);
  280. const float* src0 = (const float*)&srcData[y0*srcPitch + x0*16];
  281. dstData[0] = src0[0];
  282. dstData[1] = src0[1];
  283. dstData[2] = src0[2];
  284. dstData[3] = src0[3];
  285. }
  286. }
  287. }
  288. }
  289. return output;
  290. }
  291. ImageContainer* imageCubemapFromStripRgba32F(bx::AllocatorI* _allocator, const ImageContainer& _input, bx::Error* _err)
  292. {
  293. BX_ERROR_SCOPE(_err);
  294. if (_input.m_depth != 1
  295. && _input.m_numLayers != 1
  296. && _input.m_format != TextureFormat::RGBA32F
  297. && ( (_input.m_width != _input.m_height*6) || (_input.m_width*6 != _input.m_height) ) )
  298. {
  299. BX_ERROR_SET(_err, BIMG_ERROR, "Input image format is not strip projection.");
  300. return NULL;
  301. }
  302. const bool horizontal = _input.m_width == _input.m_height*6;
  303. const uint32_t srcPitch = _input.m_width*16;
  304. const uint32_t dstWidth = horizontal ? _input.m_height : _input.m_width;
  305. const uint32_t dstPitch = dstWidth*16;
  306. const uint32_t step = horizontal ? dstPitch : dstPitch*dstWidth;
  307. ImageContainer* output = imageAlloc(_allocator
  308. , _input.m_format
  309. , uint16_t(dstWidth)
  310. , uint16_t(dstWidth)
  311. , uint16_t(1)
  312. , 1
  313. , true
  314. , false
  315. );
  316. const uint8_t* srcData = (const uint8_t*)_input.m_data;
  317. for (uint8_t side = 0; side < 6 && _err->isOk(); ++side, srcData += step)
  318. {
  319. ImageMip dstMip;
  320. imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  321. bx::memCopy(const_cast<uint8_t*>(dstMip.m_data), dstPitch, srcData, srcPitch, dstPitch, dstWidth);
  322. }
  323. return output;
  324. }
  325. inline float areaElement(float _x, float _y)
  326. {
  327. return bx::atan2(_x*_y, bx::sqrt(_x*_x + _y*_y + 1.0f));
  328. }
  329. float texelSolidAngle(float _u, float _v, float _invFaceSize)
  330. {
  331. // Reference(s):
  332. // - https://web.archive.org/web/20180614195754/http://www.mpia.de/~mathar/public/mathar20051002.pdf
  333. // - https://web.archive.org/web/20180614195725/http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
  334. //
  335. const float x0 = _u - _invFaceSize;
  336. const float x1 = _u + _invFaceSize;
  337. const float y0 = _v - _invFaceSize;
  338. const float y1 = _v + _invFaceSize;
  339. return
  340. + areaElement(x1, y1)
  341. - areaElement(x0, y1)
  342. - areaElement(x1, y0)
  343. + areaElement(x0, y0)
  344. ;
  345. }
  346. ImageContainer* imageCubemapNormalSolidAngle(bx::AllocatorI* _allocator, uint32_t _size)
  347. {
  348. const uint32_t dstWidth = _size;
  349. const uint32_t dstPitch = dstWidth*16;
  350. const float texelSize = 1.0f / float(dstWidth);
  351. ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, uint16_t(dstWidth), uint16_t(dstWidth), 1, 1, true, false);
  352. for (uint8_t side = 0; side < 6; ++side)
  353. {
  354. ImageMip mip;
  355. imageGetRawData(*output, side, 0, output->m_data, output->m_size, mip);
  356. for (uint32_t yy = 0; yy < dstWidth; ++yy)
  357. {
  358. for (uint32_t xx = 0; xx < dstWidth; ++xx)
  359. {
  360. float* dstData = (float*)&mip.m_data[yy*dstPitch+xx*16];
  361. const float uu = float(xx)*texelSize*2.0f - 1.0f;
  362. const float vv = float(yy)*texelSize*2.0f - 1.0f;
  363. bx::store(dstData, texelUvToDir(side, uu, vv) );
  364. dstData[3] = texelSolidAngle(uu, vv, texelSize);
  365. }
  366. }
  367. }
  368. return output;
  369. }
  370. struct Aabb
  371. {
  372. Aabb()
  373. {
  374. m_min[0] = bx::max<float>();
  375. m_min[1] = bx::max<float>();
  376. m_max[0] = bx::min<float>();
  377. m_max[1] = bx::min<float>();
  378. }
  379. void add(float _x, float _y)
  380. {
  381. m_min[0] = bx::min(m_min[0], _x);
  382. m_min[1] = bx::min(m_min[1], _y);
  383. m_max[0] = bx::max(m_max[0], _x);
  384. m_max[1] = bx::max(m_max[1], _y);
  385. }
  386. void clamp(float _min, float _max)
  387. {
  388. m_min[0] = bx::clamp(m_min[0], _min, _max);
  389. m_min[1] = bx::clamp(m_min[1], _min, _max);
  390. m_max[0] = bx::clamp(m_max[0], _min, _max);
  391. m_max[1] = bx::clamp(m_max[1], _min, _max);
  392. }
  393. bool isEmpty() const
  394. {
  395. // Has to have at least two points added so that no value is equal to initial state.
  396. return ( (m_min[0] == bx::max<float>() )
  397. || (m_min[1] == bx::max<float>() )
  398. || (m_max[0] == bx::min<float>() )
  399. || (m_max[1] == bx::min<float>() )
  400. );
  401. }
  402. float m_min[2];
  403. float m_max[2];
  404. };
  405. void calcFilterArea(Aabb* _outFilterArea, const bx::Vec3& _dir, float _filterSize)
  406. {
  407. /// ______
  408. /// | |
  409. /// | |
  410. /// | x |
  411. /// |______|
  412. ///
  413. // Get face and hit coordinates.
  414. float uu, vv;
  415. uint8_t hitFaceIdx;
  416. dirToTexelUv(uu, vv, hitFaceIdx, _dir);
  417. /// ........
  418. /// . .
  419. /// . ___.
  420. /// . | x |
  421. /// ...|___|
  422. ///
  423. // Calculate hit face filter bounds.
  424. Aabb hitFaceFilterBounds;
  425. hitFaceFilterBounds.add(uu-_filterSize, vv-_filterSize);
  426. hitFaceFilterBounds.add(uu+_filterSize, vv+_filterSize);
  427. hitFaceFilterBounds.clamp(0.0f, 1.0f);
  428. // Output result for hit face.
  429. bx::memCopy(&_outFilterArea[hitFaceIdx], &hitFaceFilterBounds, sizeof(Aabb));
  430. /// Filter area might extend on neighbour faces.
  431. /// Case when extending over the right edge:
  432. ///
  433. /// --> U
  434. /// | ......
  435. /// v . .
  436. /// V . .
  437. /// . .
  438. /// ....... ...... .......
  439. /// . . . .
  440. /// . . .....__min .
  441. /// . . . . | -> amount
  442. /// ....... .....x.__|....
  443. /// . . . max
  444. /// . ........
  445. /// . .
  446. /// ......
  447. /// . .
  448. /// . .
  449. /// . .
  450. /// ......
  451. ///
  452. struct NeighourFaceBleed
  453. {
  454. float m_amount;
  455. float m_bbMin;
  456. float m_bbMax;
  457. };
  458. const NeighourFaceBleed bleed[CubeMapFace::Edge::Count] =
  459. {
  460. { // Left
  461. _filterSize - uu,
  462. hitFaceFilterBounds.m_min[1],
  463. hitFaceFilterBounds.m_max[1],
  464. },
  465. { // Right
  466. uu + _filterSize - 1.0f,
  467. hitFaceFilterBounds.m_min[1],
  468. hitFaceFilterBounds.m_max[1],
  469. },
  470. { // Top
  471. _filterSize - vv,
  472. hitFaceFilterBounds.m_min[0],
  473. hitFaceFilterBounds.m_max[0],
  474. },
  475. { // Bottom
  476. vv + _filterSize - 1.0f,
  477. hitFaceFilterBounds.m_min[0],
  478. hitFaceFilterBounds.m_max[0],
  479. },
  480. };
  481. // Determine bleeding for each side.
  482. for (uint8_t side = 0; side < 4; ++side)
  483. {
  484. uint8_t currentFaceIdx = hitFaceIdx;
  485. for (float bleedAmount = bleed[side].m_amount; bleedAmount > 0.0f; bleedAmount -= 1.0f)
  486. {
  487. uint8_t neighbourFaceIdx = s_cubeMapFaceNeighbours[currentFaceIdx][side].m_faceIdx;
  488. uint8_t neighbourFaceEdge = s_cubeMapFaceNeighbours[currentFaceIdx][side].m_faceEdge;
  489. currentFaceIdx = neighbourFaceIdx;
  490. /// https://code.google.com/p/cubemapgen/source/browse/trunk/CCubeMapProcessor.cpp#773
  491. ///
  492. /// Handle situations when bbMin and bbMax should be flipped.
  493. ///
  494. /// L - Left ....................T-T
  495. /// R - Right v .
  496. /// T - Top __________ .
  497. /// B - Bottom . | .
  498. /// . | .
  499. /// . |<...R-T .
  500. /// . | v v
  501. /// .......... ..........|__________ __________
  502. /// . . . . .
  503. /// . . . . .
  504. /// . . . . .
  505. /// . . . . .
  506. /// __________ .......... .......... __________
  507. /// ^ | . ^
  508. /// . | . .
  509. /// B-L..>| . .
  510. /// | . .
  511. /// |__________. .
  512. /// ^ .
  513. /// ....................B-B
  514. ///
  515. /// Those are:
  516. /// B-L, B-B
  517. /// T-R, T-T
  518. /// (and in reverse order, R-T and L-B)
  519. ///
  520. /// If we add, R-R and L-L (which never occur), we get:
  521. /// B-L, B-B
  522. /// T-R, T-T
  523. /// R-T, R-R
  524. /// L-B, L-L
  525. ///
  526. /// And if L = 0, R = 1, T = 2, B = 3 as in NeighbourSides enumeration,
  527. /// a general rule can be derived for when to flip bbMin and bbMax:
  528. /// if ((a+b) == 3 || (a == b))
  529. /// {
  530. /// ..flip bbMin and bbMax
  531. /// }
  532. ///
  533. float bbMin = bleed[side].m_bbMin;
  534. float bbMax = bleed[side].m_bbMax;
  535. if ( (side == neighbourFaceEdge)
  536. || (3 == (side + neighbourFaceEdge) ) )
  537. {
  538. // Flip.
  539. bbMin = 1.0f - bbMin;
  540. bbMax = 1.0f - bbMax;
  541. }
  542. switch (neighbourFaceEdge)
  543. {
  544. case CubeMapFace::Edge::Left:
  545. {
  546. /// --> U
  547. /// | .............
  548. /// v . .
  549. /// V x___ .
  550. /// | | .
  551. /// | | .
  552. /// |___x .
  553. /// . .
  554. /// .............
  555. ///
  556. _outFilterArea[neighbourFaceIdx].add(0.0f, bbMin);
  557. _outFilterArea[neighbourFaceIdx].add(bleedAmount, bbMax);
  558. }
  559. break;
  560. case CubeMapFace::Edge::Right:
  561. {
  562. /// --> U
  563. /// | .............
  564. /// v . .
  565. /// V . x___.
  566. /// . | |
  567. /// . | |
  568. /// . |___x
  569. /// . .
  570. /// .............
  571. ///
  572. _outFilterArea[neighbourFaceIdx].add(1.0f - bleedAmount, bbMin);
  573. _outFilterArea[neighbourFaceIdx].add(1.0f, bbMax);
  574. }
  575. break;
  576. case CubeMapFace::Edge::Top:
  577. {
  578. /// --> U
  579. /// | ...x____ ...
  580. /// v . | | .
  581. /// V . |____x .
  582. /// . .
  583. /// . .
  584. /// . .
  585. /// ............
  586. ///
  587. _outFilterArea[neighbourFaceIdx].add(bbMin, 0.0f);
  588. _outFilterArea[neighbourFaceIdx].add(bbMax, bleedAmount);
  589. }
  590. break;
  591. case CubeMapFace::Edge::Bottom:
  592. {
  593. /// --> U
  594. /// | ............
  595. /// v . .
  596. /// V . .
  597. /// . .
  598. /// . x____ .
  599. /// . | | .
  600. /// ...|____x...
  601. ///
  602. _outFilterArea[neighbourFaceIdx].add(bbMin, 1.0f - bleedAmount);
  603. _outFilterArea[neighbourFaceIdx].add(bbMax, 1.0f);
  604. }
  605. break;
  606. }
  607. // Clamp bounding box to face size.
  608. _outFilterArea[neighbourFaceIdx].clamp(0.0f, 1.0f);
  609. }
  610. }
  611. }
  612. struct Sampler
  613. {
  614. Sampler(const ImageContainer& _image, uint16_t _side, float _lod, float (*func)(float) )
  615. {
  616. const float lod = bx::clamp(_lod, 0.0f, float(_image.m_numMips - 1) );
  617. imageGetRawData(
  618. _image
  619. , _side
  620. , uint8_t(func(lod) )
  621. , _image.m_data
  622. , _image.m_size
  623. , mip
  624. );
  625. }
  626. ImageMip mip;
  627. };
  628. void texelFetch(float* _rgba, const Sampler& _sampler, uint32_t _u, uint32_t _v)
  629. {
  630. const uint32_t bpp = _sampler.mip.m_bpp;
  631. const uint32_t pitch = _sampler.mip.m_width*bpp/8;
  632. const uint8_t* texel = _sampler.mip.m_data + _v*pitch + _u*bpp/8;
  633. UnpackFn unpack = getUnpack(_sampler.mip.m_format);
  634. unpack(_rgba, texel);
  635. }
  636. void sampleCubeMap(float* _rgba, const ImageContainer& _image, const bx::Vec3& _dir, float _lod)
  637. {
  638. float uu, vv;
  639. uint8_t side;
  640. dirToTexelUv(uu, vv, side, _dir);
  641. const float fu = bx::fract(uu);
  642. const float fv = bx::fract(vv);
  643. const float fl = bx::fract(_lod);
  644. float rgbaA[4];
  645. {
  646. Sampler sampler(_image, side, _lod, bx::floor);
  647. const uint32_t widthMinusOne = sampler.mip.m_width-1;
  648. const uint32_t u0 = uint32_t(uu*widthMinusOne+0.5f);
  649. const uint32_t v0 = uint32_t(vv*widthMinusOne+0.5f);
  650. const uint32_t u1 = bx::min(u0 + 1, widthMinusOne);
  651. const uint32_t v1 = bx::min(v0 + 1, widthMinusOne);
  652. float rgba00[4];
  653. texelFetch(rgba00, sampler, u0, v0);
  654. float rgba01[4];
  655. texelFetch(rgba01, sampler, u0, v1);
  656. float rgba10[4];
  657. texelFetch(rgba10, sampler, u1, v0);
  658. float rgba11[4];
  659. texelFetch(rgba11, sampler, u1, v1);
  660. rgbaA[0] = bx::lerp(bx::lerp(rgba00[0], rgba01[0], fv), bx::lerp(rgba10[0], rgba11[0], fv), fu);
  661. rgbaA[1] = bx::lerp(bx::lerp(rgba00[1], rgba01[1], fv), bx::lerp(rgba10[1], rgba11[1], fv), fu);
  662. rgbaA[2] = bx::lerp(bx::lerp(rgba00[2], rgba01[2], fv), bx::lerp(rgba10[2], rgba11[2], fv), fu);
  663. rgbaA[3] = bx::lerp(bx::lerp(rgba00[3], rgba01[3], fv), bx::lerp(rgba10[3], rgba11[3], fv), fu);
  664. }
  665. float rgbaB[4];
  666. {
  667. Sampler sampler(_image, side, _lod, bx::ceil);
  668. const uint32_t widthMinusOne = sampler.mip.m_width-1;
  669. const uint32_t u0 = uint32_t(uu*widthMinusOne+0.5f);
  670. const uint32_t v0 = uint32_t(vv*widthMinusOne+0.5f);
  671. const uint32_t u1 = bx::min(u0 + 1, widthMinusOne);
  672. const uint32_t v1 = bx::min(v0 + 1, widthMinusOne);
  673. float rgba00[4];
  674. texelFetch(rgba00, sampler, u0, v0);
  675. float rgba01[4];
  676. texelFetch(rgba01, sampler, u0, v1);
  677. float rgba10[4];
  678. texelFetch(rgba10, sampler, u1, v0);
  679. float rgba11[4];
  680. texelFetch(rgba11, sampler, u1, v1);
  681. rgbaB[0] = bx::lerp(bx::lerp(rgba00[0], rgba01[0], fv), bx::lerp(rgba10[0], rgba11[0], fv), fu);
  682. rgbaB[1] = bx::lerp(bx::lerp(rgba00[1], rgba01[1], fv), bx::lerp(rgba10[1], rgba11[1], fv), fu);
  683. rgbaB[2] = bx::lerp(bx::lerp(rgba00[2], rgba01[2], fv), bx::lerp(rgba10[2], rgba11[2], fv), fu);
  684. rgbaB[3] = bx::lerp(bx::lerp(rgba00[3], rgba01[3], fv), bx::lerp(rgba10[3], rgba11[3], fv), fu);
  685. }
  686. _rgba[0] = bx::lerp(rgbaA[0], rgbaB[0], fl);
  687. _rgba[1] = bx::lerp(rgbaA[1], rgbaB[1], fl);
  688. _rgba[2] = bx::lerp(rgbaA[2], rgbaB[2], fl);
  689. _rgba[3] = bx::lerp(rgbaA[3], rgbaB[3], fl);
  690. }
  691. bx::Vec3 importanceSampleGgx(float _u, float _v, float _roughness, const bx::Vec3& _normal, const bx::Vec3& _tangentX, const bx::Vec3& _tangentY)
  692. {
  693. const float aa = bx::square(_roughness);
  694. const float phi = bx::kPi2 * _u;
  695. const float cosTheta = bx::sqrt( (1.0f - _v) / (1.0f + (bx::square(aa) - 1.0f) * _v) );
  696. const float sinTheta = bx::sqrt(bx::abs(1.0f - bx::square(cosTheta) ) );
  697. const float hh[3] =
  698. {
  699. sinTheta * bx::cos(phi),
  700. sinTheta * bx::sin(phi),
  701. cosTheta,
  702. };
  703. return
  704. {
  705. _tangentX.x * hh[0] + _tangentY.x * hh[1] + _normal.x * hh[2],
  706. _tangentX.y * hh[0] + _tangentY.y * hh[1] + _normal.y * hh[2],
  707. _tangentX.z * hh[0] + _tangentY.z * hh[1] + _normal.z * hh[2],
  708. };
  709. }
  710. float normalDistributionGgx(float _ndoth, float _roughness)
  711. {
  712. const float alpha = bx::square(_roughness);
  713. const float alphaSq = bx::square(alpha);
  714. const float denom = bx::square(_ndoth) * (alphaSq - 1.0f) + 1.0f;
  715. const float denomSq = bx::square(denom);
  716. return alphaSq/(bx::kPi * denomSq);
  717. }
  718. void processFilterAreaGgx(
  719. float* _result
  720. , const ImageContainer& _image
  721. , uint8_t _lod
  722. , const bx::Vec3& _dir
  723. , float _roughness
  724. )
  725. {
  726. ImageMip mip;
  727. imageGetRawData(_image, 0, _lod, _image.m_data, _image.m_size, mip);
  728. const uint32_t bpp = getBitsPerPixel(_image.m_format);
  729. constexpr int32_t kNumSamples = 512;
  730. const uint32_t pitch = mip.m_width*bpp/8;
  731. const float widthMinusOne = float(mip.m_width-1);
  732. const float mipBias = 0.5f*bx::log2(bx::square(float(_image.m_width) )/float(kNumSamples) );
  733. UnpackFn unpack = getUnpack(_image.m_format);
  734. float color[3] = { 0.0f, 0.0f, 0.0f };
  735. float totalWeight = 0.0f;
  736. // Golden Ratio Sequences for Low-Discrepancy Sampling
  737. // https://web.archive.org/web/20180717194847/https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
  738. //
  739. // kGoldenSection = (0.5f*bx::sqrt(5.0f) + 0.5f) - 1.0f = 0.61803398875f
  740. //
  741. const float kGoldenSection = 0.61803398875f;
  742. float offset = kGoldenSection;
  743. bx::Vec3 tangentX(bx::init::None);
  744. bx::Vec3 tangentY(bx::init::None);
  745. bx::calcTangentFrame(tangentX, tangentY, _dir);
  746. for (uint32_t ii = 0; ii < kNumSamples; ++ii)
  747. {
  748. offset += kGoldenSection;
  749. const float vv = ii/float(kNumSamples);
  750. const bx::Vec3 hh = importanceSampleGgx(offset, vv, _roughness, _dir, tangentX, tangentY);
  751. const float ddoth2 = 2.0f * bx::dot(_dir, hh);
  752. const bx::Vec3 ll = bx::sub(bx::mul(hh, ddoth2), _dir);
  753. const float ndotl = bx::clamp(bx::dot(_dir, ll), 0.0f, 1.0f);
  754. if (ndotl > 0.0f)
  755. {
  756. const float ndoth = bx::clamp(bx::dot(_dir, hh), 0.0f, 1.0f);
  757. const float vdoth = ndoth;
  758. // Chapter 20. GPU-Based Importance Sampling
  759. // http://archive.today/2018.07.14-004914/https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
  760. //
  761. const float pdf = normalDistributionGgx(ndoth, _roughness) * ndoth / (4.0f * vdoth);
  762. const float lod = bx::max(0.0f, mipBias - 0.5f*bx::log2(pdf));
  763. float rgba[4];
  764. sampleCubeMap(rgba, _image, ll, lod);
  765. // Optimized Reversible Tonemapper for Resolve
  766. // https://web.archive.org/web/20180717182019/https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
  767. // "a single sample with a large HDR value can over-power all other samples"
  768. // "instead accept a bias in the resolve and reduce the weighting of samples
  769. // as a function of how bright they are"
  770. // Include ndotl here to "fold the weighting into the tonemap operation"
  771. //
  772. const float tm = ndotl / (bx::max(rgba[0], rgba[1], rgba[2]) + 1.0f);
  773. color[0] += rgba[0] * tm;
  774. color[1] += rgba[1] * tm;
  775. color[2] += rgba[2] * tm;
  776. totalWeight += ndotl;
  777. }
  778. }
  779. if (0.0f < totalWeight)
  780. {
  781. // Optimized Reversible Tonemapper for Resolve
  782. // https://web.archive.org/web/20180717182019/https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
  783. // Average, then reverse the tonemapper
  784. //
  785. const float invWeight = 1.0f/totalWeight;
  786. color[0] = color[0] * invWeight;
  787. color[1] = color[1] * invWeight;
  788. color[2] = color[2] * invWeight;
  789. const float invTm = 1.0f / (1.0f - bx::max(0.00001f, bx::max(color[0], color[1], color[2])));
  790. _result[0] = color[0] * invTm;
  791. _result[1] = color[1] * invTm;
  792. _result[2] = color[2] * invTm;
  793. }
  794. else
  795. {
  796. float uu, vv;
  797. uint8_t face;
  798. dirToTexelUv(uu, vv, face, _dir);
  799. imageGetRawData(_image, face, 0, _image.m_data, _image.m_size, mip);
  800. const uint32_t xx = uint32_t(uu*widthMinusOne);
  801. const uint32_t yy = uint32_t(vv*widthMinusOne);
  802. float rgba[4];
  803. unpack(rgba, mip.m_data + yy*pitch + xx*bpp/8);
  804. _result[0] = rgba[0];
  805. _result[1] = rgba[1];
  806. _result[2] = rgba[2];
  807. }
  808. }
  809. void processFilterArea(
  810. float* _result
  811. , const ImageContainer& _image
  812. , const ImageContainer& _nsa
  813. , uint8_t _lod
  814. , const Aabb* _aabb
  815. , const bx::Vec3& _dir
  816. , float _specularPower
  817. , float _specularAngle
  818. )
  819. {
  820. float color[3] = { 0.0f, 0.0f, 0.0f };
  821. float totalWeight = 0.0f;
  822. const uint32_t bpp = getBitsPerPixel(_image.m_format);
  823. UnpackFn unpack = getUnpack(_image.m_format);
  824. for (uint8_t side = 0; side < 6; ++side)
  825. {
  826. if (_aabb[side].isEmpty() )
  827. {
  828. continue;
  829. }
  830. ImageMip nsaMip;
  831. imageGetRawData(_nsa, side, 0, _nsa.m_data, _nsa.m_size, nsaMip);
  832. ImageMip mip;
  833. if (imageGetRawData(_image, side, _lod, _image.m_data, _image.m_size, mip) )
  834. {
  835. const uint32_t pitch = mip.m_width*bpp/8;
  836. const float widthMinusOne = float(mip.m_width-1);
  837. const float texelSize = 1.0f/float(mip.m_width);
  838. BX_UNUSED(texelSize);
  839. const uint32_t minX = uint32_t(_aabb[side].m_min[0] * widthMinusOne);
  840. const uint32_t maxX = uint32_t(_aabb[side].m_max[0] * widthMinusOne);
  841. const uint32_t minY = uint32_t(_aabb[side].m_min[1] * widthMinusOne);
  842. const uint32_t maxY = uint32_t(_aabb[side].m_max[1] * widthMinusOne);
  843. for (uint32_t yy = minY; yy <= maxY; ++yy)
  844. {
  845. const uint8_t* row = mip.m_data + yy*pitch;
  846. BX_UNUSED(row);
  847. for (uint32_t xx = minX; xx <= maxX; ++xx)
  848. {
  849. const float* normal = (const float*)&nsaMip.m_data[(yy*nsaMip.m_width+xx)*(nsaMip.m_bpp/8)];
  850. const float solidAngle = normal[3];
  851. const float ndotl = bx::clamp(bx::dot(bx::load<bx::Vec3>(normal), _dir), 0.0f, 1.0f);
  852. if (ndotl >= _specularAngle)
  853. {
  854. const float weight = solidAngle * bx::pow(ndotl, _specularPower);
  855. float rgba[4];
  856. unpack(rgba, row + xx*bpp/8);
  857. color[0] += rgba[0] * weight;
  858. color[1] += rgba[1] * weight;
  859. color[2] += rgba[2] * weight;
  860. totalWeight += weight;
  861. }
  862. }
  863. }
  864. if (0.0f < totalWeight)
  865. {
  866. const float invWeight = 1.0f/totalWeight;
  867. _result[0] = color[0] * invWeight;
  868. _result[1] = color[1] * invWeight;
  869. _result[2] = color[2] * invWeight;
  870. }
  871. else
  872. {
  873. float uu, vv;
  874. uint8_t face;
  875. dirToTexelUv(uu, vv, face, _dir);
  876. imageGetRawData(_image, face, 0, _image.m_data, _image.m_size, mip);
  877. const uint32_t xx = uint32_t(uu*widthMinusOne);
  878. const uint32_t yy = uint32_t(vv*widthMinusOne);
  879. float rgba[4];
  880. unpack(rgba, mip.m_data + yy*pitch + xx*bpp/8);
  881. _result[0] = rgba[0];
  882. _result[1] = rgba[1];
  883. _result[2] = rgba[2];
  884. }
  885. }
  886. }
  887. }
  888. ImageContainer* imageGenerateMips(bx::AllocatorI* _allocator, const ImageContainer& _image)
  889. {
  890. if (_image.m_format != TextureFormat::RGBA8
  891. && _image.m_format != TextureFormat::RGBA32F)
  892. {
  893. return NULL;
  894. }
  895. ImageContainer* output = imageAlloc(_allocator, _image.m_format, uint16_t(_image.m_width), uint16_t(_image.m_height), uint16_t(_image.m_depth), _image.m_numLayers, _image.m_cubeMap, true);
  896. const uint32_t numMips = output->m_numMips;
  897. const uint32_t numLayers = output->m_numLayers;
  898. const uint32_t numSides = output->m_cubeMap ? 6 : 1;
  899. for (uint32_t layer = 0; layer < numLayers; ++layer)
  900. {
  901. for (uint8_t side = 0; side < numSides; ++side)
  902. {
  903. ImageMip mip;
  904. if (imageGetRawData(_image, uint16_t(layer*numSides + side), 0, _image.m_data, _image.m_size, mip) )
  905. {
  906. for (uint8_t lod = 0; lod < numMips; ++lod)
  907. {
  908. ImageMip srcMip;
  909. imageGetRawData(*output, uint16_t(layer*numSides + side), lod == 0 ? 0 : lod-1, output->m_data, output->m_size, srcMip);
  910. ImageMip dstMip;
  911. imageGetRawData(*output, uint16_t(layer*numSides + side), lod, output->m_data, output->m_size, dstMip);
  912. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  913. if (0 == lod)
  914. {
  915. bx::memCopy(dstData, mip.m_data, mip.m_size);
  916. }
  917. else if (output->m_format == TextureFormat::RGBA8)
  918. {
  919. imageRgba8Downsample2x2(
  920. dstData
  921. , srcMip.m_width
  922. , srcMip.m_height
  923. , srcMip.m_depth
  924. , srcMip.m_width*4
  925. , dstMip.m_width*4
  926. , srcMip.m_data
  927. );
  928. }
  929. else if (output->m_format == TextureFormat::RGBA32F)
  930. {
  931. imageRgba32fDownsample2x2(
  932. dstData
  933. , srcMip.m_width
  934. , srcMip.m_height
  935. , srcMip.m_depth
  936. , srcMip.m_width*16
  937. , srcMip.m_data
  938. );
  939. }
  940. }
  941. }
  942. }
  943. }
  944. return output;
  945. }
  946. /// Returns the angle of cosine power function where the results are above a small empirical treshold.
  947. static float cosinePowerFilterAngle(float _cosinePower)
  948. {
  949. // Bigger value leads to performance improvement but might hurt the results.
  950. // 0.00001f was tested empirically and it gives almost the same values as reference.
  951. const float treshold = 0.00001f;
  952. // Cosine power filter is: pow(cos(angle), power).
  953. // We want the value of the angle above each result is <= treshold.
  954. // So: angle = acos(pow(treshold, 1.0 / power))
  955. return bx::acos(bx::pow(treshold, 1.0f / _cosinePower));
  956. }
  957. float glossinessFor(float _mip, float _mipCount)
  958. {
  959. return bx::max(0.0f, 1.0f - _mip/(_mipCount-1.0000001f) );
  960. }
  961. float applyLightingModel(float _specularPower, LightingModel::Enum _lightingModel)
  962. {
  963. // Reference(s):
  964. // - https://web.archive.org/web/20180622232018/https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
  965. // - https://web.archive.org/web/20180622232041/https://seblagarde.wordpress.com/2012/03/29/relationship-between-phong-and-blinn-lighting-model/
  966. //
  967. switch (_lightingModel)
  968. {
  969. case LightingModel::PhongBrdf: return _specularPower + 1.0f;
  970. case LightingModel::Blinn: return _specularPower/4.0f;
  971. case LightingModel::BlinnBrdf: return _specularPower/4.0f + 1.0f;
  972. default: break;
  973. };
  974. return _specularPower;
  975. }
  976. ImageContainer* imageCubemapRadianceFilter(bx::AllocatorI* _allocator, const ImageContainer& _image, LightingModel::Enum _lightingModel, bx::Error* _err)
  977. {
  978. if (!_image.m_cubeMap)
  979. {
  980. BX_ERROR_SET(_err, BIMG_ERROR, "Input image is not cubemap.");
  981. return NULL;
  982. }
  983. ImageContainer* input = imageConvert(_allocator, TextureFormat::RGBA32F, _image, true);
  984. if (1 >= input->m_numMips)
  985. {
  986. ImageContainer* temp = imageGenerateMips(_allocator, *input);
  987. imageFree(input);
  988. input = temp;
  989. }
  990. ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, uint16_t(input->m_width), uint16_t(input->m_width), 1, 1, true, true);
  991. for (uint8_t side = 0; side < 6; ++side)
  992. {
  993. ImageMip srcMip;
  994. imageGetRawData(*input, side, 0, input->m_data, input->m_size, srcMip);
  995. ImageMip dstMip;
  996. imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  997. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  998. bx::memCopy(dstData, srcMip.m_data, srcMip.m_size);
  999. }
  1000. const float glossScale = 10.0f;
  1001. const float glossBias = 1.0f;
  1002. for (uint8_t lod = 1, numMips = input->m_numMips; lod < numMips; ++lod)
  1003. {
  1004. ImageContainer* nsa = NULL;
  1005. if (LightingModel::Ggx != _lightingModel)
  1006. {
  1007. nsa = imageCubemapNormalSolidAngle(_allocator, bx::max<uint32_t>(_image.m_width>>lod, 1) );
  1008. }
  1009. for (uint8_t side = 0; side < 6; ++side)
  1010. {
  1011. ImageMip mip;
  1012. imageGetRawData(*output, side, lod, output->m_data, output->m_size, mip);
  1013. const uint32_t dstWidth = mip.m_width;
  1014. const uint32_t dstPitch = dstWidth*16;
  1015. const float minAngle = bx::atan2(1.0f, float(dstWidth) );
  1016. const float maxAngle = bx::kPiHalf;
  1017. const float toFilterSize = 1.0f/(minAngle*dstWidth*2.0f);
  1018. const float glossiness = glossinessFor(lod, float(numMips) );
  1019. const float roughness = 1.0f-glossiness;
  1020. const float specularPowerRef = bx::pow(2.0f, glossiness*glossScale + glossBias);
  1021. const float specularPower = applyLightingModel(specularPowerRef, _lightingModel);
  1022. const float filterAngle = bx::clamp(cosinePowerFilterAngle(specularPower), minAngle, maxAngle);
  1023. const float cosAngle = bx::max(0.0f, bx::cos(filterAngle) );
  1024. const float texelSize = 1.0f/float(dstWidth);
  1025. const float filterSize = bx::max(texelSize, filterAngle * toFilterSize);
  1026. for (uint32_t yy = 0; yy < dstWidth; ++yy)
  1027. {
  1028. for (uint32_t xx = 0; xx < dstWidth; ++xx)
  1029. {
  1030. float* dstData = (float*)&mip.m_data[yy*dstPitch+xx*16];
  1031. const float uu = float(xx)*texelSize*2.0f - 1.0f;
  1032. const float vv = float(yy)*texelSize*2.0f - 1.0f;
  1033. bx::Vec3 dir = texelUvToDir(side, uu, vv);
  1034. if (LightingModel::Ggx == _lightingModel)
  1035. {
  1036. processFilterAreaGgx(dstData, *input, lod, dir, roughness);
  1037. }
  1038. else
  1039. {
  1040. Aabb aabb[6];
  1041. calcFilterArea(aabb, dir, filterSize);
  1042. processFilterArea(dstData, *input, *nsa, lod, aabb, dir, specularPower, cosAngle);
  1043. }
  1044. }
  1045. }
  1046. }
  1047. if (NULL != nsa)
  1048. {
  1049. imageFree(nsa);
  1050. }
  1051. }
  1052. return output;
  1053. }
  1054. } // namespace bimg