WebGPUNodeBuilder.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import WebGPUUniformsGroup from './WebGPUUniformsGroup.js';
  2. import { FloatUniform, Vector3Uniform } from './WebGPUUniform.js';
  3. import WebGPUSampler from './WebGPUSampler.js';
  4. import { WebGPUSampledTexture } from './WebGPUSampledTexture.js';
  5. import NodeSlot from '../nodes/core/NodeSlot.js';
  6. import NodeBuilder from '../nodes/core/NodeBuilder.js';
  7. class WebGPUNodeUniformsGroup extends WebGPUUniformsGroup {
  8. constructor( shaderStage ) {
  9. super( 'nodeUniforms' );
  10. let shaderStageVisibility;
  11. if ( shaderStage === 'vertex' ) shaderStageVisibility = GPUShaderStage.VERTEX;
  12. else if ( shaderStage === 'fragment' ) shaderStageVisibility = GPUShaderStage.FRAGMENT;
  13. this.setVisibility( shaderStageVisibility );
  14. //this.setOnBeforeUpdate( this._onBeforeUpdate );
  15. }
  16. /*
  17. _onBeforeUpdate( object, camera ) {
  18. const material = object.material;
  19. }
  20. */
  21. }
  22. class WebGPUNodeBuilder extends NodeBuilder {
  23. constructor( material, renderer ) {
  24. super( material, renderer );
  25. this.bindingIndex = 2;
  26. this.bindings = { vertex: [], fragment: [] };
  27. this.uniformsGroup = {};
  28. this._parseMaterial();
  29. }
  30. _parseMaterial() {
  31. const material = this.material;
  32. if ( material.isMeshBasicMaterial || material.isPointsMaterial ) {
  33. if ( material.colorNode !== undefined ) {
  34. this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec3' ) );
  35. }
  36. if ( material.opacityNode !== undefined ) {
  37. this.addSlot( 'fragment', new NodeSlot( material.opacityNode, 'OPACITY', 'float' ) );
  38. }
  39. }
  40. }
  41. getTexture( textureSnippet, uvSnippet ) {
  42. return `texture( sampler2D( ${textureSnippet}, ${textureSnippet}_sampler ), ${uvSnippet} )`;
  43. }
  44. getPropertyName( nodeUniform ) {
  45. if ( nodeUniform.type === 'texture' ) {
  46. return nodeUniform.name;
  47. } else {
  48. return `nodeUniforms.${nodeUniform.name}`;
  49. }
  50. }
  51. getBindings( shaderStage ) {
  52. return this.bindings[ shaderStage ];
  53. }
  54. getUniformFromNode( node, shaderStage, type ) {
  55. const uniformNode = super.getUniformFromNode( node, shaderStage, type );
  56. const nodeData = this.getDataFromNode( node, shaderStage );
  57. if ( nodeData.webgpuUniform === undefined ) {
  58. let uniform;
  59. if ( type === 'texture' ) {
  60. const sampler = new WebGPUSampler( `${uniformNode.name}_sampler`, uniformNode.value );
  61. const texture = new WebGPUSampledTexture( uniformNode.name, uniformNode.value );
  62. // Array.unshift: add first textures in sequence
  63. this.bindings[ shaderStage ].unshift( sampler, texture );
  64. } else {
  65. let uniformsGroup = this.uniformsGroup[ shaderStage ];
  66. if ( uniformsGroup === undefined ) {
  67. uniformsGroup = new WebGPUNodeUniformsGroup( shaderStage );
  68. this.uniformsGroup[ shaderStage ] = uniformsGroup;
  69. this.bindings[ shaderStage ].push( uniformsGroup );
  70. }
  71. if ( type === 'float' ) {
  72. uniform = new FloatUniform( uniformNode );
  73. } else if ( type === 'vec3' ) {
  74. uniform = new Vector3Uniform( uniformNode.name, uniformNode.value );
  75. } else {
  76. throw new Error( `Uniform "${type}" not declared.` );
  77. }
  78. uniformsGroup.addUniform( uniform );
  79. }
  80. nodeData.webgpuUniform = uniform;
  81. }
  82. return uniformNode;
  83. }
  84. getUniformsOutput( shaderStage ) {
  85. const uniforms = this.uniforms[ shaderStage ];
  86. let uniformsCode = '';
  87. let uniformGroupCode = '';
  88. let bindingIndex = this.bindingIndex;
  89. for ( let uniform of uniforms ) {
  90. if (uniform.type === 'texture') {
  91. uniformsCode += `layout(set = 0, binding = ${bindingIndex++}) uniform sampler ${uniform.name}_sampler;`;
  92. uniformsCode += `layout(set = 0, binding = ${bindingIndex++}) uniform texture2D ${uniform.name};`;
  93. } else {
  94. if (!uniformGroupCode) {
  95. uniformGroupCode = `layout(set = 0, binding = ${bindingIndex++}) uniform NodeUniforms {`;
  96. }
  97. uniformGroupCode += `uniform ${uniform.type} ${uniform.name};`;
  98. }
  99. }
  100. if (uniformGroupCode) {
  101. uniformGroupCode += `} nodeUniforms;`;
  102. uniformsCode += uniformGroupCode;
  103. }
  104. console.log( uniformsCode );
  105. return uniformsCode;
  106. }
  107. buildShader( shaderStage, code ) {
  108. // use regex maybe for security?
  109. const versionStrIndex = code.indexOf( "\n" );
  110. let finalCode = code.substr( 0, versionStrIndex ) + "\n\n";
  111. const shaderCodes = this.build( shaderStage );
  112. finalCode += shaderCodes.defines;
  113. finalCode += code.substr( versionStrIndex );
  114. return finalCode;
  115. }
  116. parse( vertexShader, fragmentShader ) {
  117. vertexShader = this.buildShader( 'vertex', vertexShader );
  118. fragmentShader = this.buildShader( 'fragment', fragmentShader );
  119. return {
  120. vertexShader,
  121. fragmentShader
  122. };
  123. }
  124. }
  125. export default WebGPUNodeBuilder;