image_cubemap_filter.cpp 34 KB

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