anisotropyBlock.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
  2. import { NodeMaterialBlock } from '../../nodeMaterialBlock';
  3. import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
  4. import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
  5. import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from '../../nodeMaterialBlockConnectionPoint';
  6. import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
  7. import { _TypeStore } from '../../../../Misc/typeStore';
  8. import { AbstractMesh } from '../../../../Meshes/abstractMesh';
  9. import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject";
  10. /**
  11. * Block used to implement the anisotropy module of the PBR material
  12. */
  13. export class AnisotropyBlock extends NodeMaterialBlock {
  14. /**
  15. * The two properties below are set by the main PBR block prior to calling methods of this class.
  16. * This is to avoid having to add them as inputs here whereas they are already inputs of the main block, so already known.
  17. * It's less burden on the user side in the editor part.
  18. */
  19. /** @hidden */
  20. public worldPositionConnectionPoint: NodeMaterialConnectionPoint;
  21. /** @hidden */
  22. public worldNormalConnectionPoint: NodeMaterialConnectionPoint;
  23. /**
  24. * Create a new AnisotropyBlock
  25. * @param name defines the block name
  26. */
  27. public constructor(name: string) {
  28. super(name, NodeMaterialBlockTargets.Fragment);
  29. this._isUnique = true;
  30. this.registerInput("intensity", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
  31. this.registerInput("direction", NodeMaterialBlockConnectionPointTypes.Vector2, true, NodeMaterialBlockTargets.Fragment);
  32. this.registerInput("texture", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
  33. this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2, true); // need this property and the next one in case there's no PerturbNormal block connected to the main PBR block
  34. this.registerInput("worldTangent", NodeMaterialBlockConnectionPointTypes.Vector4, true);
  35. this.registerOutput("anisotropy", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
  36. new NodeMaterialConnectionPointCustomObject("anisotropy", this, NodeMaterialConnectionPointDirection.Output, AnisotropyBlock, "AnisotropyBlock"));
  37. }
  38. /**
  39. * Initialize the block and prepare the context for build
  40. * @param state defines the state that will be used for the build
  41. */
  42. public initialize(state: NodeMaterialBuildState) {
  43. state._excludeVariableName("anisotropicOut");
  44. state._excludeVariableName("TBN");
  45. }
  46. /**
  47. * Gets the current class name
  48. * @returns the class name
  49. */
  50. public getClassName() {
  51. return "AnisotropyBlock";
  52. }
  53. /**
  54. * Gets the intensity input component
  55. */
  56. public get intensity(): NodeMaterialConnectionPoint {
  57. return this._inputs[0];
  58. }
  59. /**
  60. * Gets the direction input component
  61. */
  62. public get direction(): NodeMaterialConnectionPoint {
  63. return this._inputs[1];
  64. }
  65. /**
  66. * Gets the texture input component
  67. */
  68. public get texture(): NodeMaterialConnectionPoint {
  69. return this._inputs[2];
  70. }
  71. /**
  72. * Gets the uv input component
  73. */
  74. public get uv(): NodeMaterialConnectionPoint {
  75. return this._inputs[3];
  76. }
  77. /**
  78. * Gets the worldTangent input component
  79. */
  80. public get worldTangent(): NodeMaterialConnectionPoint {
  81. return this._inputs[4];
  82. }
  83. /**
  84. * Gets the anisotropy object output component
  85. */
  86. public get anisotropy(): NodeMaterialConnectionPoint {
  87. return this._outputs[0];
  88. }
  89. private _generateTBNSpace(state: NodeMaterialBuildState) {
  90. let code = "";
  91. let comments = `//${this.name}`;
  92. let uv = this.uv;
  93. let worldPosition = this.worldPositionConnectionPoint;
  94. let worldNormal = this.worldNormalConnectionPoint;
  95. let worldTangent = this.worldTangent;
  96. if (!uv.isConnected) {
  97. // we must set the uv input as optional because we may not end up in this method (in case a PerturbNormal block is linked to the PBR material)
  98. // in which case uv is not required. But if we do come here, we do need the uv, so we have to raise an error but not with throw, else
  99. // it will stop the building of the node material and will lead to errors in the editor!
  100. console.error("You must connect the 'uv' input of the Anisotropy block!");
  101. }
  102. state._emitExtension("derivatives", "#extension GL_OES_standard_derivatives : enable");
  103. let tangentReplaceString = { search: /defined\(TANGENT\)/g, replace: worldTangent.isConnected ? "defined(TANGENT)" : "defined(IGNORE)" };
  104. if (worldTangent.isConnected) {
  105. code += `vec3 tbnNormal = normalize(${worldNormal.associatedVariableName}.xyz);\r\n`;
  106. code += `vec3 tbnTangent = normalize(${worldTangent.associatedVariableName}.xyz);\r\n`;
  107. code += `vec3 tbnBitangent = cross(tbnNormal, tbnTangent);\r\n`;
  108. code += `mat3 vTBN = mat3(tbnTangent, tbnBitangent, tbnNormal);\r\n`;
  109. }
  110. code += `
  111. #if defined(${worldTangent.isConnected ? "TANGENT" : "IGNORE"}) && defined(NORMAL)
  112. mat3 TBN = vTBN;
  113. #else
  114. mat3 TBN = cotangent_frame(${worldNormal.associatedVariableName + ".xyz"}, ${"v_" + worldPosition.associatedVariableName + ".xyz"}, ${uv.isConnected ? uv.associatedVariableName : "vec2(0.)"}, vec2(1., 1.));
  115. #endif\r\n`;
  116. state._emitFunctionFromInclude("bumpFragmentMainFunctions", comments, {
  117. replaceStrings: [
  118. tangentReplaceString,
  119. ]
  120. });
  121. return code;
  122. }
  123. /**
  124. * Gets the main code of the block (fragment side)
  125. * @param state current state of the node material building
  126. * @param generateTBNSpace if true, the code needed to create the TBN coordinate space is generated
  127. * @returns the shader code
  128. */
  129. public getCode(state: NodeMaterialBuildState, generateTBNSpace = false): string {
  130. let code = "";
  131. if (generateTBNSpace) {
  132. code += this._generateTBNSpace(state);
  133. }
  134. const intensity = this.intensity.isConnected ? this.intensity.associatedVariableName : "1.0";
  135. const direction = this.direction.isConnected ? this.direction.associatedVariableName : "vec2(1., 0.)";
  136. const texture = this.texture.isConnected ? this.texture.associatedVariableName : "vec3(0.)";
  137. code += `anisotropicOutParams anisotropicOut;
  138. anisotropicBlock(
  139. vec3(${direction}, ${intensity}),
  140. #ifdef ANISOTROPIC_TEXTURE
  141. ${texture},
  142. #endif
  143. TBN,
  144. normalW,
  145. viewDirectionW,
  146. anisotropicOut
  147. );\r\n`;
  148. return code;
  149. }
  150. public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
  151. super.prepareDefines(mesh, nodeMaterial, defines);
  152. defines.setValue("ANISOTROPIC", true);
  153. defines.setValue("ANISOTROPIC_TEXTURE", this.texture.isConnected, true);
  154. }
  155. protected _buildBlock(state: NodeMaterialBuildState) {
  156. if (state.target === NodeMaterialBlockTargets.Fragment) {
  157. state.sharedData.blocksWithDefines.push(this);
  158. }
  159. return this;
  160. }
  161. }
  162. _TypeStore.RegisteredTypes["BABYLON.AnisotropyBlock"] = AnisotropyBlock;