NodeMaterial.js 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310
  1. import { Material } from '../Material.js';
  2. import { NormalBlending } from '../../constants.js';
  3. import { hashArray, hashString } from '../../nodes/core/NodeUtils.js';
  4. import { output, diffuseColor, emissive, varyingProperty } from '../../nodes/core/PropertyNode.js';
  5. import { materialAlphaTest, materialColor, materialOpacity, materialEmissive, materialNormal, materialLightMap, materialAO } from '../../nodes/accessors/MaterialNode.js';
  6. import { modelViewProjection } from '../../nodes/accessors/ModelViewProjectionNode.js';
  7. import { normalLocal } from '../../nodes/accessors/Normal.js';
  8. import { instancedMesh } from '../../nodes/accessors/InstancedMeshNode.js';
  9. import { batch } from '../../nodes/accessors/BatchNode.js';
  10. import { materialReference } from '../../nodes/accessors/MaterialReferenceNode.js';
  11. import { positionLocal, positionView } from '../../nodes/accessors/Position.js';
  12. import { skinning } from '../../nodes/accessors/SkinningNode.js';
  13. import { morphReference } from '../../nodes/accessors/MorphNode.js';
  14. import { fwidth, mix, smoothstep } from '../../nodes/math/MathNode.js';
  15. import { float, vec3, vec4, bool } from '../../nodes/tsl/TSLBase.js';
  16. import AONode from '../../nodes/lighting/AONode.js';
  17. import { lightingContext } from '../../nodes/lighting/LightingContextNode.js';
  18. import IrradianceNode from '../../nodes/lighting/IrradianceNode.js';
  19. import { depth, viewZToLogarithmicDepth, viewZToOrthographicDepth } from '../../nodes/display/ViewportDepthNode.js';
  20. import { cameraFar, cameraNear, cameraProjectionMatrix } from '../../nodes/accessors/Camera.js';
  21. import { clipping, clippingAlpha, hardwareClipping } from '../../nodes/accessors/ClippingNode.js';
  22. import NodeMaterialObserver from './manager/NodeMaterialObserver.js';
  23. import getAlphaHashThreshold from '../../nodes/functions/material/getAlphaHashThreshold.js';
  24. import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
  25. import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
  26. import { premultiplyAlpha } from '../../nodes/display/BlendModes.js';
  27. import { subBuild } from '../../nodes/core/SubBuildNode.js';
  28. import { warn } from '../../utils.js';
  29. /**
  30. * Base class for all node materials.
  31. *
  32. * @augments Material
  33. */
  34. class NodeMaterial extends Material {
  35. static get type() {
  36. return 'NodeMaterial';
  37. }
  38. /**
  39. * Represents the type of the node material.
  40. *
  41. * @type {string}
  42. */
  43. get type() {
  44. return this.constructor.type;
  45. }
  46. set type( _value ) { /* */ }
  47. /**
  48. * Constructs a new node material.
  49. */
  50. constructor() {
  51. super();
  52. /**
  53. * This flag can be used for type testing.
  54. *
  55. * @type {boolean}
  56. * @readonly
  57. * @default true
  58. */
  59. this.isNodeMaterial = true;
  60. /**
  61. * Whether this material is affected by fog or not.
  62. *
  63. * @type {boolean}
  64. * @default true
  65. */
  66. this.fog = true;
  67. /**
  68. * Whether this material is affected by lights or not.
  69. *
  70. * @type {boolean}
  71. * @default false
  72. */
  73. this.lights = false;
  74. /**
  75. * Whether this material uses hardware clipping or not.
  76. * This property is managed by the engine and should not be
  77. * modified by apps.
  78. *
  79. * @type {boolean}
  80. * @default false
  81. */
  82. this.hardwareClipping = false;
  83. /**
  84. * Node materials which set their `lights` property to `true`
  85. * are affected by all lights of the scene. Sometimes selective
  86. * lighting is wanted which means only _some_ lights in the scene
  87. * affect a material. This can be achieved by creating an instance
  88. * of {@link LightsNode} with a list of selective
  89. * lights and assign the node to this property.
  90. *
  91. * ```js
  92. * const customLightsNode = lights( [ light1, light2 ] );
  93. * material.lightsNode = customLightsNode;
  94. * ```
  95. *
  96. * @type {?LightsNode}
  97. * @default null
  98. */
  99. this.lightsNode = null;
  100. /**
  101. * The environment of node materials can be defined by an environment
  102. * map assigned to the `envMap` property or by `Scene.environment`
  103. * if the node material is a PBR material. This node property allows to overwrite
  104. * the default behavior and define the environment with a custom node.
  105. *
  106. * ```js
  107. * material.envNode = pmremTexture( renderTarget.texture );
  108. * ```
  109. *
  110. * @type {?Node<vec3>}
  111. * @default null
  112. */
  113. this.envNode = null;
  114. /**
  115. * The lighting of node materials might be influenced by ambient occlusion.
  116. * The default AO is inferred from an ambient occlusion map assigned to `aoMap`
  117. * and the respective `aoMapIntensity`. This node property allows to overwrite
  118. * the default and define the ambient occlusion with a custom node instead.
  119. *
  120. * If you don't want to overwrite the diffuse color but modify the existing
  121. * values instead, use {@link materialAO}.
  122. *
  123. * @type {?Node<float>}
  124. * @default null
  125. */
  126. this.aoNode = null;
  127. /**
  128. * The diffuse color of node materials is by default inferred from the
  129. * `color` and `map` properties. This node property allows to overwrite the default
  130. * and define the diffuse color with a node instead.
  131. *
  132. * ```js
  133. * material.colorNode = color( 0xff0000 ); // define red color
  134. * ```
  135. *
  136. * If you don't want to overwrite the diffuse color but modify the existing
  137. * values instead, use {@link materialColor}.
  138. *
  139. * ```js
  140. * material.colorNode = materialColor.mul( color( 0xff0000 ) ); // give diffuse colors a red tint
  141. * ```
  142. *
  143. * @type {?Node<vec3>}
  144. * @default null
  145. */
  146. this.colorNode = null;
  147. /**
  148. * The normals of node materials are by default inferred from the `normalMap`/`normalScale`
  149. * or `bumpMap`/`bumpScale` properties. This node property allows to overwrite the default
  150. * and define the normals with a node instead.
  151. *
  152. * If you don't want to overwrite the normals but modify the existing values instead,
  153. * use {@link materialNormal}.
  154. *
  155. * @type {?Node<vec3>}
  156. * @default null
  157. */
  158. this.normalNode = null;
  159. /**
  160. * The opacity of node materials is by default inferred from the `opacity`
  161. * and `alphaMap` properties. This node property allows to overwrite the default
  162. * and define the opacity with a node instead.
  163. *
  164. * If you don't want to overwrite the normals but modify the existing
  165. * value instead, use {@link materialOpacity}.
  166. *
  167. * @type {?Node<float>}
  168. * @default null
  169. */
  170. this.opacityNode = null;
  171. /**
  172. * This node can be used to implement a variety of filter-like effects. The idea is
  173. * to store the current rendering into a texture e.g. via `viewportSharedTexture()`, use it
  174. * to create an arbitrary effect and then assign the node composition to this property.
  175. * Everything behind the object using this material will now be affected by a filter.
  176. *
  177. * ```js
  178. * const material = new NodeMaterial()
  179. * material.transparent = true;
  180. *
  181. * // everything behind the object will be monochromatic
  182. * material.backdropNode = saturation( viewportSharedTexture().rgb, 0 );
  183. * ```
  184. *
  185. * Backdrop computations are part of the lighting so only lit materials can use this property.
  186. *
  187. * @type {?Node<vec3>}
  188. * @default null
  189. */
  190. this.backdropNode = null;
  191. /**
  192. * This node allows to modulate the influence of `backdropNode` to the outgoing light.
  193. *
  194. * @type {?Node<float>}
  195. * @default null
  196. */
  197. this.backdropAlphaNode = null;
  198. /**
  199. * The alpha test of node materials is by default inferred from the `alphaTest`
  200. * property. This node property allows to overwrite the default and define the
  201. * alpha test with a node instead.
  202. *
  203. * If you don't want to overwrite the alpha test but modify the existing
  204. * value instead, use {@link materialAlphaTest}.
  205. *
  206. * @type {?Node<float>}
  207. * @default null
  208. */
  209. this.alphaTestNode = null;
  210. /**
  211. * Discards the fragment if the mask value is `false`.
  212. *
  213. * @type {?Node<bool>}
  214. * @default null
  215. */
  216. this.maskNode = null;
  217. /**
  218. * The local vertex positions are computed based on multiple factors like the
  219. * attribute data, morphing or skinning. This node property allows to overwrite
  220. * the default and define local vertex positions with nodes instead.
  221. *
  222. * If you don't want to overwrite the vertex positions but modify the existing
  223. * values instead, use {@link positionLocal}.
  224. *
  225. *```js
  226. * material.positionNode = positionLocal.add( displace );
  227. * ```
  228. *
  229. * @type {?Node<vec3>}
  230. * @default null
  231. */
  232. this.positionNode = null;
  233. /**
  234. * This node property is intended for logic which modifies geometry data once or per animation step.
  235. * Apps usually place such logic randomly in initialization routines or in the animation loop.
  236. * `geometryNode` is intended as a dedicated API so there is an intended spot where geometry modifications
  237. * can be implemented.
  238. *
  239. * The idea is to assign a `Fn` definition that holds the geometry modification logic. A typical example
  240. * would be a GPU based particle system that provides a node material for usage on app level. The particle
  241. * simulation would be implemented as compute shaders and managed inside a `Fn` function. This function is
  242. * eventually assigned to `geometryNode`.
  243. *
  244. * @type {?Function}
  245. * @default null
  246. */
  247. this.geometryNode = null;
  248. /**
  249. * Allows to overwrite depth values in the fragment shader.
  250. *
  251. * @type {?Node<float>}
  252. * @default null
  253. */
  254. this.depthNode = null;
  255. /**
  256. * Allows to overwrite the position used for shadow map rendering which
  257. * is by default {@link positionWorld}, the vertex position
  258. * in world space.
  259. *
  260. * @type {?Node<float>}
  261. * @default null
  262. */
  263. this.receivedShadowPositionNode = null;
  264. /**
  265. * Allows to overwrite the geometry position used for shadow map projection which
  266. * is by default {@link positionLocal}, the vertex position in local space.
  267. *
  268. * @type {?Node<float>}
  269. * @default null
  270. */
  271. this.castShadowPositionNode = null;
  272. /**
  273. * This node can be used to influence how an object using this node material
  274. * receive shadows.
  275. *
  276. * ```js
  277. * const totalShadows = float( 1 ).toVar();
  278. * material.receivedShadowNode = Fn( ( [ shadow ] ) => {
  279. * totalShadows.mulAssign( shadow );
  280. * //return float( 1 ); // bypass received shadows
  281. * return shadow.mix( color( 0xff0000 ), 1 ); // modify shadow color
  282. * } );
  283. *
  284. * @type {?(Function|FunctionNode<vec4>)}
  285. * @default null
  286. */
  287. this.receivedShadowNode = null;
  288. /**
  289. * This node can be used to influence how an object using this node material
  290. * casts shadows. To apply a color to shadows, you can simply do:
  291. *
  292. * ```js
  293. * material.castShadowNode = vec4( 1, 0, 0, 1 );
  294. * ```
  295. *
  296. * Which can be nice to fake colored shadows of semi-transparent objects. It
  297. * is also common to use the property with `Fn` function so checks are performed
  298. * per fragment.
  299. *
  300. * ```js
  301. * materialCustomShadow.castShadowNode = Fn( () => {
  302. * hash( vertexIndex ).greaterThan( 0.5 ).discard();
  303. * return materialColor;
  304. * } )();
  305. * ```
  306. *
  307. * @type {?Node<vec4>}
  308. * @default null
  309. */
  310. this.castShadowNode = null;
  311. /**
  312. * This node can be used to define the final output of the material.
  313. *
  314. * TODO: Explain the differences to `fragmentNode`.
  315. *
  316. * @type {?Node<vec4>}
  317. * @default null
  318. */
  319. this.outputNode = null;
  320. /**
  321. * MRT configuration is done on renderer or pass level. This node allows to
  322. * overwrite what values are written into MRT targets on material level. This
  323. * can be useful for implementing selective FX features that should only affect
  324. * specific objects.
  325. *
  326. * @type {?MRTNode}
  327. * @default null
  328. */
  329. this.mrtNode = null;
  330. /**
  331. * This node property can be used if you need complete freedom in implementing
  332. * the fragment shader. Assigning a node will replace the built-in material
  333. * logic used in the fragment stage.
  334. *
  335. * @type {?Node<vec4>}
  336. * @default null
  337. */
  338. this.fragmentNode = null;
  339. /**
  340. * This node property can be used if you need complete freedom in implementing
  341. * the vertex shader. Assigning a node will replace the built-in material logic
  342. * used in the vertex stage.
  343. *
  344. * @type {?Node<vec4>}
  345. * @default null
  346. */
  347. this.vertexNode = null;
  348. // Deprecated properties
  349. Object.defineProperty( this, 'shadowPositionNode', { // @deprecated, r176
  350. get: () => {
  351. return this.receivedShadowPositionNode;
  352. },
  353. set: ( value ) => {
  354. warn( 'NodeMaterial: ".shadowPositionNode" was renamed to ".receivedShadowPositionNode".' );
  355. this.receivedShadowPositionNode = value;
  356. }
  357. } );
  358. }
  359. /**
  360. * Returns an array of child nodes for this material.
  361. *
  362. * @private
  363. * @returns {Array<{property: string, childNode: Node}>}
  364. */
  365. _getNodeChildren() {
  366. const children = [];
  367. for ( const property of Object.getOwnPropertyNames( this ) ) {
  368. if ( property.startsWith( '_' ) === true ) continue;
  369. const object = this[ property ];
  370. if ( object && object.isNode === true ) {
  371. children.push( { property, childNode: object } );
  372. }
  373. }
  374. return children;
  375. }
  376. /**
  377. * Allows to define a custom cache key that influence the material key computation
  378. * for render objects.
  379. *
  380. * @return {string} The custom cache key.
  381. */
  382. customProgramCacheKey() {
  383. const values = [];
  384. for ( const { property, childNode } of this._getNodeChildren() ) {
  385. values.push( hashString( property.slice( 0, - 4 ) ), childNode.getCacheKey() );
  386. }
  387. return this.type + hashArray( values );
  388. }
  389. /**
  390. * Builds this material with the given node builder.
  391. *
  392. * @param {NodeBuilder} builder - The current node builder.
  393. */
  394. build( builder ) {
  395. this.setup( builder );
  396. }
  397. /**
  398. * Setups a node material observer with the given builder.
  399. *
  400. * @param {NodeBuilder} builder - The current node builder.
  401. * @return {NodeMaterialObserver} The node material observer.
  402. */
  403. setupObserver( builder ) {
  404. return new NodeMaterialObserver( builder );
  405. }
  406. /**
  407. * Setups the vertex and fragment stage of this node material.
  408. *
  409. * @param {NodeBuilder} builder - The current node builder.
  410. */
  411. setup( builder ) {
  412. builder.context.setupNormal = () => subBuild( this.setupNormal( builder ), 'NORMAL', 'vec3' );
  413. builder.context.setupPositionView = () => this.setupPositionView( builder );
  414. builder.context.setupModelViewProjection = () => this.setupModelViewProjection( builder );
  415. const renderer = builder.renderer;
  416. const renderTarget = renderer.getRenderTarget();
  417. // < VERTEX STAGE >
  418. builder.addStack();
  419. const mvp = subBuild( this.setupVertex( builder ), 'VERTEX' );
  420. const vertexNode = this.vertexNode || mvp;
  421. builder.stack.outputNode = vertexNode;
  422. this.setupHardwareClipping( builder );
  423. if ( this.geometryNode !== null ) {
  424. builder.stack.outputNode = builder.stack.outputNode.bypass( this.geometryNode );
  425. }
  426. builder.addFlow( 'vertex', builder.removeStack() );
  427. // < FRAGMENT STAGE >
  428. builder.addStack();
  429. let resultNode;
  430. const clippingNode = this.setupClipping( builder );
  431. if ( this.depthWrite === true || this.depthTest === true ) {
  432. // only write depth if depth buffer is configured
  433. if ( renderTarget !== null ) {
  434. if ( renderTarget.depthBuffer === true ) this.setupDepth( builder );
  435. } else {
  436. if ( renderer.depth === true ) this.setupDepth( builder );
  437. }
  438. }
  439. if ( this.fragmentNode === null ) {
  440. this.setupDiffuseColor( builder );
  441. this.setupVariants( builder );
  442. const outgoingLightNode = this.setupLighting( builder );
  443. if ( clippingNode !== null ) builder.stack.addToStack( clippingNode );
  444. // force unsigned floats - useful for RenderTargets
  445. const basicOutput = vec4( outgoingLightNode, diffuseColor.a ).max( 0 );
  446. resultNode = this.setupOutput( builder, basicOutput );
  447. // OUTPUT NODE
  448. output.assign( resultNode );
  449. //
  450. const isCustomOutput = this.outputNode !== null;
  451. if ( isCustomOutput ) resultNode = this.outputNode;
  452. // MRT
  453. if ( renderTarget !== null ) {
  454. const mrt = renderer.getMRT();
  455. const materialMRT = this.mrtNode;
  456. if ( mrt !== null ) {
  457. if ( isCustomOutput ) output.assign( resultNode );
  458. resultNode = mrt;
  459. if ( materialMRT !== null ) {
  460. resultNode = mrt.merge( materialMRT );
  461. }
  462. } else if ( materialMRT !== null ) {
  463. resultNode = materialMRT;
  464. }
  465. }
  466. } else {
  467. let fragmentNode = this.fragmentNode;
  468. if ( fragmentNode.isOutputStructNode !== true ) {
  469. fragmentNode = vec4( fragmentNode );
  470. }
  471. resultNode = this.setupOutput( builder, fragmentNode );
  472. }
  473. builder.stack.outputNode = resultNode;
  474. builder.addFlow( 'fragment', builder.removeStack() );
  475. // < OBSERVER >
  476. builder.observer = this.setupObserver( builder );
  477. }
  478. /**
  479. * Setups the clipping node.
  480. *
  481. * @param {NodeBuilder} builder - The current node builder.
  482. * @return {ClippingNode} The clipping node.
  483. */
  484. setupClipping( builder ) {
  485. if ( builder.clippingContext === null ) return null;
  486. const { unionPlanes, intersectionPlanes } = builder.clippingContext;
  487. let result = null;
  488. if ( unionPlanes.length > 0 || intersectionPlanes.length > 0 ) {
  489. const samples = builder.renderer.currentSamples;
  490. if ( this.alphaToCoverage && samples > 1 ) {
  491. // to be added to flow when the color/alpha value has been determined
  492. result = clippingAlpha();
  493. } else {
  494. builder.stack.addToStack( clipping() );
  495. }
  496. }
  497. return result;
  498. }
  499. /**
  500. * Setups the hardware clipping if available on the current device.
  501. *
  502. * @param {NodeBuilder} builder - The current node builder.
  503. */
  504. setupHardwareClipping( builder ) {
  505. this.hardwareClipping = false;
  506. if ( builder.clippingContext === null ) return;
  507. const candidateCount = builder.clippingContext.unionPlanes.length;
  508. // 8 planes supported by WebGL ANGLE_clip_cull_distance and WebGPU clip-distances
  509. if ( candidateCount > 0 && candidateCount <= 8 && builder.isAvailable( 'clipDistance' ) ) {
  510. builder.stack.addToStack( hardwareClipping() );
  511. this.hardwareClipping = true;
  512. }
  513. return;
  514. }
  515. /**
  516. * Setups the depth of this material.
  517. *
  518. * @param {NodeBuilder} builder - The current node builder.
  519. */
  520. setupDepth( builder ) {
  521. const { renderer, camera } = builder;
  522. // Depth
  523. let depthNode = this.depthNode;
  524. if ( depthNode === null ) {
  525. const mrt = renderer.getMRT();
  526. if ( mrt && mrt.has( 'depth' ) ) {
  527. depthNode = mrt.get( 'depth' );
  528. } else if ( renderer.logarithmicDepthBuffer === true ) {
  529. if ( camera.isPerspectiveCamera ) {
  530. depthNode = viewZToLogarithmicDepth( positionView.z, cameraNear, cameraFar );
  531. } else {
  532. depthNode = viewZToOrthographicDepth( positionView.z, cameraNear, cameraFar );
  533. }
  534. }
  535. }
  536. if ( depthNode !== null ) {
  537. depth.assign( depthNode ).toStack();
  538. }
  539. }
  540. /**
  541. * Setups the position node in view space. This method exists
  542. * so derived node materials can modify the implementation e.g. sprite materials.
  543. *
  544. * @param {NodeBuilder} builder - The current node builder.
  545. * @return {Node<vec3>} The position in view space.
  546. */
  547. setupPositionView( /*builder*/ ) {
  548. return modelViewMatrix.mul( positionLocal ).xyz;
  549. }
  550. /**
  551. * Setups the position in clip space.
  552. *
  553. * @param {NodeBuilder} builder - The current node builder.
  554. * @return {Node<vec4>} The position in view space.
  555. */
  556. setupModelViewProjection( /*builder*/ ) {
  557. return cameraProjectionMatrix.mul( positionView );
  558. }
  559. /**
  560. * Setups the logic for the vertex stage.
  561. *
  562. * @param {NodeBuilder} builder - The current node builder.
  563. * @return {Node<vec4>} The position in clip space.
  564. */
  565. setupVertex( builder ) {
  566. builder.addStack();
  567. this.setupPosition( builder );
  568. builder.context.vertex = builder.removeStack();
  569. return modelViewProjection;
  570. }
  571. /**
  572. * Setups the computation of the position in local space.
  573. *
  574. * @param {NodeBuilder} builder - The current node builder.
  575. * @return {Node<vec3>} The position in local space.
  576. */
  577. setupPosition( builder ) {
  578. const { object, geometry } = builder;
  579. if ( geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color ) {
  580. morphReference( object ).toStack();
  581. }
  582. if ( object.isSkinnedMesh === true ) {
  583. skinning( object ).toStack();
  584. }
  585. if ( this.displacementMap ) {
  586. const displacementMap = materialReference( 'displacementMap', 'texture' );
  587. const displacementScale = materialReference( 'displacementScale', 'float' );
  588. const displacementBias = materialReference( 'displacementBias', 'float' );
  589. positionLocal.addAssign( normalLocal.normalize().mul( ( displacementMap.x.mul( displacementScale ).add( displacementBias ) ) ) );
  590. }
  591. if ( object.isBatchedMesh ) {
  592. batch( object ).toStack();
  593. }
  594. if ( ( object.isInstancedMesh && object.instanceMatrix && object.instanceMatrix.isInstancedBufferAttribute === true ) ) {
  595. instancedMesh( object ).toStack();
  596. }
  597. if ( this.positionNode !== null ) {
  598. positionLocal.assign( subBuild( this.positionNode, 'POSITION', 'vec3' ) );
  599. }
  600. return positionLocal;
  601. }
  602. /**
  603. * Setups the computation of the material's diffuse color.
  604. *
  605. * @param {NodeBuilder} builder - The current node builder.
  606. * @param {BufferGeometry} geometry - The geometry.
  607. */
  608. setupDiffuseColor( { object, geometry } ) {
  609. // MASK
  610. if ( this.maskNode !== null ) {
  611. // Discard if the mask is `false`
  612. bool( this.maskNode ).not().discard();
  613. }
  614. // COLOR
  615. let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor;
  616. // VERTEX COLORS
  617. if ( this.vertexColors === true && geometry.hasAttribute( 'color' ) ) {
  618. colorNode = colorNode.mul( vertexColor() );
  619. }
  620. // INSTANCED COLORS
  621. if ( object.instanceColor ) {
  622. const instanceColor = varyingProperty( 'vec3', 'vInstanceColor' );
  623. colorNode = instanceColor.mul( colorNode );
  624. }
  625. if ( object.isBatchedMesh && object._colorsTexture ) {
  626. const batchColor = varyingProperty( 'vec3', 'vBatchColor' );
  627. colorNode = batchColor.mul( colorNode );
  628. }
  629. // DIFFUSE COLOR
  630. diffuseColor.assign( colorNode );
  631. // OPACITY
  632. const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;
  633. diffuseColor.a.assign( diffuseColor.a.mul( opacityNode ) );
  634. // ALPHA TEST
  635. let alphaTestNode = null;
  636. if ( this.alphaTestNode !== null || this.alphaTest > 0 ) {
  637. alphaTestNode = this.alphaTestNode !== null ? float( this.alphaTestNode ) : materialAlphaTest;
  638. if ( this.alphaToCoverage === true ) {
  639. diffuseColor.a = smoothstep( alphaTestNode, alphaTestNode.add( fwidth( diffuseColor.a ) ), diffuseColor.a );
  640. diffuseColor.a.lessThanEqual( 0 ).discard();
  641. } else {
  642. diffuseColor.a.lessThanEqual( alphaTestNode ).discard();
  643. }
  644. }
  645. // ALPHA HASH
  646. if ( this.alphaHash === true ) {
  647. diffuseColor.a.lessThan( getAlphaHashThreshold( positionLocal ) ).discard();
  648. }
  649. // OPAQUE
  650. const isOpaque = this.transparent === false && this.blending === NormalBlending && this.alphaToCoverage === false;
  651. if ( isOpaque ) {
  652. diffuseColor.a.assign( 1.0 );
  653. }
  654. }
  655. /**
  656. * Abstract interface method that can be implemented by derived materials
  657. * to setup material-specific node variables.
  658. *
  659. * @abstract
  660. * @param {NodeBuilder} builder - The current node builder.
  661. */
  662. setupVariants( /*builder*/ ) {
  663. // Interface function.
  664. }
  665. /**
  666. * Setups the outgoing light node variable
  667. *
  668. * @return {Node<vec3>} The outgoing light node.
  669. */
  670. setupOutgoingLight() {
  671. return ( this.lights === true ) ? vec3( 0 ) : diffuseColor.rgb;
  672. }
  673. /**
  674. * Setups the normal node from the material.
  675. *
  676. * @return {Node<vec3>} The normal node.
  677. */
  678. setupNormal() {
  679. return this.normalNode ? vec3( this.normalNode ) : materialNormal;
  680. }
  681. /**
  682. * Setups the environment node from the material.
  683. *
  684. * @param {NodeBuilder} builder - The current node builder.
  685. * @return {Node<vec4>} The environment node.
  686. */
  687. setupEnvironment( /*builder*/ ) {
  688. let node = null;
  689. if ( this.envNode ) {
  690. node = this.envNode;
  691. } else if ( this.envMap ) {
  692. node = this.envMap.isCubeTexture ? materialReference( 'envMap', 'cubeTexture' ) : materialReference( 'envMap', 'texture' );
  693. }
  694. return node;
  695. }
  696. /**
  697. * Setups the light map node from the material.
  698. *
  699. * @param {NodeBuilder} builder - The current node builder.
  700. * @return {Node<vec3>} The light map node.
  701. */
  702. setupLightMap( builder ) {
  703. let node = null;
  704. if ( builder.material.lightMap ) {
  705. node = new IrradianceNode( materialLightMap );
  706. }
  707. return node;
  708. }
  709. /**
  710. * Setups the lights node based on the scene, environment and material.
  711. *
  712. * @param {NodeBuilder} builder - The current node builder.
  713. * @return {LightsNode} The lights node.
  714. */
  715. setupLights( builder ) {
  716. const materialLightsNode = [];
  717. //
  718. const envNode = this.setupEnvironment( builder );
  719. if ( envNode && envNode.isLightingNode ) {
  720. materialLightsNode.push( envNode );
  721. }
  722. const lightMapNode = this.setupLightMap( builder );
  723. if ( lightMapNode && lightMapNode.isLightingNode ) {
  724. materialLightsNode.push( lightMapNode );
  725. }
  726. if ( this.aoNode !== null || builder.material.aoMap ) {
  727. const aoNode = this.aoNode !== null ? this.aoNode : materialAO;
  728. materialLightsNode.push( new AONode( aoNode ) );
  729. }
  730. let lightsN = this.lightsNode || builder.lightsNode;
  731. if ( materialLightsNode.length > 0 ) {
  732. lightsN = builder.renderer.lighting.createNode( [ ...lightsN.getLights(), ...materialLightsNode ] );
  733. }
  734. return lightsN;
  735. }
  736. /**
  737. * This method should be implemented by most derived materials
  738. * since it defines the material's lighting model.
  739. *
  740. * @abstract
  741. * @param {NodeBuilder} builder - The current node builder.
  742. * @return {LightingModel} The lighting model.
  743. */
  744. setupLightingModel( /*builder*/ ) {
  745. // Interface function.
  746. }
  747. /**
  748. * Setups the outgoing light node.
  749. *
  750. * @param {NodeBuilder} builder - The current node builder.
  751. * @return {Node<vec3>} The outgoing light node.
  752. */
  753. setupLighting( builder ) {
  754. const { material } = builder;
  755. const { backdropNode, backdropAlphaNode, emissiveNode } = this;
  756. // OUTGOING LIGHT
  757. const lights = this.lights === true || this.lightsNode !== null;
  758. const lightsNode = lights ? this.setupLights( builder ) : null;
  759. let outgoingLightNode = this.setupOutgoingLight( builder );
  760. if ( lightsNode && lightsNode.getScope().hasLights ) {
  761. const lightingModel = this.setupLightingModel( builder ) || null;
  762. outgoingLightNode = lightingContext( lightsNode, lightingModel, backdropNode, backdropAlphaNode );
  763. } else if ( backdropNode !== null ) {
  764. outgoingLightNode = vec3( backdropAlphaNode !== null ? mix( outgoingLightNode, backdropNode, backdropAlphaNode ) : backdropNode );
  765. }
  766. // EMISSIVE
  767. if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
  768. emissive.assign( vec3( emissiveNode ? emissiveNode : materialEmissive ) );
  769. outgoingLightNode = outgoingLightNode.add( emissive );
  770. }
  771. return outgoingLightNode;
  772. }
  773. /**
  774. * Setup the fog.
  775. *
  776. * @param {NodeBuilder} builder - The current node builder.
  777. * @param {Node<vec4>} outputNode - The existing output node.
  778. * @return {Node<vec4>} The output node.
  779. */
  780. setupFog( builder, outputNode ) {
  781. const fogNode = builder.fogNode;
  782. if ( fogNode ) {
  783. output.assign( outputNode );
  784. outputNode = vec4( fogNode.toVar() );
  785. }
  786. return outputNode;
  787. }
  788. /**
  789. * Setups premultiplied alpha.
  790. *
  791. * @param {NodeBuilder} builder - The current node builder.
  792. * @param {Node<vec4>} outputNode - The existing output node.
  793. * @return {Node<vec4>} The output node.
  794. */
  795. setupPremultipliedAlpha( builder, outputNode ) {
  796. return premultiplyAlpha( outputNode );
  797. }
  798. /**
  799. * Setups the output node.
  800. *
  801. * @param {NodeBuilder} builder - The current node builder.
  802. * @param {Node<vec4>} outputNode - The existing output node.
  803. * @return {Node<vec4>} The output node.
  804. */
  805. setupOutput( builder, outputNode ) {
  806. // FOG
  807. if ( this.fog === true ) {
  808. outputNode = this.setupFog( builder, outputNode );
  809. }
  810. // PREMULTIPLIED ALPHA
  811. if ( this.premultipliedAlpha === true ) {
  812. outputNode = this.setupPremultipliedAlpha( builder, outputNode );
  813. }
  814. return outputNode;
  815. }
  816. /**
  817. * Most classic material types have a node pendant e.g. for `MeshBasicMaterial`
  818. * there is `MeshBasicNodeMaterial`. This utility method is intended for
  819. * defining all material properties of the classic type in the node type.
  820. *
  821. * @param {Material} material - The material to copy properties with their values to this node material.
  822. */
  823. setDefaultValues( material ) {
  824. // This approach is to reuse the native refreshUniforms*
  825. // and turn available the use of features like transmission and environment in core
  826. for ( const property in material ) {
  827. const value = material[ property ];
  828. if ( this[ property ] === undefined ) {
  829. this[ property ] = value;
  830. if ( value && value.clone ) this[ property ] = value.clone();
  831. }
  832. }
  833. const descriptors = Object.getOwnPropertyDescriptors( material.constructor.prototype );
  834. for ( const key in descriptors ) {
  835. if ( Object.getOwnPropertyDescriptor( this.constructor.prototype, key ) === undefined &&
  836. descriptors[ key ].get !== undefined ) {
  837. Object.defineProperty( this.constructor.prototype, key, descriptors[ key ] );
  838. }
  839. }
  840. }
  841. /**
  842. * Serializes this material to JSON.
  843. *
  844. * @param {?(Object|string)} meta - The meta information for serialization.
  845. * @return {Object} The serialized node.
  846. */
  847. toJSON( meta ) {
  848. const isRoot = ( meta === undefined || typeof meta === 'string' );
  849. if ( isRoot ) {
  850. meta = {
  851. textures: {},
  852. images: {},
  853. nodes: {}
  854. };
  855. }
  856. const data = Material.prototype.toJSON.call( this, meta );
  857. data.inputNodes = {};
  858. for ( const { property, childNode } of this._getNodeChildren() ) {
  859. data.inputNodes[ property ] = childNode.toJSON( meta ).uuid;
  860. }
  861. // TODO: Copied from Object3D.toJSON
  862. function extractFromCache( cache ) {
  863. const values = [];
  864. for ( const key in cache ) {
  865. const data = cache[ key ];
  866. delete data.metadata;
  867. values.push( data );
  868. }
  869. return values;
  870. }
  871. if ( isRoot ) {
  872. const textures = extractFromCache( meta.textures );
  873. const images = extractFromCache( meta.images );
  874. const nodes = extractFromCache( meta.nodes );
  875. if ( textures.length > 0 ) data.textures = textures;
  876. if ( images.length > 0 ) data.images = images;
  877. if ( nodes.length > 0 ) data.nodes = nodes;
  878. }
  879. return data;
  880. }
  881. /**
  882. * Copies the properties of the given node material to this instance.
  883. *
  884. * @param {NodeMaterial} source - The material to copy.
  885. * @return {NodeMaterial} A reference to this node material.
  886. */
  887. copy( source ) {
  888. this.lightsNode = source.lightsNode;
  889. this.envNode = source.envNode;
  890. this.colorNode = source.colorNode;
  891. this.normalNode = source.normalNode;
  892. this.opacityNode = source.opacityNode;
  893. this.backdropNode = source.backdropNode;
  894. this.backdropAlphaNode = source.backdropAlphaNode;
  895. this.alphaTestNode = source.alphaTestNode;
  896. this.maskNode = source.maskNode;
  897. this.positionNode = source.positionNode;
  898. this.geometryNode = source.geometryNode;
  899. this.depthNode = source.depthNode;
  900. this.receivedShadowPositionNode = source.receivedShadowPositionNode;
  901. this.castShadowPositionNode = source.castShadowPositionNode;
  902. this.receivedShadowNode = source.receivedShadowNode;
  903. this.castShadowNode = source.castShadowNode;
  904. this.outputNode = source.outputNode;
  905. this.mrtNode = source.mrtNode;
  906. this.fragmentNode = source.fragmentNode;
  907. this.vertexNode = source.vertexNode;
  908. return super.copy( source );
  909. }
  910. }
  911. export default NodeMaterial;
粤ICP备19079148号