1
+ import { shaderMaterial } from './shaderMaterial'
1
2
import * as THREE from 'three'
2
3
import { toCreasedNormals } from 'three/examples/jsm/utils/BufferGeometryUtils'
3
- import { shaderMaterial } from './shaderMaterial'
4
4
5
5
export type OutlinesProps = {
6
6
/** Outline color, default: black */
7
- color : THREE . Color
7
+ color ?: THREE . Color
8
+ /** Line thickness is independent of zoom, default: false */
9
+ screenspace ?: boolean
8
10
/** Outline opacity, default: 1 */
9
- opacity : number
11
+ opacity ? : number
10
12
/** Outline transparency, default: false */
11
- transparent : boolean
13
+ transparent ? : boolean
12
14
/** Outline thickness, default 0.05 */
13
- thickness : number
15
+ thickness ? : number
14
16
/** Geometry crease angle (0 === no crease), default: Math.PI */
15
- angle : number
17
+ angle ?: number
18
+ toneMapped ?: boolean
19
+ polygonOffset ?: boolean
20
+ polygonOffsetFactor ?: number
21
+ renderOrder ?: number
22
+ /** needed if `screenspace` is true */
23
+ gl ?: THREE . WebGLRenderer
16
24
}
17
25
18
26
export type OutlinesType = {
@@ -25,12 +33,20 @@ export type OutlinesType = {
25
33
}
26
34
27
35
const OutlinesMaterial = shaderMaterial (
28
- { color : new THREE . Color ( 'black' ) , opacity : 1 , thickness : 0.05 } ,
36
+ {
37
+ screenspace : false ,
38
+ color : new THREE . Color ( 'black' ) ,
39
+ opacity : 1 ,
40
+ thickness : 0.05 ,
41
+ size : new THREE . Vector2 ( ) ,
42
+ } ,
29
43
/* glsl */ `
30
44
#include <common>
31
45
#include <morphtarget_pars_vertex>
32
46
#include <skinning_pars_vertex>
33
47
uniform float thickness;
48
+ uniform float screenspace;
49
+ uniform vec2 size;
34
50
void main() {
35
51
#if defined (USE_SKINNING)
36
52
#include <beginnormal_vertex>
@@ -43,14 +59,22 @@ const OutlinesMaterial = shaderMaterial(
43
59
#include <morphtarget_vertex>
44
60
#include <skinning_vertex>
45
61
#include <project_vertex>
46
- vec4 transformedNormal = vec4(normal, 0.0);
47
- vec4 transformedPosition = vec4(transformed, 1.0);
62
+ vec4 tNormal = vec4(normal, 0.0);
63
+ vec4 tPosition = vec4(transformed, 1.0);
48
64
#ifdef USE_INSTANCING
49
- transformedNormal = instanceMatrix * transformedNormal ;
50
- transformedPosition = instanceMatrix * transformedPosition ;
65
+ tNormal = instanceMatrix * tNormal ;
66
+ tPosition = instanceMatrix * tPosition ;
51
67
#endif
52
- vec3 newPosition = transformedPosition.xyz + transformedNormal.xyz * thickness;
53
- gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
68
+ if (screenspace == 0.0) {
69
+ vec3 newPosition = tPosition.xyz + tNormal.xyz * thickness;
70
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
71
+ } else {
72
+ vec4 clipPosition = projectionMatrix * modelViewMatrix * tPosition;
73
+ vec4 clipNormal = projectionMatrix * modelViewMatrix * tNormal;
74
+ vec2 offset = normalize(clipNormal.xy) * thickness / size * clipPosition.w * 2.0;
75
+ clipPosition.xy += offset;
76
+ gl_Position = clipPosition;
77
+ }
54
78
}` ,
55
79
/* glsl */ `
56
80
uniform vec3 color;
@@ -66,36 +90,48 @@ export function Outlines({
66
90
color = new THREE . Color ( 'black' ) ,
67
91
opacity = 1 ,
68
92
transparent = false ,
93
+ screenspace = false ,
94
+ toneMapped = true ,
95
+ polygonOffset = false ,
96
+ polygonOffsetFactor = 0 ,
97
+ renderOrder = 0 ,
69
98
thickness = 0.05 ,
70
99
angle = Math . PI ,
100
+ gl,
71
101
} : Partial < OutlinesProps > ) : OutlinesType {
72
102
const group = new THREE . Group ( )
73
103
74
104
let shapeProps : OutlinesProps = {
75
105
color,
76
106
opacity,
77
107
transparent,
108
+ screenspace,
109
+ toneMapped,
110
+ polygonOffset,
111
+ polygonOffsetFactor,
112
+ renderOrder,
78
113
thickness,
79
114
angle,
80
115
}
81
116
82
- function updateMesh ( angle : number ) {
117
+ function updateMesh ( angle ? : number ) {
83
118
const parent = group . parent as THREE . Mesh & THREE . SkinnedMesh & THREE . InstancedMesh
84
119
group . clear ( )
85
120
if ( parent && parent . geometry ) {
86
121
let mesh
122
+ const material = new OutlinesMaterial ( { side : THREE . BackSide } )
87
123
if ( parent . skeleton ) {
88
124
mesh = new THREE . SkinnedMesh ( )
89
- mesh . material = new OutlinesMaterial ( { side : THREE . BackSide } )
125
+ mesh . material = material
90
126
mesh . bind ( parent . skeleton , parent . bindMatrix )
91
127
group . add ( mesh )
92
128
} else if ( parent . isInstancedMesh ) {
93
- mesh = new THREE . InstancedMesh ( parent . geometry , new OutlinesMaterial ( { side : THREE . BackSide } ) , parent . count )
129
+ mesh = new THREE . InstancedMesh ( parent . geometry , material , parent . count )
94
130
mesh . instanceMatrix = parent . instanceMatrix
95
131
group . add ( mesh )
96
132
} else {
97
133
mesh = new THREE . Mesh ( )
98
- mesh . material = new OutlinesMaterial ( { side : THREE . BackSide } )
134
+ mesh . material = material
99
135
group . add ( mesh )
100
136
}
101
137
mesh . geometry = angle ? toCreasedNormals ( parent . geometry , angle ) : parent . geometry
@@ -106,8 +142,35 @@ export function Outlines({
106
142
shapeProps = { ...shapeProps , ...newProps }
107
143
const mesh = group . children [ 0 ] as THREE . Mesh < THREE . BufferGeometry , THREE . Material >
108
144
if ( mesh ) {
109
- const { transparent, thickness, color, opacity } = shapeProps
110
- Object . assign ( mesh . material , { transparent, thickness, color, opacity } )
145
+ const {
146
+ transparent,
147
+ thickness,
148
+ color,
149
+ opacity,
150
+ screenspace,
151
+ toneMapped,
152
+ polygonOffset,
153
+ polygonOffsetFactor,
154
+ renderOrder,
155
+ } = shapeProps
156
+ const contextSize = new THREE . Vector2 ( )
157
+ if ( ! gl && shapeProps . screenspace ) {
158
+ console . warn ( 'Outlines: "screenspace" requires a WebGLRenderer instance to calculate the outline size' )
159
+ }
160
+ if ( gl ) gl . getSize ( contextSize )
161
+
162
+ Object . assign ( mesh . material , {
163
+ transparent,
164
+ thickness,
165
+ color,
166
+ opacity,
167
+ size : contextSize ,
168
+ screenspace,
169
+ toneMapped,
170
+ polygonOffset,
171
+ polygonOffsetFactor,
172
+ } )
173
+ if ( renderOrder !== undefined ) mesh . renderOrder = renderOrder
111
174
}
112
175
}
113
176
0 commit comments