babylon.panoramaToCubemap.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. module BABYLON.Internals {
  2. /**
  3. * CubeMap information grouping all the data for each faces as well as the cubemap size.
  4. */
  5. export interface CubeMapInfo {
  6. /**
  7. * The pixel array for the front face.
  8. * This is stored in RGB, left to right, up to down format.
  9. */
  10. front: Float32Array;
  11. /**
  12. * The pixel array for the back face.
  13. * This is stored in RGB, left to right, up to down format.
  14. */
  15. back: Float32Array;
  16. /**
  17. * The pixel array for the left face.
  18. * This is stored in RGB, left to right, up to down format.
  19. */
  20. left: Float32Array;
  21. /**
  22. * The pixel array for the right face.
  23. * This is stored in RGB, left to right, up to down format.
  24. */
  25. right: Float32Array;
  26. /**
  27. * The pixel array for the up face.
  28. * This is stored in RGB, left to right, up to down format.
  29. */
  30. up: Float32Array;
  31. /**
  32. * The pixel array for the down face.
  33. * This is stored in RGB, left to right, up to down format.
  34. */
  35. down: Float32Array;
  36. /**
  37. * The size of the cubemap stored.
  38. *
  39. * Each faces will be size * size pixels.
  40. */
  41. size: number;
  42. }
  43. /**
  44. * Helper class usefull to convert panorama picture to their cubemap representation in 6 faces.
  45. */
  46. export class PanoramaToCubeMapTools {
  47. private static FACE_FRONT = [
  48. new Vector3(-1.0, -1.0, -1.0),
  49. new Vector3(1.0, -1.0, -1.0),
  50. new Vector3(-1.0, 1.0, -1.0),
  51. new Vector3(1.0, 1.0, -1.0)
  52. ];
  53. private static FACE_BACK = [
  54. new Vector3(1.0, -1.0, 1.0),
  55. new Vector3(-1.0, -1.0, 1.0),
  56. new Vector3(1.0, 1.0, 1.0),
  57. new Vector3(-1.0, 1.0, 1.0)
  58. ];
  59. private static FACE_RIGHT = [
  60. new Vector3(1.0, -1.0, -1.0),
  61. new Vector3(1.0, -1.0, 1.0),
  62. new Vector3(1.0, 1.0, -1.0),
  63. new Vector3(1.0, 1.0, 1.0)
  64. ];
  65. private static FACE_LEFT = [
  66. new Vector3(-1.0, -1.0, 1.0),
  67. new Vector3(-1.0, -1.0, -1.0),
  68. new Vector3(-1.0, 1.0, 1.0),
  69. new Vector3(-1.0, 1.0, -1.0)
  70. ];
  71. private static FACE_DOWN = [
  72. new Vector3(-1.0, 1.0, -1.0),
  73. new Vector3(1.0, 1.0, -1.0),
  74. new Vector3(-1.0, 1.0, 1.0),
  75. new Vector3(1.0, 1.0, 1.0)
  76. ];
  77. private static FACE_UP = [
  78. new Vector3(-1.0, -1.0, 1.0),
  79. new Vector3(1.0, -1.0, 1.0),
  80. new Vector3(-1.0, -1.0, -1.0),
  81. new Vector3(1.0, -1.0, -1.0)
  82. ];
  83. /**
  84. * Converts a panorma stored in RGB right to left up to down format into a cubemap (6 faces).
  85. *
  86. * @param float32Array The source data.
  87. * @param inputWidth The width of the input panorama.
  88. * @param inputhHeight The height of the input panorama.
  89. * @param size The willing size of the generated cubemap (each faces will be size * size pixels)
  90. * @return The cubemap data
  91. */
  92. public static ConvertPanoramaToCubemap(float32Array: Float32Array, inputWidth: number, inputHeight: number, size: number): CubeMapInfo {
  93. if (!float32Array) {
  94. throw "ConvertPanoramaToCubemap: input cannot be null";
  95. }
  96. if (float32Array.length != inputWidth * inputHeight * 3) {
  97. throw "ConvertPanoramaToCubemap: input size is wrong";
  98. }
  99. var textureFront = this.CreateCubemapTexture(size, this.FACE_FRONT, float32Array, inputWidth, inputHeight);
  100. var textureBack = this.CreateCubemapTexture(size, this.FACE_BACK, float32Array, inputWidth, inputHeight);
  101. var textureLeft = this.CreateCubemapTexture(size, this.FACE_LEFT, float32Array, inputWidth, inputHeight);
  102. var textureRight = this.CreateCubemapTexture(size, this.FACE_RIGHT, float32Array, inputWidth, inputHeight);
  103. var textureUp = this.CreateCubemapTexture(size, this.FACE_UP, float32Array, inputWidth, inputHeight);
  104. var textureDown = this.CreateCubemapTexture(size, this.FACE_DOWN, float32Array, inputWidth, inputHeight);
  105. return {
  106. front: textureFront,
  107. back: textureBack,
  108. left: textureLeft,
  109. right: textureRight,
  110. up: textureUp,
  111. down: textureDown,
  112. size: size
  113. };
  114. }
  115. private static CreateCubemapTexture(texSize: number, faceData: Vector3[], float32Array: Float32Array, inputWidth: number, inputHeight: number) {
  116. var buffer = new ArrayBuffer(texSize * texSize * 4 * 3);
  117. var textureArray = new Float32Array(buffer);
  118. var rotDX1 = faceData[1].subtract(faceData[0]).scale(1 / texSize);
  119. var rotDX2 = faceData[3].subtract(faceData[2]).scale(1 / texSize);
  120. var dy = 1 / texSize;
  121. var fy = 0;
  122. for (var y = 0; y < texSize; y++) {
  123. var xv1 = faceData[0];
  124. var xv2 = faceData[2];
  125. for (var x = 0; x < texSize; x++) {
  126. var v = xv2.subtract(xv1).scale(fy).add(xv1);
  127. v.normalize();
  128. var color = this.CalcProjectionSpherical(v, float32Array, inputWidth, inputHeight);
  129. // 3 channels per pixels
  130. textureArray[y * texSize * 3 + (x * 3) + 0] = color.r;
  131. textureArray[y * texSize * 3 + (x * 3) + 1] = color.g;
  132. textureArray[y * texSize * 3 + (x * 3) + 2] = color.b;
  133. xv1 = xv1.add(rotDX1);
  134. xv2 = xv2.add(rotDX2);
  135. }
  136. fy += dy;
  137. }
  138. return textureArray;
  139. }
  140. private static CalcProjectionSpherical(vDir: Vector3, float32Array: Float32Array, inputWidth: number, inputHeight: number): any {
  141. var theta = Math.atan2(vDir.z, vDir.x);
  142. var phi = Math.acos(vDir.y);
  143. while (theta < -Math.PI) theta += 2 * Math.PI;
  144. while (theta > Math.PI) theta -= 2 * Math.PI;
  145. var dx = theta / Math.PI;
  146. var dy = phi / Math.PI;
  147. // recenter.
  148. dx = dx * 0.5 + 0.5;
  149. var px = Math.round(dx * inputWidth);
  150. if (px < 0) px = 0;
  151. else if (px >= inputWidth) px = inputWidth - 1;
  152. var py = Math.round(dy * inputHeight);
  153. if (py < 0) py = 0;
  154. else if (py >= inputHeight) py = inputHeight - 1;
  155. var inputY = (inputHeight - py - 1);
  156. var r = float32Array[inputY * inputWidth * 3 + (px * 3) + 0];
  157. var g = float32Array[inputY * inputWidth * 3 + (px * 3) + 1];
  158. var b = float32Array[inputY * inputWidth * 3 + (px * 3) + 2];
  159. return {
  160. r: r,
  161. g: g,
  162. b: b
  163. };
  164. }
  165. }
  166. }