WebGLNodeBuilder.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. import NodeBuilder from '../../nodes/core/NodeBuilder.js';
  2. import NodeSlot from '../../nodes/core/NodeSlot.js';
  3. import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
  4. import { ShaderChunk, LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
  5. const shaderStages = [ 'vertex', 'fragment' ];
  6. function getIncludeSnippet( name ) {
  7. return `#include <${name}>`;
  8. }
  9. function getShaderStageProperty( shaderStage ) {
  10. return `${shaderStage}Shader`;
  11. }
  12. class WebGLNodeBuilder extends NodeBuilder {
  13. constructor( material, renderer, shader ) {
  14. super( material, renderer );
  15. this.shader = shader;
  16. this._parseMaterial();
  17. }
  18. _parseMaterial() {
  19. const material = this.material;
  20. // parse inputs
  21. if ( material.colorNode && material.colorNode.isNode ) {
  22. this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );
  23. }
  24. if ( material.opacityNode && material.opacityNode.isNode ) {
  25. this.addSlot( 'fragment', new NodeSlot( material.opacityNode, 'OPACITY', 'float' ) );
  26. }
  27. if ( material.normalNode && material.normalNode.isNode ) {
  28. this.addSlot( 'fragment', new NodeSlot( material.normalNode, 'NORMAL', 'vec3' ) );
  29. }
  30. if ( material.emissiveNode && material.emissiveNode.isNode ) {
  31. this.addSlot( 'fragment', new NodeSlot( material.emissiveNode, 'EMISSIVE', 'vec3' ) );
  32. }
  33. if ( material.metalnessNode && material.metalnessNode.isNode ) {
  34. this.addSlot( 'fragment', new NodeSlot( material.metalnessNode, 'METALNESS', 'float' ) );
  35. }
  36. if ( material.roughnessNode && material.roughnessNode.isNode ) {
  37. this.addSlot( 'fragment', new NodeSlot( material.roughnessNode, 'ROUGHNESS', 'float' ) );
  38. }
  39. if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
  40. this.addSlot( 'fragment', new NodeSlot( material.clearcoatNode, 'CLEARCOAT', 'float' ) );
  41. }
  42. if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
  43. this.addSlot( 'fragment', new NodeSlot( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) );
  44. }
  45. if ( material.envNode && material.envNode.isNode ) {
  46. const envRadianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.RADIANCE, material.envNode );
  47. const envIrradianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.IRRADIANCE, material.envNode );
  48. this.addSlot( 'fragment', new NodeSlot( envRadianceNode, 'RADIANCE', 'vec3' ) );
  49. this.addSlot( 'fragment', new NodeSlot( envIrradianceNode, 'IRRADIANCE', 'vec3' ) );
  50. }
  51. if ( material.sizeNode && material.sizeNode.isNode ) {
  52. this.addSlot( 'vertex', new NodeSlot( material.sizeNode, 'SIZE', 'float' ) );
  53. }
  54. if ( material.positionNode && material.positionNode.isNode ) {
  55. this.addSlot( 'vertex', new NodeSlot( material.positionNode, 'POSITION', 'vec3' ) );
  56. }
  57. }
  58. getTexture( textureProperty, uvSnippet, biasSnippet = null ) {
  59. if ( biasSnippet !== null ) {
  60. return `texture2D( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
  61. } else {
  62. return `texture2D( ${textureProperty}, ${uvSnippet} )`;
  63. }
  64. }
  65. getCubeTexture( textureProperty, uvSnippet, biasSnippet = null ) {
  66. const textureCube = 'textureCubeLodEXT'; // textureCubeLodEXT textureLod
  67. if ( biasSnippet !== null ) {
  68. return `${textureCube}( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
  69. } else {
  70. return `${textureCube}( ${textureProperty}, ${uvSnippet} )`;
  71. }
  72. }
  73. getUniforms( shaderStage ) {
  74. const uniforms = this.uniforms[ shaderStage ];
  75. let snippet = '';
  76. for ( const uniform of uniforms ) {
  77. if ( uniform.type === 'texture' ) {
  78. snippet += `uniform sampler2D ${uniform.name}; `;
  79. } else if ( uniform.type === 'cubeTexture' ) {
  80. snippet += `uniform samplerCube ${uniform.name}; `;
  81. } else {
  82. const vectorType = this.getVectorType( uniform.type );
  83. snippet += `uniform ${vectorType} ${uniform.name}; `;
  84. }
  85. }
  86. return snippet;
  87. }
  88. getAttributes( shaderStage ) {
  89. let snippet = '';
  90. if ( shaderStage === 'vertex' ) {
  91. const attributes = this.attributes;
  92. for ( let index = 0; index < attributes.length; index ++ ) {
  93. const attribute = attributes[ index ];
  94. // ignore common attributes to prevent redefinitions
  95. if ( attribute.name === 'uv' || attribute.name === 'position' || attribute.name === 'normal' )
  96. continue;
  97. snippet += `attribute ${attribute.type} ${attribute.name}; `;
  98. }
  99. }
  100. return snippet;
  101. }
  102. getVarys( shaderStage ) {
  103. let snippet = '';
  104. const varys = this.varys;
  105. for ( let index = 0; index < varys.length; index ++ ) {
  106. const vary = varys[ index ];
  107. snippet += `varying ${vary.type} ${vary.name}; `;
  108. }
  109. return snippet;
  110. }
  111. addCodeAfterSnippet( shaderStage, snippet, code ) {
  112. const shaderProperty = getShaderStageProperty( shaderStage );
  113. let source = this.shader[ shaderProperty ];
  114. const index = source.indexOf( snippet );
  115. if ( index !== - 1 ) {
  116. const start = source.substring( 0, index + snippet.length );
  117. const end = source.substring( index + snippet.length );
  118. source = `${start}\n${code}\n${end}`;
  119. }
  120. this.shader[ shaderProperty ] = source;
  121. }
  122. addCodeAfterInclude( shaderStage, includeName, code ) {
  123. const includeSnippet = getIncludeSnippet( includeName );
  124. this.addCodeAfterSnippet( shaderStage, includeSnippet, code );
  125. }
  126. replaceCode( shaderStage, source, target ) {
  127. const shaderProperty = getShaderStageProperty( shaderStage );
  128. this.shader[ shaderProperty ] = this.shader[ shaderProperty ].replaceAll( source, target );
  129. }
  130. parseInclude( shaderStage, ...includes ) {
  131. for ( const name of includes ) {
  132. const includeSnippet = getIncludeSnippet( name );
  133. const code = ShaderChunk[ name ];
  134. this.replaceCode( shaderStage, includeSnippet, code );
  135. }
  136. }
  137. getTextureEncodingFromMap( map ) {
  138. const isWebGL2 = this.renderer.capabilities.isWebGL2;
  139. if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
  140. return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
  141. }
  142. return super.getTextureEncodingFromMap( map );
  143. }
  144. build() {
  145. super.build();
  146. this._addSnippets();
  147. this._buildShader();
  148. return this;
  149. }
  150. _addSnippets() {
  151. this.parseInclude( 'fragment', 'lights_physical_fragment' );
  152. this.addCodeAfterInclude( 'fragment', 'normal_fragment_begin',
  153. `#ifdef NODE_NORMAL
  154. NODE_CODE_NORMAL
  155. normal = NODE_NORMAL;
  156. #endif` );
  157. this.addCodeAfterInclude( 'fragment', 'color_fragment',
  158. `#ifdef NODE_COLOR
  159. NODE_CODE_COLOR
  160. diffuseColor = NODE_COLOR;
  161. #endif` );
  162. this.addCodeAfterInclude( 'fragment', 'alphamap_fragment',
  163. `#ifdef NODE_OPACITY
  164. NODE_CODE_OPACITY
  165. diffuseColor.a *= NODE_OPACITY;
  166. #endif` );
  167. this.addCodeAfterInclude( 'fragment', 'emissivemap_fragment',
  168. `#ifdef NODE_EMISSIVE
  169. NODE_CODE_EMISSIVE
  170. totalEmissiveRadiance = NODE_EMISSIVE;
  171. #endif` );
  172. this.addCodeAfterInclude( 'fragment', 'roughnessmap_fragment',
  173. `#ifdef NODE_ROUGHNESS
  174. NODE_CODE_ROUGHNESS
  175. roughnessFactor = NODE_ROUGHNESS;
  176. #endif` );
  177. this.addCodeAfterInclude( 'fragment', 'metalnessmap_fragment',
  178. `#ifdef NODE_METALNESS
  179. NODE_CODE_METALNESS
  180. metalnessFactor = NODE_METALNESS;
  181. #endif` );
  182. this.addCodeAfterSnippet( 'fragment', 'material.clearcoatRoughness = clearcoatRoughness;',
  183. `#ifdef NODE_CLEARCOAT
  184. NODE_CODE_CLEARCOAT
  185. material.clearcoat = NODE_CLEARCOAT;
  186. #endif
  187. #ifdef NODE_CLEARCOAT_ROUGHNESS
  188. NODE_CODE_CLEARCOAT_ROUGHNESS
  189. material.clearcoatRoughness = NODE_CLEARCOAT_ROUGHNESS;
  190. #endif` );
  191. this.addCodeAfterInclude( 'fragment', 'lights_fragment_begin',
  192. `#ifdef NODE_RADIANCE
  193. NODE_CODE_RADIANCE
  194. radiance += NODE_RADIANCE;
  195. NODE_CODE_IRRADIANCE
  196. iblIrradiance += PI * NODE_IRRADIANCE;
  197. #endif` );
  198. this.addCodeAfterInclude( 'vertex', 'begin_vertex',
  199. `#ifdef NODE_POSITION
  200. NODE_CODE_POSITION
  201. transformed = NODE_POSITION;
  202. #endif` );
  203. this.addCodeAfterSnippet( 'vertex', 'gl_PointSize = size;',
  204. `#ifdef NODE_SIZE
  205. NODE_CODE_SIZE
  206. gl_PointSize = NODE_SIZE;
  207. #endif` );
  208. for ( const shaderStage of shaderStages ) {
  209. this.addCodeAfterSnippet( shaderStage, 'main() {',
  210. `#ifdef NODE_CODE
  211. NODE_CODE
  212. #endif` );
  213. }
  214. }
  215. _buildShader() {
  216. for ( const shaderStage of shaderStages ) {
  217. // uniforms
  218. for ( const uniform of this.uniforms[ shaderStage ] ) {
  219. this.shader.uniforms[ uniform.name ] = uniform;
  220. }
  221. // code
  222. const shaderProperty = getShaderStageProperty( shaderStage );
  223. const nodeCode = this[ shaderProperty ];
  224. this.shader[ shaderProperty ] = nodeCode + this.shader[ shaderProperty ];
  225. }
  226. }
  227. }
  228. export { WebGLNodeBuilder };