phoria2 1.0.0 → 1.2.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 +71 -13
- package/dist/bloom.js +1 -1
- package/dist/charts.js +397 -384
- package/dist/charts.js.map +1 -1
- package/dist/core.js +4451 -3568
- package/dist/core.js.map +1 -1
- package/dist/edit.js +67 -67
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +6 -2
- package/dist/explain.js.map +1 -1
- package/dist/gltf.js +211 -166
- package/dist/gltf.js.map +1 -1
- package/dist/motion.js +26 -26
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +49 -49
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +188 -86
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +8 -13
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +3 -3
- package/dist/tooltip.js.map +1 -1
- package/dist/types/OrbitControl.d.ts +6 -2
- package/dist/types/Raycaster.d.ts +10 -0
- package/dist/types/View.d.ts +64 -1
- package/dist/types/charts/Chart.d.ts +7 -0
- package/dist/types/explain.d.ts +3 -0
- package/dist/types/frame.d.ts +7 -0
- package/dist/types/geometry/Geometry.d.ts +14 -2
- package/dist/types/keyboard.d.ts +48 -0
- package/dist/types/labels.d.ts +4 -0
- package/dist/types/material/Material.d.ts +125 -0
- package/dist/types/material/Texture.d.ts +37 -5
- package/dist/types/scene/Node.d.ts +19 -14
- package/dist/types/scene/drawables.d.ts +32 -14
- package/dist/types/scene/lights.d.ts +16 -2
- package/dist/types/shadow.d.ts +2 -1
- package/dist/types/types.d.ts +14 -0
- package/dist/types/value/Color.d.ts +0 -4
- package/dist/types/value/Quat.d.ts +4 -2
- package/dist/types/webgl/WebGLRenderer.d.ts +6 -0
- package/dist/types/webgl/passes.d.ts +37 -13
- package/dist/types/webgl/resources.d.ts +24 -2
- package/dist/types/webgl/shaders.d.ts +4 -2
- package/llms-full.txt +153 -15
- package/package.json +4 -1
- package/schema/scene.schema.json +146 -1
- package/src/OrbitControl.js +7 -3
- package/src/Raycaster.js +58 -0
- package/src/View.js +207 -42
- package/src/charts/Chart.js +20 -2
- package/src/explain.js +6 -2
- package/src/frame.js +29 -9
- package/src/geometry/Geometry.js +24 -4
- package/src/gltf/load.js +50 -6
- package/src/keyboard.js +298 -0
- package/src/labels.js +4 -1
- package/src/material/Material.js +104 -19
- package/src/material/Texture.js +116 -11
- package/src/scene/Node.js +20 -9
- package/src/scene/Scene.js +2 -0
- package/src/scene/cameras.js +4 -1
- package/src/scene/drawables.js +48 -12
- package/src/scene/lights.js +40 -6
- package/src/shadow.js +27 -4
- package/src/types.js +6 -0
- package/src/value/Color.js +21 -0
- package/src/value/Quat.js +15 -6
- package/src/watch.js +25 -1
- package/src/webgl/WebGLRenderer.js +51 -12
- package/src/webgl/passes.js +190 -32
- package/src/webgl/picking.js +0 -1
- package/src/webgl/resources.js +89 -8
- package/src/webgl/shaders.js +350 -48
package/src/webgl/shaders.js
CHANGED
|
@@ -10,6 +10,8 @@ export const ATTRIBUTES = {
|
|
|
10
10
|
color: 3,
|
|
11
11
|
instanceMatrix: 4, // 4-7, a mat4 takes four slots
|
|
12
12
|
instanceColor: 8,
|
|
13
|
+
tangent: 9, // brushed metal's direction: along the UVs' u, and which way their v runs
|
|
14
|
+
uv2: 10, // a second UV set, for baked occlusion and light maps
|
|
13
15
|
// lines: per-segment endpoints and colours, and the corner of the quad being drawn
|
|
14
16
|
segmentStart: 0,
|
|
15
17
|
segmentEnd: 1,
|
|
@@ -62,16 +64,30 @@ vec3 fogged(vec3 c) { return c; }
|
|
|
62
64
|
// depth or stencil only - of what isn't cut away
|
|
63
65
|
// How a map is read: by UV, or projected from the mesh's three axes (triplanar) - the same in every pass
|
|
64
66
|
// that reads one, so a cut-out's shadow has the holes it is drawn with. Needs vUv declared first.
|
|
67
|
+
// the samplers a mesh's maps are read through
|
|
68
|
+
const MAP_SAMPLERS = ['uMap', 'uRoughnessMap', 'uMetalnessMap', 'uNormalMap', 'uBumpMap', 'uAoMap', 'uEmissiveMap', 'uLightMap']
|
|
69
|
+
|
|
65
70
|
const SAMPLING = /* glsl */ `
|
|
71
|
+
#ifdef UV2
|
|
72
|
+
in vec2 vUv2;
|
|
73
|
+
#endif
|
|
74
|
+
// each map's own transform - repeat, rotation and offset - and the UV set it reads: the first
|
|
75
|
+
// unless the renderer defines its set as vUv2. uMapCoord is where uMap is read and uMapTexel what
|
|
76
|
+
// it holds there - spelled out per map, as WebGL's GLSL has no token pasting
|
|
77
|
+
${MAP_SAMPLERS.map((m) => `uniform mat3 ${m}Uv;
|
|
78
|
+
#ifndef ${m}Set
|
|
79
|
+
#define ${m}Set vUv
|
|
80
|
+
#endif
|
|
81
|
+
#define ${m}Coord (${m}Uv * vec3(${m}Set, 1.0)).xy`).join('\n')}
|
|
66
82
|
#ifdef TRIPLANAR
|
|
67
83
|
in vec3 vObjectPosition;
|
|
68
84
|
in vec3 vObjectNormal;
|
|
69
85
|
in mat3 vObjectToWorld;
|
|
70
86
|
uniform float uTriplanar; // repeats per unit of the mesh's own space
|
|
71
|
-
uniform vec4 uUvTransform; // the map's repeat xy and offset zw, applied to each projection as to UVs
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
// a projection's coordinates through the map's transform, as UVs would be
|
|
89
|
+
vec2 projected(vec2 p, mat3 t) {
|
|
90
|
+
return (t * vec3(p, 1.0)).xy;
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
// how much each axis's projection counts: mostly the one the surface faces, blended at the turns
|
|
@@ -80,13 +96,13 @@ vec3 triplanarWeights(vec3 n) {
|
|
|
80
96
|
return w / (w.x + w.y + w.z);
|
|
81
97
|
}
|
|
82
98
|
// a map seen from the three axes and blended - for meshes with no UVs, scans and CAD models
|
|
83
|
-
vec4 triplanar(sampler2D s) {
|
|
99
|
+
vec4 triplanar(sampler2D s, mat3 t) {
|
|
84
100
|
vec3 p = vObjectPosition * uTriplanar, w = triplanarWeights(normalize(vObjectNormal));
|
|
85
|
-
return texture(s, projected(p.zy)) * w.x + texture(s, projected(p.xz)) * w.y + texture(s, projected(p.xy)) * w.z;
|
|
101
|
+
return texture(s, projected(p.zy, t)) * w.x + texture(s, projected(p.xz, t)) * w.y + texture(s, projected(p.xy, t)) * w.z;
|
|
86
102
|
}
|
|
87
|
-
|
|
103
|
+
${MAP_SAMPLERS.map((m) => `#define ${m}Texel triplanar(${m}, ${m}Uv)`).join('\n')}
|
|
88
104
|
#else
|
|
89
|
-
|
|
105
|
+
${MAP_SAMPLERS.map((m) => `#define ${m}Texel texture(${m}, ${m}Coord)`).join('\n')}
|
|
90
106
|
#endif
|
|
91
107
|
`
|
|
92
108
|
|
|
@@ -110,7 +126,7 @@ void main() {
|
|
|
110
126
|
#ifdef ALPHA_TEST
|
|
111
127
|
float alpha = uOpacity;
|
|
112
128
|
#ifdef MAP
|
|
113
|
-
alpha *=
|
|
129
|
+
alpha *= uMapTexel.a;
|
|
114
130
|
#endif
|
|
115
131
|
if (alpha < uAlphaTest) discard;
|
|
116
132
|
#endif
|
|
@@ -186,11 +202,18 @@ layout(location = 3) in vec3 color;
|
|
|
186
202
|
layout(location = 4) in mat4 instanceMatrix;
|
|
187
203
|
layout(location = 8) in vec3 instanceColor;
|
|
188
204
|
#endif
|
|
205
|
+
#ifdef ANISOTROPY
|
|
206
|
+
layout(location = 9) in vec4 tangent;
|
|
207
|
+
out vec4 vTangent;
|
|
208
|
+
#endif
|
|
209
|
+
#ifdef UV2
|
|
210
|
+
layout(location = 10) in vec2 uv2;
|
|
211
|
+
out vec2 vUv2;
|
|
212
|
+
#endif
|
|
189
213
|
|
|
190
214
|
uniform mat4 uModel;
|
|
191
215
|
uniform mat3 uNormalMatrix;
|
|
192
216
|
uniform mat4 uViewProjection;
|
|
193
|
-
uniform vec4 uUvTransform; // repeat xy, offset zw
|
|
194
217
|
uniform int uHighlightIndex; // -2 none, -1 the whole node, otherwise the instance
|
|
195
218
|
#ifdef FOG
|
|
196
219
|
uniform mat4 uView;
|
|
@@ -238,7 +261,18 @@ void main() {
|
|
|
238
261
|
vec4 world = uModel * local;
|
|
239
262
|
vWorldPosition = world.xyz;
|
|
240
263
|
vNormal = uNormalMatrix * n;
|
|
241
|
-
vUv = uv
|
|
264
|
+
vUv = uv;
|
|
265
|
+
#ifdef UV2
|
|
266
|
+
vUv2 = uv2;
|
|
267
|
+
#endif
|
|
268
|
+
#ifdef ANISOTROPY
|
|
269
|
+
// a direction along the surface, so it turns as the model does
|
|
270
|
+
#ifdef INSTANCED
|
|
271
|
+
vTangent = vec4(mat3(uModel) * (m * tangent.xyz), tangent.w);
|
|
272
|
+
#else
|
|
273
|
+
vTangent = vec4(mat3(uModel) * tangent.xyz, tangent.w);
|
|
274
|
+
#endif
|
|
275
|
+
#endif
|
|
242
276
|
#ifdef TRIPLANAR
|
|
243
277
|
vObjectPosition = position;
|
|
244
278
|
vObjectNormal = normal;
|
|
@@ -306,6 +340,38 @@ uniform float uBumpScale;
|
|
|
306
340
|
uniform sampler2D uAoMap;
|
|
307
341
|
uniform float uAoMapIntensity;
|
|
308
342
|
#endif
|
|
343
|
+
#ifdef LIGHT_MAP
|
|
344
|
+
uniform sampler2D uLightMap;
|
|
345
|
+
uniform float uLightMapIntensity;
|
|
346
|
+
#endif
|
|
347
|
+
uniform float uEnvStrength; // this material's share of the environment's light and reflections
|
|
348
|
+
#ifdef TRANSMISSION
|
|
349
|
+
uniform float uTransmission;
|
|
350
|
+
#endif
|
|
351
|
+
#ifdef CLEARCOAT
|
|
352
|
+
uniform float uClearcoat;
|
|
353
|
+
uniform float uClearcoatRoughness;
|
|
354
|
+
#endif
|
|
355
|
+
uniform vec4 uSpecular; // a non-metal's reflectance face on (from its ior), and at grazing angles
|
|
356
|
+
#ifdef MATCAP
|
|
357
|
+
uniform sampler2D uMatcap;
|
|
358
|
+
uniform mat4 uView;
|
|
359
|
+
#endif
|
|
360
|
+
#ifdef TOON
|
|
361
|
+
uniform float uToon; // how many tones, 2 or more
|
|
362
|
+
#endif
|
|
363
|
+
#ifdef SHEEN
|
|
364
|
+
uniform vec3 uSheenColor; // linear, sheen applied
|
|
365
|
+
uniform float uSheenRoughness;
|
|
366
|
+
#endif
|
|
367
|
+
#ifdef ANISOTROPY
|
|
368
|
+
uniform float uAnisotropy;
|
|
369
|
+
uniform vec2 uAnisotropyDirection; // cos, sin of the brushing's turn from the UVs' u
|
|
370
|
+
in vec4 vTangent;
|
|
371
|
+
#endif
|
|
372
|
+
#ifdef TRANSLUCENCY
|
|
373
|
+
uniform vec4 uTranslucency; // the light let through: linear colour with the amount applied, and the amount
|
|
374
|
+
#endif
|
|
309
375
|
|
|
310
376
|
uniform vec3 uAmbient;
|
|
311
377
|
uniform vec3 uHemisphereSky;
|
|
@@ -377,25 +443,110 @@ float V_SmithGGXCorrelated(float NoV, float NoL, float a) {
|
|
|
377
443
|
return 0.5 / max(gv + gl, 1e-5);
|
|
378
444
|
}
|
|
379
445
|
|
|
380
|
-
vec3 F_Schlick(float VoH, vec3 f0) {
|
|
381
|
-
return f0 + (
|
|
446
|
+
vec3 F_Schlick(float VoH, vec3 f0, float f90) {
|
|
447
|
+
return f0 + (f90 - f0) * pow(1.0 - VoH, 5.0);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
#ifdef ANISOTROPY
|
|
451
|
+
// brushed metal: the brushing's direction, the way across it, and roughness along and across it
|
|
452
|
+
vec3 brushT, brushB;
|
|
453
|
+
float brushAT, brushAB;
|
|
454
|
+
|
|
455
|
+
// GGX stretched along the brushing (KHR_materials_anisotropy): distribution and visibility together
|
|
456
|
+
float anisotropicDV(vec3 v, vec3 l, vec3 h, float NoV, float NoL, float NoH) {
|
|
457
|
+
float a2 = brushAT * brushAB;
|
|
458
|
+
vec3 f = vec3(brushAB * dot(brushT, h), brushAT * dot(brushB, h), a2 * NoH);
|
|
459
|
+
float w2 = a2 / dot(f, f);
|
|
460
|
+
float gv = NoL * length(vec3(brushAT * dot(brushT, v), brushAB * dot(brushB, v), NoV));
|
|
461
|
+
float gl = NoV * length(vec3(brushAT * dot(brushT, l), brushAB * dot(brushB, l), NoL));
|
|
462
|
+
return a2 * w2 * w2 / PI * 0.5 / max(gv + gl, 1e-5);
|
|
382
463
|
}
|
|
464
|
+
#endif
|
|
383
465
|
|
|
384
466
|
// Light intensity 1 on a white surface facing it gives white: the Lambert term isn't divided by
|
|
385
467
|
// PI, so the specular term is multiplied by it to keep the balance. (glTF intensities in lux and
|
|
386
468
|
// candela are divided by PI when loaded.)
|
|
387
|
-
|
|
469
|
+
#ifdef TOON
|
|
470
|
+
// the highlight's colour: white on a non-metal, the surface's colour on a metal, fading as it roughens
|
|
471
|
+
vec3 toonHighlight;
|
|
472
|
+
|
|
473
|
+
// light in flat tones, antialiased where they meet: a step up a little before each tone's middle,
|
|
474
|
+
// so more of a surface is lit than dark
|
|
475
|
+
float toonTone(float x) {
|
|
476
|
+
float steps = uToon - 1.0;
|
|
477
|
+
float b = x * steps + 0.6;
|
|
478
|
+
float w = fwidth(b);
|
|
479
|
+
return min(floor(b) + smoothstep(1.0 - w, 1.0, fract(b)), steps) / steps;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// a cartoon's shading: flat tones of the colour, and one hard-edged highlight where GGX's is over
|
|
483
|
+
// half its peak
|
|
484
|
+
vec3 toonShade(vec3 n, vec3 v, vec3 l, vec3 radiance, vec3 diffuse, float a) {
|
|
485
|
+
float lit = toonTone(clamp(dot(n, l), 0.0, 1.0));
|
|
486
|
+
float s = D_GGX(clamp(dot(n, normalize(l + v)), 0.0, 1.0), a) * PI * a * a;
|
|
487
|
+
float w = fwidth(s);
|
|
488
|
+
float spot = smoothstep(0.5 - w, 0.5 + w, s) * step(0.001, lit);
|
|
489
|
+
return (diffuse * lit + toonHighlight * spot) * radiance;
|
|
490
|
+
}
|
|
491
|
+
#endif
|
|
492
|
+
|
|
493
|
+
vec3 shade(vec3 n, vec3 v, vec3 l, vec3 radiance, vec3 diffuse, vec3 f0, float f90, float a) {
|
|
494
|
+
#ifdef TOON
|
|
495
|
+
return toonShade(n, v, l, radiance, diffuse, a);
|
|
496
|
+
#endif
|
|
388
497
|
float NoL = clamp(dot(n, l), 0.0, 1.0);
|
|
389
498
|
if (NoL <= 0.0) return vec3(0.0);
|
|
390
499
|
vec3 h = normalize(l + v);
|
|
391
500
|
float NoV = clamp(abs(dot(n, v)), 1e-4, 1.0);
|
|
392
501
|
float NoH = clamp(dot(n, h), 0.0, 1.0);
|
|
393
502
|
float VoH = clamp(dot(v, h), 0.0, 1.0);
|
|
394
|
-
vec3 F = F_Schlick(VoH, f0);
|
|
503
|
+
vec3 F = F_Schlick(VoH, f0, f90);
|
|
504
|
+
#ifdef ANISOTROPY
|
|
505
|
+
vec3 specular = anisotropicDV(v, l, h, NoV, NoL, NoH) * F * PI;
|
|
506
|
+
#else
|
|
395
507
|
vec3 specular = D_GGX(NoH, a) * V_SmithGGXCorrelated(NoV, NoL, a) * F * PI;
|
|
508
|
+
#endif
|
|
396
509
|
return (diffuse * (1.0 - F) + specular) * radiance * NoL;
|
|
397
510
|
}
|
|
398
511
|
|
|
512
|
+
#ifdef SHEEN
|
|
513
|
+
// cloth's sheen: the Charlie distribution (Estevez and Kulla) with Neubelt's visibility
|
|
514
|
+
vec3 sheenShade(vec3 n, vec3 v, vec3 l, vec3 radiance, float a) {
|
|
515
|
+
float NoL = clamp(dot(n, l), 0.0, 1.0);
|
|
516
|
+
if (NoL <= 0.0) return vec3(0.0);
|
|
517
|
+
vec3 h = normalize(l + v);
|
|
518
|
+
float NoV = clamp(abs(dot(n, v)), 1e-4, 1.0);
|
|
519
|
+
float NoH = clamp(dot(n, h), 0.0, 1.0);
|
|
520
|
+
float invA = 1.0 / a;
|
|
521
|
+
float sin2h = max(1.0 - NoH * NoH, 0.0078125);
|
|
522
|
+
float D = (2.0 + invA) * pow(sin2h, invA * 0.5) / (2.0 * PI);
|
|
523
|
+
float V = clamp(1.0 / (4.0 * (NoL + NoV - NoL * NoV)), 0.0, 1.0);
|
|
524
|
+
return uSheenColor * D * V * PI * radiance * NoL;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// how much of the surroundings' light the sheen sends back: a fit to its integral (from three.js)
|
|
528
|
+
float sheenEnvironment(float NoV, float r) {
|
|
529
|
+
float r2 = r * r;
|
|
530
|
+
float a = r < 0.25 ? -339.2 * r2 + 161.4 * r - 25.9 : -8.48 * r2 + 14.3 * r - 9.95;
|
|
531
|
+
float b = r < 0.25 ? 44.0 * r2 - 23.7 * r + 3.26 : 1.97 * r2 - 3.27 * r + 0.72;
|
|
532
|
+
return min(exp(a * NoV + b) + (r < 0.25 ? 0.0 : 0.1 * (r - 0.25)), PI);
|
|
533
|
+
}
|
|
534
|
+
#endif
|
|
535
|
+
|
|
536
|
+
#ifdef CLEARCOAT
|
|
537
|
+
// a clear coat's highlight: a glossy dielectric layer (f0 0.04) on the surface's own normal, so
|
|
538
|
+
// normal and bump maps show beneath it, under a smooth coat
|
|
539
|
+
vec3 coatShade(vec3 n, vec3 v, vec3 l, vec3 radiance, float a) {
|
|
540
|
+
float NoL = clamp(dot(n, l), 0.0, 1.0);
|
|
541
|
+
if (NoL <= 0.0) return vec3(0.0);
|
|
542
|
+
vec3 h = normalize(l + v);
|
|
543
|
+
float NoV = clamp(abs(dot(n, v)), 1e-4, 1.0);
|
|
544
|
+
float NoH = clamp(dot(n, h), 0.0, 1.0);
|
|
545
|
+
float VoH = clamp(dot(v, h), 0.0, 1.0);
|
|
546
|
+
return D_GGX(NoH, a) * V_SmithGGXCorrelated(NoV, NoL, a) * F_Schlick(VoH, vec3(0.04), 1.0) * PI * radiance * NoL;
|
|
547
|
+
}
|
|
548
|
+
#endif
|
|
549
|
+
|
|
399
550
|
// The built-in 'studio' environment: a soft, bright overhead box over a darker floor, as a
|
|
400
551
|
// function of direction - no texture needed.
|
|
401
552
|
vec3 studio(vec3 d) {
|
|
@@ -406,13 +557,13 @@ vec3 studio(vec3 d) {
|
|
|
406
557
|
}
|
|
407
558
|
|
|
408
559
|
// Karis' analytic approximation of the split-sum environment BRDF.
|
|
409
|
-
vec3 envBRDF(vec3 f0, float roughness, float NoV) {
|
|
560
|
+
vec3 envBRDF(vec3 f0, float f90, float roughness, float NoV) {
|
|
410
561
|
vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
|
411
562
|
vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
|
|
412
563
|
vec4 r = roughness * c0 + c1;
|
|
413
564
|
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
|
414
565
|
vec2 ab = vec2(-1.04, 1.04) * a004 + r.zw;
|
|
415
|
-
return f0 * ab.x + ab.y;
|
|
566
|
+
return f0 * ab.x + ab.y * f90;
|
|
416
567
|
}
|
|
417
568
|
|
|
418
569
|
#ifdef ENV_MAP
|
|
@@ -429,7 +580,7 @@ vec3 envIrradiance(vec3 n) {
|
|
|
429
580
|
// A height map as bumps: the normal tilted by how the height changes across the pixel, from
|
|
430
581
|
// screen-space derivatives (Mikkelsen) - no tangents, and it works with UVs or triplanar alike
|
|
431
582
|
vec3 bumpNormal(vec3 n) {
|
|
432
|
-
float h =
|
|
583
|
+
float h = uBumpMapTexel.r * uBumpScale;
|
|
433
584
|
vec2 dh = vec2(dFdx(h), dFdy(h));
|
|
434
585
|
vec3 sx = dFdx(vWorldPosition), sy = dFdy(vWorldPosition);
|
|
435
586
|
vec3 r1 = cross(sy, n), r2 = cross(n, sx);
|
|
@@ -444,9 +595,9 @@ vec3 bumpNormal(vec3 n) {
|
|
|
444
595
|
// 'whiteout' blend), blended, then turned into the world - no tangents and no UVs needed
|
|
445
596
|
vec3 triplanarNormal(float strength) {
|
|
446
597
|
vec3 n = normalize(vObjectNormal), p = vObjectPosition * uTriplanar, w = triplanarWeights(n);
|
|
447
|
-
vec3 tx = texture(uNormalMap, projected(p.zy)).xyz * 2.0 - 1.0;
|
|
448
|
-
vec3 ty = texture(uNormalMap, projected(p.xz)).xyz * 2.0 - 1.0;
|
|
449
|
-
vec3 tz = texture(uNormalMap, projected(p.xy)).xyz * 2.0 - 1.0;
|
|
598
|
+
vec3 tx = texture(uNormalMap, projected(p.zy, uNormalMapUv)).xyz * 2.0 - 1.0;
|
|
599
|
+
vec3 ty = texture(uNormalMap, projected(p.xz, uNormalMapUv)).xyz * 2.0 - 1.0;
|
|
600
|
+
vec3 tz = texture(uNormalMap, projected(p.xy, uNormalMapUv)).xyz * 2.0 - 1.0;
|
|
450
601
|
tx.xy *= strength; ty.xy *= strength; tz.xy *= strength;
|
|
451
602
|
tx = vec3(tx.xy + n.zy, abs(tx.z) * n.x);
|
|
452
603
|
ty = vec3(ty.xy + n.xz, abs(ty.z) * n.y);
|
|
@@ -455,12 +606,23 @@ vec3 triplanarNormal(float strength) {
|
|
|
455
606
|
}
|
|
456
607
|
#endif
|
|
457
608
|
|
|
609
|
+
#ifdef ANISOTROPY
|
|
610
|
+
// The brushing's direction: the vertices' tangents (along the UVs' u), smooth across the mesh,
|
|
611
|
+
// laid onto the shading normal and turned by the rotation
|
|
612
|
+
vec3 brushDirection(vec3 n) {
|
|
613
|
+
vec3 t = vTangent.xyz - n * dot(n, vTangent.xyz);
|
|
614
|
+
float len = length(t);
|
|
615
|
+
t = len > 1e-6 ? t / len : normalize(cross(abs(n.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0), n));
|
|
616
|
+
return t * uAnisotropyDirection.x + cross(n, t) * (vTangent.w < 0.0 ? -1.0 : 1.0) * uAnisotropyDirection.y;
|
|
617
|
+
}
|
|
618
|
+
#endif
|
|
619
|
+
|
|
458
620
|
#ifdef NORMAL_MAP
|
|
459
621
|
// Tangent space from screen-space derivatives (Schüler's cotangent frame), so geometry needs no
|
|
460
622
|
// tangents. UVs run down the image (glTF) while normal maps point +Y up it, hence -b.
|
|
461
|
-
vec3 perturbNormal(vec3 n, vec3 m) {
|
|
623
|
+
vec3 perturbNormal(vec3 n, vec3 m, vec2 uv) {
|
|
462
624
|
vec3 dp1 = dFdx(vWorldPosition), dp2 = dFdy(vWorldPosition);
|
|
463
|
-
vec2 duv1 = dFdx(
|
|
625
|
+
vec2 duv1 = dFdx(uv), duv2 = dFdy(uv);
|
|
464
626
|
vec3 dp2perp = cross(dp2, n), dp1perp = cross(n, dp1);
|
|
465
627
|
vec3 t = dp2perp * duv1.x + dp1perp * duv2.x;
|
|
466
628
|
vec3 b = dp2perp * duv1.y + dp1perp * duv2.y;
|
|
@@ -526,21 +688,60 @@ float pointShadowAt(vec3 light, vec3 n, float distance) {
|
|
|
526
688
|
return lit / 9.0;
|
|
527
689
|
}
|
|
528
690
|
#endif
|
|
691
|
+
|
|
692
|
+
#ifdef MATCAP
|
|
693
|
+
// A matcap - a picture of a lit sphere - looked up by the normal as the camera sees it, turned to
|
|
694
|
+
// face along the line of sight so it doesn't stretch towards the edges of the view
|
|
695
|
+
vec3 matcapAt(vec3 n, vec3 v) {
|
|
696
|
+
vec3 nv = normalize(mat3(uView) * n), vv = normalize(mat3(uView) * v);
|
|
697
|
+
vec3 x = normalize(vec3(vv.z, 0.0, -vv.x));
|
|
698
|
+
vec3 y = cross(vv, x);
|
|
699
|
+
vec2 uv = vec2(dot(x, nv), dot(y, nv)) * 0.495 + 0.5;
|
|
700
|
+
// the picture's top is up
|
|
701
|
+
return texture(uMatcap, vec2(uv.x, 1.0 - uv.y)).rgb;
|
|
702
|
+
}
|
|
703
|
+
#endif
|
|
704
|
+
|
|
705
|
+
// light arriving from all round, on a surface facing n: flat, hemisphere and environment light
|
|
706
|
+
vec3 irradianceAt(vec3 n) {
|
|
707
|
+
vec3 e = uAmbient + mix(uHemisphereGround, uHemisphereSky, n.y * 0.5 + 0.5);
|
|
708
|
+
#ifdef ENV_MAP
|
|
709
|
+
e += envIrradiance(n) * uEnvIntensity * uEnvStrength;
|
|
710
|
+
#elif defined(STUDIO)
|
|
711
|
+
e += studio(n) * 0.35 * uEnvStrength;
|
|
712
|
+
#endif
|
|
713
|
+
return e;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// what a surface reflects of its surroundings along r - blurrier the rougher it is
|
|
717
|
+
vec3 reflected(vec3 r, vec3 n, float roughness, vec3 irradiance) {
|
|
718
|
+
#ifdef ENV_MAP
|
|
719
|
+
// a lower mip, convolved for that roughness
|
|
720
|
+
return textureLod(uEnvMap, equirectUv(r), roughness * uEnvMaxLod).rgb * uEnvIntensity * uEnvStrength;
|
|
721
|
+
#elif defined(STUDIO)
|
|
722
|
+
// leaned towards the surface normal
|
|
723
|
+
return mix(studio(r), studio(n), roughness * roughness) * uEnvStrength;
|
|
724
|
+
#else
|
|
725
|
+
return irradiance * 0.5;
|
|
726
|
+
#endif
|
|
727
|
+
}
|
|
529
728
|
#endif
|
|
530
729
|
|
|
531
730
|
void main() {
|
|
532
731
|
clipAway(vWorldPosition);
|
|
533
732
|
vec4 base = vec4(uColor * vColor, uOpacity);
|
|
534
733
|
#ifdef MAP
|
|
535
|
-
base *=
|
|
734
|
+
base *= uMapTexel; // sRGB textures are decoded to linear by the hardware
|
|
536
735
|
#endif
|
|
537
736
|
#ifdef ALPHA_TEST
|
|
538
737
|
if (base.a < uAlphaTest) discard;
|
|
539
738
|
base.a = 1.0;
|
|
540
739
|
#endif
|
|
740
|
+
// how much of what is behind it this hides: its opacity - less for glass, which lets light through
|
|
741
|
+
float coverage = base.a;
|
|
541
742
|
vec3 emissive = uEmissive;
|
|
542
743
|
#ifdef EMISSIVE_MAP
|
|
543
|
-
emissive *=
|
|
744
|
+
emissive *= uEmissiveMapTexel.rgb;
|
|
544
745
|
#endif
|
|
545
746
|
|
|
546
747
|
#ifdef LIT
|
|
@@ -561,27 +762,64 @@ void main() {
|
|
|
561
762
|
if (!gl_FrontFacing) n = -n;
|
|
562
763
|
#endif
|
|
563
764
|
#else
|
|
564
|
-
|
|
765
|
+
// tangent space from the map's own UVs, so it turns with a rotated map
|
|
766
|
+
vec2 normalUv = uNormalMapCoord;
|
|
767
|
+
vec3 m = texture(uNormalMap, normalUv).xyz * 2.0 - 1.0;
|
|
565
768
|
m.xy *= uNormalScale;
|
|
566
|
-
n = perturbNormal(n, m);
|
|
769
|
+
n = perturbNormal(n, m, normalUv);
|
|
567
770
|
#endif
|
|
568
771
|
#endif
|
|
569
772
|
#ifdef BUMP_MAP
|
|
570
773
|
n = bumpNormal(n);
|
|
571
774
|
#endif
|
|
572
775
|
vec3 v = normalize(uCameraPosition - vWorldPosition);
|
|
776
|
+
#ifdef MATCAP
|
|
777
|
+
// its look from the picture, by the normal: no lights, and its colours kept as they are, as unlit ones are
|
|
778
|
+
vec3 color = base.rgb * matcapAt(n, v) + emissive;
|
|
779
|
+
#else
|
|
573
780
|
float roughness = uRoughness;
|
|
574
781
|
float metalness = uMetalness;
|
|
575
782
|
#ifdef ROUGHNESS_MAP
|
|
576
|
-
roughness *=
|
|
783
|
+
roughness *= uRoughnessMapTexel.g;
|
|
577
784
|
#endif
|
|
578
785
|
#ifdef METALNESS_MAP
|
|
579
|
-
metalness *=
|
|
786
|
+
metalness *= uMetalnessMapTexel.b;
|
|
580
787
|
#endif
|
|
581
788
|
roughness = clamp(roughness, 0.04, 1.0);
|
|
582
789
|
float a = roughness * roughness;
|
|
583
790
|
vec3 diffuse = base.rgb * (1.0 - metalness);
|
|
584
|
-
vec3 f0 = mix(
|
|
791
|
+
vec3 f0 = mix(uSpecular.rgb, base.rgb, metalness);
|
|
792
|
+
float f90 = mix(uSpecular.a, 1.0, metalness);
|
|
793
|
+
#ifdef TOON
|
|
794
|
+
// a cartoon's metal is its colour too, with a highlight of that colour
|
|
795
|
+
diffuse = base.rgb;
|
|
796
|
+
toonHighlight = mix(vec3(uSpecular.a), base.rgb, metalness) * (1.0 - roughness);
|
|
797
|
+
#endif
|
|
798
|
+
#ifdef TRANSMISSION
|
|
799
|
+
// glass: what passes through isn't scattered back, so less diffuse colour - and the reflections
|
|
800
|
+
// stay whole, however clear it is
|
|
801
|
+
diffuse *= 1.0 - uTransmission;
|
|
802
|
+
#endif
|
|
803
|
+
#ifdef CLEARCOAT
|
|
804
|
+
float coatRoughness = clamp(uClearcoatRoughness, 0.04, 1.0);
|
|
805
|
+
float coatA = coatRoughness * coatRoughness;
|
|
806
|
+
vec3 coat = vec3(0.0);
|
|
807
|
+
#endif
|
|
808
|
+
#ifdef SHEEN
|
|
809
|
+
float sheenRoughness = clamp(uSheenRoughness, 0.07, 1.0);
|
|
810
|
+
vec3 sheen = vec3(0.0);
|
|
811
|
+
#endif
|
|
812
|
+
#ifdef ANISOTROPY
|
|
813
|
+
brushT = brushDirection(n);
|
|
814
|
+
brushB = cross(n, brushT);
|
|
815
|
+
brushAT = mix(a, 1.0, uAnisotropy * uAnisotropy);
|
|
816
|
+
brushAB = max(a, 0.001);
|
|
817
|
+
#endif
|
|
818
|
+
#ifdef TRANSLUCENCY
|
|
819
|
+
// thin surfaces: what isn't scattered back comes through, lit from the far side
|
|
820
|
+
vec3 through = diffuse * uTranslucency.rgb;
|
|
821
|
+
diffuse *= 1.0 - uTranslucency.a;
|
|
822
|
+
#endif
|
|
585
823
|
|
|
586
824
|
vec3 color = vec3(0.0);
|
|
587
825
|
#if NUM_DIRECTIONAL > 0
|
|
@@ -591,7 +829,24 @@ void main() {
|
|
|
591
829
|
// the shadow-casting light always comes first
|
|
592
830
|
if (i == 0) radiance *= shadowAt(surfaceNormal, uDirectionalDirection[0]);
|
|
593
831
|
#endif
|
|
594
|
-
color += shade(n, v, uDirectionalDirection[i], radiance, diffuse, f0, a);
|
|
832
|
+
color += shade(n, v, uDirectionalDirection[i], radiance, diffuse, f0, f90, a);
|
|
833
|
+
#ifdef CLEARCOAT
|
|
834
|
+
coat += coatShade(surfaceNormal, v, uDirectionalDirection[i], radiance, coatA);
|
|
835
|
+
#endif
|
|
836
|
+
#ifdef SHEEN
|
|
837
|
+
sheen += sheenShade(n, v, uDirectionalDirection[i], radiance, sheenRoughness * sheenRoughness);
|
|
838
|
+
#endif
|
|
839
|
+
#ifdef TRANSLUCENCY
|
|
840
|
+
float behind = clamp(-dot(n, uDirectionalDirection[i]), 0.0, 1.0);
|
|
841
|
+
if (behind > 0.0) {
|
|
842
|
+
vec3 light = uDirectionalColor[i];
|
|
843
|
+
#ifdef SHADOW
|
|
844
|
+
// shadowed as the far side is: what lights it comes through
|
|
845
|
+
if (i == 0) light *= shadowAt(-surfaceNormal, uDirectionalDirection[0]);
|
|
846
|
+
#endif
|
|
847
|
+
color += through * light * behind;
|
|
848
|
+
}
|
|
849
|
+
#endif
|
|
595
850
|
}
|
|
596
851
|
#endif
|
|
597
852
|
#if NUM_PUNCTUAL > 0
|
|
@@ -613,42 +868,89 @@ void main() {
|
|
|
613
868
|
#ifdef POINT_SHADOW
|
|
614
869
|
if (float(i) == uPointShadowIndex) radiance *= pointShadowAt(uPunctualPosition[i], surfaceNormal, d);
|
|
615
870
|
#endif
|
|
616
|
-
color += shade(n, v, l, radiance, diffuse, f0, a);
|
|
871
|
+
color += shade(n, v, l, radiance, diffuse, f0, f90, a);
|
|
872
|
+
#ifdef CLEARCOAT
|
|
873
|
+
coat += coatShade(surfaceNormal, v, l, radiance, coatA);
|
|
874
|
+
#endif
|
|
875
|
+
#ifdef SHEEN
|
|
876
|
+
sheen += sheenShade(n, v, l, radiance, sheenRoughness * sheenRoughness);
|
|
877
|
+
#endif
|
|
878
|
+
#ifdef TRANSLUCENCY
|
|
879
|
+
float behind = clamp(-dot(n, l), 0.0, 1.0);
|
|
880
|
+
if (behind > 0.0) {
|
|
881
|
+
vec3 light = uPunctualColor[i] * falloff * cone;
|
|
882
|
+
#ifdef SPOT_SHADOWS
|
|
883
|
+
if (tile >= 0.0 && cone > 0.0) light *= spotShadowAt(int(tile), -surfaceNormal, d);
|
|
884
|
+
#endif
|
|
885
|
+
#ifdef POINT_SHADOW
|
|
886
|
+
if (float(i) == uPointShadowIndex) light *= pointShadowAt(uPunctualPosition[i], -surfaceNormal, d);
|
|
887
|
+
#endif
|
|
888
|
+
color += through * light * behind;
|
|
889
|
+
}
|
|
890
|
+
#endif
|
|
617
891
|
}
|
|
618
892
|
#endif
|
|
619
893
|
|
|
620
894
|
// ambient: flat, hemisphere and environment light
|
|
621
|
-
vec3 irradiance =
|
|
895
|
+
vec3 irradiance = irradianceAt(n);
|
|
622
896
|
float NoV = clamp(abs(dot(n, v)), 1e-4, 1.0);
|
|
623
|
-
vec3 brdf = envBRDF(f0, roughness, NoV);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
#elif defined(STUDIO)
|
|
631
|
-
irradiance += studio(n) * 0.35;
|
|
632
|
-
vec3 r = reflect(-v, n);
|
|
633
|
-
// rougher surfaces see a blurrier reflection: lean it towards the surface normal
|
|
634
|
-
ambientSpecular = mix(studio(r), studio(n), roughness * roughness) * brdf;
|
|
897
|
+
vec3 brdf = envBRDF(f0, f90, roughness, NoV);
|
|
898
|
+
#ifdef ANISOTROPY
|
|
899
|
+
// reflections drawn out along the brushing: off a normal bent across it (KHR_materials_anisotropy)
|
|
900
|
+
vec3 brushNormal = cross(cross(brushB, v), brushB);
|
|
901
|
+
float bend = 1.0 - uAnisotropy * (1.0 - roughness);
|
|
902
|
+
bend *= bend;
|
|
903
|
+
vec3 r = reflect(-v, normalize(mix(brushNormal, n, bend * bend)));
|
|
635
904
|
#else
|
|
636
|
-
|
|
905
|
+
vec3 r = reflect(-v, n);
|
|
637
906
|
#endif
|
|
907
|
+
#ifdef TOON
|
|
908
|
+
// a flat fill, so the tones stay flat: the light from all round, averaged over up and down
|
|
909
|
+
vec3 indirect = diffuse * 0.5 * (irradianceAt(vec3(0.0, 1.0, 0.0)) + irradianceAt(vec3(0.0, -1.0, 0.0)));
|
|
910
|
+
#else
|
|
911
|
+
vec3 ambientSpecular = reflected(r, n, roughness, irradiance) * brdf;
|
|
638
912
|
vec3 indirect = diffuse * irradiance + ambientSpecular;
|
|
913
|
+
#endif
|
|
914
|
+
#ifdef TRANSLUCENCY
|
|
915
|
+
indirect += through * irradianceAt(-n);
|
|
916
|
+
#endif
|
|
917
|
+
#ifdef LIGHT_MAP
|
|
918
|
+
// light baked into a texture, as more light from all round
|
|
919
|
+
indirect += diffuse * uLightMapTexel.rgb * uLightMapIntensity;
|
|
920
|
+
#endif
|
|
639
921
|
#ifdef AO_MAP
|
|
640
|
-
indirect *= mix(1.0,
|
|
922
|
+
indirect *= mix(1.0, uAoMapTexel.r, uAoMapIntensity);
|
|
641
923
|
#endif
|
|
642
924
|
color += indirect + emissive;
|
|
925
|
+
#ifdef SHEEN
|
|
926
|
+
// over the base, which loses a little of its light to it
|
|
927
|
+
sheen += irradiance * uSheenColor * sheenEnvironment(NoV, sheenRoughness);
|
|
928
|
+
color = color * (1.0 - 0.157 * max(uSheenColor.r, max(uSheenColor.g, uSheenColor.b))) + sheen;
|
|
929
|
+
#endif
|
|
930
|
+
#ifdef CLEARCOAT
|
|
931
|
+
// the coat's reflection, over the base - which loses the light the coat reflects away
|
|
932
|
+
float coatNoV = clamp(abs(dot(surfaceNormal, v)), 1e-4, 1.0);
|
|
933
|
+
coat += reflected(reflect(-v, surfaceNormal), surfaceNormal, coatRoughness, irradiance) * envBRDF(vec3(0.04), 1.0, coatRoughness, coatNoV);
|
|
934
|
+
color = color * (1.0 - uClearcoat * F_Schlick(coatNoV, vec3(0.04), 1.0).x) + coat * uClearcoat;
|
|
935
|
+
#endif
|
|
936
|
+
#ifdef TRANSMISSION
|
|
937
|
+
// most of what is behind glass shows through face on; towards its edges, where it reflects nearly
|
|
938
|
+
// everything (Fresnel), less and less
|
|
939
|
+
coverage *= mix(1.0, clamp(max(brdf.r, max(brdf.g, brdf.b)), 0.0, 1.0), uTransmission);
|
|
940
|
+
#endif
|
|
643
941
|
color = toneMap(color);
|
|
942
|
+
#endif
|
|
644
943
|
#else
|
|
645
944
|
vec3 color = base.rgb + emissive;
|
|
646
945
|
#endif
|
|
647
946
|
color = mix(color, uHighlight.rgb, vHighlight * uHighlight.a);
|
|
947
|
+
// a highlight shows on glass as on anything else
|
|
948
|
+
coverage = mix(coverage, base.a, vHighlight * uHighlight.a);
|
|
648
949
|
color = fogged(color);
|
|
649
950
|
|
|
650
|
-
// premultiplied alpha, so the canvas composites correctly over the page
|
|
651
|
-
|
|
951
|
+
// premultiplied alpha, so the canvas composites correctly over the page. The colour is the light
|
|
952
|
+
// the surface sends back - faded with its opacity; coverage is how much of what is behind it hides
|
|
953
|
+
fragColor = vec4(linearToSrgb(color) * base.a, coverage);
|
|
652
954
|
}
|
|
653
955
|
`
|
|
654
956
|
|