ToonOutlinePassNode.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { float, nodeObject, normalize, vec4 } from '../tsl/TSLBase.js';
  2. import { Color } from '../../math/Color.js';
  3. import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
  4. import { cameraProjectionMatrix } from '../../nodes/accessors/Camera.js';
  5. import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
  6. import { positionLocal } from '../../nodes/accessors/Position.js';
  7. import { normalLocal } from '../../nodes/accessors/Normal.js';
  8. import { BackSide } from '../../constants.js';
  9. import PassNode from './PassNode.js';
  10. /**
  11. * Represents a render pass for producing a toon outline effect on compatible objects.
  12. * Only 3D objects with materials of type `MeshToonMaterial` and `MeshToonNodeMaterial`
  13. * will receive the outline.
  14. *
  15. * ```js
  16. * const postProcessing = new PostProcessing( renderer );
  17. *
  18. * const scenePass = toonOutlinePass( scene, camera );
  19. *
  20. * postProcessing.outputNode = scenePass;
  21. * ```
  22. * @augments PassNode
  23. */
  24. class ToonOutlinePassNode extends PassNode {
  25. static get type() {
  26. return 'ToonOutlinePassNode';
  27. }
  28. /**
  29. * Constructs a new outline pass node.
  30. *
  31. * @param {Scene} scene - A reference to the scene.
  32. * @param {Camera} camera - A reference to the camera.
  33. * @param {Node} colorNode - Defines the outline's color.
  34. * @param {Node} thicknessNode - Defines the outline's thickness.
  35. * @param {Node} alphaNode - Defines the outline's alpha.
  36. */
  37. constructor( scene, camera, colorNode, thicknessNode, alphaNode ) {
  38. super( PassNode.COLOR, scene, camera );
  39. /**
  40. * Defines the outline's color.
  41. *
  42. * @type {Node}
  43. */
  44. this.colorNode = colorNode;
  45. /**
  46. * Defines the outline's thickness.
  47. *
  48. * @type {Node}
  49. */
  50. this.thicknessNode = thicknessNode;
  51. /**
  52. * Defines the outline's alpha.
  53. *
  54. * @type {Node}
  55. */
  56. this.alphaNode = alphaNode;
  57. /**
  58. * An internal material cache.
  59. *
  60. * @private
  61. * @type {WeakMap}
  62. */
  63. this._materialCache = new WeakMap();
  64. }
  65. updateBefore( frame ) {
  66. const { renderer } = frame;
  67. const currentRenderObjectFunction = renderer.getRenderObjectFunction();
  68. renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, group, lightsNode, clippingContext ) => {
  69. // only render outline for supported materials
  70. if ( material.isMeshToonMaterial || material.isMeshToonNodeMaterial ) {
  71. if ( material.wireframe === false ) {
  72. const outlineMaterial = this._getOutlineMaterial( material );
  73. renderer.renderObject( object, scene, camera, geometry, outlineMaterial, group, lightsNode, clippingContext );
  74. }
  75. }
  76. // default
  77. renderer.renderObject( object, scene, camera, geometry, material, group, lightsNode, clippingContext );
  78. } );
  79. super.updateBefore( frame );
  80. renderer.setRenderObjectFunction( currentRenderObjectFunction );
  81. }
  82. /**
  83. * Creates the material used for outline rendering.
  84. *
  85. * @private
  86. * @return {NodeMaterial} The outline material.
  87. */
  88. _createMaterial() {
  89. const material = new NodeMaterial();
  90. material.isMeshToonOutlineMaterial = true;
  91. material.name = 'Toon_Outline';
  92. material.side = BackSide;
  93. // vertex node
  94. const outlineNormal = normalLocal.negate();
  95. const mvp = cameraProjectionMatrix.mul( modelViewMatrix );
  96. const ratio = float( 1.0 ); // TODO: support outline thickness ratio for each vertex
  97. const pos = mvp.mul( vec4( positionLocal, 1.0 ) );
  98. const pos2 = mvp.mul( vec4( positionLocal.add( outlineNormal ), 1.0 ) );
  99. const norm = normalize( pos.sub( pos2 ) ); // NOTE: subtract pos2 from pos because BackSide objectNormal is negative
  100. material.vertexNode = pos.add( norm.mul( this.thicknessNode ).mul( pos.w ).mul( ratio ) );
  101. // color node
  102. material.colorNode = vec4( this.colorNode, this.alphaNode );
  103. return material;
  104. }
  105. /**
  106. * For the given toon material, this method returns a correspoding
  107. * outline material.
  108. *
  109. * @private
  110. * @param {(MeshToonMaterial|MeshToonNodeMaterial)} originalMaterial - The toon material.
  111. * @return {NodeMaterial} The outline material.
  112. */
  113. _getOutlineMaterial( originalMaterial ) {
  114. let outlineMaterial = this._materialCache.get( originalMaterial );
  115. if ( outlineMaterial === undefined ) {
  116. outlineMaterial = this._createMaterial();
  117. this._materialCache.set( originalMaterial, outlineMaterial );
  118. }
  119. return outlineMaterial;
  120. }
  121. }
  122. export default ToonOutlinePassNode;
  123. export const toonOutlinePass = ( scene, camera, color = new Color( 0, 0, 0 ), thickness = 0.003, alpha = 1 ) => nodeObject( new ToonOutlinePassNode( scene, camera, nodeObject( color ), nodeObject( thickness ), nodeObject( alpha ) ) );
粤ICP备19079148号