LineMaterial.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. import {
  2. ShaderLib,
  3. ShaderMaterial,
  4. UniformsLib,
  5. UniformsUtils,
  6. Vector2,
  7. } from 'three';
  8. UniformsLib.line = {
  9. worldUnits: { value: 1 },
  10. linewidth: { value: 1 },
  11. resolution: { value: new Vector2( 1, 1 ) },
  12. dashOffset: { value: 0 },
  13. dashScale: { value: 1 },
  14. dashSize: { value: 1 },
  15. gapSize: { value: 1 } // todo FIX - maybe change to totalSize
  16. };
  17. ShaderLib[ 'line' ] = {
  18. uniforms: UniformsUtils.merge( [
  19. UniformsLib.common,
  20. UniformsLib.fog,
  21. UniformsLib.line
  22. ] ),
  23. vertexShader:
  24. /* glsl */`
  25. #include <common>
  26. #include <color_pars_vertex>
  27. #include <fog_pars_vertex>
  28. #include <logdepthbuf_pars_vertex>
  29. #include <clipping_planes_pars_vertex>
  30. uniform float linewidth;
  31. uniform vec2 resolution;
  32. attribute vec3 instanceStart;
  33. attribute vec3 instanceEnd;
  34. attribute vec3 instanceColorStart;
  35. attribute vec3 instanceColorEnd;
  36. #ifdef WORLD_UNITS
  37. varying vec4 worldPos;
  38. varying vec3 worldStart;
  39. varying vec3 worldEnd;
  40. #ifdef USE_DASH
  41. varying vec2 vUv;
  42. #endif
  43. #else
  44. varying vec2 vUv;
  45. #endif
  46. #ifdef USE_DASH
  47. uniform float dashScale;
  48. attribute float instanceDistanceStart;
  49. attribute float instanceDistanceEnd;
  50. varying float vLineDistance;
  51. #endif
  52. void trimSegment( const in vec4 start, inout vec4 end ) {
  53. // trim end segment so it terminates between the camera plane and the near plane
  54. // conservative estimate of the near plane
  55. float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
  56. float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
  57. float nearEstimate = - 0.5 * b / a;
  58. float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
  59. end.xyz = mix( start.xyz, end.xyz, alpha );
  60. }
  61. void main() {
  62. #ifdef USE_COLOR
  63. vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
  64. #endif
  65. #ifdef USE_DASH
  66. vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
  67. vUv = uv;
  68. #endif
  69. float aspect = resolution.x / resolution.y;
  70. // camera space
  71. vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
  72. vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
  73. #ifdef WORLD_UNITS
  74. worldStart = start.xyz;
  75. worldEnd = end.xyz;
  76. #else
  77. vUv = uv;
  78. #endif
  79. // special case for perspective projection, and segments that terminate either in, or behind, the camera plane
  80. // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
  81. // but we need to perform ndc-space calculations in the shader, so we must address this issue directly
  82. // perhaps there is a more elegant solution -- WestLangley
  83. bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
  84. if ( perspective ) {
  85. if ( start.z < 0.0 && end.z >= 0.0 ) {
  86. trimSegment( start, end );
  87. } else if ( end.z < 0.0 && start.z >= 0.0 ) {
  88. trimSegment( end, start );
  89. }
  90. }
  91. // clip space
  92. vec4 clipStart = projectionMatrix * start;
  93. vec4 clipEnd = projectionMatrix * end;
  94. // ndc space
  95. vec3 ndcStart = clipStart.xyz / clipStart.w;
  96. vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
  97. // direction
  98. vec2 dir = ndcEnd.xy - ndcStart.xy;
  99. // account for clip-space aspect ratio
  100. dir.x *= aspect;
  101. dir = normalize( dir );
  102. #ifdef WORLD_UNITS
  103. vec3 worldDir = normalize( end.xyz - start.xyz );
  104. vec3 tmpFwd = normalize( mix( start.xyz, end.xyz, 0.5 ) );
  105. vec3 worldUp = normalize( cross( worldDir, tmpFwd ) );
  106. vec3 worldFwd = cross( worldDir, worldUp );
  107. worldPos = position.y < 0.5 ? start: end;
  108. // height offset
  109. float hw = linewidth * 0.5;
  110. worldPos.xyz += position.x < 0.0 ? hw * worldUp : - hw * worldUp;
  111. // don't extend the line if we're rendering dashes because we
  112. // won't be rendering the endcaps
  113. #ifndef USE_DASH
  114. // cap extension
  115. worldPos.xyz += position.y < 0.5 ? - hw * worldDir : hw * worldDir;
  116. // add width to the box
  117. worldPos.xyz += worldFwd * hw;
  118. // endcaps
  119. if ( position.y > 1.0 || position.y < 0.0 ) {
  120. worldPos.xyz -= worldFwd * 2.0 * hw;
  121. }
  122. #endif
  123. // project the worldpos
  124. vec4 clip = projectionMatrix * worldPos;
  125. // shift the depth of the projected points so the line
  126. // segments overlap neatly
  127. vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;
  128. clip.z = clipPose.z * clip.w;
  129. #else
  130. vec2 offset = vec2( dir.y, - dir.x );
  131. // undo aspect ratio adjustment
  132. dir.x /= aspect;
  133. offset.x /= aspect;
  134. // sign flip
  135. if ( position.x < 0.0 ) offset *= - 1.0;
  136. // endcaps
  137. if ( position.y < 0.0 ) {
  138. offset += - dir;
  139. } else if ( position.y > 1.0 ) {
  140. offset += dir;
  141. }
  142. // adjust for linewidth
  143. offset *= linewidth;
  144. // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
  145. offset /= resolution.y;
  146. // select end
  147. vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
  148. // back to clip space
  149. offset *= clip.w;
  150. clip.xy += offset;
  151. #endif
  152. gl_Position = clip;
  153. vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
  154. #include <logdepthbuf_vertex>
  155. #include <clipping_planes_vertex>
  156. #include <fog_vertex>
  157. }
  158. `,
  159. fragmentShader:
  160. /* glsl */`
  161. uniform vec3 diffuse;
  162. uniform float opacity;
  163. uniform float linewidth;
  164. #ifdef USE_DASH
  165. uniform float dashOffset;
  166. uniform float dashSize;
  167. uniform float gapSize;
  168. #endif
  169. varying float vLineDistance;
  170. #ifdef WORLD_UNITS
  171. varying vec4 worldPos;
  172. varying vec3 worldStart;
  173. varying vec3 worldEnd;
  174. #ifdef USE_DASH
  175. varying vec2 vUv;
  176. #endif
  177. #else
  178. varying vec2 vUv;
  179. #endif
  180. #include <common>
  181. #include <color_pars_fragment>
  182. #include <fog_pars_fragment>
  183. #include <logdepthbuf_pars_fragment>
  184. #include <clipping_planes_pars_fragment>
  185. vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
  186. float mua;
  187. float mub;
  188. vec3 p13 = p1 - p3;
  189. vec3 p43 = p4 - p3;
  190. vec3 p21 = p2 - p1;
  191. float d1343 = dot( p13, p43 );
  192. float d4321 = dot( p43, p21 );
  193. float d1321 = dot( p13, p21 );
  194. float d4343 = dot( p43, p43 );
  195. float d2121 = dot( p21, p21 );
  196. float denom = d2121 * d4343 - d4321 * d4321;
  197. float numer = d1343 * d4321 - d1321 * d4343;
  198. mua = numer / denom;
  199. mua = clamp( mua, 0.0, 1.0 );
  200. mub = ( d1343 + d4321 * ( mua ) ) / d4343;
  201. mub = clamp( mub, 0.0, 1.0 );
  202. return vec2( mua, mub );
  203. }
  204. void main() {
  205. float alpha = opacity;
  206. vec4 diffuseColor = vec4( diffuse, alpha );
  207. #include <clipping_planes_fragment>
  208. #ifdef USE_DASH
  209. if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
  210. if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
  211. #endif
  212. #ifdef WORLD_UNITS
  213. // Find the closest points on the view ray and the line segment
  214. vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
  215. vec3 lineDir = worldEnd - worldStart;
  216. vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
  217. vec3 p1 = worldStart + lineDir * params.x;
  218. vec3 p2 = rayEnd * params.y;
  219. vec3 delta = p1 - p2;
  220. float len = length( delta );
  221. float norm = len / linewidth;
  222. #ifndef USE_DASH
  223. #ifdef USE_ALPHA_TO_COVERAGE
  224. float dnorm = fwidth( norm );
  225. alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
  226. #else
  227. if ( norm > 0.5 ) {
  228. discard;
  229. }
  230. #endif
  231. #endif
  232. #else
  233. #ifdef USE_ALPHA_TO_COVERAGE
  234. // artifacts appear on some hardware if a derivative is taken within a conditional
  235. float a = vUv.x;
  236. float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
  237. float len2 = a * a + b * b;
  238. float dlen = fwidth( len2 );
  239. if ( abs( vUv.y ) > 1.0 ) {
  240. alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
  241. }
  242. #else
  243. if ( abs( vUv.y ) > 1.0 ) {
  244. float a = vUv.x;
  245. float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
  246. float len2 = a * a + b * b;
  247. if ( len2 > 1.0 ) discard;
  248. }
  249. #endif
  250. #endif
  251. #include <logdepthbuf_fragment>
  252. #include <color_fragment>
  253. gl_FragColor = vec4( diffuseColor.rgb, alpha );
  254. #include <tonemapping_fragment>
  255. #include <colorspace_fragment>
  256. #include <fog_fragment>
  257. #include <premultiplied_alpha_fragment>
  258. }
  259. `
  260. };
  261. /**
  262. * A material for drawing wireframe-style geometries.
  263. *
  264. * Unlike {@link LineBasicMaterial}, it supports arbitrary line widths and allows using world units
  265. * instead of screen space units. This material is used with {@link LineSegments2} and {@link Line2}.
  266. *
  267. * This module can only be used with {@link WebGLRenderer}. When using {@link WebGPURenderer},
  268. * use {@link Line2NodeMaterial}.
  269. *
  270. * @augments ShaderMaterial
  271. * @three_import import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
  272. */
  273. class LineMaterial extends ShaderMaterial {
  274. /**
  275. * Constructs a new line segments geometry.
  276. *
  277. * @param {Object} [parameters] - An object with one or more properties
  278. * defining the material's appearance. Any property of the material
  279. * (including any property from inherited materials) can be passed
  280. * in here. Color values can be passed any type of value accepted
  281. * by {@link Color#set}.
  282. */
  283. constructor( parameters ) {
  284. super( {
  285. type: 'LineMaterial',
  286. uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ),
  287. vertexShader: ShaderLib[ 'line' ].vertexShader,
  288. fragmentShader: ShaderLib[ 'line' ].fragmentShader,
  289. clipping: true // required for clipping support
  290. } );
  291. /**
  292. * This flag can be used for type testing.
  293. *
  294. * @type {boolean}
  295. * @readonly
  296. * @default true
  297. */
  298. this.isLineMaterial = true;
  299. this.setValues( parameters );
  300. }
  301. /**
  302. * The material's color.
  303. *
  304. * @type {Color}
  305. * @default (1,1,1)
  306. */
  307. get color() {
  308. return this.uniforms.diffuse.value;
  309. }
  310. set color( value ) {
  311. this.uniforms.diffuse.value = value;
  312. }
  313. /**
  314. * Whether the material's sizes (width, dash gaps) are in world units.
  315. *
  316. * @type {boolean}
  317. * @default false
  318. */
  319. get worldUnits() {
  320. return 'WORLD_UNITS' in this.defines;
  321. }
  322. set worldUnits( value ) {
  323. if ( ( value === true ) !== this.worldUnits ) {
  324. this.needsUpdate = true;
  325. }
  326. if ( value === true ) {
  327. this.defines.WORLD_UNITS = '';
  328. } else {
  329. delete this.defines.WORLD_UNITS;
  330. }
  331. }
  332. /**
  333. * Controls line thickness in CSS pixel units when `worldUnits` is `false` (default),
  334. * or in world units when `worldUnits` is `true`.
  335. *
  336. * @type {number}
  337. * @default 1
  338. */
  339. get linewidth() {
  340. return this.uniforms.linewidth.value;
  341. }
  342. set linewidth( value ) {
  343. if ( ! this.uniforms.linewidth ) return;
  344. this.uniforms.linewidth.value = value;
  345. }
  346. /**
  347. * Whether the line is dashed, or solid.
  348. *
  349. * @type {boolean}
  350. * @default false
  351. */
  352. get dashed() {
  353. return 'USE_DASH' in this.defines;
  354. }
  355. set dashed( value ) {
  356. if ( ( value === true ) !== this.dashed ) {
  357. this.needsUpdate = true;
  358. }
  359. if ( value === true ) {
  360. this.defines.USE_DASH = '';
  361. } else {
  362. delete this.defines.USE_DASH;
  363. }
  364. }
  365. /**
  366. * The scale of the dashes and gaps.
  367. *
  368. * @type {number}
  369. * @default 1
  370. */
  371. get dashScale() {
  372. return this.uniforms.dashScale.value;
  373. }
  374. set dashScale( value ) {
  375. this.uniforms.dashScale.value = value;
  376. }
  377. /**
  378. * The size of the dash.
  379. *
  380. * @type {number}
  381. * @default 1
  382. */
  383. get dashSize() {
  384. return this.uniforms.dashSize.value;
  385. }
  386. set dashSize( value ) {
  387. this.uniforms.dashSize.value = value;
  388. }
  389. /**
  390. * Where in the dash cycle the dash starts.
  391. *
  392. * @type {number}
  393. * @default 0
  394. */
  395. get dashOffset() {
  396. return this.uniforms.dashOffset.value;
  397. }
  398. set dashOffset( value ) {
  399. this.uniforms.dashOffset.value = value;
  400. }
  401. /**
  402. * The size of the gap.
  403. *
  404. * @type {number}
  405. * @default 0
  406. */
  407. get gapSize() {
  408. return this.uniforms.gapSize.value;
  409. }
  410. set gapSize( value ) {
  411. this.uniforms.gapSize.value = value;
  412. }
  413. /**
  414. * The opacity.
  415. *
  416. * @type {number}
  417. * @default 1
  418. */
  419. get opacity() {
  420. return this.uniforms.opacity.value;
  421. }
  422. set opacity( value ) {
  423. if ( ! this.uniforms ) return;
  424. this.uniforms.opacity.value = value;
  425. }
  426. /**
  427. * The size of the viewport, in screen pixels. This must be kept updated to make
  428. * screen-space rendering accurate.The `LineSegments2.onBeforeRender` callback
  429. * performs the update for visible objects.
  430. *
  431. * @type {Vector2}
  432. */
  433. get resolution() {
  434. return this.uniforms.resolution.value;
  435. }
  436. set resolution( value ) {
  437. this.uniforms.resolution.value.copy( value );
  438. }
  439. /**
  440. * Whether to use alphaToCoverage or not. When enabled, this can improve the
  441. * anti-aliasing of line edges when using MSAA.
  442. *
  443. * @type {boolean}
  444. */
  445. get alphaToCoverage() {
  446. return 'USE_ALPHA_TO_COVERAGE' in this.defines;
  447. }
  448. set alphaToCoverage( value ) {
  449. if ( ! this.defines ) return;
  450. if ( ( value === true ) !== this.alphaToCoverage ) {
  451. this.needsUpdate = true;
  452. }
  453. if ( value === true ) {
  454. this.defines.USE_ALPHA_TO_COVERAGE = '';
  455. } else {
  456. delete this.defines.USE_ALPHA_TO_COVERAGE;
  457. }
  458. }
  459. }
  460. export { LineMaterial };
粤ICP备19079148号