WebGLNodeBuilder.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. import { defaultShaderStages, NodeFrame, MathNode, GLSLNodeParser, NodeBuilder } from 'three/nodes';
  2. import SlotNode from './SlotNode.js';
  3. import { PerspectiveCamera, ShaderChunk, ShaderLib, UniformsUtils, UniformsLib,
  4. LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
  5. const nodeFrame = new NodeFrame();
  6. nodeFrame.camera = new PerspectiveCamera();
  7. const nodeShaderLib = {
  8. LineBasicNodeMaterial: ShaderLib.basic,
  9. MeshBasicNodeMaterial: ShaderLib.basic,
  10. PointsNodeMaterial: ShaderLib.points,
  11. MeshStandardNodeMaterial: ShaderLib.standard,
  12. MeshPhysicalNodeMaterial: ShaderLib.physical
  13. };
  14. const glslMethods = {
  15. [ MathNode.ATAN2 ]: 'atan'
  16. };
  17. function getIncludeSnippet( name ) {
  18. return `#include <${name}>`;
  19. }
  20. function getShaderStageProperty( shaderStage ) {
  21. return `${shaderStage}Shader`;
  22. }
  23. class WebGLNodeBuilder extends NodeBuilder {
  24. constructor( object, renderer, shader ) {
  25. super( object, renderer, new GLSLNodeParser() );
  26. this.shader = shader;
  27. this.slots = { vertex: [], fragment: [] };
  28. this._parseShaderLib();
  29. this._parseInclude( 'fragment', 'lights_physical_fragment', 'clearcoat_normal_fragment_begin', 'transmission_fragment' );
  30. this._parseObject();
  31. this._sortSlotsToFlow();
  32. }
  33. getMethod( method ) {
  34. return glslMethods[ method ] || method;
  35. }
  36. addSlot( shaderStage, slotNode ) {
  37. this.slots[ shaderStage ].push( slotNode );
  38. }
  39. addFlowCode( code ) {
  40. if ( ! /;\s*$/.test( code ) ) {
  41. code += ';';
  42. }
  43. super.addFlowCode( code + '\n\t' );
  44. }
  45. _parseShaderLib() {
  46. const type = this.material.type;
  47. // shader lib
  48. if ( nodeShaderLib[ type ] !== undefined ) {
  49. const shaderLib = nodeShaderLib[ type ];
  50. const shader = this.shader;
  51. shader.vertexShader = shaderLib.vertexShader;
  52. shader.fragmentShader = shaderLib.fragmentShader;
  53. shader.uniforms = UniformsUtils.merge( [ shaderLib.uniforms, UniformsLib.lights ] );
  54. }
  55. }
  56. _parseObject() {
  57. const { material, renderer } = this;
  58. if ( renderer.toneMappingNode?.isNode === true ) {
  59. this.addSlot( 'fragment', new SlotNode( {
  60. node: material.colorNode,
  61. nodeType: 'vec4',
  62. source: getIncludeSnippet( 'tonemapping_fragment' ),
  63. target: ''
  64. } ) );
  65. }
  66. // parse inputs
  67. if ( material.colorNode && material.colorNode.isNode ) {
  68. this.addSlot( 'fragment', new SlotNode( {
  69. node: material.colorNode,
  70. nodeType: 'vec4',
  71. source: getIncludeSnippet( 'color_fragment' ),
  72. target: 'diffuseColor = %RESULT%;',
  73. inclusionType: 'append'
  74. } ) );
  75. }
  76. if ( material.opacityNode && material.opacityNode.isNode ) {
  77. this.addSlot( 'fragment', new SlotNode( {
  78. node: material.opacityNode,
  79. nodeType: 'float',
  80. source: getIncludeSnippet( 'alphatest_fragment' ),
  81. target: 'diffuseColor.a = %RESULT%;',
  82. inclusionType: 'append'
  83. } ) );
  84. }
  85. if ( material.normalNode && material.normalNode.isNode ) {
  86. this.addSlot( 'fragment', new SlotNode( {
  87. node: material.normalNode,
  88. nodeType: 'vec3',
  89. source: getIncludeSnippet( 'normal_fragment_begin' ),
  90. target: 'normal = %RESULT%;',
  91. inclusionType: 'append'
  92. } ) );
  93. }
  94. if ( material.emissiveNode && material.emissiveNode.isNode ) {
  95. this.addSlot( 'fragment', new SlotNode( {
  96. node: material.emissiveNode,
  97. nodeType: 'vec3',
  98. source: getIncludeSnippet( 'emissivemap_fragment' ),
  99. target: 'totalEmissiveRadiance = %RESULT%;',
  100. inclusionType: 'append'
  101. } ) );
  102. }
  103. if ( material.isMeshStandardNodeMaterial ) {
  104. if ( material.metalnessNode && material.metalnessNode.isNode ) {
  105. this.addSlot( 'fragment', new SlotNode( {
  106. node: material.metalnessNode,
  107. nodeType: 'float',
  108. source: getIncludeSnippet( 'metalnessmap_fragment' ),
  109. target: 'metalnessFactor = %RESULT%;',
  110. inclusionType: 'append'
  111. } ) );
  112. }
  113. if ( material.roughnessNode && material.roughnessNode.isNode ) {
  114. this.addSlot( 'fragment', new SlotNode( {
  115. node: material.roughnessNode,
  116. nodeType: 'float',
  117. source: getIncludeSnippet( 'roughnessmap_fragment' ),
  118. target: 'roughnessFactor = %RESULT%;',
  119. inclusionType: 'append'
  120. } ) );
  121. }
  122. if ( material.isMeshPhysicalNodeMaterial ) {
  123. if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
  124. this.addSlot( 'fragment', new SlotNode( {
  125. node: material.clearcoatNode,
  126. nodeType: 'float',
  127. source: 'material.clearcoat = clearcoat;',
  128. target: 'material.clearcoat = %RESULT%;'
  129. } ) );
  130. if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
  131. this.addSlot( 'fragment', new SlotNode( {
  132. node: material.clearcoatRoughnessNode,
  133. nodeType: 'float',
  134. source: 'material.clearcoatRoughness = clearcoatRoughness;',
  135. target: 'material.clearcoatRoughness = %RESULT%;'
  136. } ) );
  137. }
  138. if ( material.clearcoatNormalNode && material.clearcoatNormalNode.isNode ) {
  139. this.addSlot( 'fragment', new SlotNode( {
  140. node: material.clearcoatNormalNode,
  141. nodeType: 'vec3',
  142. source: 'vec3 clearcoatNormal = geometryNormal;',
  143. target: 'vec3 clearcoatNormal = %RESULT%;'
  144. } ) );
  145. }
  146. material.defines.USE_CLEARCOAT = '';
  147. } else {
  148. delete material.defines.USE_CLEARCOAT;
  149. }
  150. if ( material.sheenNode && material.sheenNode.isNode ) {
  151. this.addSlot( 'fragment', new SlotNode( {
  152. node: material.sheenNode,
  153. nodeType: 'vec3',
  154. source: 'material.sheenColor = sheenColor;',
  155. target: 'material.sheenColor = %RESULT%;'
  156. } ) );
  157. if ( material.sheenRoughnessNode && material.sheenRoughnessNode.isNode ) {
  158. this.addSlot( 'fragment', new SlotNode( {
  159. node: material.sheenRoughnessNode,
  160. nodeType: 'float',
  161. source: 'material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );',
  162. target: 'material.sheenRoughness = clamp( %RESULT%, 0.07, 1.0 );'
  163. } ) );
  164. }
  165. material.defines.USE_SHEEN = '';
  166. } else {
  167. delete material.defines.USE_SHEEN;
  168. }
  169. if ( material.iridescenceNode && material.iridescenceNode.isNode ) {
  170. this.addSlot( 'fragment', new SlotNode( {
  171. node: material.iridescenceNode,
  172. nodeType: 'float',
  173. source: 'material.iridescence = iridescence;',
  174. target: 'material.iridescence = %RESULT%;'
  175. } ) );
  176. if ( material.iridescenceIORNode && material.iridescenceIORNode.isNode ) {
  177. this.addSlot( 'fragment', new SlotNode( {
  178. node: material.iridescenceIORNode,
  179. nodeType: 'float',
  180. source: 'material.iridescenceIOR = iridescenceIOR;',
  181. target: 'material.iridescenceIOR = %RESULT%;'
  182. } ) );
  183. }
  184. if ( material.iridescenceThicknessNode && material.iridescenceThicknessNode.isNode ) {
  185. this.addSlot( 'fragment', new SlotNode( {
  186. node: material.iridescenceThicknessNode,
  187. nodeType: 'float',
  188. source: 'material.iridescenceThickness = iridescenceThicknessMaximum;',
  189. target: 'material.iridescenceThickness = %RESULT%;'
  190. } ) );
  191. }
  192. material.defines.USE_IRIDESCENCE = '';
  193. } else {
  194. delete material.defines.USE_IRIDESCENCE;
  195. }
  196. if ( material.iorNode && material.iorNode.isNode ) {
  197. this.addSlot( 'fragment', new SlotNode( {
  198. node: material.iorNode,
  199. nodeType: 'float',
  200. source: 'material.ior = ior;',
  201. target: 'material.ior = %RESULT%;'
  202. } ) );
  203. }
  204. if ( material.specularColorNode && material.specularColorNode.isNode ) {
  205. this.addSlot( 'fragment', new SlotNode( {
  206. node: material.specularColorNode,
  207. nodeType: 'vec3',
  208. source: 'vec3 specularColorFactor = specularColor;',
  209. target: 'vec3 specularColorFactor = %RESULT%;'
  210. } ) );
  211. }
  212. if ( material.specularIntensityNode && material.specularIntensityNode.isNode ) {
  213. this.addSlot( 'fragment', new SlotNode( {
  214. node: material.specularIntensityNode,
  215. nodeType: 'float',
  216. source: 'float specularIntensityFactor = specularIntensity;',
  217. target: 'float specularIntensityFactor = %RESULT%;'
  218. } ) );
  219. }
  220. if ( material.transmissionNode && material.transmissionNode.isNode ) {
  221. this.addSlot( 'fragment', new SlotNode( {
  222. node: material.transmissionNode,
  223. nodeType: 'float',
  224. source: 'material.transmission = transmission;',
  225. target: 'material.transmission = %RESULT%;'
  226. } ) );
  227. if ( material.thicknessNode && material.thicknessNode.isNode ) {
  228. this.addSlot( 'fragment', new SlotNode( {
  229. node: material.thicknessNode,
  230. nodeType: 'float',
  231. source: 'material.thickness = thickness;',
  232. target: 'material.thickness = %RESULT%;'
  233. } ) );
  234. }
  235. if ( material.thicknessNode && material.thicknessNode.isNode ) {
  236. this.addSlot( 'fragment', new SlotNode( {
  237. node: material.thicknessNode,
  238. nodeType: 'float',
  239. source: 'material.thickness = thickness;',
  240. target: 'material.thickness = %RESULT%;'
  241. } ) );
  242. }
  243. if ( material.attenuationDistanceNode && material.attenuationDistanceNode.isNode ) {
  244. this.addSlot( 'fragment', new SlotNode( {
  245. node: material.attenuationDistanceNode,
  246. nodeType: 'float',
  247. source: 'material.attenuationDistance = attenuationDistance;',
  248. target: 'material.attenuationDistance = %RESULT%;'
  249. } ) );
  250. }
  251. if ( material.attenuationColorNode && material.attenuationColorNode.isNode ) {
  252. this.addSlot( 'fragment', new SlotNode( {
  253. node: material.attenuationColorNode,
  254. nodeType: 'vec3',
  255. source: 'material.attenuationColor = attenuationColor;',
  256. target: 'material.attenuationColor = %RESULT%;'
  257. } ) );
  258. }
  259. material.transmission = 1;
  260. material.defines.USE_TRANSMISSION = '';
  261. } else {
  262. material.transmission = 0;
  263. delete material.defines.USE_TRANSMISSION;
  264. }
  265. }
  266. }
  267. //
  268. if ( material.positionNode && material.positionNode.isNode ) {
  269. this.addSlot( 'vertex', new SlotNode( {
  270. node: material.positionNode,
  271. nodeType: 'vec3',
  272. source: getIncludeSnippet( 'begin_vertex' ),
  273. target: 'transformed = %RESULT%;',
  274. inclusionType: 'append'
  275. } ) );
  276. }
  277. if ( material.sizeNode && material.sizeNode.isNode ) {
  278. this.addSlot( 'vertex', new SlotNode( {
  279. node: material.sizeNode,
  280. nodeType: 'float',
  281. source: 'gl_PointSize = size;',
  282. target: 'gl_PointSize = %RESULT%;'
  283. } ) );
  284. }
  285. }
  286. getTexture( textureProperty, uvSnippet ) {
  287. return `texture2D( ${textureProperty}, ${uvSnippet} )`;
  288. }
  289. getTextureBias( textureProperty, uvSnippet, biasSnippet ) {
  290. if ( this.material.extensions !== undefined ) this.material.extensions.shaderTextureLOD = true;
  291. return `textureLod( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
  292. }
  293. getCubeTexture( textureProperty, uvSnippet ) {
  294. return `textureCube( ${textureProperty}, ${uvSnippet} )`;
  295. }
  296. getCubeTextureBias( textureProperty, uvSnippet, biasSnippet ) {
  297. if ( this.material.extensions !== undefined ) this.material.extensions.shaderTextureLOD = true;
  298. return `textureLod( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
  299. }
  300. getUniforms( shaderStage ) {
  301. const uniforms = this.uniforms[ shaderStage ];
  302. let snippet = '';
  303. for ( const uniform of uniforms ) {
  304. if ( uniform.type === 'texture' ) {
  305. snippet += `uniform sampler2D ${uniform.name}; `;
  306. } else if ( uniform.type === 'cubeTexture' ) {
  307. snippet += `uniform samplerCube ${uniform.name}; `;
  308. } else {
  309. const vectorType = this.getVectorType( uniform.type );
  310. snippet += `uniform ${vectorType} ${uniform.name}; `;
  311. }
  312. }
  313. return snippet;
  314. }
  315. getAttributes( shaderStage ) {
  316. let snippet = '';
  317. if ( shaderStage === 'vertex' ) {
  318. const attributes = this.attributes;
  319. for ( const attribute of attributes ) {
  320. // ignore common attributes to prevent redefinitions
  321. if ( attribute.name === 'uv' || attribute.name === 'position' || attribute.name === 'normal' )
  322. continue;
  323. snippet += `attribute ${attribute.type} ${attribute.name}; `;
  324. }
  325. }
  326. return snippet;
  327. }
  328. getVaryings( /* shaderStage */ ) {
  329. let snippet = '';
  330. const varyings = this.varyings;
  331. for ( const varying of varyings ) {
  332. snippet += `varying ${varying.type} ${varying.name}; `;
  333. }
  334. return snippet;
  335. }
  336. addCodeAfterCode( shaderStage, snippet, code ) {
  337. const shaderProperty = getShaderStageProperty( shaderStage );
  338. let source = this[ shaderProperty ];
  339. const index = source.indexOf( snippet );
  340. if ( index !== - 1 ) {
  341. const start = source.substring( 0, index + snippet.length );
  342. const end = source.substring( index + snippet.length );
  343. source = `${start}\n${code}\n${end}`;
  344. }
  345. this[ shaderProperty ] = source;
  346. }
  347. replaceCode( shaderStage, source, target ) {
  348. const shaderProperty = getShaderStageProperty( shaderStage );
  349. this[ shaderProperty ] = this[ shaderProperty ].replaceAll( source, target );
  350. }
  351. getTextureEncodingFromMap( map ) {
  352. const isWebGL2 = this.renderer.capabilities.isWebGL2;
  353. if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
  354. return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
  355. }
  356. return super.getTextureEncodingFromMap( map );
  357. }
  358. getFrontFacing() {
  359. return 'gl_FrontFacing';
  360. }
  361. buildCode() {
  362. const shaderData = {};
  363. for ( const shaderStage of defaultShaderStages ) {
  364. const uniforms = this.getUniforms( shaderStage );
  365. const attributes = this.getAttributes( shaderStage );
  366. const varyings = this.getVaryings( shaderStage );
  367. const vars = this.getVars( shaderStage );
  368. const codes = this.getCodes( shaderStage );
  369. shaderData[ shaderStage ] = `${this.getSignature()}
  370. // <node_builder>
  371. // uniforms
  372. ${uniforms}
  373. // attributes
  374. ${attributes}
  375. // varyings
  376. ${varyings}
  377. // vars
  378. ${vars}
  379. // codes
  380. ${codes}
  381. // </node_builder>
  382. ${this.shader[ getShaderStageProperty( shaderStage ) ]}
  383. `;
  384. }
  385. this.vertexShader = shaderData.vertex;
  386. this.fragmentShader = shaderData.fragment;
  387. }
  388. build() {
  389. super.build();
  390. this._addSnippets();
  391. this._addUniforms();
  392. this._updateUniforms();
  393. this.shader.vertexShader = this.vertexShader;
  394. this.shader.fragmentShader = this.fragmentShader;
  395. return this;
  396. }
  397. _parseInclude( shaderStage, ...includes ) {
  398. for ( const name of includes ) {
  399. const includeSnippet = getIncludeSnippet( name );
  400. const code = ShaderChunk[ name ];
  401. const shaderProperty = getShaderStageProperty( shaderStage );
  402. this.shader[ shaderProperty ] = this.shader[ shaderProperty ].replaceAll( includeSnippet, code );
  403. }
  404. }
  405. _sortSlotsToFlow() {
  406. for ( const shaderStage of defaultShaderStages ) {
  407. const sourceCode = this.shader[ getShaderStageProperty( shaderStage ) ];
  408. const slots = this.slots[ shaderStage ].sort( ( slotA, slotB ) => {
  409. if ( sourceCode.indexOf( slotA.source ) == - 1 ) {
  410. //console.log( slotA, sourceCode.indexOf( slotA.source ), sourceCode.indexOf( slotB.source ) );
  411. //console.log(sourceCode);
  412. }
  413. return sourceCode.indexOf( slotA.source ) > sourceCode.indexOf( slotB.source ) ? 1 : - 1;
  414. } );
  415. for ( const slotNode of slots ) {
  416. this.addFlow( shaderStage, slotNode );
  417. }
  418. }
  419. }
  420. _addSnippets() {
  421. for ( const shaderStage of defaultShaderStages ) {
  422. for ( const slotNode of this.slots[ shaderStage ] ) {
  423. const flowData = this.getFlowData( slotNode/*, shaderStage*/ );
  424. const inclusionType = slotNode.inclusionType;
  425. const source = slotNode.source;
  426. const target = flowData.code + '\n\t' + slotNode.target.replace( '%RESULT%', flowData.result );
  427. if ( inclusionType === 'append' ) {
  428. this.addCodeAfterCode( shaderStage, source, target );
  429. } else if ( inclusionType === 'replace' ) {
  430. this.replaceCode( shaderStage, source, target );
  431. } else {
  432. console.warn( `Inclusion type "${ inclusionType }" not compatible.` );
  433. }
  434. }
  435. this.addCodeAfterCode(
  436. shaderStage,
  437. 'main() {',
  438. this.flowCode[ shaderStage ]
  439. );
  440. }
  441. }
  442. _addUniforms() {
  443. for ( const shaderStage of defaultShaderStages ) {
  444. // uniforms
  445. for ( const uniform of this.uniforms[ shaderStage ] ) {
  446. this.shader.uniforms[ uniform.name ] = uniform;
  447. }
  448. }
  449. }
  450. _updateUniforms() {
  451. nodeFrame.object = this.object;
  452. nodeFrame.renderer = this.renderer;
  453. for ( const node of this.updateNodes ) {
  454. nodeFrame.updateNode( node );
  455. }
  456. }
  457. }
  458. export { WebGLNodeBuilder };