ToonOutlinePassNode.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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. class ToonOutlinePassNode extends PassNode {
  11. static get type() {
  12. return 'ToonOutlinePassNode';
  13. }
  14. constructor( scene, camera, colorNode, thicknessNode, alphaNode ) {
  15. super( PassNode.COLOR, scene, camera );
  16. this.colorNode = colorNode;
  17. this.thicknessNode = thicknessNode;
  18. this.alphaNode = alphaNode;
  19. this._materialCache = new WeakMap();
  20. }
  21. updateBefore( frame ) {
  22. const { renderer } = frame;
  23. const currentRenderObjectFunction = renderer.getRenderObjectFunction();
  24. renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, group, lightsNode ) => {
  25. // only render outline for supported materials
  26. if ( material.isMeshToonMaterial || material.isMeshToonNodeMaterial ) {
  27. if ( material.wireframe === false ) {
  28. const outlineMaterial = this._getOutlineMaterial( material );
  29. renderer.renderObject( object, scene, camera, geometry, outlineMaterial, group, lightsNode );
  30. }
  31. }
  32. // default
  33. renderer.renderObject( object, scene, camera, geometry, material, group, lightsNode );
  34. } );
  35. super.updateBefore( frame );
  36. renderer.setRenderObjectFunction( currentRenderObjectFunction );
  37. }
  38. _createMaterial() {
  39. const material = new NodeMaterial();
  40. material.isMeshToonOutlineMaterial = true;
  41. material.name = 'Toon_Outline';
  42. material.side = BackSide;
  43. // vertex node
  44. const outlineNormal = normalLocal.negate();
  45. const mvp = cameraProjectionMatrix.mul( modelViewMatrix );
  46. const ratio = float( 1.0 ); // TODO: support outline thickness ratio for each vertex
  47. const pos = mvp.mul( vec4( positionLocal, 1.0 ) );
  48. const pos2 = mvp.mul( vec4( positionLocal.add( outlineNormal ), 1.0 ) );
  49. const norm = normalize( pos.sub( pos2 ) ); // NOTE: subtract pos2 from pos because BackSide objectNormal is negative
  50. material.vertexNode = pos.add( norm.mul( this.thicknessNode ).mul( pos.w ).mul( ratio ) );
  51. // color node
  52. material.colorNode = vec4( this.colorNode, this.alphaNode );
  53. return material;
  54. }
  55. _getOutlineMaterial( originalMaterial ) {
  56. let outlineMaterial = this._materialCache.get( originalMaterial );
  57. if ( outlineMaterial === undefined ) {
  58. outlineMaterial = this._createMaterial();
  59. this._materialCache.set( originalMaterial, outlineMaterial );
  60. }
  61. return outlineMaterial;
  62. }
  63. }
  64. export default ToonOutlinePassNode;
  65. 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号