reflectionBlock.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
  2. import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
  3. import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from '../../nodeMaterialBlockConnectionPoint';
  4. import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
  5. import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
  6. import { _TypeStore } from '../../../../Misc/typeStore';
  7. import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject";
  8. import { ReflectionTextureBaseBlock } from '../Dual/reflectionTextureBaseBlock';
  9. import { AbstractMesh } from '../../../../Meshes/abstractMesh';
  10. import { Nullable } from '../../../../types';
  11. import { Texture } from '../../../Textures/texture';
  12. import { BaseTexture } from '../../../Textures/baseTexture';
  13. import { Mesh } from '../../../../Meshes/mesh';
  14. import { SubMesh } from '../../../../Meshes/subMesh';
  15. import { Effect } from '../../../effect';
  16. import { editableInPropertyPage, PropertyTypeForEdition } from "../../nodeMaterialDecorator";
  17. import { Scene } from '../../../../scene';
  18. import { Scalar } from '../../../../Maths/math.scalar';
  19. /**
  20. * Block used to implement the reflection module of the PBR material
  21. */
  22. export class ReflectionBlock extends ReflectionTextureBaseBlock {
  23. /** @hidden */
  24. public _defineLODReflectionAlpha: string;
  25. /** @hidden */
  26. public _defineLinearSpecularReflection: string;
  27. private _vEnvironmentIrradianceName: string;
  28. /** @hidden */
  29. public _vReflectionMicrosurfaceInfosName: string;
  30. /** @hidden */
  31. public _vReflectionInfosName: string;
  32. /** @hidden */
  33. public _vReflectionFilteringInfoName: string;
  34. private _scene: Scene;
  35. /**
  36. * The properties below are set by the main PBR block prior to calling methods of this class.
  37. * This is to avoid having to add them as inputs here whereas they are already inputs of the main block, so already known.
  38. * It's less burden on the user side in the editor part.
  39. */
  40. /** @hidden */
  41. public worldPositionConnectionPoint: NodeMaterialConnectionPoint;
  42. /** @hidden */
  43. public worldNormalConnectionPoint: NodeMaterialConnectionPoint;
  44. /** @hidden */
  45. public cameraPositionConnectionPoint: NodeMaterialConnectionPoint;
  46. /** @hidden */
  47. public viewConnectionPoint: NodeMaterialConnectionPoint;
  48. /**
  49. * Defines if the material uses spherical harmonics vs spherical polynomials for the
  50. * diffuse part of the IBL.
  51. */
  52. @editableInPropertyPage("Spherical Harmonics", PropertyTypeForEdition.Boolean, "ADVANCED", { "notifiers": { "update": true }})
  53. public useSphericalHarmonics: boolean = true;
  54. /**
  55. * Force the shader to compute irradiance in the fragment shader in order to take bump in account.
  56. */
  57. @editableInPropertyPage("Force irradiance in fragment", PropertyTypeForEdition.Boolean, "ADVANCED", { "notifiers": { "update": true }})
  58. public forceIrradianceInFragment: boolean = false;
  59. /**
  60. * Create a new ReflectionBlock
  61. * @param name defines the block name
  62. */
  63. public constructor(name: string) {
  64. super(name);
  65. this._isUnique = true;
  66. this.registerInput("position", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Vertex);
  67. this.registerInput("world", NodeMaterialBlockConnectionPointTypes.Matrix, false, NodeMaterialBlockTargets.Vertex);
  68. this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
  69. this.registerOutput("reflection", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
  70. new NodeMaterialConnectionPointCustomObject("reflection", this, NodeMaterialConnectionPointDirection.Output, ReflectionBlock, "ReflectionBlock"));
  71. }
  72. /**
  73. * Gets the current class name
  74. * @returns the class name
  75. */
  76. public getClassName() {
  77. return "ReflectionBlock";
  78. }
  79. /**
  80. * Gets the position input component
  81. */
  82. public get position(): NodeMaterialConnectionPoint {
  83. return this._inputs[0];
  84. }
  85. /**
  86. * Gets the world position input component
  87. */
  88. public get worldPosition(): NodeMaterialConnectionPoint {
  89. return this.worldPositionConnectionPoint;
  90. }
  91. /**
  92. * Gets the world normal input component
  93. */
  94. public get worldNormal(): NodeMaterialConnectionPoint {
  95. return this.worldNormalConnectionPoint;
  96. }
  97. /**
  98. * Gets the world input component
  99. */
  100. public get world(): NodeMaterialConnectionPoint {
  101. return this._inputs[1];
  102. }
  103. /**
  104. * Gets the camera (or eye) position component
  105. */
  106. public get cameraPosition(): NodeMaterialConnectionPoint {
  107. return this.cameraPositionConnectionPoint;
  108. }
  109. /**
  110. * Gets the view input component
  111. */
  112. public get view(): NodeMaterialConnectionPoint {
  113. return this.viewConnectionPoint;
  114. }
  115. /**
  116. * Gets the color input component
  117. */
  118. public get color(): NodeMaterialConnectionPoint {
  119. return this._inputs[2];
  120. }
  121. /**
  122. * Gets the reflection object output component
  123. */
  124. public get reflection(): NodeMaterialConnectionPoint {
  125. return this._outputs[0];
  126. }
  127. /**
  128. * Returns true if the block has a texture (either its own texture or the environment texture from the scene, if set)
  129. */
  130. public get hasTexture(): boolean {
  131. return !!this._getTexture();
  132. }
  133. /**
  134. * Gets the reflection color (either the name of the variable if the color input is connected, else a default value)
  135. */
  136. public get reflectionColor(): string {
  137. return this.color.isConnected ? this.color.associatedVariableName : "vec3(1., 1., 1.)";
  138. }
  139. protected _getTexture(): Nullable<BaseTexture> {
  140. if (this.texture) {
  141. return this.texture;
  142. }
  143. return this._scene.environmentTexture;
  144. }
  145. public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
  146. super.prepareDefines(mesh, nodeMaterial, defines);
  147. const reflectionTexture = this._getTexture();
  148. const reflection = reflectionTexture && reflectionTexture.getTextureMatrix;
  149. defines.setValue("REFLECTION", reflection, true);
  150. if (!reflection) {
  151. return;
  152. }
  153. defines.setValue(this._defineLODReflectionAlpha, reflectionTexture!.lodLevelInAlpha, true);
  154. defines.setValue(this._defineLinearSpecularReflection, reflectionTexture!.linearSpecularLOD, true);
  155. defines.setValue(this._defineOppositeZ, this._scene.useRightHandedSystem ? !reflectionTexture!.invertZ : reflectionTexture!.invertZ, true);
  156. defines.setValue("SPHERICAL_HARMONICS", this.useSphericalHarmonics, true);
  157. defines.setValue("GAMMAREFLECTION", reflectionTexture!.gammaSpace, true);
  158. defines.setValue("RGBDREFLECTION", reflectionTexture!.isRGBD, true);
  159. if (reflectionTexture && reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
  160. if (reflectionTexture.isCube) {
  161. defines.setValue("USESPHERICALFROMREFLECTIONMAP", true);
  162. defines.setValue("USEIRRADIANCEMAP", false);
  163. if (this.forceIrradianceInFragment || this._scene.getEngine().getCaps().maxVaryingVectors <= 8) {
  164. defines.setValue("USESPHERICALINVERTEX", false);
  165. }
  166. else {
  167. defines.setValue("USESPHERICALINVERTEX", true);
  168. }
  169. }
  170. }
  171. }
  172. public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh, subMesh?: SubMesh) {
  173. super.bind(effect, nodeMaterial, mesh);
  174. const reflectionTexture = this._getTexture();
  175. if (!reflectionTexture || !subMesh) {
  176. return;
  177. }
  178. if (reflectionTexture.isCube) {
  179. effect.setTexture(this._cubeSamplerName, reflectionTexture);
  180. } else {
  181. effect.setTexture(this._2DSamplerName, reflectionTexture);
  182. }
  183. const width = reflectionTexture.getSize().width;
  184. effect.setFloat3(this._vReflectionMicrosurfaceInfosName, width, reflectionTexture.lodGenerationScale, reflectionTexture.lodGenerationOffset);
  185. effect.setFloat2(this._vReflectionFilteringInfoName, width, Scalar.Log2(width));
  186. const defines = subMesh._materialDefines as NodeMaterialDefines;
  187. const polynomials = reflectionTexture.sphericalPolynomial;
  188. if (defines.USESPHERICALFROMREFLECTIONMAP && polynomials) {
  189. if (defines.SPHERICAL_HARMONICS) {
  190. const preScaledHarmonics = polynomials.preScaledHarmonics;
  191. effect.setVector3("vSphericalL00", preScaledHarmonics.l00);
  192. effect.setVector3("vSphericalL1_1", preScaledHarmonics.l1_1);
  193. effect.setVector3("vSphericalL10", preScaledHarmonics.l10);
  194. effect.setVector3("vSphericalL11", preScaledHarmonics.l11);
  195. effect.setVector3("vSphericalL2_2", preScaledHarmonics.l2_2);
  196. effect.setVector3("vSphericalL2_1", preScaledHarmonics.l2_1);
  197. effect.setVector3("vSphericalL20", preScaledHarmonics.l20);
  198. effect.setVector3("vSphericalL21", preScaledHarmonics.l21);
  199. effect.setVector3("vSphericalL22", preScaledHarmonics.l22);
  200. }
  201. else {
  202. effect.setFloat3("vSphericalX", polynomials.x.x, polynomials.x.y, polynomials.x.z);
  203. effect.setFloat3("vSphericalY", polynomials.y.x, polynomials.y.y, polynomials.y.z);
  204. effect.setFloat3("vSphericalZ", polynomials.z.x, polynomials.z.y, polynomials.z.z);
  205. effect.setFloat3("vSphericalXX_ZZ", polynomials.xx.x - polynomials.zz.x,
  206. polynomials.xx.y - polynomials.zz.y,
  207. polynomials.xx.z - polynomials.zz.z);
  208. effect.setFloat3("vSphericalYY_ZZ", polynomials.yy.x - polynomials.zz.x,
  209. polynomials.yy.y - polynomials.zz.y,
  210. polynomials.yy.z - polynomials.zz.z);
  211. effect.setFloat3("vSphericalZZ", polynomials.zz.x, polynomials.zz.y, polynomials.zz.z);
  212. effect.setFloat3("vSphericalXY", polynomials.xy.x, polynomials.xy.y, polynomials.xy.z);
  213. effect.setFloat3("vSphericalYZ", polynomials.yz.x, polynomials.yz.y, polynomials.yz.z);
  214. effect.setFloat3("vSphericalZX", polynomials.zx.x, polynomials.zx.y, polynomials.zx.z);
  215. }
  216. }
  217. }
  218. /**
  219. * Gets the code to inject in the vertex shader
  220. * @param state current state of the node material building
  221. * @returns the shader code
  222. */
  223. public handleVertexSide(state: NodeMaterialBuildState): string {
  224. let code = super.handleVertexSide(state);
  225. state._emitFunctionFromInclude("harmonicsFunctions", `//${this.name}`, {
  226. replaceStrings: [
  227. { search: /uniform vec3 vSphericalL00;[\s\S]*?uniform vec3 vSphericalL22;/g, replace: "" },
  228. { search: /uniform vec3 vSphericalX;[\s\S]*?uniform vec3 vSphericalZX;/g, replace: "" },
  229. ]
  230. });
  231. const reflectionVectorName = state._getFreeVariableName("reflectionVector");
  232. this._vEnvironmentIrradianceName = state._getFreeVariableName("vEnvironmentIrradiance");
  233. state._emitVaryingFromString(this._vEnvironmentIrradianceName, "vec3", "defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX)");
  234. state._emitUniformFromString("vSphericalL00", "vec3", "SPHERICAL_HARMONICS");
  235. state._emitUniformFromString("vSphericalL1_1", "vec3", "SPHERICAL_HARMONICS");
  236. state._emitUniformFromString("vSphericalL10", "vec3", "SPHERICAL_HARMONICS");
  237. state._emitUniformFromString("vSphericalL11", "vec3", "SPHERICAL_HARMONICS");
  238. state._emitUniformFromString("vSphericalL2_2", "vec3", "SPHERICAL_HARMONICS");
  239. state._emitUniformFromString("vSphericalL2_1", "vec3", "SPHERICAL_HARMONICS");
  240. state._emitUniformFromString("vSphericalL20", "vec3", "SPHERICAL_HARMONICS");
  241. state._emitUniformFromString("vSphericalL21", "vec3", "SPHERICAL_HARMONICS");
  242. state._emitUniformFromString("vSphericalL22", "vec3", "SPHERICAL_HARMONICS");
  243. state._emitUniformFromString("vSphericalX", "vec3", "SPHERICAL_HARMONICS", true);
  244. state._emitUniformFromString("vSphericalY", "vec3", "SPHERICAL_HARMONICS", true);
  245. state._emitUniformFromString("vSphericalZ", "vec3", "SPHERICAL_HARMONICS", true);
  246. state._emitUniformFromString("vSphericalXX_ZZ", "vec3", "SPHERICAL_HARMONICS", true);
  247. state._emitUniformFromString("vSphericalYY_ZZ", "vec3", "SPHERICAL_HARMONICS", true);
  248. state._emitUniformFromString("vSphericalZZ", "vec3", "SPHERICAL_HARMONICS", true);
  249. state._emitUniformFromString("vSphericalXY", "vec3", "SPHERICAL_HARMONICS", true);
  250. state._emitUniformFromString("vSphericalYZ", "vec3", "SPHERICAL_HARMONICS", true);
  251. state._emitUniformFromString("vSphericalZX", "vec3", "SPHERICAL_HARMONICS", true);
  252. code +=
  253. `#if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX)
  254. vec3 ${reflectionVectorName} = vec3(${this._reflectionMatrixName} * vec4(normalize(${this.worldNormal.associatedVariableName}).xyz, 0)).xyz;
  255. #ifdef ${this._defineOppositeZ}
  256. ${reflectionVectorName}.z *= -1.0;
  257. #endif
  258. ${this._vEnvironmentIrradianceName} = computeEnvironmentIrradiance(${reflectionVectorName});
  259. #endif\r\n`;
  260. return code;
  261. }
  262. /**
  263. * Gets the main code of the block (fragment side)
  264. * @param state current state of the node material building
  265. * @param normalVarName name of the existing variable corresponding to the normal
  266. * @returns the shader code
  267. */
  268. public getCode(state: NodeMaterialBuildState, normalVarName: string): string {
  269. let code = "";
  270. this.handleFragmentSideInits(state);
  271. state._emitFunctionFromInclude("harmonicsFunctions", `//${this.name}`, {
  272. replaceStrings: [
  273. { search: /uniform vec3 vSphericalL00;[\s\S]*?uniform vec3 vSphericalL22;/g, replace: "" },
  274. { search: /uniform vec3 vSphericalX;[\s\S]*?uniform vec3 vSphericalZX;/g, replace: "" },
  275. ]
  276. });
  277. state._emitFunction("sampleReflection", `
  278. #ifdef ${this._define3DName}
  279. #define sampleReflection(s, c) textureCube(s, c)
  280. #else
  281. #define sampleReflection(s, c) texture2D(s, c)
  282. #endif\r\n`, `//${this.name}`);
  283. state._emitFunction("sampleReflectionLod", `
  284. #ifdef ${this._define3DName}
  285. #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
  286. #else
  287. #define sampleReflectionLod(s, c, l) texture2DLodEXT(s, c, l)
  288. #endif\r\n`, `//${this.name}`);
  289. const computeReflectionCoordsFunc = `
  290. vec3 computeReflectionCoordsPBR(vec4 worldPos, vec3 worldNormal) {
  291. ${this.handleFragmentSideCodeReflectionCoords('worldNormal', 'worldPos', true)}
  292. return ${this._reflectionVectorName};
  293. }\r\n`;
  294. state._emitFunction("computeReflectionCoordsPBR", computeReflectionCoordsFunc, `//${this.name}`);
  295. this._vReflectionMicrosurfaceInfosName = state._getFreeVariableName("vReflectionMicrosurfaceInfos");
  296. state._emitUniformFromString(this._vReflectionMicrosurfaceInfosName, "vec3");
  297. this._vReflectionInfosName = state._getFreeVariableName("vReflectionInfos");
  298. this._vReflectionFilteringInfoName = state._getFreeVariableName("vReflectionFilteringInfo");
  299. state._emitUniformFromString(this._vReflectionFilteringInfoName, "vec2");
  300. code += `#ifdef REFLECTION
  301. vec2 ${this._vReflectionInfosName} = vec2(1., 0.);
  302. reflectionOutParams reflectionOut;
  303. reflectionBlock(
  304. ${"v_" + this.worldPosition.associatedVariableName + ".xyz"},
  305. ${normalVarName},
  306. alphaG,
  307. ${this._vReflectionMicrosurfaceInfosName},
  308. ${this._vReflectionInfosName},
  309. ${this.reflectionColor},
  310. #ifdef ANISOTROPIC
  311. anisotropicOut,
  312. #endif
  313. #if defined(${this._defineLODReflectionAlpha}) && !defined(${this._defineSkyboxName})
  314. NdotVUnclamped,
  315. #endif
  316. #ifdef ${this._defineLinearSpecularReflection}
  317. roughness,
  318. #endif
  319. #ifdef ${this._define3DName}
  320. ${this._cubeSamplerName},
  321. #else
  322. ${this._2DSamplerName},
  323. #endif
  324. #if defined(NORMAL) && defined(USESPHERICALINVERTEX)
  325. ${this._vEnvironmentIrradianceName},
  326. #endif
  327. #ifdef USESPHERICALFROMREFLECTIONMAP
  328. #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX)
  329. ${this._reflectionMatrixName},
  330. #endif
  331. #endif
  332. #ifdef USEIRRADIANCEMAP
  333. irradianceSampler, // ** not handled **
  334. #endif
  335. #ifndef LODBASEDMICROSFURACE
  336. #ifdef ${this._define3DName}
  337. ${this._cubeSamplerName},
  338. ${this._cubeSamplerName},
  339. #else
  340. ${this._2DSamplerName},
  341. ${this._2DSamplerName},
  342. #endif
  343. #endif
  344. #ifdef REALTIME_FILTERING
  345. ${this._vReflectionFilteringInfoName},
  346. #endif
  347. reflectionOut
  348. );
  349. #endif\r\n`;
  350. return code;
  351. }
  352. protected _buildBlock(state: NodeMaterialBuildState) {
  353. this._scene = state.sharedData.scene;
  354. if (state.target !== NodeMaterialBlockTargets.Fragment) {
  355. this._defineLODReflectionAlpha = state._getFreeDefineName("LODINREFLECTIONALPHA");
  356. this._defineLinearSpecularReflection = state._getFreeDefineName("LINEARSPECULARREFLECTION");
  357. }
  358. return this;
  359. }
  360. protected _dumpPropertiesCode() {
  361. let codeString: string = super._dumpPropertiesCode();
  362. if (this.texture) {
  363. codeString += `${this._codeVariableName}.texture.gammaSpace = ${this.texture.gammaSpace};\r\n`;
  364. }
  365. codeString += `${this._codeVariableName}.useSphericalHarmonics = ${this.useSphericalHarmonics};\r\n`;
  366. codeString += `${this._codeVariableName}.forceIrradianceInFragment = ${this.forceIrradianceInFragment};\r\n`;
  367. return codeString;
  368. }
  369. public serialize(): any {
  370. let serializationObject = super.serialize();
  371. serializationObject.useSphericalHarmonics = this.useSphericalHarmonics;
  372. serializationObject.forceIrradianceInFragment = this.forceIrradianceInFragment;
  373. serializationObject.gammaSpace = this.texture?.gammaSpace ?? true;
  374. return serializationObject;
  375. }
  376. public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
  377. super._deserialize(serializationObject, scene, rootUrl);
  378. this.useSphericalHarmonics = serializationObject.useSphericalHarmonics;
  379. this.forceIrradianceInFragment = serializationObject.forceIrradianceInFragment;
  380. if (this.texture) {
  381. this.texture.gammaSpace = serializationObject.gammaSpace;
  382. }
  383. }
  384. }
  385. _TypeStore.RegisteredTypes["BABYLON.ReflectionBlock"] = ReflectionBlock;