subSurfaceBlock.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import { NodeMaterialBlock } from '../../nodeMaterialBlock';
  2. import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
  3. import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
  4. import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from '../../nodeMaterialBlockConnectionPoint';
  5. import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
  6. import { _TypeStore } from '../../../../Misc/typeStore';
  7. import { editableInPropertyPage, PropertyTypeForEdition } from "../../nodeMaterialDecorator";
  8. import { InputBlock } from '../Input/inputBlock';
  9. import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject";
  10. import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
  11. import { AbstractMesh } from '../../../../Meshes/abstractMesh';
  12. import { ReflectionBlock } from './reflectionBlock';
  13. import { Nullable } from '../../../../types';
  14. import { RefractionBlock } from './refractionBlock';
  15. /**
  16. * Block used to implement the sub surface module of the PBR material
  17. */
  18. export class SubSurfaceBlock extends NodeMaterialBlock {
  19. /**
  20. * Create a new SubSurfaceBlock
  21. * @param name defines the block name
  22. */
  23. public constructor(name: string) {
  24. super(name, NodeMaterialBlockTargets.Fragment);
  25. this._isUnique = true;
  26. this.registerInput("minThickness", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
  27. this.registerInput("maxThickness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
  28. this.registerInput("thicknessTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
  29. this.registerInput("tintColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
  30. this.registerInput("translucencyIntensity", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
  31. this.registerInput("translucencyDiffusionDistance", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
  32. this.registerInput("refraction", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
  33. new NodeMaterialConnectionPointCustomObject("refraction", this, NodeMaterialConnectionPointDirection.Input, RefractionBlock, "RefractionBlock"));
  34. this.registerOutput("subsurface", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
  35. new NodeMaterialConnectionPointCustomObject("subsurface", this, NodeMaterialConnectionPointDirection.Output, SubSurfaceBlock, "SubSurfaceBlock"));
  36. }
  37. /**
  38. * Stores the intensity of the different subsurface effects in the thickness texture.
  39. * * the green channel is the translucency intensity.
  40. * * the blue channel is the scattering intensity.
  41. * * the alpha channel is the refraction intensity.
  42. */
  43. @editableInPropertyPage("Mask from thickness texture", PropertyTypeForEdition.Boolean, "PROPERTIES", { "notifiers": { "update": true }})
  44. public useMaskFromThicknessTexture: boolean = false;
  45. /**
  46. * Initialize the block and prepare the context for build
  47. * @param state defines the state that will be used for the build
  48. */
  49. public initialize(state: NodeMaterialBuildState) {
  50. state._excludeVariableName("subSurfaceOut");
  51. state._excludeVariableName("vThicknessParam");
  52. state._excludeVariableName("vTintColor");
  53. state._excludeVariableName("vSubSurfaceIntensity");
  54. }
  55. /**
  56. * Gets the current class name
  57. * @returns the class name
  58. */
  59. public getClassName() {
  60. return "SubSurfaceBlock";
  61. }
  62. /**
  63. * Gets the min thickness input component
  64. */
  65. public get minThickness(): NodeMaterialConnectionPoint {
  66. return this._inputs[0];
  67. }
  68. /**
  69. * Gets the max thickness input component
  70. */
  71. public get maxThickness(): NodeMaterialConnectionPoint {
  72. return this._inputs[1];
  73. }
  74. /**
  75. * Gets the thickness texture component
  76. */
  77. public get thicknessTexture(): NodeMaterialConnectionPoint {
  78. return this._inputs[2];
  79. }
  80. /**
  81. * Gets the tint color input component
  82. */
  83. public get tintColor(): NodeMaterialConnectionPoint {
  84. return this._inputs[3];
  85. }
  86. /**
  87. * Gets the translucency intensity input component
  88. */
  89. public get translucencyIntensity(): NodeMaterialConnectionPoint {
  90. return this._inputs[4];
  91. }
  92. /**
  93. * Gets the translucency diffusion distance input component
  94. */
  95. public get translucencyDiffusionDistance(): NodeMaterialConnectionPoint {
  96. return this._inputs[5];
  97. }
  98. /**
  99. * Gets the refraction object parameters
  100. */
  101. public get refraction(): NodeMaterialConnectionPoint {
  102. return this._inputs[6];
  103. }
  104. /**
  105. * Gets the sub surface object output component
  106. */
  107. public get subsurface(): NodeMaterialConnectionPoint {
  108. return this._outputs[0];
  109. }
  110. public autoConfigure(material: NodeMaterial) {
  111. if (!this.minThickness.isConnected) {
  112. let minThicknessInput = new InputBlock("SubSurface min thickness", NodeMaterialBlockTargets.Fragment, NodeMaterialBlockConnectionPointTypes.Float);
  113. minThicknessInput.value = 0;
  114. minThicknessInput.output.connectTo(this.minThickness);
  115. }
  116. }
  117. public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
  118. super.prepareDefines(mesh, nodeMaterial, defines);
  119. const translucencyEnabled = this.translucencyDiffusionDistance.isConnected || this.translucencyIntensity.isConnected;
  120. defines.setValue("SUBSURFACE", translucencyEnabled || this.refraction.isConnected, true);
  121. defines.setValue("SS_TRANSLUCENCY", translucencyEnabled, true);
  122. defines.setValue("SS_THICKNESSANDMASK_TEXTURE", this.thicknessTexture.isConnected, true);
  123. defines.setValue("SS_MASK_FROM_THICKNESS_TEXTURE", this.useMaskFromThicknessTexture, true);
  124. }
  125. /**
  126. * Gets the main code of the block (fragment side)
  127. * @param state current state of the node material building
  128. * @param ssBlock instance of a SubSurfaceBlock or null if the code must be generated without an active sub surface module
  129. * @param reflectionBlock instance of a ReflectionBlock null if the code must be generated without an active reflection module
  130. * @param worldPosVarName name of the variable holding the world position
  131. * @returns the shader code
  132. */
  133. public static GetCode(state: NodeMaterialBuildState, ssBlock: Nullable<SubSurfaceBlock>, reflectionBlock: Nullable<ReflectionBlock>, worldPosVarName: string): string {
  134. let code = "";
  135. const minThickness = ssBlock?.minThickness.isConnected ? ssBlock.minThickness.associatedVariableName : "0.";
  136. const maxThickness = ssBlock?.maxThickness.isConnected ? ssBlock.maxThickness.associatedVariableName : "1.";
  137. const thicknessTexture = ssBlock?.thicknessTexture.isConnected ? ssBlock.thicknessTexture.associatedVariableName : "vec4(0.)";
  138. const tintColor = ssBlock?.tintColor.isConnected ? ssBlock.tintColor.associatedVariableName : "vec3(1.)";
  139. const translucencyIntensity = ssBlock?.translucencyIntensity.isConnected ? ssBlock?.translucencyIntensity.associatedVariableName : "1.";
  140. const translucencyDiffusionDistance = ssBlock?.translucencyDiffusionDistance.isConnected ? ssBlock?.translucencyDiffusionDistance.associatedVariableName : "vec3(1.)";
  141. const refractionBlock: Nullable<RefractionBlock> = (ssBlock?.refraction.isConnected ? ssBlock?.refraction.connectedPoint?.ownerBlock : null) as Nullable<RefractionBlock>;
  142. const refractionTintAtDistance = refractionBlock?.tintAtDistance.isConnected ? refractionBlock.tintAtDistance.associatedVariableName : "1.";
  143. const refractionIntensity = refractionBlock?.intensity.isConnected ? refractionBlock.intensity.associatedVariableName : "1.";
  144. const refractionView = refractionBlock?.view.isConnected ? refractionBlock.view.associatedVariableName : "";
  145. code += refractionBlock?.getCode(state) ?? "";
  146. code += `subSurfaceOutParams subSurfaceOut;
  147. #ifdef SUBSURFACE
  148. vec2 vThicknessParam = vec2(${minThickness}, ${maxThickness} - ${minThickness});
  149. vec4 vTintColor = vec4(${tintColor}, ${refractionTintAtDistance});
  150. vec3 vSubSurfaceIntensity = vec3(${refractionIntensity}, ${translucencyIntensity}, 0.);
  151. subSurfaceBlock(
  152. vSubSurfaceIntensity,
  153. vThicknessParam,
  154. vTintColor,
  155. normalW,
  156. specularEnvironmentReflectance,
  157. #ifdef SS_THICKNESSANDMASK_TEXTURE
  158. ${thicknessTexture},
  159. #endif
  160. #ifdef REFLECTION
  161. #ifdef SS_TRANSLUCENCY
  162. ${reflectionBlock?._reflectionMatrixName},
  163. #ifdef USESPHERICALFROMREFLECTIONMAP
  164. #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX)
  165. reflectionOut.irradianceVector,
  166. #endif
  167. #endif
  168. #ifdef USEIRRADIANCEMAP
  169. irradianceSampler,
  170. #endif
  171. #endif
  172. #endif
  173. #ifdef SS_REFRACTION
  174. ${worldPosVarName}.xyz,
  175. viewDirectionW,
  176. ${refractionView},
  177. surfaceAlbedo,
  178. ${refractionBlock?._vRefractionInfosName ?? ""},
  179. ${refractionBlock?._refractionMatrixName ?? ""},
  180. ${refractionBlock?._vRefractionMicrosurfaceInfosName ?? ""},
  181. vLightingIntensity,
  182. #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
  183. alpha,
  184. #endif
  185. #ifdef ${refractionBlock?._defineLODRefractionAlpha ?? "IGNORE"}
  186. NdotVUnclamped,
  187. #endif
  188. #ifdef ${refractionBlock?._defineLinearSpecularRefraction ?? "IGNORE"}
  189. roughness,
  190. #else
  191. alphaG,
  192. #endif
  193. #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
  194. ${refractionBlock?._cubeSamplerName ?? ""},
  195. #else
  196. ${refractionBlock?._2DSamplerName ?? ""},
  197. #endif
  198. #ifndef LODBASEDMICROSFURACE
  199. #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
  200. ${refractionBlock?._cubeSamplerName ?? ""},
  201. ${refractionBlock?._cubeSamplerName ?? ""},
  202. #else
  203. ${refractionBlock?._2DSamplerName ?? ""},
  204. ${refractionBlock?._2DSamplerName ?? ""},
  205. #endif
  206. #endif
  207. #ifdef ANISOTROPIC
  208. anisotropicOut,
  209. #endif
  210. #endif
  211. #ifdef SS_TRANSLUCENCY
  212. ${translucencyDiffusionDistance},
  213. #endif
  214. subSurfaceOut
  215. );
  216. #ifdef SS_REFRACTION
  217. surfaceAlbedo = subSurfaceOut.surfaceAlbedo;
  218. #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
  219. alpha = subSurfaceOut.alpha;
  220. #endif
  221. #endif
  222. #else
  223. subSurfaceOut.specularEnvironmentReflectance = specularEnvironmentReflectance;
  224. #endif\r\n`;
  225. return code;
  226. }
  227. protected _buildBlock(state: NodeMaterialBuildState) {
  228. if (state.target === NodeMaterialBlockTargets.Fragment) {
  229. state.sharedData.blocksWithDefines.push(this);
  230. }
  231. return this;
  232. }
  233. }
  234. _TypeStore.RegisteredTypes["BABYLON.SubSurfaceBlock"] = SubSurfaceBlock;