environmentSerializer.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import { Vector3, Tools } from "babylonjs";
  2. import { TextureCube, PixelFormat, PixelType } from './texture';
  3. /**
  4. * Spherical polynomial coefficients (counter part to spherical harmonic coefficients used in shader irradiance calculation)
  5. * @ignoreChildren
  6. */
  7. export interface SphericalPolynomalCoefficients {
  8. x: Vector3;
  9. y: Vector3;
  10. z: Vector3;
  11. xx: Vector3;
  12. yy: Vector3;
  13. zz: Vector3;
  14. yz: Vector3;
  15. zx: Vector3;
  16. xy: Vector3;
  17. }
  18. /**
  19. * Wraps data and maps required for environments with physically based rendering
  20. */
  21. export interface PBREnvironment {
  22. /**
  23. * Spherical Polynomial Coefficients representing an irradiance map
  24. */
  25. irradiancePolynomialCoefficients: SphericalPolynomalCoefficients;
  26. /**
  27. * Specular cubemap
  28. */
  29. specularTexture?: TextureCube;
  30. /**
  31. * A scale factor applied to RGB values after reading from environment maps
  32. */
  33. textureIntensityScale: number;
  34. }
  35. /**
  36. * Environment map representations: layouts, projections and approximations
  37. */
  38. export type MapType =
  39. 'irradiance_sh_coefficients_9' |
  40. 'cubemap_faces';
  41. /**
  42. * Image type used for environment map
  43. */
  44. export type ImageType = 'png';
  45. //Payload Descriptor
  46. /**
  47. * A generic field in JSON that report's its type
  48. */
  49. export interface TypedObject<T> {
  50. type: T;
  51. }
  52. /**
  53. * Describes a range of bytes starting at byte pos (inclusive) and finishing at byte pos + length - 1
  54. */
  55. export interface ByteRange {
  56. pos: number;
  57. length: number;
  58. }
  59. /**
  60. * Complete Spectre Environment JSON Descriptor
  61. */
  62. export interface EnvJsonDescriptor {
  63. radiance: TypedObject<MapType>;
  64. irradiance: TypedObject<MapType>;
  65. specular: TypedObject<MapType>;
  66. }
  67. /**
  68. * Spherical harmonic coefficients to provide an irradiance map
  69. */
  70. export interface IrradianceSHCoefficients9 extends TypedObject<MapType> {
  71. l00: Array<number>;
  72. l1_1: Array<number>;
  73. l10: Array<number>;
  74. l11: Array<number>;
  75. l2_2: Array<number>;
  76. l2_1: Array<number>;
  77. l20: Array<number>;
  78. l21: Array<number>;
  79. l22: Array<number>;
  80. }
  81. /**
  82. * A generic set of images, where the image content is specified by byte ranges in the mipmaps field
  83. */
  84. export interface ImageSet<T> extends TypedObject<MapType> {
  85. imageType: ImageType;
  86. width: number;
  87. height: number;
  88. mipmaps: Array<T>;
  89. multiplier: number;
  90. }
  91. /**
  92. * A set of cubemap faces
  93. */
  94. export type CubemapFaces = ImageSet<Array<ByteRange>>;
  95. /**
  96. * A single image containing an atlas of equirectangular-projection maps across all mip levels
  97. */
  98. export type EquirectangularMipmapAtlas = ImageSet<ByteRange>;
  99. /**
  100. * A static class proving methods to aid parsing Spectre environment files
  101. */
  102. export class EnvironmentDeserializer {
  103. /**
  104. * Parses an arraybuffer into a new PBREnvironment object
  105. * @param arrayBuffer The arraybuffer of the Spectre environment file
  106. * @return a PBREnvironment object
  107. */
  108. public static Parse(arrayBuffer: ArrayBuffer): PBREnvironment {
  109. var environment: PBREnvironment = {
  110. //irradiance
  111. irradiancePolynomialCoefficients: {
  112. x: new Vector3(0, 0, 0),
  113. y: new Vector3(0, 0, 0),
  114. z: new Vector3(0, 0, 0),
  115. xx: new Vector3(0, 0, 0),
  116. yy: new Vector3(0, 0, 0),
  117. zz: new Vector3(0, 0, 0),
  118. yz: new Vector3(0, 0, 0),
  119. zx: new Vector3(0, 0, 0),
  120. xy: new Vector3(0, 0, 0)
  121. },
  122. //specular
  123. textureIntensityScale: 1.0,
  124. };
  125. //read .env
  126. let littleEndian = false;
  127. let magicBytes = [0x86, 0x16, 0x87, 0x96, 0xf6, 0xd6, 0x96, 0x36];
  128. let dataView = new DataView(arrayBuffer);
  129. let pos = 0;
  130. for (let i = 0; i < magicBytes.length; i++) {
  131. if (dataView.getUint8(pos++) !== magicBytes[i]) {
  132. Tools.Error('Not a Spectre environment map');
  133. }
  134. }
  135. let version = dataView.getUint16(pos, littleEndian); pos += 2;
  136. if (version !== 1) {
  137. Tools.Warn('Unsupported Spectre environment map version "' + version + '"');
  138. }
  139. //read json descriptor - collect characters up to null terminator
  140. let descriptorString = '';
  141. let charCode = 0x00;
  142. while ((charCode = dataView.getUint8(pos++))) {
  143. descriptorString += String.fromCharCode(charCode);
  144. }
  145. let descriptor: EnvJsonDescriptor = JSON.parse(descriptorString);
  146. let payloadPos = pos;
  147. //irradiance
  148. switch (descriptor.irradiance.type) {
  149. case 'irradiance_sh_coefficients_9':
  150. //irradiance
  151. let harmonics = <IrradianceSHCoefficients9>descriptor.irradiance;
  152. EnvironmentDeserializer._ConvertSHIrradianceToLambertianRadiance(harmonics);
  153. //harmonics now represent radiance
  154. EnvironmentDeserializer._ConvertSHToSP(harmonics, environment.irradiancePolynomialCoefficients);
  155. break;
  156. default:
  157. Tools.Error('Unhandled MapType descriptor.irradiance.type (' + descriptor.irradiance.type + ')');
  158. }
  159. //specular
  160. switch (descriptor.specular.type) {
  161. case 'cubemap_faces':
  162. var specularDescriptor = <CubemapFaces>descriptor.specular;
  163. let specularTexture = environment.specularTexture = new TextureCube(PixelFormat.RGBA, PixelType.UNSIGNED_BYTE);
  164. environment.textureIntensityScale = specularDescriptor.multiplier != null ? specularDescriptor.multiplier : 1.0;
  165. let mipmaps = specularDescriptor.mipmaps;
  166. let imageType = specularDescriptor.imageType;
  167. for (let l = 0; l < mipmaps.length; l++) {
  168. let faceRanges = mipmaps[l];
  169. specularTexture.source[l] = [];
  170. for (let i = 0; i < 6; i++) {
  171. let range = faceRanges[i];
  172. let bytes = new Uint8Array(arrayBuffer, payloadPos + range.pos, range.length);
  173. switch (imageType) {
  174. case 'png':
  175. //construct image element from bytes
  176. let image = new Image();
  177. let src = URL.createObjectURL(new Blob([bytes], { type: 'image/png' }));
  178. image.src = src;
  179. specularTexture.source[l][i] = image;
  180. break;
  181. default:
  182. Tools.Error('Unhandled ImageType descriptor.specular.imageType (' + imageType + ')');
  183. }
  184. }
  185. }
  186. break;
  187. default:
  188. Tools.Error('Unhandled MapType descriptor.specular.type (' + descriptor.specular.type + ')');
  189. }
  190. return environment;
  191. }
  192. /**
  193. * Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
  194. * L = (1/pi) * E * rho
  195. *
  196. * This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
  197. * @param harmonics Spherical harmonic coefficients (9)
  198. */
  199. private static _ConvertSHIrradianceToLambertianRadiance(harmonics: any): void {
  200. EnvironmentDeserializer._ScaleSH(harmonics, 1 / Math.PI);
  201. // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
  202. // (The pixel shader must apply albedo after texture fetches, etc).
  203. }
  204. /**
  205. * Convert spherical harmonics to spherical polynomial coefficients
  206. * @param harmonics Spherical harmonic coefficients (9)
  207. * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
  208. */
  209. private static _ConvertSHToSP(harmonics: any, outPolynomialCoefficents: SphericalPolynomalCoefficients) {
  210. const rPi = 1 / Math.PI;
  211. //x
  212. outPolynomialCoefficents.x.x = 1.02333 * harmonics.l11[0] * rPi;
  213. outPolynomialCoefficents.x.y = 1.02333 * harmonics.l11[1] * rPi;
  214. outPolynomialCoefficents.x.z = 1.02333 * harmonics.l11[2] * rPi;
  215. outPolynomialCoefficents.y.x = 1.02333 * harmonics.l1_1[0] * rPi;
  216. outPolynomialCoefficents.y.y = 1.02333 * harmonics.l1_1[1] * rPi;
  217. outPolynomialCoefficents.y.z = 1.02333 * harmonics.l1_1[2] * rPi;
  218. outPolynomialCoefficents.z.x = 1.02333 * harmonics.l10[0] * rPi;
  219. outPolynomialCoefficents.z.y = 1.02333 * harmonics.l10[1] * rPi;
  220. outPolynomialCoefficents.z.z = 1.02333 * harmonics.l10[2] * rPi;
  221. //xx
  222. outPolynomialCoefficents.xx.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] + 0.429043 * harmonics.l22[0]) * rPi;
  223. outPolynomialCoefficents.xx.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] + 0.429043 * harmonics.l22[1]) * rPi;
  224. outPolynomialCoefficents.xx.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] + 0.429043 * harmonics.l22[2]) * rPi;
  225. outPolynomialCoefficents.yy.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] - 0.429043 * harmonics.l22[0]) * rPi;
  226. outPolynomialCoefficents.yy.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] - 0.429043 * harmonics.l22[1]) * rPi;
  227. outPolynomialCoefficents.yy.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] - 0.429043 * harmonics.l22[2]) * rPi;
  228. outPolynomialCoefficents.zz.x = (0.886277 * harmonics.l00[0] + 0.495417 * harmonics.l20[0]) * rPi;
  229. outPolynomialCoefficents.zz.y = (0.886277 * harmonics.l00[1] + 0.495417 * harmonics.l20[1]) * rPi;
  230. outPolynomialCoefficents.zz.z = (0.886277 * harmonics.l00[2] + 0.495417 * harmonics.l20[2]) * rPi;
  231. //yz
  232. outPolynomialCoefficents.yz.x = 0.858086 * harmonics.l2_1[0] * rPi;
  233. outPolynomialCoefficents.yz.y = 0.858086 * harmonics.l2_1[1] * rPi;
  234. outPolynomialCoefficents.yz.z = 0.858086 * harmonics.l2_1[2] * rPi;
  235. outPolynomialCoefficents.zx.x = 0.858086 * harmonics.l21[0] * rPi;
  236. outPolynomialCoefficents.zx.y = 0.858086 * harmonics.l21[1] * rPi;
  237. outPolynomialCoefficents.zx.z = 0.858086 * harmonics.l21[2] * rPi;
  238. outPolynomialCoefficents.xy.x = 0.858086 * harmonics.l2_2[0] * rPi;
  239. outPolynomialCoefficents.xy.y = 0.858086 * harmonics.l2_2[1] * rPi;
  240. outPolynomialCoefficents.xy.z = 0.858086 * harmonics.l2_2[2] * rPi;
  241. }
  242. /**
  243. * Multiplies harmonic coefficients in place
  244. * @param harmonics Spherical harmonic coefficients (9)
  245. * @param scaleFactor Value to multiply by
  246. */
  247. private static _ScaleSH(harmonics: any, scaleFactor: number) {
  248. harmonics.l00[0] *= scaleFactor;
  249. harmonics.l00[1] *= scaleFactor;
  250. harmonics.l00[2] *= scaleFactor;
  251. harmonics.l1_1[0] *= scaleFactor;
  252. harmonics.l1_1[1] *= scaleFactor;
  253. harmonics.l1_1[2] *= scaleFactor;
  254. harmonics.l10[0] *= scaleFactor;
  255. harmonics.l10[1] *= scaleFactor;
  256. harmonics.l10[2] *= scaleFactor;
  257. harmonics.l11[0] *= scaleFactor;
  258. harmonics.l11[1] *= scaleFactor;
  259. harmonics.l11[2] *= scaleFactor;
  260. harmonics.l2_2[0] *= scaleFactor;
  261. harmonics.l2_2[1] *= scaleFactor;
  262. harmonics.l2_2[2] *= scaleFactor;
  263. harmonics.l2_1[0] *= scaleFactor;
  264. harmonics.l2_1[1] *= scaleFactor;
  265. harmonics.l2_1[2] *= scaleFactor;
  266. harmonics.l20[0] *= scaleFactor;
  267. harmonics.l20[1] *= scaleFactor;
  268. harmonics.l20[2] *= scaleFactor;
  269. harmonics.l21[0] *= scaleFactor;
  270. harmonics.l21[1] *= scaleFactor;
  271. harmonics.l21[2] *= scaleFactor;
  272. harmonics.l22[0] *= scaleFactor;
  273. harmonics.l22[1] *= scaleFactor;
  274. harmonics.l22[2] *= scaleFactor;
  275. }
  276. }