CCubeMapProcessor.cpp 90 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314
  1. //=============================================================================
  2. // (C) 2005 ATI Research, Inc., All rights reserved.
  3. //=============================================================================
  4. // Modified from original
  5. #include "CCubeMapProcessor.h"
  6. #include <AzCore/Math/MathUtils.h>
  7. #include <AzCore/std/string/string.h>
  8. #define CP_PI 3.14159265358979323846f
  9. namespace ImageProcessingAtom
  10. {
  11. //------------------------------------------------------------------------------
  12. // D3D cube map face specification
  13. // mapping from 3D x,y,z cube map lookup coordinates
  14. // to 2D within face u,v coordinates
  15. //
  16. // --------------------> U direction
  17. // | (within-face texture space)
  18. // | _____
  19. // | | |
  20. // | | +Y |
  21. // | _____|_____|_____ _____
  22. // | | | | | |
  23. // | | -X | +Z | +X | -Z |
  24. // | |_____|_____|_____|_____|
  25. // | | |
  26. // | | -Y |
  27. // | |_____|
  28. // |
  29. // v V direction
  30. // (within-face texture space)
  31. //------------------------------------------------------------------------------
  32. //Information about neighbors and how texture coorrdinates change across faces
  33. // in ORDER of left, right, top, bottom (e.g. edges corresponding to u=0,
  34. // u=1, v=0, v=1 in the 2D coordinate system of the particular face.
  35. //Note this currently assumes the D3D cube face ordering and orientation
  36. CPCubeMapNeighbor sg_CubeNgh[6][4] =
  37. {
  38. //XPOS face
  39. {{CP_FACE_Z_POS, CP_EDGE_RIGHT },
  40. {CP_FACE_Z_NEG, CP_EDGE_LEFT },
  41. {CP_FACE_Y_POS, CP_EDGE_RIGHT },
  42. {CP_FACE_Y_NEG, CP_EDGE_RIGHT }},
  43. //XNEG face
  44. {{CP_FACE_Z_NEG, CP_EDGE_RIGHT },
  45. {CP_FACE_Z_POS, CP_EDGE_LEFT },
  46. {CP_FACE_Y_POS, CP_EDGE_LEFT },
  47. {CP_FACE_Y_NEG, CP_EDGE_LEFT }},
  48. //YPOS face
  49. {{CP_FACE_X_NEG, CP_EDGE_TOP },
  50. {CP_FACE_X_POS, CP_EDGE_TOP },
  51. {CP_FACE_Z_NEG, CP_EDGE_TOP },
  52. {CP_FACE_Z_POS, CP_EDGE_TOP }},
  53. //YNEG face
  54. {{CP_FACE_X_NEG, CP_EDGE_BOTTOM},
  55. {CP_FACE_X_POS, CP_EDGE_BOTTOM},
  56. {CP_FACE_Z_POS, CP_EDGE_BOTTOM},
  57. {CP_FACE_Z_NEG, CP_EDGE_BOTTOM}},
  58. //ZPOS face
  59. {{CP_FACE_X_NEG, CP_EDGE_RIGHT },
  60. {CP_FACE_X_POS, CP_EDGE_LEFT },
  61. {CP_FACE_Y_POS, CP_EDGE_BOTTOM },
  62. {CP_FACE_Y_NEG, CP_EDGE_TOP }},
  63. //ZNEG face
  64. {{CP_FACE_X_POS, CP_EDGE_RIGHT },
  65. {CP_FACE_X_NEG, CP_EDGE_LEFT },
  66. {CP_FACE_Y_POS, CP_EDGE_TOP },
  67. {CP_FACE_Y_NEG, CP_EDGE_BOTTOM }}
  68. };
  69. //3x2 matrices that map cube map indexing vectors in 3d
  70. // (after face selection and divide through by the
  71. // _ABSOLUTE VALUE_ of the max coord)
  72. // into NVC space
  73. //Note this currently assumes the D3D cube face ordering and orientation
  74. #define CP_UDIR 0
  75. #define CP_VDIR 1
  76. #define CP_FACEAXIS 2
  77. float sgFace2DMapping[6][3][3] = {
  78. //XPOS face
  79. {{ 0, 0, -1}, //u towards negative Z
  80. { 0, -1, 0}, //v towards negative Y
  81. {1, 0, 0}}, //pos X axis
  82. //XNEG face
  83. {{0, 0, 1}, //u towards positive Z
  84. {0, -1, 0}, //v towards negative Y
  85. {-1, 0, 0}}, //neg X axis
  86. //YPOS face
  87. {{1, 0, 0}, //u towards positive X
  88. {0, 0, 1}, //v towards positive Z
  89. {0, 1 , 0}}, //pos Y axis
  90. //YNEG face
  91. {{1, 0, 0}, //u towards positive X
  92. {0, 0 , -1}, //v towards negative Z
  93. {0, -1 , 0}}, //neg Y axis
  94. //ZPOS face
  95. {{1, 0, 0}, //u towards positive X
  96. {0, -1, 0}, //v towards negative Y
  97. {0, 0, 1}}, //pos Z axis
  98. //ZNEG face
  99. {{-1, 0, 0}, //u towards negative X
  100. {0, -1, 0}, //v towards negative Y
  101. {0, 0, -1}}, //neg Z axis
  102. };
  103. //The 12 edges of the cubemap, (entries are used to index into the neighbor table)
  104. // this table is used to average over the edges.
  105. int32 sg_CubeEdgeList[12][2] = {
  106. {CP_FACE_X_POS, CP_EDGE_LEFT},
  107. {CP_FACE_X_POS, CP_EDGE_RIGHT},
  108. {CP_FACE_X_POS, CP_EDGE_TOP},
  109. {CP_FACE_X_POS, CP_EDGE_BOTTOM},
  110. {CP_FACE_X_NEG, CP_EDGE_LEFT},
  111. {CP_FACE_X_NEG, CP_EDGE_RIGHT},
  112. {CP_FACE_X_NEG, CP_EDGE_TOP},
  113. {CP_FACE_X_NEG, CP_EDGE_BOTTOM},
  114. {CP_FACE_Z_POS, CP_EDGE_TOP},
  115. {CP_FACE_Z_POS, CP_EDGE_BOTTOM},
  116. {CP_FACE_Z_NEG, CP_EDGE_TOP},
  117. {CP_FACE_Z_NEG, CP_EDGE_BOTTOM}
  118. };
  119. //Information about which of the 8 cube corners are correspond to the
  120. // the 4 corners in each cube face
  121. // the order is upper left, upper right, lower left, lower right
  122. int32 sg_CubeCornerList[6][4] = {
  123. { CP_CORNER_PPP, CP_CORNER_PPN, CP_CORNER_PNP, CP_CORNER_PNN }, // XPOS face
  124. { CP_CORNER_NPN, CP_CORNER_NPP, CP_CORNER_NNN, CP_CORNER_NNP }, // XNEG face
  125. { CP_CORNER_NPN, CP_CORNER_PPN, CP_CORNER_NPP, CP_CORNER_PPP }, // YPOS face
  126. { CP_CORNER_NNP, CP_CORNER_PNP, CP_CORNER_NNN, CP_CORNER_PNN }, // YNEG face
  127. { CP_CORNER_NPP, CP_CORNER_PPP, CP_CORNER_NNP, CP_CORNER_PNP }, // ZPOS face
  128. { CP_CORNER_PPN, CP_CORNER_NPN, CP_CORNER_PNN, CP_CORNER_NNN } // ZNEG face
  129. };
  130. //--------------------------------------------------------------------------------------
  131. // Convert cubemap face texel coordinates and face idx to 3D vector
  132. // note the U and V coords are integer coords and range from 0 to size-1
  133. // this routine can be used to generate a normalizer cube map
  134. //--------------------------------------------------------------------------------------
  135. void TexelCoordToVect(int32 a_FaceIdx, float a_U, float a_V, int32 a_Size, float *a_XYZ)
  136. {
  137. float nvcU, nvcV;
  138. float tempVec[3];
  139. //scale up to [-1, 1] range (inclusive)
  140. nvcU = (2.0f * ((float)a_U + 0.5f) / a_Size ) - 1.0f;
  141. nvcV = (2.0f * ((float)a_V + 0.5f) / a_Size ) - 1.0f;
  142. //generate x,y,z vector (xform 2d NVC coord to 3D vector)
  143. //U contribution
  144. VM_SCALE3(a_XYZ, sgFace2DMapping[a_FaceIdx][CP_UDIR], nvcU);
  145. //V contribution
  146. VM_SCALE3(tempVec, sgFace2DMapping[a_FaceIdx][CP_VDIR], nvcV);
  147. VM_ADD3(a_XYZ, tempVec, a_XYZ);
  148. //add face axis
  149. VM_ADD3(a_XYZ, sgFace2DMapping[a_FaceIdx][CP_FACEAXIS], a_XYZ);
  150. //normalize vector
  151. VM_NORM3(a_XYZ, a_XYZ);
  152. }
  153. //--------------------------------------------------------------------------------------
  154. // Convert 3D vector to cubemap face texel coordinates and face idx
  155. // note the U and V coords are integer coords and range from 0 to size-1
  156. // this routine can be used to generate a normalizer cube map
  157. //
  158. // returns face IDX and texel coords
  159. //--------------------------------------------------------------------------------------
  160. void VectToTexelCoord(float *a_XYZ, int32 a_Size, int32 *a_FaceIdx, float *a_U, float *a_V )
  161. {
  162. float nvcU, nvcV;
  163. float absXYZ[3];
  164. float maxCoord;
  165. float onFaceXYZ[3];
  166. int32 faceIdx;
  167. float u, v;
  168. //absolute value 3
  169. VM_ABS3(absXYZ, a_XYZ);
  170. if( (absXYZ[0] >= absXYZ[1]) && (absXYZ[0] >= absXYZ[2]) )
  171. {
  172. maxCoord = absXYZ[0];
  173. if(a_XYZ[0] >= 0) //face = XPOS
  174. {
  175. faceIdx = CP_FACE_X_POS;
  176. }
  177. else
  178. {
  179. faceIdx = CP_FACE_X_NEG;
  180. }
  181. }
  182. else if ( (absXYZ[1] >= absXYZ[0]) && (absXYZ[1] >= absXYZ[2]) )
  183. {
  184. maxCoord = absXYZ[1];
  185. if(a_XYZ[1] >= 0) //face = XPOS
  186. {
  187. faceIdx = CP_FACE_Y_POS;
  188. }
  189. else
  190. {
  191. faceIdx = CP_FACE_Y_NEG;
  192. }
  193. }
  194. else // if( (absXYZ[2] > absXYZ[0]) && (absXYZ[2] > absXYZ[1]) )
  195. {
  196. maxCoord = absXYZ[2];
  197. if(a_XYZ[2] >= 0) //face = XPOS
  198. {
  199. faceIdx = CP_FACE_Z_POS;
  200. }
  201. else
  202. {
  203. faceIdx = CP_FACE_Z_NEG;
  204. }
  205. }
  206. //divide through by max coord so face vector lies on cube face
  207. VM_SCALE3(onFaceXYZ, a_XYZ, 1.0f/maxCoord);
  208. nvcU = VM_DOTPROD3(sgFace2DMapping[ faceIdx ][CP_UDIR], onFaceXYZ );
  209. nvcV = VM_DOTPROD3(sgFace2DMapping[ faceIdx ][CP_VDIR], onFaceXYZ );
  210. u = (a_Size - 1.0f) * 0.5f * (nvcU + 1.0f);
  211. v = (a_Size - 1.0f) * 0.5f * (nvcV + 1.0f);
  212. *a_FaceIdx = faceIdx;
  213. *a_U = u;
  214. *a_V = v;
  215. }
  216. //--------------------------------------------------------------------------------------
  217. // gets texel ptr in a cube map given a direction vector, and an array of
  218. // CImageSurfaces that represent the cube faces.
  219. //
  220. //--------------------------------------------------------------------------------------
  221. CP_ITYPE *GetCubeMapTexelPtr(float *a_XYZ, CImageSurface *a_Surface)
  222. {
  223. float u, v;
  224. int32 faceIdx;
  225. //get face idx and u, v texel coordinate in face
  226. VectToTexelCoord(a_XYZ, a_Surface[0].m_Width, &faceIdx, &u, &v );
  227. u = static_cast<float>(VM_MIN((int32)u, a_Surface[0].m_Width - 1));
  228. v = static_cast<float>(VM_MIN((int32)v, a_Surface[0].m_Width - 1));
  229. return( a_Surface[faceIdx].GetSurfaceTexelPtr(static_cast<int32>(u), static_cast<int32>(v)) );
  230. }
  231. //--------------------------------------------------------------------------------------
  232. // returns a bilinear filtered texel value
  233. //
  234. //--------------------------------------------------------------------------------------
  235. void GetCubeMapTexelBilinear(float *a_XYZ, CImageSurface *a_Surface, CP_ITYPE* result, int32 numChannels)
  236. {
  237. float u, v;
  238. int32 faceIdx;
  239. //get face idx and u, v texel coordinate in face
  240. VectToTexelCoord(a_XYZ, a_Surface[0].m_Width, &faceIdx, &u, &v);
  241. //sample the four points in the quad around this point
  242. int32 uPoint = (int32)u;
  243. int32 vPoint = (int32)v;
  244. //top Left
  245. int32 uQuad = (int32)uPoint;
  246. int32 vQuad = (int32)vPoint;
  247. CP_ITYPE* sampleTL = (a_Surface[faceIdx].GetSurfaceTexelPtr(uQuad, vQuad));
  248. //top right
  249. uQuad = VM_MIN(uPoint + 1, a_Surface[0].m_Width - 1);
  250. vQuad = vPoint;
  251. CP_ITYPE* sampleTR = (a_Surface[faceIdx].GetSurfaceTexelPtr(uQuad, vQuad));
  252. //bottom left
  253. uQuad = uPoint;
  254. vQuad = VM_MIN(vPoint + 1, a_Surface[0].m_Width - 1);
  255. CP_ITYPE* sampleBL = (a_Surface[faceIdx].GetSurfaceTexelPtr(uQuad, vQuad));
  256. //bottom right
  257. uQuad = VM_MIN(uPoint + 1, a_Surface[0].m_Width - 1);
  258. vQuad = VM_MIN(vPoint + 1, a_Surface[0].m_Width - 1);
  259. CP_ITYPE* sampleBR = (a_Surface[faceIdx].GetSurfaceTexelPtr(uQuad, vQuad));
  260. //compute interpolated value
  261. float uDelta = u - uPoint;
  262. float vDelta = v - vPoint;
  263. for (uint32 i = 0; i < (uint32)numChannels; i++)
  264. {
  265. float topValue = sampleTL[i] * (1.0f - uDelta) + sampleTR[i] * uDelta;
  266. float bottomValue = sampleBL[i] * (1.0f - uDelta) + sampleBR[i] * uDelta;
  267. result[i] = topValue * (1.0f - vDelta) + bottomValue * vDelta;
  268. }
  269. }
  270. //--------------------------------------------------------------------------------------
  271. // Compute solid angle of given texel in cubemap face for weighting taps in the
  272. // kernel by the area they project to on the unit sphere.
  273. //
  274. // Note that this code uses an approximation to the solid angle, by treating the
  275. // two triangles that make up the quad comprising the texel as planar. If more
  276. // accuracy is required, the solid angle per triangle lying on the sphere can be
  277. // computed using the sum of the interior angles - PI.
  278. //
  279. //--------------------------------------------------------------------------------------
  280. float TexelCoordSolidAngle(int32 a_FaceIdx, float a_U, float a_V, int32 a_Size)
  281. {
  282. float cornerVect[4][3];
  283. double cornerVect64[4][3];
  284. float halfTexelStep = 0.5f; //note u, and v are in texel coords (where each texel is one unit)
  285. double edgeVect0[3];
  286. double edgeVect1[3];
  287. double xProdVect[3];
  288. double texelArea;
  289. //compute 4 corner vectors of texel
  290. TexelCoordToVect(a_FaceIdx, a_U - halfTexelStep, a_V - halfTexelStep, a_Size, cornerVect[0] );
  291. TexelCoordToVect(a_FaceIdx, a_U - halfTexelStep, a_V + halfTexelStep, a_Size, cornerVect[1] );
  292. TexelCoordToVect(a_FaceIdx, a_U + halfTexelStep, a_V - halfTexelStep, a_Size, cornerVect[2] );
  293. TexelCoordToVect(a_FaceIdx, a_U + halfTexelStep, a_V + halfTexelStep, a_Size, cornerVect[3] );
  294. VM_NORM3_UNTYPED(cornerVect64[0], cornerVect[0] );
  295. VM_NORM3_UNTYPED(cornerVect64[1], cornerVect[1] );
  296. VM_NORM3_UNTYPED(cornerVect64[2], cornerVect[2] );
  297. VM_NORM3_UNTYPED(cornerVect64[3], cornerVect[3] );
  298. //area of triangle defined by corners 0, 1, and 2
  299. VM_SUB3_UNTYPED(edgeVect0, cornerVect64[1], cornerVect64[0] );
  300. VM_SUB3_UNTYPED(edgeVect1, cornerVect64[2], cornerVect64[0] );
  301. VM_XPROD3_UNTYPED(xProdVect, edgeVect0, edgeVect1 );
  302. texelArea = 0.5f * sqrt( VM_DOTPROD3_UNTYPED(xProdVect, xProdVect ) );
  303. //area of triangle defined by corners 1, 2, and 3
  304. VM_SUB3_UNTYPED(edgeVect0, cornerVect64[2], cornerVect64[1] );
  305. VM_SUB3_UNTYPED(edgeVect1, cornerVect64[3], cornerVect64[1] );
  306. VM_XPROD3_UNTYPED(xProdVect, edgeVect0, edgeVect1 );
  307. texelArea += 0.5f * sqrt( VM_DOTPROD3_UNTYPED(xProdVect, xProdVect ) );
  308. return static_cast<float>(texelArea);
  309. }
  310. //--------------------------------------------------------------------------------------
  311. //Builds a normalizer cubemap
  312. //
  313. // Takes in a cube face size, and an array of 6 surfaces to write the cube faces into
  314. //
  315. // Note that this normalizer cube map stores the vectors in unbiased -1 to 1 range.
  316. // if _bx2 style scaled and biased vectors are needed, uncomment the SCALE and BIAS
  317. // below
  318. //--------------------------------------------------------------------------------------
  319. void CCubeMapProcessor::BuildNormalizerCubemap(int32 a_Size, CImageSurface *a_Surface )
  320. {
  321. int32 iCubeFace, u, v;
  322. //iterate over cube faces
  323. for(iCubeFace=0; iCubeFace<6; iCubeFace++)
  324. {
  325. a_Surface[iCubeFace].Clear();
  326. a_Surface[iCubeFace].Init(a_Size, a_Size, 3);
  327. //fast texture walk, build normalizer cube map
  328. CP_ITYPE *texelPtr = a_Surface[iCubeFace].m_ImgData;
  329. for(v=0; v < a_Surface[iCubeFace].m_Height; v++)
  330. {
  331. for(u=0; u < a_Surface[iCubeFace].m_Width; u++)
  332. {
  333. TexelCoordToVect(iCubeFace, (float)u, (float)v, a_Size, texelPtr);
  334. //VM_SCALE3(texelPtr, texelPtr, 0.5f);
  335. //VM_BIAS3(texelPtr, texelPtr, 0.5f);
  336. texelPtr += a_Surface[iCubeFace].m_NumChannels;
  337. }
  338. }
  339. }
  340. }
  341. //--------------------------------------------------------------------------------------
  342. //Builds a normalizer cubemap, with the texels solid angle stored in the fourth component
  343. //
  344. //Takes in a cube face size, and an array of 6 surfaces to write the cube faces into
  345. //
  346. //Note that this normalizer cube map stores the vectors in unbiased -1 to 1 range.
  347. // if _bx2 style scaled and biased vectors are needed, uncomment the SCALE and BIAS
  348. // below
  349. //--------------------------------------------------------------------------------------
  350. void CCubeMapProcessor::BuildNormalizerSolidAngleCubemap(int32 a_Size, CImageSurface *a_Surface )
  351. {
  352. //iterate over cube faces
  353. for(int32 iCubeFace=0; iCubeFace<6; iCubeFace++)
  354. {
  355. a_Surface[iCubeFace].Clear();
  356. a_Surface[iCubeFace].Init(a_Size, a_Size, 4); //First three channels for norm cube, and last channel for solid angle
  357. }
  358. //iterate over cube faces
  359. for(int32 iCubeFace=0; iCubeFace<6; iCubeFace++)
  360. {
  361. const int32 height = a_Surface[iCubeFace].m_Height;
  362. const int32 width = a_Surface[iCubeFace].m_Width;
  363. for(int32 v=0; v<height; v++)
  364. {
  365. //fast texture walk, build normalizer cube map
  366. CP_ITYPE *texelPtr = a_Surface[iCubeFace].m_ImgData + v * width * a_Surface[iCubeFace].m_NumChannels;
  367. for(int32 u=0; u<width; u++)
  368. {
  369. TexelCoordToVect(iCubeFace, (float)u, (float)v, a_Size, texelPtr);
  370. //VM_SCALE3(texelPtr, texelPtr, 0.5f);
  371. //VM_BIAS3(texelPtr, texelPtr, 0.5f);
  372. *(texelPtr + 3) = TexelCoordSolidAngle(iCubeFace, (float)u, (float)v, a_Size);
  373. texelPtr += a_Surface[iCubeFace].m_NumChannels;
  374. }
  375. }
  376. }
  377. }
  378. //--------------------------------------------------------------------------------------
  379. //Clear filter extents for the 6 cube map faces
  380. //--------------------------------------------------------------------------------------
  381. void CCubeMapProcessor::ClearFilterExtents(CBBoxInt32 *aFilterExtents)
  382. {
  383. int32 iCubeFaces;
  384. for(iCubeFaces=0; iCubeFaces<6; iCubeFaces++)
  385. {
  386. aFilterExtents[iCubeFaces].Clear();
  387. }
  388. }
  389. //--------------------------------------------------------------------------------------
  390. //Define per-face bounding box filter extents
  391. //
  392. // These define conservative texel regions in each of the faces the filter can possibly
  393. // process. When the pixels in the regions are actually processed, the dot product
  394. // between the tap vector and the center tap vector is used to determine the weight of
  395. // the tap and whether or not the tap is within the cone.
  396. //
  397. //--------------------------------------------------------------------------------------
  398. void CCubeMapProcessor::DetermineFilterExtents(float *a_CenterTapDir, int32 a_SrcSize, int32 a_BBoxSize,
  399. CBBoxInt32 *a_FilterExtents )
  400. {
  401. int32 u, v;
  402. int32 faceIdx;
  403. int32 minU, minV, maxU, maxV;
  404. int32 i;
  405. //neighboring face and bleed over amount, and width of BBOX for
  406. // left, right, top, and bottom edges of this face
  407. int32 bleedOverAmount[4];
  408. int32 bleedOverBBoxMin[4];
  409. int32 bleedOverBBoxMax[4];
  410. int32 neighborFace;
  411. int32 neighborEdge;
  412. //get face idx, and u, v info from center tap dir
  413. float uFloat, vFloat;
  414. VectToTexelCoord(a_CenterTapDir, a_SrcSize, &faceIdx, &uFloat, &vFloat);
  415. u = (int32)uFloat;
  416. v = (int32)vFloat;
  417. //define bbox size within face
  418. a_FilterExtents[faceIdx].Augment(u - a_BBoxSize, v - a_BBoxSize, 0);
  419. a_FilterExtents[faceIdx].Augment(u + a_BBoxSize, v + a_BBoxSize, 0);
  420. a_FilterExtents[faceIdx].ClampMin(0, 0, 0);
  421. a_FilterExtents[faceIdx].ClampMax(a_SrcSize-1, a_SrcSize-1, 0);
  422. //u and v extent in face corresponding to center tap
  423. minU = a_FilterExtents[faceIdx].m_minCoord[0];
  424. minV = a_FilterExtents[faceIdx].m_minCoord[1];
  425. maxU = a_FilterExtents[faceIdx].m_maxCoord[0];
  426. maxV = a_FilterExtents[faceIdx].m_maxCoord[1];
  427. //bleed over amounts for face across u=0 edge (left)
  428. bleedOverAmount[0] = (a_BBoxSize - u);
  429. bleedOverBBoxMin[0] = minV;
  430. bleedOverBBoxMax[0] = maxV;
  431. //bleed over amounts for face across u=1 edge (right)
  432. bleedOverAmount[1] = (u + a_BBoxSize) - (a_SrcSize-1);
  433. bleedOverBBoxMin[1] = minV;
  434. bleedOverBBoxMax[1] = maxV;
  435. //bleed over to face across v=0 edge (up)
  436. bleedOverAmount[2] = (a_BBoxSize - v);
  437. bleedOverBBoxMin[2] = minU;
  438. bleedOverBBoxMax[2] = maxU;
  439. //bleed over to face across v=1 edge (down)
  440. bleedOverAmount[3] = (v + a_BBoxSize) - (a_SrcSize-1);
  441. bleedOverBBoxMin[3] = minU;
  442. bleedOverBBoxMax[3] = maxU;
  443. //compute bleed over regions in neighboring faces
  444. for(i=0; i<4; i++)
  445. {
  446. if(bleedOverAmount[i] > 0)
  447. {
  448. neighborFace = sg_CubeNgh[faceIdx][i].m_Face;
  449. neighborEdge = sg_CubeNgh[faceIdx][i].m_Edge;
  450. //For certain types of edge abutments, the bleedOverBBoxMin, and bleedOverBBoxMax need to
  451. // be flipped: the cases are
  452. // if a left edge mates with a left or bottom edge on the neighbor
  453. // if a top edge mates with a top or right edge on the neighbor
  454. // if a right edge mates with a right or top edge on the neighbor
  455. // if a bottom edge mates with a bottom or left edge on the neighbor
  456. //Seeing as the edges are enumerated as follows
  457. // left =0
  458. // right =1
  459. // top =2
  460. // bottom =3
  461. //
  462. // so if the edge enums are the same, or the sum of the enums == 3,
  463. // the bbox needs to be flipped
  464. if( (i == neighborEdge) || ((i+neighborEdge) == 3) )
  465. {
  466. bleedOverBBoxMin[i] = (a_SrcSize-1) - bleedOverBBoxMin[i];
  467. bleedOverBBoxMax[i] = (a_SrcSize-1) - bleedOverBBoxMax[i];
  468. }
  469. //The way the bounding box is extended onto the neighboring face
  470. // depends on which edge of neighboring face abuts with this one
  471. switch(sg_CubeNgh[faceIdx][i].m_Edge)
  472. {
  473. case CP_EDGE_LEFT:
  474. a_FilterExtents[neighborFace].Augment(0, bleedOverBBoxMin[i], 0);
  475. a_FilterExtents[neighborFace].Augment(bleedOverAmount[i], bleedOverBBoxMax[i], 0);
  476. break;
  477. case CP_EDGE_RIGHT:
  478. a_FilterExtents[neighborFace].Augment( (a_SrcSize-1), bleedOverBBoxMin[i], 0);
  479. a_FilterExtents[neighborFace].Augment( (a_SrcSize-1) - bleedOverAmount[i], bleedOverBBoxMax[i], 0);
  480. break;
  481. case CP_EDGE_TOP:
  482. a_FilterExtents[neighborFace].Augment(bleedOverBBoxMin[i], 0, 0);
  483. a_FilterExtents[neighborFace].Augment(bleedOverBBoxMax[i], bleedOverAmount[i], 0);
  484. break;
  485. case CP_EDGE_BOTTOM:
  486. a_FilterExtents[neighborFace].Augment(bleedOverBBoxMin[i], (a_SrcSize-1), 0);
  487. a_FilterExtents[neighborFace].Augment(bleedOverBBoxMax[i], (a_SrcSize-1) - bleedOverAmount[i], 0);
  488. break;
  489. }
  490. //clamp filter extents in non-center tap faces to remain within surface
  491. a_FilterExtents[neighborFace].ClampMin(0, 0, 0);
  492. a_FilterExtents[neighborFace].ClampMax(a_SrcSize-1, a_SrcSize-1, 0);
  493. }
  494. //If the bleed over amount bleeds past the adjacent face onto the opposite face
  495. // from the center tap face, then process the opposite face entirely for now.
  496. //Note that the cases in which this happens, what usually happens is that
  497. // more than one edge bleeds onto the opposite face, and the bounding box
  498. // encompasses the entire cube map face.
  499. if(bleedOverAmount[i] > a_SrcSize)
  500. {
  501. uint32 oppositeFaceIdx;
  502. //determine opposite face
  503. switch(faceIdx)
  504. {
  505. case CP_FACE_X_POS:
  506. oppositeFaceIdx = CP_FACE_X_NEG;
  507. break;
  508. case CP_FACE_X_NEG:
  509. oppositeFaceIdx = CP_FACE_X_POS;
  510. break;
  511. case CP_FACE_Y_POS:
  512. oppositeFaceIdx = CP_FACE_Y_NEG;
  513. break;
  514. case CP_FACE_Y_NEG:
  515. oppositeFaceIdx = CP_FACE_Y_POS;
  516. break;
  517. case CP_FACE_Z_POS:
  518. oppositeFaceIdx = CP_FACE_Z_NEG;
  519. break;
  520. default: // CP_FACE_Z_NEG:
  521. oppositeFaceIdx = CP_FACE_Z_POS;
  522. break;
  523. }
  524. //just encompass entire face for now
  525. a_FilterExtents[oppositeFaceIdx].Augment(0, 0, 0);
  526. a_FilterExtents[oppositeFaceIdx].Augment((a_SrcSize-1), (a_SrcSize-1), 0);
  527. }
  528. }
  529. }
  530. //--------------------------------------------------------------------------------------
  531. //ProcessFilterExtents
  532. // Process bounding box in each cube face
  533. //
  534. //--------------------------------------------------------------------------------------
  535. void CCubeMapProcessor::ProcessFilterExtents(float *a_CenterTapDir, float a_DotProdThresh,
  536. CBBoxInt32 *a_FilterExtents, CImageSurface *a_NormCubeMap, CImageSurface *a_SrcCubeMap,
  537. CP_ITYPE *a_DstVal, uint32 a_FilterType, bool a_bUseSolidAngleWeighting, float a_SpecularPower)
  538. {
  539. //accumulators are 64-bit floats in order to have the precision needed
  540. // over a summation of a large number of pixels
  541. double dstAccumFace[6][4];
  542. double weightAccumFace[6];
  543. const int32 nSrcChannels = a_SrcCubeMap[0].m_NumChannels;
  544. //norm cube map and srcCubeMap have same face width
  545. const int32 faceWidth = a_NormCubeMap[0].m_Width;
  546. //amount to add to pointer to move to next scanline in images
  547. const int32 normCubePitch = faceWidth * a_NormCubeMap[0].m_NumChannels;
  548. const int32 srcCubePitch = faceWidth * a_SrcCubeMap[0].m_NumChannels;
  549. //iterate over cubefaces
  550. for(int32 iFaceIdx=0; iFaceIdx<6; iFaceIdx++ )
  551. {
  552. //dest accum
  553. for(int32 k=0; k<m_NumChannels; k++)
  554. {
  555. dstAccumFace[iFaceIdx][k] = 0.0f;
  556. }
  557. weightAccumFace[iFaceIdx] = 0.0f;
  558. //if bbox is non empty
  559. if(a_FilterExtents[iFaceIdx].Empty() == false)
  560. {
  561. //pointers used to walk across the image surface to accumulate taps
  562. CP_ITYPE *normCubeRowStartPtr;
  563. CP_ITYPE *srcCubeRowStartPtr;
  564. int32 uStart, uEnd;
  565. int32 vStart, vEnd;
  566. uStart = a_FilterExtents[iFaceIdx].m_minCoord[0];
  567. vStart = a_FilterExtents[iFaceIdx].m_minCoord[1];
  568. uEnd = a_FilterExtents[iFaceIdx].m_maxCoord[0];
  569. vEnd = a_FilterExtents[iFaceIdx].m_maxCoord[1];
  570. normCubeRowStartPtr = a_NormCubeMap[iFaceIdx].m_ImgData + (a_NormCubeMap[iFaceIdx].m_NumChannels *
  571. ((vStart * faceWidth) + uStart) );
  572. srcCubeRowStartPtr = a_SrcCubeMap[iFaceIdx].m_ImgData + (a_SrcCubeMap[iFaceIdx].m_NumChannels *
  573. ((vStart * faceWidth) + uStart) );
  574. //note that <= is used to ensure filter extents always encompass at least one pixel if bbox is non empty
  575. for(int32 v = vStart; v <= vEnd; v++)
  576. {
  577. int32 normCubeRowWalk;
  578. int32 srcCubeRowWalk;
  579. normCubeRowWalk = 0;
  580. srcCubeRowWalk = 0;
  581. for(int32 u = uStart; u <= uEnd; u++)
  582. {
  583. //pointers used to walk across the image surface to accumulate taps
  584. CP_ITYPE *texelVect;
  585. CP_ITYPE tapDotProd; //dot product between center tap and current tap
  586. //pointer to direction in cube map associated with texel
  587. texelVect = (normCubeRowStartPtr + normCubeRowWalk);
  588. //check dot product to see if texel is within cone
  589. tapDotProd = VM_DOTPROD3(texelVect, a_CenterTapDir);
  590. if( tapDotProd >= a_DotProdThresh )
  591. {
  592. CP_ITYPE weight;
  593. //for now just weight all taps equally, but ideally
  594. // weight should be proportional to the solid angle of the tap
  595. if(a_bUseSolidAngleWeighting == true)
  596. { //solid angle stored in 4th channel of normalizer/solid angle cube map
  597. weight = *(texelVect+3);
  598. }
  599. else
  600. { //all taps equally weighted
  601. weight = 1.0f;
  602. }
  603. switch(a_FilterType)
  604. {
  605. case CP_FILTER_TYPE_COSINE_POWER:
  606. {
  607. if(tapDotProd > 0.0f)
  608. {
  609. weight *= pow(tapDotProd, a_SpecularPower) * tapDotProd;
  610. }
  611. else
  612. {
  613. weight = 0;
  614. }
  615. }
  616. break;
  617. case CP_FILTER_TYPE_CONE:
  618. case CP_FILTER_TYPE_ANGULAR_GAUSSIAN:
  619. {
  620. //weights are in same lookup table for both of these filter types
  621. weight *= m_FilterLUT[(int32)(tapDotProd * (m_NumFilterLUTEntries - 1))];
  622. }
  623. break;
  624. case CP_FILTER_TYPE_COSINE:
  625. {
  626. if(tapDotProd > 0.0f)
  627. {
  628. weight *= tapDotProd;
  629. }
  630. else
  631. {
  632. weight = 0.0f;
  633. }
  634. }
  635. break;
  636. case CP_FILTER_TYPE_DISC:
  637. default:
  638. break;
  639. }
  640. //iterate over channels
  641. for(int32 k=0; k<nSrcChannels; k++) //(aSrcCubeMap[iFaceIdx].m_NumChannels) //up to 4 channels
  642. {
  643. dstAccumFace[iFaceIdx][k] += weight * *(srcCubeRowStartPtr + srcCubeRowWalk);
  644. srcCubeRowWalk++;
  645. }
  646. weightAccumFace[iFaceIdx] += weight; //accumulate weight
  647. }
  648. else
  649. {
  650. //step across source pixel
  651. srcCubeRowWalk += nSrcChannels;
  652. }
  653. normCubeRowWalk += a_NormCubeMap[iFaceIdx].m_NumChannels;
  654. }
  655. normCubeRowStartPtr += normCubePitch;
  656. srcCubeRowStartPtr += srcCubePitch;
  657. }
  658. }
  659. }
  660. // reduction to 1 value from 6 faces
  661. double dstAccum[4];
  662. double weightAccum;
  663. //dest accum
  664. for(int32 k=0; k<m_NumChannels; k++)
  665. {
  666. dstAccum[k] = 0.0f;
  667. }
  668. weightAccum = 0.0f;
  669. for(int32 iFaceIdx=0; iFaceIdx<6; iFaceIdx++ )
  670. {
  671. //dest accum
  672. for(int32 k=0; k<m_NumChannels; k++)
  673. {
  674. dstAccum[k] += dstAccumFace[iFaceIdx][k];
  675. }
  676. weightAccum += weightAccumFace[iFaceIdx];
  677. }
  678. //divide through by weights if weight is non zero
  679. if(weightAccum != 0.0f)
  680. {
  681. for(int32 k=0; k<m_NumChannels; k++)
  682. {
  683. a_DstVal[k] = (float)(dstAccum[k] / weightAccum);
  684. }
  685. }
  686. else
  687. { //otherwise sample nearest
  688. CP_ITYPE *texelPtr;
  689. texelPtr = GetCubeMapTexelPtr(a_CenterTapDir, a_SrcCubeMap);
  690. for(int32 k=0; k<m_NumChannels; k++)
  691. {
  692. a_DstVal[k] = texelPtr[k];
  693. }
  694. }
  695. }
  696. //--------------------------------------------------------------------------------------
  697. // Fixup cube edges
  698. //
  699. // average texels on cube map faces across the edges
  700. //--------------------------------------------------------------------------------------
  701. void CCubeMapProcessor::FixupCubeEdges(CImageSurface *a_CubeMap, int32 a_FixupType, int32 a_FixupWidth)
  702. {
  703. int32 i, j, k;
  704. int32 face;
  705. int32 edge;
  706. int32 neighborFace;
  707. int32 neighborEdge;
  708. int32 nChannels = a_CubeMap[0].m_NumChannels;
  709. int32 size = a_CubeMap[0].m_Width;
  710. CPCubeMapNeighbor neighborInfo;
  711. CP_ITYPE* edgeStartPtr;
  712. CP_ITYPE* neighborEdgeStartPtr;
  713. int32 edgeWalk;
  714. int32 neighborEdgeWalk;
  715. //pointer walk to walk one texel away from edge in perpendicular direction
  716. int32 edgePerpWalk;
  717. int32 neighborEdgePerpWalk;
  718. //number of texels inward towards cubeface center to apply fixup to
  719. int32 fixupDist;
  720. int32 iFixup;
  721. // note that if functionality to filter across the three texels for each corner, then
  722. CP_ITYPE *cornerPtr[8][3]; //indexed by corner and face idx
  723. CP_ITYPE *faceCornerPtrs[4]; //corner pointers for face
  724. int32 cornerNumPtrs[8]; //indexed by corner and face idx
  725. int32 iCorner; //corner iterator
  726. int32 iFace; //iterator for faces
  727. int32 corner;
  728. //if there is no fixup, or fixup width = 0, do nothing
  729. if((a_FixupType == CP_FIXUP_NONE) ||
  730. (a_FixupWidth == 0) )
  731. {
  732. return;
  733. }
  734. //special case 1x1 cubemap, average face colors
  735. if( a_CubeMap[0].m_Width == 1 )
  736. {
  737. //iterate over channels
  738. for(k=0; k<nChannels; k++)
  739. {
  740. CP_ITYPE accum = 0.0f;
  741. //iterate over faces to accumulate face colors
  742. for(iFace=0; iFace<6; iFace++)
  743. {
  744. accum += *(a_CubeMap[iFace].m_ImgData + k);
  745. }
  746. //compute average over 6 face colors
  747. accum /= 6.0f;
  748. //iterate over faces to distribute face colors
  749. for(iFace=0; iFace<6; iFace++)
  750. {
  751. *(a_CubeMap[iFace].m_ImgData + k) = accum;
  752. }
  753. }
  754. return;
  755. }
  756. //iterate over corners
  757. for(iCorner = 0; iCorner < 8; iCorner++ )
  758. {
  759. cornerNumPtrs[iCorner] = 0;
  760. }
  761. //iterate over faces to collect list of corner texel pointers
  762. for(iFace=0; iFace<6; iFace++ )
  763. {
  764. //the 4 corner pointers for this face
  765. faceCornerPtrs[0] = a_CubeMap[iFace].m_ImgData;
  766. faceCornerPtrs[1] = a_CubeMap[iFace].m_ImgData + ( (size - 1) * nChannels );
  767. faceCornerPtrs[2] = a_CubeMap[iFace].m_ImgData + ( (size) * (size - 1) * nChannels );
  768. faceCornerPtrs[3] = a_CubeMap[iFace].m_ImgData + ( (((size) * (size - 1)) + (size - 1)) * nChannels );
  769. //iterate over face corners to collect cube corner pointers
  770. for(i=0; i<4; i++ )
  771. {
  772. corner = sg_CubeCornerList[iFace][i];
  773. cornerPtr[corner][ cornerNumPtrs[corner] ] = faceCornerPtrs[i];
  774. cornerNumPtrs[corner]++;
  775. }
  776. }
  777. //iterate over corners to average across corner tap values
  778. for(iCorner = 0; iCorner < 8; iCorner++ )
  779. {
  780. for(k=0; k<nChannels; k++)
  781. {
  782. CP_ITYPE cornerTapAccum;
  783. cornerTapAccum = 0.0f;
  784. //iterate over corner texels and average results
  785. for(i=0; i<3; i++ )
  786. {
  787. cornerTapAccum += *(cornerPtr[iCorner][i] + k);
  788. }
  789. //divide by 3 to compute average of corner tap values
  790. cornerTapAccum *= (1.0f / 3.0f);
  791. //iterate over corner texels and average results
  792. for(i=0; i<3; i++ )
  793. {
  794. *(cornerPtr[iCorner][i] + k) = cornerTapAccum;
  795. }
  796. }
  797. }
  798. //maximum width of fixup region is one half of the cube face size
  799. fixupDist = VM_MIN( a_FixupWidth, size / 2);
  800. //iterate over the twelve edges of the cube to average across edges
  801. for(i=0; i<12; i++)
  802. {
  803. face = sg_CubeEdgeList[i][0];
  804. edge = sg_CubeEdgeList[i][1];
  805. neighborInfo = sg_CubeNgh[face][edge];
  806. neighborFace = neighborInfo.m_Face;
  807. neighborEdge = neighborInfo.m_Edge;
  808. edgeStartPtr = a_CubeMap[face].m_ImgData;
  809. neighborEdgeStartPtr = a_CubeMap[neighborFace].m_ImgData;
  810. edgeWalk = 0;
  811. neighborEdgeWalk = 0;
  812. //amount to pointer to sample taps away from cube face
  813. edgePerpWalk = 0;
  814. neighborEdgePerpWalk = 0;
  815. //Determine walking pointers based on edge type
  816. // e.g. CP_EDGE_LEFT, CP_EDGE_RIGHT, CP_EDGE_TOP, CP_EDGE_BOTTOM
  817. switch(edge)
  818. {
  819. case CP_EDGE_LEFT:
  820. // no change to faceEdgeStartPtr
  821. edgeWalk = nChannels * size;
  822. edgePerpWalk = nChannels;
  823. break;
  824. case CP_EDGE_RIGHT:
  825. edgeStartPtr += (size - 1) * nChannels;
  826. edgeWalk = nChannels * size;
  827. edgePerpWalk = -nChannels;
  828. break;
  829. case CP_EDGE_TOP:
  830. // no change to faceEdgeStartPtr
  831. edgeWalk = nChannels;
  832. edgePerpWalk = nChannels * size;
  833. break;
  834. case CP_EDGE_BOTTOM:
  835. edgeStartPtr += (size) * (size - 1) * nChannels;
  836. edgeWalk = nChannels;
  837. edgePerpWalk = -(nChannels * size);
  838. break;
  839. }
  840. //For certain types of edge abutments, the neighbor edge walk needs to
  841. // be flipped: the cases are
  842. // if a left edge mates with a left or bottom edge on the neighbor
  843. // if a top edge mates with a top or right edge on the neighbor
  844. // if a right edge mates with a right or top edge on the neighbor
  845. // if a bottom edge mates with a bottom or left edge on the neighbor
  846. //Seeing as the edges are enumerated as follows
  847. // left =0
  848. // right =1
  849. // top =2
  850. // bottom =3
  851. //
  852. //If the edge enums are the same, or the sum of the enums == 3,
  853. // the neighbor edge walk needs to be flipped
  854. if( (edge == neighborEdge) || ((edge + neighborEdge) == 3) )
  855. { //swapped direction neighbor edge walk
  856. switch(neighborEdge)
  857. {
  858. case CP_EDGE_LEFT: //start at lower left and walk up
  859. neighborEdgeStartPtr += (size - 1) * (size) * nChannels;
  860. neighborEdgeWalk = -(nChannels * size);
  861. neighborEdgePerpWalk = nChannels;
  862. break;
  863. case CP_EDGE_RIGHT: //start at lower right and walk up
  864. neighborEdgeStartPtr += ((size - 1)*(size) + (size - 1)) * nChannels;
  865. neighborEdgeWalk = -(nChannels * size);
  866. neighborEdgePerpWalk = -nChannels;
  867. break;
  868. case CP_EDGE_TOP: //start at upper right and walk left
  869. neighborEdgeStartPtr += (size - 1) * nChannels;
  870. neighborEdgeWalk = -nChannels;
  871. neighborEdgePerpWalk = (nChannels * size);
  872. break;
  873. case CP_EDGE_BOTTOM: //start at lower right and walk left
  874. neighborEdgeStartPtr += ((size - 1)*(size) + (size - 1)) * nChannels;
  875. neighborEdgeWalk = -nChannels;
  876. neighborEdgePerpWalk = -(nChannels * size);
  877. break;
  878. }
  879. }
  880. else
  881. { //swapped direction neighbor edge walk
  882. switch(neighborEdge)
  883. {
  884. case CP_EDGE_LEFT: //start at upper left and walk down
  885. //no change to neighborEdgeStartPtr for this case since it points
  886. // to the upper left corner already
  887. neighborEdgeWalk = nChannels * size;
  888. neighborEdgePerpWalk = nChannels;
  889. break;
  890. case CP_EDGE_RIGHT: //start at upper right and walk down
  891. neighborEdgeStartPtr += (size - 1) * nChannels;
  892. neighborEdgeWalk = nChannels * size;
  893. neighborEdgePerpWalk = -nChannels;
  894. break;
  895. case CP_EDGE_TOP: //start at upper left and walk left
  896. //no change to neighborEdgeStartPtr for this case since it points
  897. // to the upper left corner already
  898. neighborEdgeWalk = nChannels;
  899. neighborEdgePerpWalk = (nChannels * size);
  900. break;
  901. case CP_EDGE_BOTTOM: //start at lower left and walk left
  902. neighborEdgeStartPtr += (size) * (size - 1) * nChannels;
  903. neighborEdgeWalk = nChannels;
  904. neighborEdgePerpWalk = -(nChannels * size);
  905. break;
  906. }
  907. }
  908. //Perform edge walk, to average across the 12 edges and smoothly propagate change to
  909. //nearby neighborhood
  910. //step ahead one texel on edge
  911. edgeStartPtr += edgeWalk;
  912. neighborEdgeStartPtr += neighborEdgeWalk;
  913. // note that this loop does not process the corner texels, since they have already been
  914. // averaged across faces across earlier
  915. for(j=1; j<(size - 1); j++)
  916. {
  917. //for each set of taps along edge, average them
  918. // and rewrite the results into the edges
  919. for(k = 0; k<nChannels; k++)
  920. {
  921. CP_ITYPE edgeTap, neighborEdgeTap, avgTap; //edge tap, neighborEdgeTap and the average of the two
  922. CP_ITYPE edgeTapDev, neighborEdgeTapDev;
  923. edgeTap = *(edgeStartPtr + k);
  924. neighborEdgeTap = *(neighborEdgeStartPtr + k);
  925. //compute average of tap intensity values
  926. avgTap = 0.5f * (edgeTap + neighborEdgeTap);
  927. //propagate average of taps to edge taps
  928. (*(edgeStartPtr + k)) = avgTap;
  929. (*(neighborEdgeStartPtr + k)) = avgTap;
  930. edgeTapDev = edgeTap - avgTap;
  931. neighborEdgeTapDev = neighborEdgeTap - avgTap;
  932. //iterate over taps in direction perpendicular to edge, and
  933. // adjust intensity values gradualy to obscure change in intensity values of
  934. // edge averaging.
  935. for(iFixup = 1; iFixup < fixupDist; iFixup++)
  936. {
  937. //fractional amount to apply change in tap intensity along edge to taps
  938. // in a perpendicular direction to edge
  939. CP_ITYPE fixupFrac = (CP_ITYPE)(fixupDist - iFixup) / (CP_ITYPE)(fixupDist);
  940. CP_ITYPE fixupWeight = 0.0f;
  941. switch(a_FixupType )
  942. {
  943. case CP_FIXUP_PULL_LINEAR:
  944. {
  945. fixupWeight = fixupFrac;
  946. }
  947. break;
  948. case CP_FIXUP_PULL_HERMITE:
  949. {
  950. //hermite spline interpolation between 1 and 0 with both pts derivatives = 0
  951. // e.g. smooth step
  952. // the full formula for hermite interpolation is:
  953. //
  954. // [ 2 -2 1 1 ][ p0 ]
  955. // [t^3 t^2 t 1 ][ -3 3 -2 -1 ][ p1 ]
  956. // [ 0 0 1 0 ][ d0 ]
  957. // [ 1 0 0 0 ][ d1 ]
  958. //
  959. // Where p0 and p1 are the point locations and d0, and d1 are their respective derivatives
  960. // t is the parameteric coordinate used to specify an interpoltion point on the spline
  961. // and ranges from 0 to 1.
  962. // if p0 = 0 and p1 = 1, and d0 and d1 = 0, the interpolation reduces to
  963. //
  964. // p(t) = - 2t^3 + 3t^2
  965. fixupWeight = ((-2.0f * fixupFrac + 3.0f) * fixupFrac * fixupFrac);
  966. }
  967. break;
  968. case CP_FIXUP_AVERAGE_LINEAR:
  969. {
  970. fixupWeight = fixupFrac;
  971. //perform weighted average of edge tap value and current tap
  972. // fade off weight linearly as a function of distance from edge
  973. edgeTapDev =
  974. (*(edgeStartPtr + (iFixup * edgePerpWalk) + k)) - avgTap;
  975. neighborEdgeTapDev =
  976. (*(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k)) - avgTap;
  977. }
  978. break;
  979. case CP_FIXUP_AVERAGE_HERMITE:
  980. {
  981. fixupWeight = ((-2.0f * fixupFrac + 3.0f) * fixupFrac * fixupFrac);
  982. //perform weighted average of edge tap value and current tap
  983. // fade off weight using hermite spline with distance from edge
  984. // as parametric coordinate
  985. edgeTapDev =
  986. (*(edgeStartPtr + (iFixup * edgePerpWalk) + k)) - avgTap;
  987. neighborEdgeTapDev =
  988. (*(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k)) - avgTap;
  989. }
  990. break;
  991. }
  992. // vary intensity of taps within fixup region toward edge values to hide changes made to edge taps
  993. *(edgeStartPtr + (iFixup * edgePerpWalk) + k) -= (fixupWeight * edgeTapDev);
  994. *(neighborEdgeStartPtr + (iFixup * neighborEdgePerpWalk) + k) -= (fixupWeight * neighborEdgeTapDev);
  995. }
  996. }
  997. edgeStartPtr += edgeWalk;
  998. neighborEdgeStartPtr += neighborEdgeWalk;
  999. }
  1000. }
  1001. }
  1002. //--------------------------------------------------------------------------------------
  1003. //Constructor
  1004. //--------------------------------------------------------------------------------------
  1005. CCubeMapProcessor::CCubeMapProcessor(void)
  1006. {
  1007. int32 i;
  1008. //If zero filtering threads are specified then all filtering is performed in the
  1009. // process that called the cubemap filtering routines.
  1010. //Otherwise, the filtering is performed in separate filtering threads that cubemap generates
  1011. m_NumFilterThreads = CP_INITIAL_NUM_FILTER_THREADS;
  1012. //clear all threads
  1013. for(i=0; i<CP_MAX_FILTER_THREADS; i++ )
  1014. {
  1015. m_bThreadInitialized[i] = false;
  1016. m_ThreadID[i] = 0;
  1017. }
  1018. m_InputSize = 0;
  1019. m_OutputSize = 0;
  1020. m_NumMipLevels = 0;
  1021. m_NumChannels = 0;
  1022. m_NumFilterLUTEntries = 0;
  1023. m_FilterLUT = NULL;
  1024. m_shutdownWorkerThreadSignal = false;
  1025. //Constructors are automatically called for m_InputSurface and m_OutputSurface arrays
  1026. }
  1027. //--------------------------------------------------------------------------------------
  1028. //destructor
  1029. //--------------------------------------------------------------------------------------
  1030. CCubeMapProcessor::~CCubeMapProcessor()
  1031. {
  1032. Clear();
  1033. }
  1034. //--------------------------------------------------------------------------------------
  1035. // Stop any currently running threads, and clear all allocated data from cube map
  1036. // processor.
  1037. //
  1038. // To use the cube map processor after calling Clear(....), you need to call Init(....)
  1039. // again
  1040. //--------------------------------------------------------------------------------------
  1041. void CCubeMapProcessor::Clear(void)
  1042. {
  1043. int32 i, j;
  1044. TerminateActiveThreads();
  1045. for(i=0; i<CP_MAX_FILTER_THREADS; i++ )
  1046. {
  1047. m_bThreadInitialized[i] = false;
  1048. }
  1049. m_InputSize = 0;
  1050. m_OutputSize = 0;
  1051. m_NumMipLevels = 0;
  1052. m_NumChannels = 0;
  1053. //Iterate over faces for input images
  1054. for (j = 0; j < CP_MAX_MIPLEVELS; j++)
  1055. {
  1056. for (i = 0; i < 6; i++)
  1057. {
  1058. m_InputSurface[j][i].Clear();
  1059. }
  1060. }
  1061. //Iterate over mip chain, and allocate memory for mip-chain
  1062. for(j=0; j<CP_MAX_MIPLEVELS; j++)
  1063. {
  1064. //Iterate over faces for output images
  1065. for(i=0; i<6; i++)
  1066. {
  1067. m_OutputSurface[j][i].Clear();
  1068. }
  1069. }
  1070. m_NumFilterLUTEntries = 0;
  1071. CP_SAFE_DELETE_ARRAY( m_FilterLUT );
  1072. }
  1073. //--------------------------------------------------------------------------------------
  1074. // Terminates execution of active threads
  1075. //
  1076. //--------------------------------------------------------------------------------------
  1077. void CCubeMapProcessor::TerminateActiveThreads(void)
  1078. {
  1079. int32 i;
  1080. //signal all the threads to terminate
  1081. m_shutdownWorkerThreadSignal = true;
  1082. for(i=0; i<CP_MAX_FILTER_THREADS; i++)
  1083. {
  1084. if( m_bThreadInitialized[i] == true)
  1085. {
  1086. m_ThreadHandle[i].join();
  1087. m_bThreadInitialized[i] = false;
  1088. m_Status = CP_STATUS_FILTER_TERMINATED;
  1089. }
  1090. }
  1091. //reset the shutdown signal
  1092. m_shutdownWorkerThreadSignal = false;
  1093. }
  1094. //--------------------------------------------------------------------------------------
  1095. //Init cube map processor
  1096. //
  1097. //--------------------------------------------------------------------------------------
  1098. void CCubeMapProcessor::Init(int32 a_InputSize, int32 a_OutputSize, int32 a_MaxNumMipLevels, int32 a_NumChannels)
  1099. {
  1100. int32 i, j;
  1101. int32 mipLevelSize;
  1102. m_Status = CP_STATUS_READY;
  1103. //since input is being modified, terminate any active filtering threads
  1104. TerminateActiveThreads();
  1105. m_InputSize = a_InputSize;
  1106. m_OutputSize = a_OutputSize;
  1107. m_NumChannels = a_NumChannels;
  1108. m_NumMipLevels = a_MaxNumMipLevels;
  1109. //first miplevel size
  1110. mipLevelSize = m_OutputSize;
  1111. //Iterate over mip chain, and init CImageSurfaces for mip-chain
  1112. for(j=0; j<a_MaxNumMipLevels; j++)
  1113. {
  1114. //Iterate over faces
  1115. for(i=0; i<6; i++)
  1116. {
  1117. m_InputSurface[j][i].Init(mipLevelSize, mipLevelSize, a_NumChannels);
  1118. m_OutputSurface[j][i].Init(mipLevelSize, mipLevelSize, a_NumChannels);
  1119. }
  1120. //next mip level is half size
  1121. mipLevelSize >>= 1;
  1122. //terminate if mip chain becomes too small
  1123. if(mipLevelSize == 0)
  1124. {
  1125. return;
  1126. }
  1127. }
  1128. }
  1129. //--------------------------------------------------------------------------------------
  1130. //Copy and convert cube map face data from an external image/surface into this object
  1131. //
  1132. // a_FaceIdx = a value 0 to 5 speciying which face to copy into (one of the CP_FACE_? )
  1133. // a_Level = mip level to copy into
  1134. // a_SrcType = data type of image being copyed from (one of the CP_TYPE_? types)
  1135. // a_SrcNumChannels = number of channels of the image being copied from (usually 1 to 4)
  1136. // a_SrcPitch = number of bytes per row of the source image being copied from
  1137. // a_SrcDataPtr = pointer to the image data to copy from
  1138. // a_Degamma = original gamma level of input image to undo by degamma
  1139. // a_Scale = scale to apply to pixel values after degamma (in linear space)
  1140. //--------------------------------------------------------------------------------------
  1141. void CCubeMapProcessor::SetInputFaceData(int32 a_FaceIdx, int32 a_MipIdx, int32 a_SrcType, int32 a_SrcNumChannels,
  1142. int32 a_SrcPitch, void *a_SrcDataPtr, float a_MaxClamp, float a_Degamma, float a_Scale)
  1143. {
  1144. //since input is being modified, terminate any active filtering threads
  1145. TerminateActiveThreads();
  1146. m_InputSurface[a_MipIdx][a_FaceIdx].SetImageDataClampDegammaScale( a_SrcType, a_SrcNumChannels, a_SrcPitch,
  1147. a_SrcDataPtr, a_MaxClamp, a_Degamma, a_Scale );
  1148. }
  1149. //--------------------------------------------------------------------------------------
  1150. //Copy and convert cube map face data from this object into an external image/surface
  1151. //
  1152. // a_FaceIdx = a value 0 to 5 speciying which face to copy into (one of the CP_FACE_? )
  1153. // a_Level = mip level to copy into
  1154. // a_DstType = data type of image to copy to (one of the CP_TYPE_? types)
  1155. // a_DstNumChannels = number of channels of the image to copy to (usually 1 to 4)
  1156. // a_DstPitch = number of bytes per row of the dest image to copy to
  1157. // a_DstDataPtr = pointer to the image data to copy to
  1158. // a_Scale = scale to apply to pixel values (in linear space) before gamma for output
  1159. // a_Gamma = gamma level to apply to pixels after scaling
  1160. //--------------------------------------------------------------------------------------
  1161. void CCubeMapProcessor::GetInputFaceData(int32 a_FaceIdx, int32 a_MipIdx, int32 a_DstType, int32 a_DstNumChannels,
  1162. int32 a_DstPitch, void *a_DstDataPtr, float a_Scale, float a_Gamma)
  1163. {
  1164. m_InputSurface[a_MipIdx][a_FaceIdx].GetImageDataScaleGamma( a_DstType, a_DstNumChannels, a_DstPitch,
  1165. a_DstDataPtr, a_Scale, a_Gamma );
  1166. }
  1167. //--------------------------------------------------------------------------------------
  1168. //ChannelSwapInputFaceData
  1169. // swizzle data in first 4 channels for input faces
  1170. //
  1171. //--------------------------------------------------------------------------------------
  1172. void CCubeMapProcessor::ChannelSwapInputFaceData(int32 a_Channel0Src, int32 a_Channel1Src,
  1173. int32 a_Channel2Src, int32 a_Channel3Src )
  1174. {
  1175. int32 iMip, iFace, u, v, k;
  1176. int32 size;
  1177. CP_ITYPE texelData[4];
  1178. int32 channelSrcArray[4];
  1179. //since input is being modified, terminate any active filtering threads
  1180. TerminateActiveThreads();
  1181. size = m_InputSize;
  1182. channelSrcArray[0] = a_Channel0Src;
  1183. channelSrcArray[1] = a_Channel1Src;
  1184. channelSrcArray[2] = a_Channel2Src;
  1185. channelSrcArray[3] = a_Channel3Src;
  1186. //Iterate over mips and faces for input images
  1187. for (iMip = 0; iMip < m_NumMipLevels; iMip++)
  1188. {
  1189. for (iFace = 0; iFace < 6; iFace++)
  1190. {
  1191. for (v = 0; v < size; v++)
  1192. {
  1193. for (u = 0; u < size; u++)
  1194. {
  1195. //get channel data
  1196. for (k = 0; k < m_NumChannels; k++)
  1197. {
  1198. texelData[k] = *(m_InputSurface[iMip][iFace].GetSurfaceTexelPtr(u, v) + k);
  1199. }
  1200. //repack channel data accoring to swizzle information
  1201. for (k = 0; k < m_NumChannels; k++)
  1202. {
  1203. *(m_InputSurface[iMip][iFace].GetSurfaceTexelPtr(u, v) + k) =
  1204. texelData[channelSrcArray[k]];
  1205. }
  1206. }
  1207. }
  1208. }
  1209. // prepare size for next mip level
  1210. size >>= 1;
  1211. }
  1212. }
  1213. //--------------------------------------------------------------------------------------
  1214. //ChannelSwapOutputFaceData
  1215. // swizzle data in first 4 channels for input faces
  1216. //
  1217. //--------------------------------------------------------------------------------------
  1218. void CCubeMapProcessor::ChannelSwapOutputFaceData(int32 a_Channel0Src, int32 a_Channel1Src,
  1219. int32 a_Channel2Src, int32 a_Channel3Src )
  1220. {
  1221. int32 iFace, iMipLevel, u, v, k;
  1222. CP_ITYPE texelData[4];
  1223. int32 channelSrcArray[4];
  1224. //since output is being modified, terminate any active filtering threads
  1225. TerminateActiveThreads();
  1226. channelSrcArray[0] = a_Channel0Src;
  1227. channelSrcArray[1] = a_Channel1Src;
  1228. channelSrcArray[2] = a_Channel2Src;
  1229. channelSrcArray[3] = a_Channel3Src;
  1230. //Iterate over faces for input images
  1231. for(iMipLevel=0; iMipLevel<m_NumMipLevels; iMipLevel++ )
  1232. {
  1233. for(iFace=0; iFace<6; iFace++)
  1234. {
  1235. for(v=0; v<m_OutputSurface[iMipLevel][iFace].m_Height; v++ )
  1236. {
  1237. for(u=0; u<m_OutputSurface[iMipLevel][iFace].m_Width; u++ )
  1238. {
  1239. //get channel data
  1240. for(k=0; k<m_NumChannels; k++)
  1241. {
  1242. texelData[k] = *(m_OutputSurface[iMipLevel][iFace].GetSurfaceTexelPtr(u, v) + k);
  1243. }
  1244. //repack channel data accoring to swizzle information
  1245. for(k=0; k<m_NumChannels; k++)
  1246. {
  1247. *(m_OutputSurface[iMipLevel][iFace].GetSurfaceTexelPtr(u, v) + k) = texelData[ channelSrcArray[k] ];
  1248. }
  1249. }
  1250. }
  1251. }
  1252. }
  1253. }
  1254. //--------------------------------------------------------------------------------------
  1255. //Copy and convert cube map face data out of this class into an external image/surface
  1256. //
  1257. // a_FaceIdx = a value 0 to 5 specifying which face to copy from (one of the CP_FACE_? )
  1258. // a_Level = mip level to copy from
  1259. // a_DstType = data type of image to copyed into (one of the CP_TYPE_? types)
  1260. // a_DstNumChannels = number of channels of the image to copyed into (usually 1 to 4)
  1261. // a_DstPitch = number of bytes per row of the source image to copyed into
  1262. // a_DstDataPtr = pointer to the image data to copyed into
  1263. // a_Scale = scale to apply to pixel values (in linear space) before gamma for output
  1264. // a_Gamma = gamma level to apply to pixels after scaling
  1265. //--------------------------------------------------------------------------------------
  1266. void CCubeMapProcessor::GetOutputFaceData(int32 a_FaceIdx, int32 a_Level, int32 a_DstType,
  1267. int32 a_DstNumChannels, int32 a_DstPitch, void *a_DstDataPtr, float a_Scale, float a_Gamma )
  1268. {
  1269. switch(a_DstType)
  1270. {
  1271. case CP_VAL_UNORM8:
  1272. case CP_VAL_UNORM8_BGRA:
  1273. case CP_VAL_UNORM16:
  1274. case CP_VAL_FLOAT16:
  1275. case CP_VAL_FLOAT32:
  1276. {
  1277. m_OutputSurface[a_Level][a_FaceIdx].GetImageDataScaleGamma( a_DstType, a_DstNumChannels,
  1278. a_DstPitch, a_DstDataPtr, a_Scale, a_Gamma );
  1279. }
  1280. break;
  1281. default:
  1282. break;
  1283. }
  1284. }
  1285. //--------------------------------------------------------------------------------------
  1286. //Cube map filtering and mip chain generation.
  1287. // the cube map filtereing is specified using a number of parameters:
  1288. // Filtering per miplevel is specified using 2D cone angle (in degrees) that
  1289. // indicates the region of the hemisphere to filter over for each tap.
  1290. //
  1291. // Note that the top mip level is also a filtered version of the original input images
  1292. // as well in order to create mip chains for diffuse environment illumination.
  1293. // The cone angle for the top level is specified by a_BaseAngle. This can be used to
  1294. // generate mipchains used to store the resutls of preintegration across the hemisphere.
  1295. //
  1296. // Then the mip angle used to genreate the next level of the mip chain from the first level
  1297. // is a_InitialMipAngle
  1298. //
  1299. // The angle for the subsequent levels of the mip chain are specified by their parents
  1300. // filtering angle and a per-level scale and bias
  1301. // newAngle = oldAngle * a_MipAnglePerLevelScale;
  1302. //
  1303. //--------------------------------------------------------------------------------------
  1304. static float ComputeBaseFilterAngle(float cosinePower)
  1305. {
  1306. // Find angle for which: cos(a) ^ cosinePower = epsilon
  1307. const float epsilon = 0.000001f;
  1308. float angle = acosf(powf(epsilon, 1.0f / cosinePower));
  1309. angle *= 180.0f / CP_PI;
  1310. angle *= 2.0f;
  1311. return angle;
  1312. }
  1313. inline float RadicalInverse2(uint32 bits)
  1314. {
  1315. // Van der Corput radical inverse in base 2
  1316. // Reverse bits
  1317. bits = (bits << 16u) | (bits >> 16u);
  1318. bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
  1319. bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
  1320. bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
  1321. bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
  1322. return float(bits) * 2.3283064365386963e-10f; // float(bits) * 2^-32
  1323. }
  1324. inline void HammersleySequence(uint32 sampleIndex, uint32 sampleCount, float* vXi)
  1325. {
  1326. vXi[0] = float(sampleIndex) / float(sampleCount);
  1327. vXi[1] = RadicalInverse2(sampleIndex);
  1328. }
  1329. void ImportanceSampleGGX(float* vXi, float alphaRoughnessSqr, float* vNormal, float* vOut)
  1330. {
  1331. float phi = 2.0f * CP_PI * vXi[0];
  1332. float cosTheta = sqrtf((1.0f - vXi[1]) / ( 1.0f + (alphaRoughnessSqr - 1.0f) * vXi[1]));
  1333. float sinTheta = sqrtf(1.0f - cosTheta * cosTheta);
  1334. float vH[3];
  1335. vH[0] = sinTheta * cosf(phi);
  1336. vH[1] = sinTheta * sinf(phi);
  1337. vH[2] = cosTheta;
  1338. float vUpVectorX[3] = {1.0f, 0.0f, 0.0f};
  1339. float vUpVectorZ[3] = {0.0f, 0.0f, 1.0f};
  1340. float vTangentX[3];
  1341. float vTangentY[3];
  1342. float vTempVec[3];
  1343. // Build local frame
  1344. VM_XPROD3(vTempVec, fabs(vNormal[2]) < 0.999f ? vUpVectorZ : vUpVectorX, vNormal);
  1345. VM_NORM3(vTangentX, vTempVec);
  1346. VM_XPROD3(vTangentY, vNormal, vTangentX);
  1347. // Convert from tangent to world space
  1348. vOut[0] = vTangentX[0] * vH[0] + vTangentY[0] * vH[1] + vNormal[0] * vH[2];
  1349. vOut[1] = vTangentX[1] * vH[0] + vTangentY[1] * vH[1] + vNormal[1] * vH[2];
  1350. vOut[2] = vTangentX[2] * vH[0] + vTangentY[2] * vH[1] + vNormal[2] * vH[2];
  1351. }
  1352. void CCubeMapProcessor::FilterCubeSurfacesGGX(int32 a_MipIdx, int32 a_SampleCount, int32 a_FaceIdxStart, int32 a_FaceIdxEnd, int32 a_ThreadIdx)
  1353. {
  1354. // we don't want to convolve mip0 as it's theoretically a perfect mirror with zero roughness
  1355. AZ_Assert(a_MipIdx > 0, "FilterCubeSurfacesGGX called for mip 0");
  1356. CImageSurface* dstCubeMap = m_OutputSurface[a_MipIdx];
  1357. const uint32 numChannels = VM_MIN(m_NumChannels, 4);
  1358. const int32 dstSize = dstCubeMap[0].m_Width;
  1359. const uint32 maxMipIndex = (uint32)m_NumMipLevels - 1;
  1360. // Convert smoothness to roughness (needs to match shader code)
  1361. // The roughness value in microfacet calculations (called "alpha" in the literature) does not give perceptually
  1362. // linear results. Disney found that squaring the roughness value before using it in microfacet equations causes
  1363. // the user-provided roughness parameter to be more perceptually linear.
  1364. // See Burley's Disney PBR: https://pdfs.semanticscholar.org/eeee/3b125c09044d3e2f58ed0e4b1b66a677886d.pdf
  1365. float smoothness = VM_MAX(1.0f - ((float)a_MipIdx / maxMipIndex), 0.0f);
  1366. float perceptualRoughness = 1.0f - smoothness;
  1367. float alphaRoughness = perceptualRoughness * perceptualRoughness;
  1368. float alphaRoughnessSqr = alphaRoughness * alphaRoughness;
  1369. //thread progress
  1370. m_ThreadProgress[a_ThreadIdx].m_StartFace = a_FaceIdxStart;
  1371. m_ThreadProgress[a_ThreadIdx].m_EndFace = a_FaceIdxEnd;
  1372. CP_ITYPE* sourceTexelA = new CP_ITYPE[numChannels];
  1373. CP_ITYPE* sourceTexelB = new CP_ITYPE[numChannels];
  1374. //process required faces
  1375. for(int32 iCubeFace = a_FaceIdxStart; iCubeFace <= a_FaceIdxEnd && !m_shutdownWorkerThreadSignal; iCubeFace++)
  1376. {
  1377. //iterate over dst cube map face texel
  1378. for(int32 v = 0; v < dstSize && !m_shutdownWorkerThreadSignal; v++)
  1379. {
  1380. CP_ITYPE *texelPtr = dstCubeMap[iCubeFace].m_ImgData + v * dstCubeMap[iCubeFace].m_NumChannels * dstSize;
  1381. m_ThreadProgress[a_ThreadIdx].m_CurrentFace = iCubeFace;
  1382. m_ThreadProgress[a_ThreadIdx].m_CurrentRow = v;
  1383. for (int32 u = 0; u < dstSize && !m_shutdownWorkerThreadSignal; u++)
  1384. {
  1385. float color[4] = { 0 };
  1386. float totalWeight = 0;
  1387. float vH[3];
  1388. float vL[3];
  1389. //assume normal and view vector to be vCenterTapDir
  1390. float vCenterTapDir[3];
  1391. TexelCoordToVect(iCubeFace, (float)u, (float)v, dstSize, vCenterTapDir);
  1392. for (uint32 i = 0; i < (uint32)a_SampleCount && !m_shutdownWorkerThreadSignal; i++)
  1393. {
  1394. float vXi[2];
  1395. HammersleySequence(i, a_SampleCount, vXi);
  1396. ImportanceSampleGGX(vXi, alphaRoughnessSqr, vCenterTapDir, vH);
  1397. float fVdotH = VM_DOTPROD3(vCenterTapDir, vH);
  1398. vL[0] = 2 * fVdotH * vH[0] - vCenterTapDir[0];
  1399. vL[1] = 2 * fVdotH * vH[1] - vCenterTapDir[1];
  1400. vL[2] = 2 * fVdotH * vH[2] - vCenterTapDir[2];
  1401. float fNdotL = VM_DOTPROD3(vCenterTapDir, vL);
  1402. if (fNdotL > 0)
  1403. {
  1404. //compute specular D term (must match shader BRDF)
  1405. float dh = alphaRoughnessSqr / (CP_PI * powf(fVdotH * fVdotH * (alphaRoughnessSqr - 1.0f) + 1.0f, 2.0f));
  1406. //calculate the PDF (probability distribution) of the sample to determine the best mip level.
  1407. //lower probability sample directions use a smaller mip so they cover a larger sample area, which will
  1408. //blend the sample values and reduce artifacts
  1409. float pdf = dh * fVdotH / (4.0f * fVdotH);
  1410. float solidAngleTexel = 4.0f * CP_PI / (6.0f * m_InputSurface[0][0].m_Width * m_InputSurface[0][0].m_Width);
  1411. float solidAngleSample = 1.0f / (a_SampleCount * pdf);
  1412. float mip = 0.5f * log2f(solidAngleSample / solidAngleTexel) + 1.0f;
  1413. //determine surrounding mip levels
  1414. uint32 mipA = static_cast<uint32>(floor(mip));
  1415. uint32 mipB = mipA + 1;
  1416. float lerp = 0.0f;
  1417. VM_CLAMP(lerp, mip - mipA, 0.0f, 1.0f);
  1418. if (mipA >= maxMipIndex)
  1419. {
  1420. mipA = mipB = maxMipIndex;
  1421. lerp = 0.0f;
  1422. }
  1423. //retrieve bilinear filtered texel from each mip
  1424. GetCubeMapTexelBilinear(vL, m_InputSurface[mipA], sourceTexelA, numChannels);
  1425. GetCubeMapTexelBilinear(vL, m_InputSurface[mipB], sourceTexelB, numChannels);
  1426. //interpolate each channel value from the two bilinear mip samples for trilinear filtering
  1427. for (uint32 k = 0; k < numChannels; k++)
  1428. {
  1429. color[k] += (((1.0f - lerp) * sourceTexelA[k]) + (lerp * sourceTexelB[k])) * fNdotL;
  1430. }
  1431. totalWeight += fNdotL;
  1432. }
  1433. }
  1434. for (uint32 k = 0; k < numChannels; k++)
  1435. {
  1436. texelPtr[k] = color[k] / totalWeight;
  1437. }
  1438. texelPtr += dstCubeMap[iCubeFace].m_NumChannels;
  1439. }
  1440. }
  1441. }
  1442. delete[] sourceTexelA;
  1443. delete[] sourceTexelB;
  1444. }
  1445. void CCubeMapProcessor::FilterCubeMapMipChain(float a_BaseFilterAngle, float a_InitialMipAngle, float a_MipAnglePerLevelScale,
  1446. int32 a_FilterType, int32 a_FixupType, int32 a_FixupWidth, bool a_bUseSolidAngle, float a_GlossScale, float a_GlossBias,
  1447. int32 a_SampleCountGGX)
  1448. {
  1449. int32 i;
  1450. float coneAngle;
  1451. if(a_FilterType == CP_FILTER_TYPE_COSINE_POWER || a_FilterType == CP_FILTER_TYPE_GGX)
  1452. {
  1453. // Don't filter top mipmap
  1454. a_BaseFilterAngle = 0;
  1455. }
  1456. //Build filter lookup tables based on the source miplevel size
  1457. PrecomputeFilterLookupTables(a_FilterType, m_InputSurface[0][0].m_Width, a_BaseFilterAngle);
  1458. //initialize thread progress
  1459. m_ThreadProgress[0].m_CurrentMipLevel = 0;
  1460. m_ThreadProgress[0].m_CurrentRow = 0;
  1461. m_ThreadProgress[0].m_CurrentFace = 0;
  1462. //Filter the top mip level (initial filtering used for diffuse or blurred specular lighting )
  1463. FilterCubeSurfaces(m_InputSurface[0], m_OutputSurface[0], a_BaseFilterAngle, a_FilterType, a_bUseSolidAngle,
  1464. 0, //start at face 0
  1465. 5, //end at face 5
  1466. 0); //thread 0 is processing
  1467. m_ThreadProgress[0].m_CurrentMipLevel = 1;
  1468. m_ThreadProgress[0].m_CurrentRow = 0;
  1469. m_ThreadProgress[0].m_CurrentFace = 0;
  1470. FixupCubeEdges(m_OutputSurface[0], a_FixupType, a_FixupWidth);
  1471. //Cone angle start (for generating subsequent mip levels)
  1472. coneAngle = a_InitialMipAngle;
  1473. //generate subsequent mip levels
  1474. for(i=0; i<(m_NumMipLevels-1) && !m_shutdownWorkerThreadSignal; i++)
  1475. {
  1476. m_ThreadProgress[0].m_CurrentMipLevel = i+1;
  1477. m_ThreadProgress[0].m_CurrentRow = 0;
  1478. m_ThreadProgress[0].m_CurrentFace = 0;
  1479. if (a_FilterType == CP_FILTER_TYPE_GGX)
  1480. {
  1481. FilterCubeSurfacesGGX(i + 1,
  1482. a_SampleCountGGX,
  1483. 0, //start at face 0
  1484. 5, //end at face 5
  1485. 0 //thread 0 is processing
  1486. );
  1487. }
  1488. else
  1489. {
  1490. CImageSurface* srcCubeImage = m_OutputSurface[i];
  1491. float specPow = 1.0f;
  1492. if(a_FilterType == CP_FILTER_TYPE_COSINE_POWER)
  1493. {
  1494. uint32 numMipsForGloss = m_NumMipLevels - 2; // Lowest used mip is 4x4
  1495. float gloss = VM_MAX(1.0f - (float)(i + 1) / (float)(numMipsForGloss - 1), 0.0f);
  1496. // Compute specular power (this must match shader code)
  1497. specPow = pow(2.0f, a_GlossScale * gloss + a_GlossBias);
  1498. // Blinn to Phong approximation: (R.E)^p == (N.H)^(4*p)
  1499. specPow /= 4.0f;
  1500. coneAngle = ComputeBaseFilterAngle(specPow);
  1501. srcCubeImage = m_InputSurface[0];
  1502. }
  1503. //Build filter lookup tables based on the source miplevel size
  1504. PrecomputeFilterLookupTables(a_FilterType, srcCubeImage->m_Width, coneAngle);
  1505. //filter cube surfaces
  1506. FilterCubeSurfaces(srcCubeImage, m_OutputSurface[i+1], coneAngle, a_FilterType, a_bUseSolidAngle,
  1507. 0, //start at face 0
  1508. 5, //end at face 5
  1509. 0, //thread 0 is processing
  1510. specPow);
  1511. }
  1512. m_ThreadProgress[0].m_CurrentMipLevel = i+2;
  1513. m_ThreadProgress[0].m_CurrentRow = 0;
  1514. m_ThreadProgress[0].m_CurrentFace = 0;
  1515. FixupCubeEdges(m_OutputSurface[i+1], a_FixupType, a_FixupWidth);
  1516. coneAngle = coneAngle * a_MipAnglePerLevelScale;
  1517. }
  1518. m_Status = CP_STATUS_FILTER_COMPLETED;
  1519. }
  1520. //--------------------------------------------------------------------------------------
  1521. //Builds the following lookup tables prior to filtering:
  1522. // -normalizer cube map
  1523. // -tap weight lookup table
  1524. //
  1525. //--------------------------------------------------------------------------------------
  1526. void CCubeMapProcessor::PrecomputeFilterLookupTables(uint32 a_FilterType, int32 a_SrcCubeMapWidth, float a_FilterConeAngle)
  1527. {
  1528. float srcTexelAngle;
  1529. int32 iCubeFace;
  1530. //angle about center tap that defines filter cone
  1531. float filterAngle;
  1532. //min angle a src texel can cover (in degrees)
  1533. srcTexelAngle = (180.0f / CP_PI) * atan2f(1.0f, (float)a_SrcCubeMapWidth);
  1534. //filter angle is 1/2 the cone angle
  1535. filterAngle = a_FilterConeAngle / 2.0f;
  1536. //ensure filter angle is larger than a texel
  1537. if(filterAngle < srcTexelAngle)
  1538. {
  1539. filterAngle = srcTexelAngle;
  1540. }
  1541. //ensure filter cone is always smaller than the hemisphere
  1542. if(filterAngle > 90.0f)
  1543. {
  1544. filterAngle = 90.0f;
  1545. }
  1546. //build lookup table for tap weights based on angle between current tap and center tap
  1547. BuildAngleWeightLUT(a_SrcCubeMapWidth * 2, a_FilterType, filterAngle);
  1548. //clear pre-existing normalizer cube map
  1549. for(iCubeFace=0; iCubeFace<6; iCubeFace++)
  1550. {
  1551. m_NormCubeMap[iCubeFace].Clear();
  1552. }
  1553. //Normalized vectors per cubeface and per-texel solid angle
  1554. BuildNormalizerSolidAngleCubemap(a_SrcCubeMapWidth, m_NormCubeMap);
  1555. }
  1556. //--------------------------------------------------------------------------------------
  1557. //The key to the speed of these filtering routines is to quickly define a per-face
  1558. // bounding box of pixels which enclose all the taps in the filter kernel efficiently.
  1559. // Later these pixels are selectively processed based on their dot products to see if
  1560. // they reside within the filtering cone.
  1561. //
  1562. //This is done by computing the smallest per-texel angle to get a conservative estimate
  1563. // of the number of texels needed to be covered in width and height order to filter the
  1564. // region. the bounding box for the center taps face is defined first, and if the
  1565. // filtereing region bleeds onto the other faces, bounding boxes for the other faces are
  1566. // defined next
  1567. //--------------------------------------------------------------------------------------
  1568. void CCubeMapProcessor::FilterCubeSurfaces(CImageSurface *a_SrcCubeMap, CImageSurface *a_DstCubeMap,
  1569. float a_FilterConeAngle, int32 a_FilterType, bool a_bUseSolidAngle, int32 a_FaceIdxStart,
  1570. int32 a_FaceIdxEnd, int32 a_ThreadIdx, float a_SpecularPower)
  1571. {
  1572. const int32 srcSize = a_SrcCubeMap[0].m_Width;
  1573. const int32 dstSize = a_DstCubeMap[0].m_Width;
  1574. //min angle a src texel can cover (in degrees)
  1575. const float srcTexelAngle = (180.0f / CP_PI) * atan2f(1.0f, (float)srcSize);
  1576. //angle about center tap to define filter cone
  1577. float filterAngle;
  1578. //filter angle is 1/2 the cone angle
  1579. filterAngle = a_FilterConeAngle / 2.0f;
  1580. //ensure filter angle is larger than a texel
  1581. if(filterAngle < srcTexelAngle)
  1582. {
  1583. filterAngle = srcTexelAngle;
  1584. }
  1585. //ensure filter cone is always smaller than the hemisphere
  1586. if(filterAngle > 90.0f)
  1587. {
  1588. filterAngle = 90.0f;
  1589. }
  1590. //the maximum number of texels in 1D the filter cone angle will cover
  1591. // used to determine bounding box size for filter extents
  1592. //ensure conservative region always covers at least one texel
  1593. const int32 filterSize = AZ::GetMax((int32)ceil(filterAngle / srcTexelAngle), 1);
  1594. //dotProdThresh threshold based on cone angle to determine whether or not taps
  1595. // reside within the cone angle
  1596. const float dotProdThresh = cosf( (CP_PI / 180.0f) * filterAngle );
  1597. //thread progress
  1598. m_ThreadProgress[a_ThreadIdx].m_StartFace = a_FaceIdxStart;
  1599. m_ThreadProgress[a_ThreadIdx].m_EndFace = a_FaceIdxEnd;
  1600. //process required faces
  1601. for(int32 iCubeFace = a_FaceIdxStart; iCubeFace <= a_FaceIdxEnd && !m_shutdownWorkerThreadSignal; iCubeFace++)
  1602. {
  1603. //iterate over dst cube map face texel
  1604. for(int32 v = 0; v < dstSize && !m_shutdownWorkerThreadSignal; v++)
  1605. {
  1606. CP_ITYPE *texelPtr = a_DstCubeMap[iCubeFace].m_ImgData + v * a_DstCubeMap[iCubeFace].m_NumChannels * dstSize;
  1607. m_ThreadProgress[a_ThreadIdx].m_CurrentFace = iCubeFace;
  1608. m_ThreadProgress[a_ThreadIdx].m_CurrentRow = v;
  1609. for(int32 u=0; u<dstSize && !m_shutdownWorkerThreadSignal; u++)
  1610. {
  1611. //CImageSurface normCubeMap[6]; //
  1612. CBBoxInt32 filterExtents[6]; //bounding box per face to specify region to process
  1613. // note that pixels within these regions may be rejected
  1614. // based on the
  1615. float centerTapDir[3]; //direction of center tap
  1616. //get center tap direction
  1617. TexelCoordToVect(iCubeFace, (float)u, (float)v, dstSize, centerTapDir );
  1618. //clear old per-face filter extents
  1619. ClearFilterExtents(filterExtents);
  1620. //define per-face filter extents
  1621. DetermineFilterExtents(centerTapDir, srcSize, filterSize, filterExtents );
  1622. //perform filtering of src faces using filter extents
  1623. ProcessFilterExtents(centerTapDir, dotProdThresh, filterExtents, m_NormCubeMap, a_SrcCubeMap, texelPtr, a_FilterType, a_bUseSolidAngle, a_SpecularPower);
  1624. texelPtr += a_DstCubeMap[iCubeFace].m_NumChannels;
  1625. }
  1626. }
  1627. }
  1628. }
  1629. //--------------------------------------------------------------------------------------
  1630. //starts a new thread to execute the filtering options
  1631. //
  1632. //--------------------------------------------------------------------------------------
  1633. void CCubeMapProcessor::InitiateFiltering(float a_BaseFilterAngle, float a_InitialMipAngle,
  1634. float a_MipAnglePerLevelScale, int32 a_FilterType, int32 a_FixupType, int32 a_FixupWidth, bool a_bUseSolidAngle,
  1635. float a_GlossScale, float a_GlossBias, int32 a_SampleCountGGX)
  1636. {
  1637. //set filtering options in main class to determine
  1638. m_BaseFilterAngle = a_BaseFilterAngle;
  1639. m_InitialMipAngle = a_InitialMipAngle;
  1640. m_MipAnglePerLevelScale = a_MipAnglePerLevelScale;
  1641. //terminate preexisting threads if needed
  1642. TerminateActiveThreads();
  1643. //call filtering function from the current process
  1644. FilterCubeMapMipChain(a_BaseFilterAngle, a_InitialMipAngle, a_MipAnglePerLevelScale, a_FilterType,
  1645. a_FixupType, a_FixupWidth, a_bUseSolidAngle, a_GlossScale, a_GlossBias, a_SampleCountGGX);
  1646. }
  1647. //--------------------------------------------------------------------------------------
  1648. //build filter lookup table
  1649. //
  1650. //--------------------------------------------------------------------------------------
  1651. void CCubeMapProcessor::BuildAngleWeightLUT([[maybe_unused]] int32 a_NumFilterLUTEntries, int32 a_FilterType, float a_FilterAngle)
  1652. {
  1653. int32 iLUTEntry;
  1654. CP_SAFE_DELETE_ARRAY( m_FilterLUT );
  1655. m_NumFilterLUTEntries = 4096; //a_NumFilterLUTEntries;
  1656. m_FilterLUT = new CP_ITYPE [m_NumFilterLUTEntries];
  1657. // note that CP_FILTER_TYPE_DISC weights all taps equally and does not need a lookup table
  1658. if( a_FilterType == CP_FILTER_TYPE_CONE )
  1659. {
  1660. //CP_FILTER_TYPE_CONE is a cone centered around the center tap and falls off to zero
  1661. // over the filtering radius
  1662. CP_ITYPE filtAngleRad = a_FilterAngle * CP_PI / 180.0f;
  1663. for(iLUTEntry=0; iLUTEntry<m_NumFilterLUTEntries; iLUTEntry++ )
  1664. {
  1665. CP_ITYPE angle = acos( (float)iLUTEntry / (float)(m_NumFilterLUTEntries - 1) );
  1666. CP_ITYPE filterVal;
  1667. filterVal = (filtAngleRad - angle) / filtAngleRad;
  1668. if(filterVal < 0)
  1669. {
  1670. filterVal = 0;
  1671. }
  1672. //note that gaussian is not weighted by 1.0 / (sigma* sqrt(2 * PI)) seen as weights
  1673. // weighted tap accumulation in filters is divided by sum of weights
  1674. m_FilterLUT[iLUTEntry] = filterVal;
  1675. }
  1676. }
  1677. else if( a_FilterType == CP_FILTER_TYPE_ANGULAR_GAUSSIAN )
  1678. {
  1679. //fit 3 standard deviations within angular extent of filter
  1680. CP_ITYPE stdDev = (a_FilterAngle * CP_PI / 180.0f) / 3.0f;
  1681. CP_ITYPE inv2Variance = 1.0f / (2.0f * stdDev * stdDev);
  1682. for(iLUTEntry=0; iLUTEntry<m_NumFilterLUTEntries; iLUTEntry++ )
  1683. {
  1684. CP_ITYPE angle = acos( (float)iLUTEntry / (float)(m_NumFilterLUTEntries - 1) );
  1685. CP_ITYPE filterVal;
  1686. filterVal = exp( -(angle * angle) * inv2Variance );
  1687. //note that gaussian is not weighted by 1.0 / (sigma* sqrt(2 * PI)) seen as weights
  1688. // weighted tap accumulation in filters is divided by sum of weights
  1689. m_FilterLUT[iLUTEntry] = filterVal;
  1690. }
  1691. }
  1692. }
  1693. //--------------------------------------------------------------------------------------
  1694. // WriteMipLevelIntoAlpha
  1695. //
  1696. // Writes the current mip level into alpha in order for 2.0 shaders that need to
  1697. // know the current mip-level
  1698. //--------------------------------------------------------------------------------------
  1699. void CCubeMapProcessor::WriteMipLevelIntoAlpha(void)
  1700. {
  1701. int32 iFace, iMipLevel;
  1702. //since output is being modified, terminate any active filtering threads
  1703. TerminateActiveThreads();
  1704. //generate subsequent mip levels
  1705. for(iMipLevel = 0; iMipLevel < m_NumMipLevels; iMipLevel++)
  1706. {
  1707. //Iterate over faces for input images
  1708. for(iFace = 0; iFace < 6; iFace++)
  1709. {
  1710. m_OutputSurface[iMipLevel][iFace].ClearChannelConst(3, (float) (16.0f * (iMipLevel / 255.0f)) );
  1711. }
  1712. }
  1713. }
  1714. //--------------------------------------------------------------------------------------
  1715. // Horizonally flip input cube map faces
  1716. //--------------------------------------------------------------------------------------
  1717. void CCubeMapProcessor::FlipInputCubemapFaces(void)
  1718. {
  1719. int32 iFace, iMip;
  1720. //since input is being modified, terminate any active filtering threads
  1721. TerminateActiveThreads();
  1722. //Iterate over faces for input images
  1723. for (iMip = 0; iMip < m_NumMipLevels; iMip++)
  1724. {
  1725. for (iFace = 0; iFace < 6; iFace++)
  1726. {
  1727. m_InputSurface[iMip][iFace].InPlaceHorizonalFlip();
  1728. }
  1729. }
  1730. }
  1731. //--------------------------------------------------------------------------------------
  1732. //Horizonally flip output cube map faces
  1733. //--------------------------------------------------------------------------------------
  1734. void CCubeMapProcessor::FlipOutputCubemapFaces(void)
  1735. {
  1736. int32 iFace, iMipLevel;
  1737. //since output is being modified, terminate any active filtering threads
  1738. TerminateActiveThreads();
  1739. //Iterate over faces for input images
  1740. for(iMipLevel = 0; iMipLevel < m_NumMipLevels; iMipLevel++)
  1741. {
  1742. for(iFace = 0; iFace < 6; iFace++)
  1743. {
  1744. m_OutputSurface[iMipLevel][iFace].InPlaceHorizonalFlip();
  1745. }
  1746. }
  1747. }
  1748. //--------------------------------------------------------------------------------------
  1749. // test to see if filter thread is still active
  1750. //
  1751. //--------------------------------------------------------------------------------------
  1752. bool CCubeMapProcessor::IsFilterThreadActive(uint32 a_ThreadIdx)
  1753. {
  1754. if(m_bThreadInitialized[a_ThreadIdx] == false)
  1755. {
  1756. return false;
  1757. }
  1758. else
  1759. {
  1760. if(m_ThreadHandle[a_ThreadIdx].joinable())
  1761. {
  1762. return true;
  1763. }
  1764. }
  1765. return false;
  1766. }
  1767. //--------------------------------------------------------------------------------------
  1768. //estimate fraction completed of filter thread based on current conditions
  1769. //
  1770. //--------------------------------------------------------------------------------------
  1771. void CCubeMapProcessor::EstimateFilterThreadProgress(SFilterProgress *a_FilterProgress)
  1772. {
  1773. float totalMipComputation = 0.0f; //time to compute all mip levels as a function of the time it takes
  1774. //to compute the top mip level
  1775. float progressMipComputation = 0.0f; //progress based on entirely computed mip levels
  1776. float currentMipComputation = 0.0f; //amount of computation it takes to process this entire mip level
  1777. float progressFaceComputation = 0.0f; //progress based on entirely computed faces for this mip level
  1778. float currentFaceComputation = 0.0f; //amount of computation it takes to process this entire face
  1779. float progressRowComputation = 0.0f; //progress based on entirely computed rows for this face
  1780. //estimated fraction of total computation time the current face will take
  1781. int32 i;
  1782. float filterAngle = 1.0f; //filter angle for given miplevel
  1783. int32 dstSize = 1; //destination cube map size of given mip level
  1784. int32 currentMipSize = 1; //size of mip level currently being processed
  1785. //compuate total compuation time as a function of the time
  1786. // cubemap processing for each miplevel is roughly O(n^2 * m^2)
  1787. // where n is the cube map size, and m is the filter size
  1788. // Each miplevel is half the size of the previous level,
  1789. // and the filter size in texels is roughly proportional to the
  1790. // (filter angle size * size of source cubemap texels are fetched from) ^2
  1791. // computation to generate base mip level (generated from input cube map)
  1792. if(m_BaseFilterAngle > 0.0f)
  1793. {
  1794. totalMipComputation = pow(m_InputSize * m_BaseFilterAngle , 2.0f) * (m_OutputSize * m_OutputSize);
  1795. }
  1796. else
  1797. {
  1798. totalMipComputation = pow(m_InputSize * 0.01f , 2.0f) * (m_OutputSize * m_OutputSize);
  1799. }
  1800. progressMipComputation = 0.0f;
  1801. if(a_FilterProgress->m_CurrentMipLevel > 0)
  1802. {
  1803. progressMipComputation = totalMipComputation;
  1804. }
  1805. //filtering angle for this miplevel
  1806. filterAngle = m_InitialMipAngle;
  1807. dstSize = m_OutputSize;
  1808. //computation for entire base mip level (if current level is base level)
  1809. if(a_FilterProgress->m_CurrentMipLevel == 0)
  1810. {
  1811. currentMipComputation = totalMipComputation;
  1812. currentMipSize = dstSize;
  1813. }
  1814. //compuatation to generate subsequent mip levels
  1815. for(i=1; i<m_NumMipLevels; i++)
  1816. {
  1817. float computation;
  1818. dstSize /= 2;
  1819. filterAngle *= m_MipAnglePerLevelScale;
  1820. if(filterAngle > 180)
  1821. {
  1822. filterAngle = 180;
  1823. }
  1824. //note src size is dstSize*2 since miplevels are generated from the subsequent level
  1825. computation = pow(dstSize * 2 * filterAngle, 2.0f) * (dstSize * dstSize);
  1826. totalMipComputation += computation;
  1827. //accumulate computation for completed mip levels
  1828. if(a_FilterProgress->m_CurrentMipLevel > i)
  1829. {
  1830. progressMipComputation = totalMipComputation;
  1831. }
  1832. //computation for entire current mip level
  1833. if(a_FilterProgress->m_CurrentMipLevel == i)
  1834. {
  1835. currentMipComputation = computation;
  1836. currentMipSize = dstSize;
  1837. }
  1838. }
  1839. //fraction of compuation time processing the entire current mip level will take
  1840. currentMipComputation /= totalMipComputation;
  1841. progressMipComputation /= totalMipComputation;
  1842. progressFaceComputation = currentMipComputation *
  1843. (float)(a_FilterProgress->m_CurrentFace - a_FilterProgress->m_StartFace) /
  1844. (float)(1 + a_FilterProgress->m_EndFace - a_FilterProgress->m_StartFace);
  1845. currentFaceComputation = currentMipComputation *
  1846. 1.0f /
  1847. (1 + a_FilterProgress->m_EndFace - a_FilterProgress->m_StartFace);
  1848. progressRowComputation = currentFaceComputation *
  1849. ((float)a_FilterProgress->m_CurrentRow / (float)currentMipSize);
  1850. //progress completed
  1851. a_FilterProgress->m_FractionCompleted =
  1852. progressMipComputation +
  1853. progressFaceComputation +
  1854. progressRowComputation;
  1855. if( a_FilterProgress->m_CurrentFace < 0)
  1856. {
  1857. a_FilterProgress->m_CurrentFace = 0;
  1858. }
  1859. if( a_FilterProgress->m_CurrentMipLevel < 0)
  1860. {
  1861. a_FilterProgress->m_CurrentMipLevel = 0;
  1862. }
  1863. if( a_FilterProgress->m_CurrentRow < 0)
  1864. {
  1865. a_FilterProgress->m_CurrentRow = 0;
  1866. }
  1867. }
  1868. //--------------------------------------------------------------------------------------
  1869. // Return string describing the current status of the cubemap processing threads
  1870. //
  1871. //--------------------------------------------------------------------------------------
  1872. WCHAR *CCubeMapProcessor::GetFilterProgressString(void)
  1873. {
  1874. WCHAR threadProgressString[CP_MAX_FILTER_THREADS][CP_MAX_PROGRESS_STRING];
  1875. int32 i;
  1876. for(i=0; i<m_NumFilterThreads; i++)
  1877. {
  1878. if(IsFilterThreadActive(i))
  1879. {
  1880. EstimateFilterThreadProgress(&(m_ThreadProgress[i]) );
  1881. azsnwprintf(threadProgressString[i],
  1882. CP_MAX_PROGRESS_STRING,
  1883. L"%5.2f%% Complete (Level %3d, Face %3d, Row %3d)",
  1884. 100.0f * m_ThreadProgress[i].m_FractionCompleted,
  1885. m_ThreadProgress[i].m_CurrentMipLevel,
  1886. m_ThreadProgress[i].m_CurrentFace,
  1887. m_ThreadProgress[i].m_CurrentRow
  1888. );
  1889. }
  1890. else
  1891. {
  1892. azsnwprintf(threadProgressString[i],
  1893. CP_MAX_PROGRESS_STRING,
  1894. L"Ready");
  1895. }
  1896. }
  1897. if(m_NumFilterThreads == 2)
  1898. { //display information about both threads
  1899. azsnwprintf(m_ProgressString,
  1900. CP_MAX_PROGRESS_STRING,
  1901. L"Thread0: %s \nThread1: %s",
  1902. threadProgressString[0],
  1903. threadProgressString[1]);
  1904. }
  1905. else
  1906. { //only display information about one thread
  1907. azsnwprintf(m_ProgressString,
  1908. CP_MAX_PROGRESS_STRING,
  1909. L"Thread 0: %s ",
  1910. threadProgressString[0]);
  1911. }
  1912. return m_ProgressString;
  1913. }
  1914. //--------------------------------------------------------------------------------------
  1915. //get status of cubemap processor
  1916. //
  1917. //--------------------------------------------------------------------------------------
  1918. int32 CCubeMapProcessor::GetStatus(void)
  1919. {
  1920. return m_Status;
  1921. }
  1922. //--------------------------------------------------------------------------------------
  1923. //refresh status
  1924. // sets cubemap processor to ready state if not processing
  1925. //--------------------------------------------------------------------------------------
  1926. void CCubeMapProcessor::RefreshStatus(void)
  1927. {
  1928. if(m_Status != CP_STATUS_PROCESSING )
  1929. {
  1930. m_Status = CP_STATUS_READY;
  1931. }
  1932. }
  1933. } //namespace ImageProcessingAtom