phoria2 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/dist/bloom.js +4 -4
- package/dist/bloom.js.map +1 -1
- package/dist/charts.js +527 -510
- package/dist/charts.js.map +1 -1
- package/dist/core.js +1760 -1170
- package/dist/core.js.map +1 -1
- package/dist/debug.js +297 -0
- package/dist/debug.js.map +1 -0
- package/dist/edit.js +60 -60
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +32 -13
- package/dist/explain.js.map +1 -1
- package/dist/gltf.js +155 -155
- package/dist/gltf.js.map +1 -1
- package/dist/keyboard.js +188 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/motion.js +35 -35
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +187 -114
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +116 -104
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +95 -62
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/types/View.d.ts +27 -0
- package/dist/types/area.d.ts +18 -0
- package/dist/types/debug.d.ts +74 -0
- package/dist/types/explain.d.ts +4 -0
- package/dist/types/frame.d.ts +5 -0
- package/dist/types/geometry/Geometry.d.ts +16 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/material/Material.d.ts +67 -0
- package/dist/types/material/Texture.d.ts +18 -2
- package/dist/types/particles/Emitter.d.ts +39 -2
- package/dist/types/scene/drawables.d.ts +23 -2
- package/dist/types/watch.d.ts +8 -1
- package/dist/types/webgl/hooks.d.ts +48 -0
- package/dist/types/webgl/passes.d.ts +4 -0
- package/dist/types/webgl/programs.d.ts +15 -5
- package/dist/types/webgl/resources.d.ts +12 -2
- package/dist/types/webgl/shaders.d.ts +41 -5
- package/llms-full.txt +275 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/schema/scene.schema.json +75 -0
- package/src/View.js +121 -7
- package/src/area.js +131 -0
- package/src/charts/marks.js +29 -2
- package/src/debug.js +283 -0
- package/src/explain.js +19 -0
- package/src/frame.js +7 -2
- package/src/geometry/Geometry.js +16 -1
- package/src/index.js +1 -0
- package/src/material/Material.js +130 -4
- package/src/material/Texture.js +55 -2
- package/src/particles/Emitter.js +121 -10
- package/src/scene/drawables.js +89 -8
- package/src/watch.js +26 -3
- package/src/webgl/WebGLRenderer.js +55 -7
- package/src/webgl/hooks.js +121 -0
- package/src/webgl/passes.js +17 -2
- package/src/webgl/picking.js +45 -3
- package/src/webgl/programs.js +20 -9
- package/src/webgl/resources.js +169 -15
- package/src/webgl/shaders.js +545 -38
package/src/webgl/shaders.js
CHANGED
|
@@ -12,12 +12,18 @@ export const ATTRIBUTES = {
|
|
|
12
12
|
instanceColor: 8,
|
|
13
13
|
tangent: 9, // brushed metal's direction: along the UVs' u, and which way their v runs
|
|
14
14
|
uv2: 10, // a second UV set, for baked occlusion and light maps
|
|
15
|
+
// points drawn as squares (sprites): each one's size, opacity and angle, and which point it is
|
|
16
|
+
pointSize: 11,
|
|
17
|
+
pointOpacity: 12,
|
|
18
|
+
pointAngle: 13,
|
|
19
|
+
pointIndex: 14,
|
|
15
20
|
// lines: per-segment endpoints and colours, and the corner of the quad being drawn
|
|
16
21
|
segmentStart: 0,
|
|
17
22
|
segmentEnd: 1,
|
|
18
23
|
segmentColorStart: 2,
|
|
19
24
|
segmentColorEnd: 3,
|
|
20
25
|
corner: 4,
|
|
26
|
+
segmentInfo: 5, // how far along its chain the segment starts, and whether it starts or ends one
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
// a section plane: fragments on its far side are cut away
|
|
@@ -110,9 +116,17 @@ export const CLIP_ONLY_FRAGMENT = /* glsl */ `
|
|
|
110
116
|
precision highp float;
|
|
111
117
|
in vec3 vWorldPosition;
|
|
112
118
|
out vec4 fragColor;
|
|
119
|
+
#if defined(ALPHA_TEST) || defined(HOOK_FRAGMENT)
|
|
120
|
+
in vec2 vUv;
|
|
121
|
+
#endif
|
|
122
|
+
#ifdef HOOK_FRAGMENT
|
|
123
|
+
// a fragment hook's discards cut the shadow too - a dissolving thing casts what is left of it
|
|
124
|
+
in vec3 vNormal;
|
|
125
|
+
in vec3 vColor;
|
|
126
|
+
//@hook uniforms
|
|
127
|
+
#endif
|
|
113
128
|
#ifdef ALPHA_TEST
|
|
114
129
|
// a cut-out casts the shadow of what is left of it - leaves, fences, grilles - not of its rectangle
|
|
115
|
-
in vec2 vUv;
|
|
116
130
|
uniform float uOpacity;
|
|
117
131
|
uniform float uAlphaTest;
|
|
118
132
|
#ifdef MAP
|
|
@@ -123,6 +137,15 @@ ${SAMPLING}
|
|
|
123
137
|
${CLIP}
|
|
124
138
|
void main() {
|
|
125
139
|
clipAway(vWorldPosition);
|
|
140
|
+
#ifdef HOOK_FRAGMENT
|
|
141
|
+
{
|
|
142
|
+
vec4 base = vec4(vColor, 1.0);
|
|
143
|
+
vec3 emissive = vec3(0.0);
|
|
144
|
+
vec3 normal = normalize(vNormal);
|
|
145
|
+
//@hook fragment
|
|
146
|
+
if (base.a <= 0.0) discard;
|
|
147
|
+
}
|
|
148
|
+
#endif
|
|
126
149
|
#ifdef ALPHA_TEST
|
|
127
150
|
float alpha = uOpacity;
|
|
128
151
|
#ifdef MAP
|
|
@@ -219,6 +242,7 @@ uniform int uHighlightIndex; // -2 none, -1 the whole node, otherwise the instan
|
|
|
219
242
|
uniform mat4 uView;
|
|
220
243
|
out float vFogDepth;
|
|
221
244
|
#endif
|
|
245
|
+
//@hook uniforms
|
|
222
246
|
|
|
223
247
|
out vec3 vWorldPosition;
|
|
224
248
|
out vec3 vNormal;
|
|
@@ -242,25 +266,34 @@ ${COMMON}
|
|
|
242
266
|
void main() {
|
|
243
267
|
vColor = vec3(1.0);
|
|
244
268
|
vHighlight = uHighlightIndex == -1 ? 1.0 : 0.0;
|
|
269
|
+
// the vertex in the geometry's own space - where a shader hook moves it
|
|
270
|
+
vec3 local = position;
|
|
271
|
+
vec3 n = normal;
|
|
272
|
+
#ifdef HOOK_VERTEX
|
|
273
|
+
{
|
|
274
|
+
int index = gl_InstanceID;
|
|
275
|
+
//@hook vertex
|
|
276
|
+
}
|
|
277
|
+
#endif
|
|
245
278
|
#ifdef INSTANCED
|
|
246
|
-
vec4
|
|
279
|
+
vec4 placed = instanceMatrix * vec4(local, 1.0);
|
|
247
280
|
// the instance's normal matrix as its cofactor: the inverse transpose up to scale, from three
|
|
248
281
|
// cross products, flipped back for mirroring transforms
|
|
249
282
|
mat3 m = mat3(instanceMatrix);
|
|
250
283
|
mat3 instanceNormal = mat3(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1])) * sign(dot(m[0], cross(m[1], m[2])));
|
|
251
|
-
vec3
|
|
284
|
+
vec3 placedNormal = instanceNormal * n;
|
|
252
285
|
vColor *= instanceColor;
|
|
253
286
|
if (gl_InstanceID == uHighlightIndex) vHighlight = 1.0;
|
|
254
287
|
#else
|
|
255
|
-
vec4
|
|
256
|
-
vec3
|
|
288
|
+
vec4 placed = vec4(local, 1.0);
|
|
289
|
+
vec3 placedNormal = n;
|
|
257
290
|
#endif
|
|
258
291
|
#ifdef VERTEX_COLORS
|
|
259
292
|
vColor *= srgbToLinear(color);
|
|
260
293
|
#endif
|
|
261
|
-
vec4 world = uModel *
|
|
294
|
+
vec4 world = uModel * placed;
|
|
262
295
|
vWorldPosition = world.xyz;
|
|
263
|
-
vNormal = uNormalMatrix *
|
|
296
|
+
vNormal = uNormalMatrix * placedNormal;
|
|
264
297
|
vUv = uv;
|
|
265
298
|
#ifdef UV2
|
|
266
299
|
vUv2 = uv2;
|
|
@@ -274,8 +307,8 @@ void main() {
|
|
|
274
307
|
#endif
|
|
275
308
|
#endif
|
|
276
309
|
#ifdef TRIPLANAR
|
|
277
|
-
vObjectPosition =
|
|
278
|
-
vObjectNormal =
|
|
310
|
+
vObjectPosition = local;
|
|
311
|
+
vObjectNormal = n;
|
|
279
312
|
#ifdef INSTANCED
|
|
280
313
|
vObjectToWorld = uNormalMatrix * instanceNormal;
|
|
281
314
|
#else
|
|
@@ -316,6 +349,7 @@ uniform float uAlphaTest;
|
|
|
316
349
|
#ifdef EMISSIVE_MAP
|
|
317
350
|
uniform sampler2D uEmissiveMap;
|
|
318
351
|
#endif
|
|
352
|
+
//@hook uniforms
|
|
319
353
|
|
|
320
354
|
${SAMPLING}
|
|
321
355
|
#ifdef LIT
|
|
@@ -732,6 +766,25 @@ void main() {
|
|
|
732
766
|
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
733
767
|
#ifdef MAP
|
|
734
768
|
base *= uMapTexel; // sRGB textures are decoded to linear by the hardware
|
|
769
|
+
#endif
|
|
770
|
+
vec3 emissive = uEmissive;
|
|
771
|
+
#ifdef EMISSIVE_MAP
|
|
772
|
+
emissive *= uEmissiveMapTexel.rgb;
|
|
773
|
+
#endif
|
|
774
|
+
#ifdef HOOK_FRAGMENT
|
|
775
|
+
// the shading normal in world space, facing the viewer - a fragment hook can bend it, for relief
|
|
776
|
+
// lit per pixel (bumps from noise) whatever the mesh's resolution
|
|
777
|
+
#ifdef FLAT_SHADING
|
|
778
|
+
vec3 normal = normalize(cross(dFdx(vWorldPosition), dFdy(vWorldPosition)));
|
|
779
|
+
#else
|
|
780
|
+
vec3 normal = normalize(vNormal);
|
|
781
|
+
#ifdef DOUBLE_SIDED
|
|
782
|
+
if (!gl_FrontFacing) normal = -normal;
|
|
783
|
+
#endif
|
|
784
|
+
#endif
|
|
785
|
+
{
|
|
786
|
+
//@hook fragment
|
|
787
|
+
}
|
|
735
788
|
#endif
|
|
736
789
|
#ifdef ALPHA_TEST
|
|
737
790
|
if (base.a < uAlphaTest) discard;
|
|
@@ -739,19 +792,18 @@ void main() {
|
|
|
739
792
|
#endif
|
|
740
793
|
// how much of what is behind it this hides: its opacity - less for glass, which lets light through
|
|
741
794
|
float coverage = base.a;
|
|
742
|
-
vec3 emissive = uEmissive;
|
|
743
|
-
#ifdef EMISSIVE_MAP
|
|
744
|
-
emissive *= uEmissiveMapTexel.rgb;
|
|
745
|
-
#endif
|
|
746
795
|
|
|
747
796
|
#ifdef LIT
|
|
748
|
-
#ifdef
|
|
797
|
+
#ifdef HOOK_FRAGMENT
|
|
798
|
+
// the fragment hook's normal, facing the viewer already
|
|
799
|
+
vec3 n = normalize(normal);
|
|
800
|
+
#elif defined(FLAT_SHADING)
|
|
749
801
|
// from the screen-space slope: it faces the camera whichever side of the face is seen
|
|
750
802
|
vec3 n = normalize(cross(dFdx(vWorldPosition), dFdy(vWorldPosition)));
|
|
751
803
|
#else
|
|
752
804
|
vec3 n = normalize(vNormal);
|
|
753
805
|
#endif
|
|
754
|
-
#if defined(DOUBLE_SIDED) && !defined(FLAT_SHADING)
|
|
806
|
+
#if defined(DOUBLE_SIDED) && !defined(FLAT_SHADING) && !defined(HOOK_FRAGMENT)
|
|
755
807
|
if (!gl_FrontFacing) n = -n;
|
|
756
808
|
#endif
|
|
757
809
|
vec3 surfaceNormal = n;
|
|
@@ -942,6 +994,14 @@ void main() {
|
|
|
942
994
|
#endif
|
|
943
995
|
#else
|
|
944
996
|
vec3 color = base.rgb + emissive;
|
|
997
|
+
#endif
|
|
998
|
+
#ifdef HOOK_LIT
|
|
999
|
+
{
|
|
1000
|
+
// a fade here fades what it hides too
|
|
1001
|
+
float before = max(base.a, 1e-5);
|
|
1002
|
+
//@hook lit
|
|
1003
|
+
coverage *= clamp(base.a / before, 0.0, 1.0);
|
|
1004
|
+
}
|
|
945
1005
|
#endif
|
|
946
1006
|
color = mix(color, uHighlight.rgb, vHighlight * uHighlight.a);
|
|
947
1007
|
// a highlight shows on glass as on anything else
|
|
@@ -957,6 +1017,17 @@ void main() {
|
|
|
957
1017
|
export const POINTS_VERTEX = /* glsl */ `
|
|
958
1018
|
layout(location = 0) in vec3 position;
|
|
959
1019
|
layout(location = 3) in vec3 color;
|
|
1020
|
+
#ifdef SPRITES
|
|
1021
|
+
// each point a square: one instance a point, drawn as a strip over the corners (along 0-1, across -1-1)
|
|
1022
|
+
layout(location = 4) in vec2 corner;
|
|
1023
|
+
layout(location = 11) in float pointSize;
|
|
1024
|
+
layout(location = 12) in float pointOpacity;
|
|
1025
|
+
layout(location = 13) in float pointAngle;
|
|
1026
|
+
layout(location = 14) in float pointIndex;
|
|
1027
|
+
uniform vec2 uResolution; // drawing buffer size in pixels
|
|
1028
|
+
out vec2 vUv;
|
|
1029
|
+
out float vOpacity;
|
|
1030
|
+
#endif
|
|
960
1031
|
|
|
961
1032
|
uniform mat4 uModel;
|
|
962
1033
|
uniform mat4 uView;
|
|
@@ -964,9 +1035,13 @@ uniform mat4 uProjection;
|
|
|
964
1035
|
uniform float uSize; // pixels (with the pixel ratio applied), or world units when attenuated
|
|
965
1036
|
uniform float uScale; // projection focal term x half the buffer height: world size -> pixels at depth 1
|
|
966
1037
|
uniform int uHighlightIndex; // -2 none, -1 every point, otherwise the point
|
|
1038
|
+
//@hook uniforms
|
|
967
1039
|
|
|
968
1040
|
out vec3 vColor;
|
|
969
1041
|
flat out float vHighlight;
|
|
1042
|
+
#ifdef HOOK_FRAGMENT
|
|
1043
|
+
flat out float vPointIndex;
|
|
1044
|
+
#endif
|
|
970
1045
|
#ifdef PICK
|
|
971
1046
|
flat out uint vPickElement; // which instance, point or segment, for GPU picking
|
|
972
1047
|
#endif
|
|
@@ -977,25 +1052,65 @@ out float vFogDepth;
|
|
|
977
1052
|
${COMMON}
|
|
978
1053
|
|
|
979
1054
|
void main() {
|
|
980
|
-
|
|
1055
|
+
// the point in the geometry's own space, and its size as a multiple of the material's - where a
|
|
1056
|
+
// shader hook changes them
|
|
1057
|
+
vec3 local = position;
|
|
1058
|
+
#ifdef SPRITES
|
|
1059
|
+
int self = int(pointIndex + 0.5);
|
|
1060
|
+
float size = pointSize;
|
|
1061
|
+
#else
|
|
1062
|
+
int self = gl_VertexID;
|
|
1063
|
+
float size = 1.0;
|
|
1064
|
+
#endif
|
|
1065
|
+
#ifdef HOOK_VERTEX
|
|
1066
|
+
{
|
|
1067
|
+
int index = self;
|
|
1068
|
+
//@hook vertex
|
|
1069
|
+
}
|
|
1070
|
+
#endif
|
|
1071
|
+
#ifdef HOOK_FRAGMENT
|
|
1072
|
+
vPointIndex = float(self);
|
|
1073
|
+
#endif
|
|
1074
|
+
vec4 viewPosition = uView * (uModel * vec4(local, 1.0));
|
|
1075
|
+
#ifdef SPRITES
|
|
1076
|
+
// the square's corner, turned by the point's angle: the texture turns with it
|
|
1077
|
+
vec2 c = vec2(corner.x * 2.0 - 1.0, corner.y);
|
|
1078
|
+
float s = sin(pointAngle), k = cos(pointAngle);
|
|
1079
|
+
vec2 turned = vec2(c.x * k - c.y * s, c.x * s + c.y * k);
|
|
1080
|
+
float across = uSize * size;
|
|
1081
|
+
#ifdef SIZE_ATTENUATION
|
|
1082
|
+
// world units: the square sits in the view, facing the camera
|
|
1083
|
+
viewPosition.xy += turned * across * 0.5;
|
|
1084
|
+
gl_Position = uProjection * viewPosition;
|
|
1085
|
+
#else
|
|
1086
|
+
// pixels: the square is laid out on the screen
|
|
1087
|
+
gl_Position = uProjection * viewPosition;
|
|
1088
|
+
gl_Position.xy += turned * across / uResolution * gl_Position.w;
|
|
1089
|
+
#endif
|
|
1090
|
+
// the image upright on the square, its top at the top, as a point's is
|
|
1091
|
+
vUv = vec2(c.x * 0.5 + 0.5, 0.5 - c.y * 0.5);
|
|
1092
|
+
vOpacity = pointOpacity;
|
|
1093
|
+
#else
|
|
981
1094
|
gl_Position = uProjection * viewPosition;
|
|
1095
|
+
#endif
|
|
982
1096
|
#ifdef FOG
|
|
983
1097
|
vFogDepth = -viewPosition.z;
|
|
984
1098
|
#endif
|
|
985
|
-
#ifdef
|
|
1099
|
+
#ifdef SPRITES
|
|
1100
|
+
#elif defined(SIZE_ATTENUATION)
|
|
986
1101
|
// perspective shrinks with distance; an orthographic projection (w row 0, 0, 0, 1) doesn't
|
|
987
1102
|
float depth = uProjection[3][3] == 1.0 ? 1.0 : max(-viewPosition.z, 1e-4);
|
|
988
|
-
gl_PointSize = uSize * uScale / depth;
|
|
1103
|
+
gl_PointSize = uSize * size * uScale / depth;
|
|
989
1104
|
#else
|
|
990
|
-
gl_PointSize = uSize;
|
|
1105
|
+
gl_PointSize = uSize * size;
|
|
991
1106
|
#endif
|
|
992
1107
|
vColor = vec3(1.0);
|
|
993
1108
|
#ifdef VERTEX_COLORS
|
|
994
1109
|
vColor = srgbToLinear(color);
|
|
995
1110
|
#endif
|
|
996
|
-
vHighlight = uHighlightIndex == -1 ||
|
|
1111
|
+
vHighlight = uHighlightIndex == -1 || self == uHighlightIndex ? 1.0 : 0.0;
|
|
997
1112
|
#ifdef PICK
|
|
998
|
-
vPickElement = uint(
|
|
1113
|
+
vPickElement = uint(self);
|
|
999
1114
|
#endif
|
|
1000
1115
|
}
|
|
1001
1116
|
`
|
|
@@ -1004,31 +1119,57 @@ const POINTS_FRAGMENT = /* glsl */ `
|
|
|
1004
1119
|
precision highp float;
|
|
1005
1120
|
in vec3 vColor;
|
|
1006
1121
|
flat in float vHighlight;
|
|
1122
|
+
#ifdef SPRITES
|
|
1123
|
+
in vec2 vUv;
|
|
1124
|
+
in float vOpacity;
|
|
1125
|
+
#endif
|
|
1007
1126
|
uniform vec4 uHighlight;
|
|
1008
1127
|
uniform vec3 uColor;
|
|
1009
1128
|
uniform float uOpacity;
|
|
1010
1129
|
#ifdef MAP
|
|
1011
1130
|
uniform sampler2D uMap;
|
|
1012
1131
|
#endif
|
|
1132
|
+
#ifdef HOOK_FRAGMENT
|
|
1133
|
+
flat in float vPointIndex;
|
|
1134
|
+
#endif
|
|
1135
|
+
//@hook uniforms
|
|
1013
1136
|
out vec4 fragColor;
|
|
1014
1137
|
|
|
1015
1138
|
${COMMON}
|
|
1016
1139
|
${FOG}
|
|
1017
1140
|
|
|
1018
1141
|
void main() {
|
|
1142
|
+
#ifdef SPRITES
|
|
1143
|
+
vec2 at = vUv;
|
|
1144
|
+
vec4 base = vec4(uColor * vColor, uOpacity * vOpacity);
|
|
1145
|
+
#else
|
|
1146
|
+
vec2 at = gl_PointCoord;
|
|
1019
1147
|
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
1148
|
+
#endif
|
|
1020
1149
|
#ifdef MAP
|
|
1021
|
-
base *= texture(uMap,
|
|
1150
|
+
base *= texture(uMap, at);
|
|
1022
1151
|
#else
|
|
1023
1152
|
// round points with a one pixel soft edge
|
|
1024
|
-
vec2 p =
|
|
1153
|
+
vec2 p = at * 2.0 - 1.0;
|
|
1025
1154
|
float r = dot(p, p);
|
|
1026
1155
|
float edge = fwidth(r);
|
|
1027
1156
|
base.a *= 1.0 - smoothstep(1.0 - edge, 1.0, r);
|
|
1157
|
+
#endif
|
|
1158
|
+
#ifdef HOOK_FRAGMENT
|
|
1159
|
+
{
|
|
1160
|
+
vec2 pointUv = at;
|
|
1161
|
+
int index = int(vPointIndex + 0.5);
|
|
1162
|
+
//@hook fragment
|
|
1163
|
+
}
|
|
1028
1164
|
#endif
|
|
1029
1165
|
if (base.a <= 0.0) discard;
|
|
1030
|
-
|
|
1031
|
-
|
|
1166
|
+
vec3 color = fogged(mix(base.rgb, uHighlight.rgb, vHighlight * uHighlight.a));
|
|
1167
|
+
#ifdef HOOK_LIT
|
|
1168
|
+
{
|
|
1169
|
+
//@hook lit
|
|
1170
|
+
}
|
|
1171
|
+
#endif
|
|
1172
|
+
fragColor = vec4(linearToSrgb(color) * base.a, base.a);
|
|
1032
1173
|
}
|
|
1033
1174
|
`
|
|
1034
1175
|
|
|
@@ -1037,7 +1178,9 @@ layout(location = 0) in vec3 segmentStart;
|
|
|
1037
1178
|
layout(location = 1) in vec3 segmentEnd;
|
|
1038
1179
|
layout(location = 2) in vec3 segmentColorStart;
|
|
1039
1180
|
layout(location = 3) in vec3 segmentColorEnd;
|
|
1040
|
-
|
|
1181
|
+
// the distance along its chain (segments joined end to start) where it starts, in the model's
|
|
1182
|
+
// units; 1 if it starts a chain; 1 if it ends one
|
|
1183
|
+
layout(location = 5) in vec3 segmentInfo;
|
|
1041
1184
|
|
|
1042
1185
|
uniform mat4 uModel;
|
|
1043
1186
|
uniform mat4 uView;
|
|
@@ -1045,8 +1188,24 @@ uniform mat4 uProjection;
|
|
|
1045
1188
|
uniform vec2 uResolution; // drawing buffer size in pixels
|
|
1046
1189
|
uniform float uWidth; // pixels, already scaled by the pixel ratio
|
|
1047
1190
|
uniform float uNear;
|
|
1191
|
+
uniform int uArrows; // arrowheads: 1 at the start of each chain, 2 at its end, 3 at both
|
|
1192
|
+
uniform float uArrowSize; // their length in pixels, scaled by the pixel ratio
|
|
1193
|
+
//@hook uniforms
|
|
1194
|
+
|
|
1195
|
+
// six vertices an instance make the segment's quad, as two triangles - x: 0 at its start, 1 at its
|
|
1196
|
+
// end; y: -1 or 1 across - and six more its two arrowheads, drawn only when asked for
|
|
1197
|
+
const vec2 QUAD[6] = vec2[6](vec2(0.0, -1.0), vec2(0.0, 1.0), vec2(1.0, -1.0), vec2(1.0, -1.0), vec2(0.0, 1.0), vec2(1.0, 1.0));
|
|
1048
1198
|
|
|
1049
1199
|
out vec3 vColor;
|
|
1200
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1201
|
+
out float vDistance; // along the chain, in the model's units
|
|
1202
|
+
#endif
|
|
1203
|
+
#ifdef HOOK_FRAGMENT
|
|
1204
|
+
flat out float vSegment; // which segment, for a fragment hook's index
|
|
1205
|
+
#endif
|
|
1206
|
+
#ifdef DASHED
|
|
1207
|
+
flat out float vArrow; // 1 on an arrowhead, which is never dashed
|
|
1208
|
+
#endif
|
|
1050
1209
|
#ifdef PICK
|
|
1051
1210
|
flat out uint vPickElement; // which instance, point or segment, for GPU picking
|
|
1052
1211
|
#endif
|
|
@@ -1056,9 +1215,22 @@ out float vFogDepth;
|
|
|
1056
1215
|
|
|
1057
1216
|
${COMMON}
|
|
1058
1217
|
|
|
1218
|
+
#ifdef HOOK_VERTEX
|
|
1219
|
+
// a shader hook moves each end of each segment: local is the end, along 0 at the start and 1 at the end
|
|
1220
|
+
vec3 hookVertex(vec3 local, float along, int index) {
|
|
1221
|
+
//@hook vertex
|
|
1222
|
+
return local;
|
|
1223
|
+
}
|
|
1224
|
+
#endif
|
|
1225
|
+
|
|
1059
1226
|
void main() {
|
|
1060
|
-
|
|
1061
|
-
|
|
1227
|
+
vec3 start = segmentStart, end = segmentEnd;
|
|
1228
|
+
#ifdef HOOK_VERTEX
|
|
1229
|
+
start = hookVertex(segmentStart, 0.0, gl_InstanceID);
|
|
1230
|
+
end = hookVertex(segmentEnd, 1.0, gl_InstanceID);
|
|
1231
|
+
#endif
|
|
1232
|
+
vec4 a = uView * (uModel * vec4(start, 1.0));
|
|
1233
|
+
vec4 b = uView * (uModel * vec4(end, 1.0));
|
|
1062
1234
|
// clip the segment to the near plane so lines passing behind the camera stay correct
|
|
1063
1235
|
float clipZ = -uNear;
|
|
1064
1236
|
if (a.z > clipZ && b.z > clipZ) {
|
|
@@ -1077,21 +1249,53 @@ void main() {
|
|
|
1077
1249
|
dir = len > 0.0 ? dir / len : vec2(1.0, 0.0);
|
|
1078
1250
|
vec2 normal = vec2(-dir.y, dir.x);
|
|
1079
1251
|
|
|
1080
|
-
|
|
1252
|
+
bool arrowStart = (uArrows & 1) != 0 && segmentInfo.y > 0.5;
|
|
1253
|
+
bool arrowEnd = (uArrows & 2) != 0 && segmentInfo.z > 0.5;
|
|
1254
|
+
float along, arrow = 0.0;
|
|
1255
|
+
vec2 offset;
|
|
1256
|
+
if (gl_VertexID < 6) {
|
|
1257
|
+
vec2 c = QUAD[gl_VertexID];
|
|
1258
|
+
along = c.x;
|
|
1259
|
+
// past each end by half the width so joins look continuous - or short of it, under an arrowhead
|
|
1260
|
+
float startOut = arrowStart ? -uArrowSize * 0.8 : uWidth * 0.5;
|
|
1261
|
+
float endOut = arrowEnd ? -uArrowSize * 0.8 : uWidth * 0.5;
|
|
1262
|
+
offset = normal * c.y * uWidth * 0.5 + dir * (along > 0.5 ? endOut : -startOut);
|
|
1263
|
+
} else {
|
|
1264
|
+
// an arrowhead: its tip on the chain's end, pointing out along the segment
|
|
1265
|
+
int which = (gl_VertexID - 6) / 3, k = gl_VertexID - 6 - which * 3;
|
|
1266
|
+
if (which == 0 ? !arrowStart : !arrowEnd) {
|
|
1267
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
along = float(which);
|
|
1271
|
+
vec2 d = which == 1 ? dir : -dir;
|
|
1272
|
+
float wing = uArrowSize * 0.45;
|
|
1273
|
+
offset = k == 0 ? vec2(0.0) : -d * uArrowSize + normal * (k == 1 ? wing : -wing);
|
|
1274
|
+
arrow = 1.0;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
vec4 clip = mix(ca, cb, along);
|
|
1081
1278
|
#ifdef FOG
|
|
1082
|
-
vFogDepth = -mix(a.z, b.z,
|
|
1279
|
+
vFogDepth = -mix(a.z, b.z, along);
|
|
1083
1280
|
#endif
|
|
1084
|
-
// extend past each end by half the width so joins look continuous
|
|
1085
|
-
vec2 offset = normal * corner.y * uWidth * 0.5 + dir * (corner.x * 2.0 - 1.0) * uWidth * 0.5;
|
|
1086
1281
|
clip.xy += offset / (uResolution * 0.5) * clip.w;
|
|
1087
1282
|
gl_Position = clip;
|
|
1283
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1284
|
+
vDistance = segmentInfo.x + length(end - start) * along;
|
|
1285
|
+
#endif
|
|
1286
|
+
#ifdef HOOK_FRAGMENT
|
|
1287
|
+
vSegment = float(gl_InstanceID);
|
|
1288
|
+
#endif
|
|
1289
|
+
#ifdef DASHED
|
|
1290
|
+
vArrow = arrow;
|
|
1291
|
+
#endif
|
|
1088
1292
|
#ifdef PICK
|
|
1089
1293
|
vPickElement = uint(gl_InstanceID);
|
|
1090
1294
|
#endif
|
|
1091
1295
|
|
|
1092
1296
|
vColor = vec3(1.0);
|
|
1093
1297
|
#ifdef VERTEX_COLORS
|
|
1094
|
-
vColor = srgbToLinear(mix(segmentColorStart, segmentColorEnd,
|
|
1298
|
+
vColor = srgbToLinear(mix(segmentColorStart, segmentColorEnd, along));
|
|
1095
1299
|
#endif
|
|
1096
1300
|
}
|
|
1097
1301
|
`
|
|
@@ -1101,14 +1305,44 @@ precision highp float;
|
|
|
1101
1305
|
in vec3 vColor;
|
|
1102
1306
|
uniform vec3 uColor;
|
|
1103
1307
|
uniform float uOpacity;
|
|
1308
|
+
#if defined(DASHED) || defined(HOOK_FRAGMENT)
|
|
1309
|
+
in float vDistance;
|
|
1310
|
+
#endif
|
|
1311
|
+
#ifdef HOOK_FRAGMENT
|
|
1312
|
+
flat in float vSegment;
|
|
1313
|
+
#endif
|
|
1314
|
+
//@hook uniforms
|
|
1315
|
+
#ifdef DASHED
|
|
1316
|
+
flat in float vArrow;
|
|
1317
|
+
uniform float uDash; // dash and gap lengths, in the model's units
|
|
1318
|
+
uniform float uGap;
|
|
1319
|
+
uniform float uDashOffset; // moves the dashes along: animate it for flow
|
|
1320
|
+
#endif
|
|
1104
1321
|
out vec4 fragColor;
|
|
1105
1322
|
|
|
1106
1323
|
${COMMON}
|
|
1107
1324
|
${FOG}
|
|
1108
1325
|
|
|
1109
1326
|
void main() {
|
|
1110
|
-
|
|
1111
|
-
|
|
1327
|
+
#ifdef DASHED
|
|
1328
|
+
if (vArrow < 0.5 && mod(vDistance - uDashOffset, uDash + uGap) >= uDash) discard;
|
|
1329
|
+
#endif
|
|
1330
|
+
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
1331
|
+
#ifdef HOOK_FRAGMENT
|
|
1332
|
+
{
|
|
1333
|
+
float distance = vDistance;
|
|
1334
|
+
int index = int(vSegment + 0.5);
|
|
1335
|
+
//@hook fragment
|
|
1336
|
+
}
|
|
1337
|
+
if (base.a <= 0.0) discard;
|
|
1338
|
+
#endif
|
|
1339
|
+
vec3 color = fogged(base.rgb);
|
|
1340
|
+
#ifdef HOOK_LIT
|
|
1341
|
+
{
|
|
1342
|
+
//@hook lit
|
|
1343
|
+
}
|
|
1344
|
+
#endif
|
|
1345
|
+
fragColor = vec4(linearToSrgb(color) * base.a, base.a);
|
|
1112
1346
|
}
|
|
1113
1347
|
`
|
|
1114
1348
|
|
|
@@ -1121,13 +1355,286 @@ const SOURCES = {
|
|
|
1121
1355
|
}
|
|
1122
1356
|
|
|
1123
1357
|
/**
|
|
1124
|
-
*
|
|
1358
|
+
* GLSL of a material's own, spliced in at the shaders' `//@hook` lines: uniform declarations, and
|
|
1359
|
+
* code run on each vertex, before lighting and after it.
|
|
1360
|
+
* @typedef {object} HookSource
|
|
1361
|
+
* @property {string} uniforms declarations
|
|
1362
|
+
* @property {string} [helpers] functions every hook can call - hash, noise, fbm
|
|
1363
|
+
* @property {string} vertex
|
|
1364
|
+
* @property {string} fragment
|
|
1365
|
+
* @property {string} lit
|
|
1366
|
+
*/
|
|
1367
|
+
|
|
1368
|
+
// Functions every shader hook can call - hooks are statements, so they can't define their own. All of
|
|
1369
|
+
// it is built on an integer bit-mix (lowbias32), which GLSL ES 3.00 defines exactly, so lattice
|
|
1370
|
+
// values and gradients are the same on every GPU - fract(sin(x) * 43758.5453) is not. Everything 0-1:
|
|
1371
|
+
// - hash(x): a float or vector's exact bits, mixed
|
|
1372
|
+
// - vnoise(p): value noise - random values at whole-number points, eased between (quintic); cheap,
|
|
1373
|
+
// blocky-looking at low frequencies
|
|
1374
|
+
// - snoise(p): simplex noise - gradients on a simplex grid; smoother, less grid-bound, the better
|
|
1375
|
+
// default; snoised(p) also gives its gradient, for normals
|
|
1376
|
+
// - vfbm / sfbm (p[, octaves]): octaves of either added up, each turned and scaled by 2.03 so their
|
|
1377
|
+
// grids never line up; sfbmd adds the gradient. Five octaves unless told; 1-12
|
|
1378
|
+
// - voronoi(p): cellular (Worley) noise - a random point in every unit cell; returns the distance to
|
|
1379
|
+
// the nearest (F1) and the second nearest (F2), in cells, and a random 0-1 for the nearest's cell
|
|
1380
|
+
// And the everyday ones people (and agents) reach for: srgb() to turn a colour as picked into the
|
|
1381
|
+
// linear one hooks work in, hsv2rgb() and palette() (Quilez's cosine palette) - both giving linear
|
|
1382
|
+
// colour - fresnel() for rim light, rotate2() / rotate3() matrices, remap() and saturate().
|
|
1383
|
+
// Every int highp: a fragment shader's ints are mediump unless they say so.
|
|
1384
|
+
export const HOOK_HELPERS = /* glsl */ `
|
|
1385
|
+
highp uint phoriaMix(highp uint h) {
|
|
1386
|
+
h ^= h >> 16u; h *= 0x7feb352du; h ^= h >> 15u; h *= 0x846ca68bu; h ^= h >> 16u;
|
|
1387
|
+
return h;
|
|
1388
|
+
}
|
|
1389
|
+
float phoriaUnit(highp uint h) { return float(h >> 8u) * (1.0 / 16777216.0); }
|
|
1390
|
+
float hash(float x) { return phoriaUnit(phoriaMix(floatBitsToUint(x))); }
|
|
1391
|
+
float hash(vec2 p) { highp uvec2 u = floatBitsToUint(p); return phoriaUnit(phoriaMix(u.x ^ phoriaMix(u.y + 0x9e3779b9u))); }
|
|
1392
|
+
float hash(vec3 p) { highp uvec3 u = floatBitsToUint(p); return phoriaUnit(phoriaMix(u.x ^ phoriaMix(u.y ^ phoriaMix(u.z + 0x9e3779b9u)))); }
|
|
1393
|
+
// a lattice point's bits: each coordinate times its own large odd number, mixed once
|
|
1394
|
+
highp uint phoriaCell(highp ivec3 c) {
|
|
1395
|
+
highp uvec3 u = uvec3(c) * uvec3(0x8da6b343u, 0xd8163841u, 0xcb1ab31fu);
|
|
1396
|
+
return phoriaMix(u.x ^ u.y ^ u.z);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
float vnoise(vec3 p) {
|
|
1400
|
+
vec3 i = floor(p), f = p - i;
|
|
1401
|
+
vec3 s = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
1402
|
+
highp ivec3 c = ivec3(i);
|
|
1403
|
+
float a = mix(phoriaUnit(phoriaCell(c)), phoriaUnit(phoriaCell(c + ivec3(1, 0, 0))), s.x);
|
|
1404
|
+
float b = mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 0))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 0))), s.x);
|
|
1405
|
+
float d = mix(phoriaUnit(phoriaCell(c + ivec3(0, 0, 1))), phoriaUnit(phoriaCell(c + ivec3(1, 0, 1))), s.x);
|
|
1406
|
+
float e = mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 1))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 1))), s.x);
|
|
1407
|
+
return mix(mix(a, b, s.y), mix(d, e, s.y), s.z);
|
|
1408
|
+
}
|
|
1409
|
+
float vnoise(vec2 p) {
|
|
1410
|
+
vec2 i = floor(p), f = p - i;
|
|
1411
|
+
vec2 s = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
1412
|
+
highp ivec3 c = ivec3(ivec2(i), 0);
|
|
1413
|
+
return mix(mix(phoriaUnit(phoriaCell(c)), phoriaUnit(phoriaCell(c + ivec3(1, 0, 0))), s.x),
|
|
1414
|
+
mix(phoriaUnit(phoriaCell(c + ivec3(0, 1, 0))), phoriaUnit(phoriaCell(c + ivec3(1, 1, 0))), s.x), s.y);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// simplex: a corner's share of the value and the gradient - t^4 times the gradient's ramp, falling
|
|
1418
|
+
// to nothing at the corner's reach
|
|
1419
|
+
vec2 phoriaGrad2(highp ivec2 c) {
|
|
1420
|
+
float a = phoriaUnit(phoriaCell(ivec3(c, 7))) * 6.28318530718;
|
|
1421
|
+
return vec2(cos(a), sin(a));
|
|
1422
|
+
}
|
|
1423
|
+
void phoriaCorner2(vec2 x, highp ivec2 c, inout float n, inout vec2 d) {
|
|
1424
|
+
float t = 0.5 - dot(x, x);
|
|
1425
|
+
if (t <= 0.0) return;
|
|
1426
|
+
vec2 g = phoriaGrad2(c);
|
|
1427
|
+
float t2 = t * t, gx = dot(g, x);
|
|
1428
|
+
n += t2 * t2 * gx;
|
|
1429
|
+
d += t2 * t2 * g - 8.0 * t2 * t * gx * x;
|
|
1430
|
+
}
|
|
1431
|
+
vec3 snoised(vec2 p) {
|
|
1432
|
+
const float F = 0.366025403784, G = 0.211324865405;
|
|
1433
|
+
vec2 i = floor(p + (p.x + p.y) * F);
|
|
1434
|
+
vec2 x0 = p - i + (i.x + i.y) * G;
|
|
1435
|
+
vec2 o = x0.x > x0.y ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
|
1436
|
+
highp ivec2 c = ivec2(i);
|
|
1437
|
+
float n = 0.0;
|
|
1438
|
+
vec2 d = vec2(0.0);
|
|
1439
|
+
phoriaCorner2(x0, c, n, d);
|
|
1440
|
+
phoriaCorner2(x0 - o + G, c + ivec2(o), n, d);
|
|
1441
|
+
phoriaCorner2(x0 - 1.0 + 2.0 * G, c + 1, n, d);
|
|
1442
|
+
// unit gradients peak near 1/70: scaled to -1-1, then to 0-1
|
|
1443
|
+
return vec3(0.5 + 35.0 * n, 35.0 * d);
|
|
1444
|
+
}
|
|
1445
|
+
float snoise(vec2 p) { return snoised(p).x; }
|
|
1446
|
+
|
|
1447
|
+
const vec3 PHORIA_EDGES[12] = vec3[12](vec3(1, 1, 0), vec3(-1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0),
|
|
1448
|
+
vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1), vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, 1, -1), vec3(0, -1, -1));
|
|
1449
|
+
void phoriaCorner3(vec3 x, highp ivec3 c, inout float n, inout vec3 d) {
|
|
1450
|
+
float t = 0.6 - dot(x, x);
|
|
1451
|
+
if (t <= 0.0) return;
|
|
1452
|
+
vec3 g = PHORIA_EDGES[phoriaCell(c) % 12u];
|
|
1453
|
+
float t2 = t * t, gx = dot(g, x);
|
|
1454
|
+
n += t2 * t2 * gx;
|
|
1455
|
+
d += t2 * t2 * g - 8.0 * t2 * t * gx * x;
|
|
1456
|
+
}
|
|
1457
|
+
vec4 snoised(vec3 p) {
|
|
1458
|
+
const float F = 1.0 / 3.0, G = 1.0 / 6.0;
|
|
1459
|
+
vec3 i = floor(p + dot(p, vec3(F)));
|
|
1460
|
+
vec3 x0 = p - i + dot(i, vec3(G));
|
|
1461
|
+
vec3 g = step(x0.yzx, x0.xyz), l = 1.0 - g;
|
|
1462
|
+
vec3 i1 = min(g, l.zxy), i2 = max(g, l.zxy);
|
|
1463
|
+
highp ivec3 c = ivec3(i);
|
|
1464
|
+
float n = 0.0;
|
|
1465
|
+
vec3 d = vec3(0.0);
|
|
1466
|
+
phoriaCorner3(x0, c, n, d);
|
|
1467
|
+
phoriaCorner3(x0 - i1 + G, c + ivec3(i1), n, d);
|
|
1468
|
+
phoriaCorner3(x0 - i2 + 2.0 * G, c + ivec3(i2), n, d);
|
|
1469
|
+
phoriaCorner3(x0 - 1.0 + 3.0 * G, c + 1, n, d);
|
|
1470
|
+
// edge gradients peak near 1/32: scaled to -1-1, then to 0-1
|
|
1471
|
+
return vec4(0.5 + 16.0 * n, 16.0 * d);
|
|
1472
|
+
}
|
|
1473
|
+
float snoise(vec3 p) { return snoised(p).x; }
|
|
1474
|
+
|
|
1475
|
+
// fbm: each octave turned (so grids don't line up into a visible lattice), scaled by 2.03 and moved
|
|
1476
|
+
// on, its amplitude halved; the sum divided by the amplitudes, so 0-1 like the noise
|
|
1477
|
+
const mat2 PHORIA_TURN2 = mat2(0.8, 0.6, -0.6, 0.8);
|
|
1478
|
+
const mat3 PHORIA_TURN3 = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
|
|
1479
|
+
float vfbm(vec2 p, int octaves) {
|
|
1480
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1481
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * vnoise(p); sum += a; p = PHORIA_TURN2 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1482
|
+
return v / sum;
|
|
1483
|
+
}
|
|
1484
|
+
float vfbm(vec3 p, int octaves) {
|
|
1485
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1486
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * vnoise(p); sum += a; p = PHORIA_TURN3 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1487
|
+
return v / sum;
|
|
1488
|
+
}
|
|
1489
|
+
float sfbm(vec2 p, int octaves) {
|
|
1490
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1491
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * snoise(p); sum += a; p = PHORIA_TURN2 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1492
|
+
return v / sum;
|
|
1493
|
+
}
|
|
1494
|
+
float sfbm(vec3 p, int octaves) {
|
|
1495
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1496
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) { v += a * snoise(p); sum += a; p = PHORIA_TURN3 * p * 2.03 + 17.0; a *= 0.5; }
|
|
1497
|
+
return v / sum;
|
|
1498
|
+
}
|
|
1499
|
+
// with the gradient: each octave's, carried back through the turns and scalings so far
|
|
1500
|
+
vec3 sfbmd(vec2 p, int octaves) {
|
|
1501
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1502
|
+
vec2 d = vec2(0.0);
|
|
1503
|
+
mat2 m = mat2(1.0);
|
|
1504
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) {
|
|
1505
|
+
vec3 n = snoised(p);
|
|
1506
|
+
v += a * n.x;
|
|
1507
|
+
d += a * (transpose(m) * n.yz);
|
|
1508
|
+
sum += a;
|
|
1509
|
+
p = PHORIA_TURN2 * p * 2.03 + 17.0;
|
|
1510
|
+
m = PHORIA_TURN2 * m * 2.03;
|
|
1511
|
+
a *= 0.5;
|
|
1512
|
+
}
|
|
1513
|
+
return vec3(v, d) / sum;
|
|
1514
|
+
}
|
|
1515
|
+
vec4 sfbmd(vec3 p, int octaves) {
|
|
1516
|
+
float v = 0.0, a = 0.5, sum = 0.0;
|
|
1517
|
+
vec3 d = vec3(0.0);
|
|
1518
|
+
mat3 m = mat3(1.0);
|
|
1519
|
+
for (int i = 0; i < clamp(octaves, 1, 12); i++) {
|
|
1520
|
+
vec4 n = snoised(p);
|
|
1521
|
+
v += a * n.x;
|
|
1522
|
+
d += a * (transpose(m) * n.yzw);
|
|
1523
|
+
sum += a;
|
|
1524
|
+
p = PHORIA_TURN3 * p * 2.03 + 17.0;
|
|
1525
|
+
m = PHORIA_TURN3 * m * 2.03;
|
|
1526
|
+
a *= 0.5;
|
|
1527
|
+
}
|
|
1528
|
+
return vec4(v, d) / sum;
|
|
1529
|
+
}
|
|
1530
|
+
float vfbm(vec2 p) { return vfbm(p, 5); }
|
|
1531
|
+
float vfbm(vec3 p) { return vfbm(p, 5); }
|
|
1532
|
+
float sfbm(vec2 p) { return sfbm(p, 5); }
|
|
1533
|
+
float sfbm(vec3 p) { return sfbm(p, 5); }
|
|
1534
|
+
vec3 sfbmd(vec2 p) { return sfbmd(p, 5); }
|
|
1535
|
+
vec4 sfbmd(vec3 p) { return sfbmd(p, 5); }
|
|
1536
|
+
|
|
1537
|
+
// cellular noise: each unit cell's own point, from its hash; the nearest and second nearest of the
|
|
1538
|
+
// 3 x 3 (or 3 x 3 x 3) cells round p - F1, F2 - and the nearest cell's own random number
|
|
1539
|
+
vec3 voronoi(vec2 p) {
|
|
1540
|
+
vec2 i = floor(p), f = p - i;
|
|
1541
|
+
highp ivec2 c = ivec2(i);
|
|
1542
|
+
float f1 = 8.0, f2 = 8.0, id = 0.0;
|
|
1543
|
+
for (int y = -1; y <= 1; y++) {
|
|
1544
|
+
for (int x = -1; x <= 1; x++) {
|
|
1545
|
+
highp uint h = phoriaCell(ivec3(c + ivec2(x, y), 11));
|
|
1546
|
+
vec2 o = vec2(float(x), float(y)) + vec2(phoriaUnit(h), phoriaUnit(phoriaMix(h ^ 0x68e31da4u))) - f;
|
|
1547
|
+
float d = dot(o, o);
|
|
1548
|
+
if (d < f1) { f2 = f1; f1 = d; id = phoriaUnit(phoriaMix(h + 0x1b56c4e9u)); }
|
|
1549
|
+
else if (d < f2) f2 = d;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return vec3(sqrt(f1), sqrt(f2), id);
|
|
1553
|
+
}
|
|
1554
|
+
vec3 voronoi(vec3 p) {
|
|
1555
|
+
vec3 i = floor(p), f = p - i;
|
|
1556
|
+
highp ivec3 c = ivec3(i);
|
|
1557
|
+
float f1 = 8.0, f2 = 8.0, id = 0.0;
|
|
1558
|
+
for (int z = -1; z <= 1; z++) {
|
|
1559
|
+
for (int y = -1; y <= 1; y++) {
|
|
1560
|
+
for (int x = -1; x <= 1; x++) {
|
|
1561
|
+
highp uint h = phoriaCell(c + ivec3(x, y, z)) ^ 0x2545f491u;
|
|
1562
|
+
h = phoriaMix(h);
|
|
1563
|
+
vec3 o = vec3(float(x), float(y), float(z)) - f
|
|
1564
|
+
+ vec3(phoriaUnit(h), phoriaUnit(phoriaMix(h ^ 0x68e31da4u)), phoriaUnit(phoriaMix(h ^ 0xb5297a4du)));
|
|
1565
|
+
float d = dot(o, o);
|
|
1566
|
+
if (d < f1) { f2 = f1; f1 = d; id = phoriaUnit(phoriaMix(h + 0x1b56c4e9u)); }
|
|
1567
|
+
else if (d < f2) f2 = d;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return vec3(sqrt(f1), sqrt(f2), id);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// a colour as picked (sRGB) to the linear colour hooks work in
|
|
1575
|
+
vec3 srgb(vec3 c) { return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c)); }
|
|
1576
|
+
// hue, saturation and value 0-1 to a linear colour - the colour a colour picker shows
|
|
1577
|
+
vec3 hsv2rgb(vec3 hsv) {
|
|
1578
|
+
vec3 k = clamp(abs(mod(hsv.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
|
1579
|
+
return srgb(hsv.z * mix(vec3(1.0), k, hsv.y));
|
|
1580
|
+
}
|
|
1581
|
+
vec3 hsv2rgb(float h, float s, float v) { return hsv2rgb(vec3(h, s, v)); }
|
|
1582
|
+
// a cosine palette (Quilez): a + b cos(2 pi (c t + d)), its four vectors as picked; a linear colour
|
|
1583
|
+
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) { return srgb(clamp(a + b * cos(6.28318530718 * (c * t + d)), 0.0, 1.0)); }
|
|
1584
|
+
// how edge-on a surface is seen: 0 facing the camera, 1 side on, sharpened by the power - rim light
|
|
1585
|
+
float fresnel(vec3 normal, vec3 position, float power) {
|
|
1586
|
+
vec3 v = normalize(cameraPosition - position);
|
|
1587
|
+
return pow(1.0 - clamp(abs(dot(normalize(normal), v)), 0.0, 1.0), power);
|
|
1588
|
+
}
|
|
1589
|
+
// turning: rotate2(a) * v anticlockwise by a radians; rotate3(axis, a) * v about an axis
|
|
1590
|
+
mat2 rotate2(float a) { float s = sin(a), c = cos(a); return mat2(c, s, -s, c); }
|
|
1591
|
+
mat3 rotate3(vec3 axis, float a) {
|
|
1592
|
+
vec3 u = normalize(axis);
|
|
1593
|
+
float s = sin(a), c = cos(a), t = 1.0 - c;
|
|
1594
|
+
return mat3(t * u.x * u.x + c, t * u.x * u.y + s * u.z, t * u.x * u.z - s * u.y,
|
|
1595
|
+
t * u.x * u.y - s * u.z, t * u.y * u.y + c, t * u.y * u.z + s * u.x,
|
|
1596
|
+
t * u.x * u.z + s * u.y, t * u.y * u.z - s * u.x, t * u.z * u.z + c);
|
|
1597
|
+
}
|
|
1598
|
+
// x from one range to another, not clamped
|
|
1599
|
+
float remap(float x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1600
|
+
vec2 remap(vec2 x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1601
|
+
vec3 remap(vec3 x, float a, float b, float c, float d) { return c + (x - a) * (d - c) / (b - a); }
|
|
1602
|
+
// clamped to 0-1, as HLSL has it
|
|
1603
|
+
float saturate(float x) { return clamp(x, 0.0, 1.0); }
|
|
1604
|
+
vec2 saturate(vec2 x) { return clamp(x, 0.0, 1.0); }
|
|
1605
|
+
vec3 saturate(vec3 x) { return clamp(x, 0.0, 1.0); }
|
|
1606
|
+
vec4 saturate(vec4 x) { return clamp(x, 0.0, 1.0); }
|
|
1607
|
+
`
|
|
1608
|
+
|
|
1609
|
+
// a hook's own lines are numbered from 1 in a source string of their own, so compile errors point
|
|
1610
|
+
// at the line of the hook, not of the generated shader
|
|
1611
|
+
export const HOOK_SOURCE_NUMBERS = { vertex: 101, fragment: 102, lit: 103 }
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Vertex and fragment source for a kind of drawable, or a pass's own pair, with a set of feature
|
|
1615
|
+
* defines - and a material's shader hooks, when it has them.
|
|
1125
1616
|
* @param {ShaderKind | [string, string]} kind
|
|
1126
1617
|
* @param {string[]} defines e.g. ['LIT', 'MAP']
|
|
1618
|
+
* @param {HookSource | null} [hooks]
|
|
1127
1619
|
* @returns {[string, string]}
|
|
1128
1620
|
*/
|
|
1129
|
-
export function shaderSource(kind, defines) {
|
|
1621
|
+
export function shaderSource(kind, defines, hooks = null) {
|
|
1130
1622
|
const header = `#version 300 es\n${defines.map((d) => `#define ${d}\n`).join('')}`
|
|
1131
1623
|
const [vs, fs] = typeof kind === 'string' ? SOURCES[kind] : kind
|
|
1132
|
-
return [header + vs, header + fs]
|
|
1624
|
+
if (!hooks) return [header + vs, header + fs]
|
|
1625
|
+
const first = 2 + defines.length
|
|
1626
|
+
return [header + splice(vs, hooks, first), header + splice(fs, hooks, first)]
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/** @param {string} source @param {HookSource} hooks @param {number} first the line number of the source's first line */
|
|
1630
|
+
function splice(source, hooks, first) {
|
|
1631
|
+
return source.split('\n').map((line, i) => {
|
|
1632
|
+
const at = /^\s*\/\/@hook (\w+)$/.exec(line)
|
|
1633
|
+
if (!at) return line
|
|
1634
|
+
const part = /** @type {keyof HookSource} */ (at[1])
|
|
1635
|
+
// the shader's own numbering carries on after the hook as if it weren't there
|
|
1636
|
+
const resume = `#line ${first + i + 1} 0`
|
|
1637
|
+
if (part === 'uniforms') return `${hooks.uniforms}\n${hooks.helpers ?? ''}\n${resume}`
|
|
1638
|
+
return `#line 1 ${HOOK_SOURCE_NUMBERS[part]}\n${hooks[part]}\n${resume}`
|
|
1639
|
+
}).join('\n')
|
|
1133
1640
|
}
|