@driftengine/core 4.8.2 → 4.8.4
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/CHANGELOG.json +127 -0
- package/README.md +1 -1
- package/dist/geometry/meshConcat.js +2 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +22 -1
- package/dist/math/scalar.js +1 -1
- package/dist/render/anisotropicLobe.d.ts +7 -0
- package/dist/render/anisotropicLobe.js +33 -0
- package/dist/render/backend/api.d.ts +41 -4
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/clothTextures.d.ts +27 -0
- package/dist/render/backend/webgl2/clothTextures.js +93 -0
- package/dist/render/backend/webgl2/renderer.d.ts +159 -3
- package/dist/render/backend/webgl2/renderer.js +662 -80
- package/dist/render/backend/webgl2/skinScatter.d.ts +50 -0
- package/dist/render/backend/webgl2/skinScatter.js +256 -0
- package/dist/render/backend/webgpu/buffers.d.ts +25 -11
- package/dist/render/backend/webgpu/buffers.js +98 -49
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/clothSolvePack.d.ts +27 -0
- package/dist/render/backend/webgpu/clothSolvePack.js +129 -0
- package/dist/render/backend/webgpu/clothTextures.d.ts +68 -0
- package/dist/render/backend/webgpu/clothTextures.js +145 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +18 -0
- package/dist/render/backend/webgpu/compressedUpload.js +38 -0
- package/dist/render/backend/webgpu/computeHost.d.ts +21 -0
- package/dist/render/backend/webgpu/computeHost.js +5 -0
- package/dist/render/backend/webgpu/depthPass.d.ts +29 -9
- package/dist/render/backend/webgpu/depthPass.js +43 -12
- package/dist/render/backend/webgpu/drawCommand.js +6 -2
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +43 -4
- package/dist/render/backend/webgpu/flatPass.js +111 -13
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +74 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +452 -0
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +20 -0
- package/dist/render/backend/webgpu/motionPass.js +106 -9
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +15 -3
- package/dist/render/backend/webgpu/postPass.js +25 -8
- package/dist/render/backend/webgpu/renderer.d.ts +123 -6
- package/dist/render/backend/webgpu/renderer.js +534 -58
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +33 -0
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +469 -0
- package/dist/render/backend/webgpu/skinScatterPass.d.ts +51 -0
- package/dist/render/backend/webgpu/skinScatterPass.js +253 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +37 -12
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/clothBindingData.d.ts +41 -0
- package/dist/render/clothBindingData.js +72 -0
- package/dist/render/clothPlacement.d.ts +25 -0
- package/dist/render/clothPlacement.js +85 -0
- package/dist/render/compressedSource.d.ts +66 -0
- package/dist/render/compressedSource.js +138 -0
- package/dist/render/cutoutCaster.d.ts +5 -1
- package/dist/render/cutoutCaster.js +6 -0
- package/dist/render/cutoutDither.d.ts +51 -0
- package/dist/render/cutoutDither.js +90 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/eyeRefraction.d.ts +35 -0
- package/dist/render/eyeRefraction.js +40 -0
- package/dist/render/glCompressed.d.ts +27 -0
- package/dist/render/glCompressed.js +53 -0
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/hairLobes.d.ts +70 -0
- package/dist/render/hairLobes.js +120 -0
- package/dist/render/lightBudget.d.ts +22 -0
- package/dist/render/lightBudget.js +22 -0
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mesh.d.ts +7 -0
- package/dist/render/mesh.js +24 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +6 -2
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/recon/motionVectors.d.ts +4 -2
- package/dist/render/recon/motionVectors.js +6 -3
- package/dist/render/renderQuality.d.ts +29 -2
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sceneTarget.d.ts +12 -0
- package/dist/render/sceneTarget.js +14 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/clothBinding.d.ts +22 -0
- package/dist/render/shaders/clothBinding.js +80 -0
- package/dist/render/shaders/depth.js +45 -8
- package/dist/render/shaders/flat/anisotropic.d.ts +15 -0
- package/dist/render/shaders/flat/anisotropic.js +68 -0
- package/dist/render/shaders/flat/eye.d.ts +1 -0
- package/dist/render/shaders/flat/eye.js +81 -0
- package/dist/render/shaders/flat/hair.d.ts +1 -0
- package/dist/render/shaders/flat/hair.js +101 -0
- package/dist/render/shaders/flat/index.js +42 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +113 -17
- package/dist/render/shaders/flat/models.d.ts +14 -0
- package/dist/render/shaders/flat/models.js +273 -0
- package/dist/render/shaders/flat/preamble.js +26 -24
- package/dist/render/shaders/flat/skin.d.ts +1 -0
- package/dist/render/shaders/flat/skin.js +114 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +66 -24
- package/dist/render/shaders/generated/depth.wgsl.js +66 -24
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1815 -1704
- package/dist/render/shaders/generated/flat.wgsl.js +1853 -1742
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/skinBlur.wgsl.d.ts +48 -0
- package/dist/render/shaders/generated/skinBlur.wgsl.js +54 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +102 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/skinBlur.d.ts +1 -0
- package/dist/render/shaders/skinBlur.js +103 -0
- package/dist/render/shaders/skinning.d.ts +3 -1
- package/dist/render/shaders/skinning.js +31 -4
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/shadowCasters.d.ts +13 -1
- package/dist/render/skinBlur.d.ts +41 -0
- package/dist/render/skinBlur.js +49 -0
- package/dist/render/skinScatter.d.ts +67 -0
- package/dist/render/skinScatter.js +80 -0
- package/dist/render/skinnedClothRun.d.ts +26 -0
- package/dist/render/skinnedClothRun.js +84 -0
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceModel.d.ts +129 -0
- package/dist/render/surfaceModel.js +152 -0
- package/dist/render/surfaceTexture.d.ts +55 -4
- package/dist/render/surfaceTexture.js +60 -46
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +8 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/geometry/meshConcat.ts +2 -0
- package/src/index.ts +53 -1
- package/src/math/scalar.ts +1 -1
- package/src/render/anisotropicLobe.ts +41 -0
- package/src/render/backend/api.ts +52 -3
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/clothTextures.ts +120 -0
- package/src/render/backend/webgl2/renderer.ts +750 -80
- package/src/render/backend/webgl2/skinScatter.ts +309 -0
- package/src/render/backend/webgpu/buffers.ts +137 -63
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/clothSolvePack.ts +161 -0
- package/src/render/backend/webgpu/clothTextures.ts +194 -0
- package/src/render/backend/webgpu/compressedUpload.ts +65 -0
- package/src/render/backend/webgpu/computeHost.ts +25 -0
- package/src/render/backend/webgpu/depthPass.ts +43 -10
- package/src/render/backend/webgpu/drawCommand.ts +6 -2
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +147 -8
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +502 -0
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/motionPass.ts +128 -9
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +25 -6
- package/src/render/backend/webgpu/renderer.ts +701 -52
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +470 -0
- package/src/render/backend/webgpu/skinScatterPass.ts +309 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +42 -12
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/clothBindingData.ts +89 -0
- package/src/render/clothPlacement.ts +114 -0
- package/src/render/compressedSource.ts +198 -0
- package/src/render/cutoutCaster.ts +20 -2
- package/src/render/cutoutDither.ts +99 -0
- package/src/render/decalPass.ts +2 -1
- package/src/render/eyeRefraction.ts +72 -0
- package/src/render/glCompressed.ts +86 -0
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/hairLobes.ts +188 -0
- package/src/render/lightBudget.ts +25 -0
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mesh.ts +23 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +6 -2
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/recon/motionVectors.ts +6 -3
- package/src/render/renderQuality.ts +37 -2
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sceneTarget.ts +21 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/clothBinding.ts +81 -0
- package/src/render/shaders/depth.ts +45 -8
- package/src/render/shaders/flat/anisotropic.ts +68 -0
- package/src/render/shaders/flat/eye.ts +82 -0
- package/src/render/shaders/flat/hair.ts +102 -0
- package/src/render/shaders/flat/index.ts +42 -3
- package/src/render/shaders/flat/main.ts +113 -17
- package/src/render/shaders/flat/models.ts +286 -0
- package/src/render/shaders/flat/preamble.ts +26 -24
- package/src/render/shaders/flat/skin.ts +116 -0
- package/src/render/shaders/generated/depth.wgsl.ts +66 -24
- package/src/render/shaders/generated/flat.wgsl.ts +1853 -1742
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/skinBlur.wgsl.ts +56 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/motion.wgsl.ts +102 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/skinBlur.ts +105 -0
- package/src/render/shaders/skinning.ts +31 -4
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/shadowCasters.ts +13 -0
- package/src/render/skinBlur.ts +53 -0
- package/src/render/skinScatter.ts +92 -0
- package/src/render/skinnedClothRun.ts +128 -0
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceModel.ts +274 -0
- package/src/render/surfaceTexture.ts +134 -63
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +8 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
- package/dist/math/exact.d.ts +0 -76
- package/dist/math/exact.js +0 -327
- package/src/math/exact.ts +0 -340
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vertex-stage cloth binding: a skinned vertex moved toward the cloth simulation's triangle it
|
|
3
|
+
* is bound to, by a weight painted per vertex — the GLSL of `clothPlacement.ts`, line for line.
|
|
4
|
+
*
|
|
5
|
+
* **Textures read by vertex index, not attributes**, for the reason morph targets are: the skinned
|
|
6
|
+
* variants already use all sixteen attribute locations once eight influences are in. Three
|
|
7
|
+
* textures, each wrapped onto rows at its own width (read back with `textureSize`, so no uniform
|
|
8
|
+
* joins the vertex block and no shared field moves):
|
|
9
|
+
*
|
|
10
|
+
* - **the binding**, two texels a render vertex: the three particle indices and the weight, then
|
|
11
|
+
* the barycentric (u, v) for the second and third particle and the offset along the normal;
|
|
12
|
+
* - **the particles now**, one texel a particle, world space — the simulation's output;
|
|
13
|
+
* - **the particles at rest**, one texel a particle, model space — the rest frames are built from
|
|
14
|
+
* these, so a vertex's own bind-pose normal turns as its triangle has turned.
|
|
15
|
+
*
|
|
16
|
+
* **A pipeline constant, not a permutation**, as `SKIN_EIGHT` is: declared in the skinned variants
|
|
17
|
+
* and folded away where a draw binds no cloth. The textures are bound either way, to stand-ins
|
|
18
|
+
* where there is no cloth.
|
|
19
|
+
*/
|
|
20
|
+
export const CLOTH_BINDING_GLSL = `
|
|
21
|
+
/** Whether this pipeline places vertices by a cloth binding. */
|
|
22
|
+
const bool CLOTH_BOUND = false; // wgsl:override
|
|
23
|
+
uniform highp sampler2D uClothBinding;
|
|
24
|
+
uniform highp sampler2D uClothParticles;
|
|
25
|
+
uniform highp sampler2D uClothRest;
|
|
26
|
+
|
|
27
|
+
/** Texel i of a texture wrapped onto rows of the given width. */
|
|
28
|
+
ivec2 clothTexel(int i, int width) {
|
|
29
|
+
return ivec2(i % width, i / width);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A triangle's frame: its first edge, the normal crossed with it, and its normal. */
|
|
33
|
+
mat3 clothFrame(vec3 a, vec3 b, vec3 c) {
|
|
34
|
+
vec3 edge = b - a;
|
|
35
|
+
vec3 n = normalize(cross(edge, c - a));
|
|
36
|
+
vec3 t = normalize(edge);
|
|
37
|
+
return mat3(t, cross(n, t), n);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Where this vertex's triangle puts it, the turn its normal takes, and how far it follows (the
|
|
42
|
+
* painted weight: 0 skinned, 1 cloth). One particle named three times is a vertex bound to that
|
|
43
|
+
* particle alone: on it, with no turn.
|
|
44
|
+
*/
|
|
45
|
+
float clothPlace(out vec3 position, out mat3 turn) {
|
|
46
|
+
int bindingWidth = textureSize(uClothBinding, 0).x;
|
|
47
|
+
int particleWidth = textureSize(uClothParticles, 0).x;
|
|
48
|
+
int restWidth = textureSize(uClothRest, 0).x;
|
|
49
|
+
vec4 indices = texelFetch(uClothBinding, clothTexel(gl_VertexID * 2, bindingWidth), 0);
|
|
50
|
+
vec4 at = texelFetch(uClothBinding, clothTexel(gl_VertexID * 2 + 1, bindingWidth), 0);
|
|
51
|
+
int a = int(indices.x);
|
|
52
|
+
int b = int(indices.y);
|
|
53
|
+
int c = int(indices.z);
|
|
54
|
+
vec3 pa = texelFetch(uClothParticles, clothTexel(a, particleWidth), 0).xyz;
|
|
55
|
+
if (a == b && b == c) {
|
|
56
|
+
position = pa;
|
|
57
|
+
turn = mat3(1.0);
|
|
58
|
+
return indices.w;
|
|
59
|
+
}
|
|
60
|
+
vec3 pb = texelFetch(uClothParticles, clothTexel(b, particleWidth), 0).xyz;
|
|
61
|
+
vec3 pc = texelFetch(uClothParticles, clothTexel(c, particleWidth), 0).xyz;
|
|
62
|
+
mat3 now = clothFrame(pa, pb, pc);
|
|
63
|
+
mat3 rest = clothFrame(
|
|
64
|
+
texelFetch(uClothRest, clothTexel(a, restWidth), 0).xyz,
|
|
65
|
+
texelFetch(uClothRest, clothTexel(b, restWidth), 0).xyz,
|
|
66
|
+
texelFetch(uClothRest, clothTexel(c, restWidth), 0).xyz
|
|
67
|
+
);
|
|
68
|
+
position = pa * (1.0 - at.x - at.y) + pb * at.x + pc * at.y + now[2] * at.z;
|
|
69
|
+
turn = now * transpose(rest);
|
|
70
|
+
return indices.w;
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
/** A skinned vertex stage with the cloth binding on: the same source, the constant flipped. */
|
|
75
|
+
export function clothBound(source: string): string {
|
|
76
|
+
const off = 'const bool CLOTH_BOUND = false;';
|
|
77
|
+
if (!source.includes(off)) {
|
|
78
|
+
throw new Error('clothBound: this vertex stage declares no CLOTH_BOUND constant');
|
|
79
|
+
}
|
|
80
|
+
return source.replace(off, 'const bool CLOTH_BOUND = true;');
|
|
81
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { resolveConditionals } from './conditionals.ts';
|
|
2
2
|
import { SUN_STATIC_LAYER } from '../shadowMap.ts';
|
|
3
3
|
import { CUTOUT_COVERAGE_GLSL } from './cutoutCoverage.ts';
|
|
4
|
+
import { CUTOUT_DITHER_GLSL } from '../cutoutDither.ts';
|
|
4
5
|
import { SKINNING_GLSL } from './skinning.ts';
|
|
6
|
+
import { CLOTH_BINDING_GLSL } from './clothBinding.ts';
|
|
5
7
|
import { CHANNEL_ATTRIBUTE, CHANNEL_BEND } from './vertexChannel.ts';
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -21,6 +23,11 @@ const DEPTH_VERT_SOURCE = `#version 300 es
|
|
|
21
23
|
layout(location = 0) in vec3 aPosition;
|
|
22
24
|
#if SKINNED
|
|
23
25
|
${SKINNING_GLSL}
|
|
26
|
+
/* Not in the glass-tint caster: a garment is not glass, and its layout keeps its bindings. */
|
|
27
|
+
#if GLASS
|
|
28
|
+
#else
|
|
29
|
+
${CLOTH_BINDING_GLSL}
|
|
30
|
+
#endif
|
|
24
31
|
#endif
|
|
25
32
|
|
|
26
33
|
uniform mat4 uLightViewProj;
|
|
@@ -60,20 +67,22 @@ ${CHANNEL_BEND}
|
|
|
60
67
|
|
|
61
68
|
#if CUTOUT
|
|
62
69
|
/*
|
|
63
|
-
* **What a cutout caster adds, and where.** The UV at location 5, where every mesh carries it;
|
|
70
|
+
* **What a cutout caster adds, and where.** The UV at location 5, where every mesh carries it; three
|
|
64
71
|
* uniforms declared after every other, so no field of the plain variant moves and a binder written
|
|
65
72
|
* against it stays right for this one; and two outputs after vLightPosition, which keeps location 0.
|
|
66
73
|
*/
|
|
67
74
|
layout(location = 5) in vec3 aUv;
|
|
68
75
|
uniform vec2 uUvScale;
|
|
69
|
-
uniform
|
|
76
|
+
uniform vec2 uUvOffset;
|
|
77
|
+
/* x the cutoff, y 1 for a dithered edge: see the cutout fragment stages below. */
|
|
78
|
+
uniform vec2 uAlphaCutout;
|
|
70
79
|
#endif
|
|
71
80
|
|
|
72
81
|
out vec4 vLightPosition;
|
|
73
82
|
#if CUTOUT
|
|
74
83
|
/* The texture coordinate and, in z, the texture-array layer the face wears. */
|
|
75
84
|
out vec3 vUv;
|
|
76
|
-
flat out
|
|
85
|
+
flat out vec2 vAlphaCutout;
|
|
77
86
|
#endif
|
|
78
87
|
#if GLASS
|
|
79
88
|
/* Where on the pane this is, for the tint's own normal and its angle to the light. Last, so the
|
|
@@ -100,11 +109,23 @@ void main() {
|
|
|
100
109
|
vec3 bent = world.xyz;
|
|
101
110
|
#else
|
|
102
111
|
vec3 bent = channelBend(world.xyz, aChannel.x);
|
|
112
|
+
#endif
|
|
113
|
+
#if SKINNED
|
|
114
|
+
#if GLASS
|
|
115
|
+
#else
|
|
116
|
+
/* Toward the cloth exactly as the colour pass moves it, or the shadow parts from the garment. */
|
|
117
|
+
if (CLOTH_BOUND) {
|
|
118
|
+
vec3 clothPosition;
|
|
119
|
+
mat3 turn;
|
|
120
|
+
float follow = clothPlace(clothPosition, turn);
|
|
121
|
+
bent = mix(bent, clothPosition, follow);
|
|
122
|
+
}
|
|
123
|
+
#endif
|
|
103
124
|
#endif
|
|
104
125
|
vLightPosition = uLightViewProj * vec4(bent, world.w);
|
|
105
126
|
gl_Position = vLightPosition;
|
|
106
127
|
#if CUTOUT
|
|
107
|
-
vUv = vec3(aUv.xy * uUvScale, aUv.z);
|
|
128
|
+
vUv = vec3(aUv.xy * uUvScale + uUvOffset, aUv.z);
|
|
108
129
|
vAlphaCutout = uAlphaCutout;
|
|
109
130
|
#endif
|
|
110
131
|
#if GLASS
|
|
@@ -198,12 +219,13 @@ export const DEPTH_CUTOUT_FRAG = `#version 300 es
|
|
|
198
219
|
precision highp float;
|
|
199
220
|
in vec4 vLightPosition;
|
|
200
221
|
in vec3 vUv;
|
|
201
|
-
flat in
|
|
222
|
+
flat in vec2 vAlphaCutout;
|
|
202
223
|
|
|
203
224
|
uniform highp sampler2DArray uPreviousShadowMap;
|
|
204
225
|
uniform int uPeelShadowLayer;
|
|
205
226
|
uniform highp sampler2DArray uCutoutMap;
|
|
206
227
|
${CUTOUT_COVERAGE_GLSL}
|
|
228
|
+
${CUTOUT_DITHER_GLSL}
|
|
207
229
|
void main() {
|
|
208
230
|
/* Credited for its mip level as the surface's own test is, so a leaf casts the shape it draws. */
|
|
209
231
|
vec3 at = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
@@ -214,7 +236,16 @@ void main() {
|
|
|
214
236
|
float previousDepth = textureLod(uPreviousShadowMap, vec3(uv, ${SUN_STATIC_LAYER}.0), 0.0).r;
|
|
215
237
|
if (gl_FragCoord.z <= previousDepth + 0.00001) discard;
|
|
216
238
|
}
|
|
217
|
-
|
|
239
|
+
/*
|
|
240
|
+
* A dithered edge casts the share it covers, with a pattern that does not move: a shadow map has
|
|
241
|
+
* no temporal resolve of its own and a static layer keeps whatever it was drawn with, so a moving
|
|
242
|
+
* pattern would shimmer there. The map's filter averages it into a soft edge.
|
|
243
|
+
*/
|
|
244
|
+
if (vAlphaCutout.y > 0.5) {
|
|
245
|
+
if (!cutoutKeeps(cutoutShare(alpha, vAlphaCutout.x), gl_FragCoord.xy, 0.0)) discard;
|
|
246
|
+
} else if (alpha < vAlphaCutout.x) {
|
|
247
|
+
discard;
|
|
248
|
+
}
|
|
218
249
|
}
|
|
219
250
|
`;
|
|
220
251
|
|
|
@@ -292,13 +323,14 @@ export const GLASS_TINT_CUTOUT_FRAG = `#version 300 es
|
|
|
292
323
|
precision highp float;
|
|
293
324
|
in vec4 vLightPosition;
|
|
294
325
|
in vec3 vUv;
|
|
295
|
-
flat in
|
|
326
|
+
flat in vec2 vAlphaCutout;
|
|
296
327
|
in vec3 vGlassWorld;
|
|
297
328
|
|
|
298
329
|
uniform vec4 uGlassPane;
|
|
299
330
|
uniform vec4 uGlassLight;
|
|
300
331
|
uniform highp sampler2DArray uCutoutMap;
|
|
301
332
|
${CUTOUT_COVERAGE_GLSL}
|
|
333
|
+
${CUTOUT_DITHER_GLSL}
|
|
302
334
|
out vec4 outTint;
|
|
303
335
|
|
|
304
336
|
void main() {
|
|
@@ -306,6 +338,11 @@ void main() {
|
|
|
306
338
|
vec3 at = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
307
339
|
float alpha = cutoutAlpha(texture(uCutoutMap, at).a, vUv.xy * vec2(textureSize(uCutoutMap, 0).xy));
|
|
308
340
|
${GLASS_TINT_BODY}
|
|
309
|
-
|
|
341
|
+
/* The depth cutout's own test, so the colour lands exactly where the depth does. */
|
|
342
|
+
if (vAlphaCutout.y > 0.5) {
|
|
343
|
+
if (!cutoutKeeps(cutoutShare(alpha, vAlphaCutout.x), gl_FragCoord.xy, 0.0)) discard;
|
|
344
|
+
} else if (alpha < vAlphaCutout.x) {
|
|
345
|
+
discard;
|
|
346
|
+
}
|
|
310
347
|
}
|
|
311
348
|
`;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The anisotropic model in GLSL: GGX stretched along a direction in the surface, after glTF's
|
|
3
|
+
* `KHR_materials_anisotropy` — `anisotropicLobe.ts` is the same arithmetic, and its tests hold it to
|
|
4
|
+
* the standard lobe at strength 0 and to the widths' ratio at its half maximum.
|
|
5
|
+
*
|
|
6
|
+
* **The direction** is the tangent frame's, turned by the material's rotation, and by the model
|
|
7
|
+
* map's red and green where one is bound (a direction encoded −1 to 1, glTF's packing), its blue
|
|
8
|
+
* scaling the strength. **The environment** is reflected about a normal bent toward the stretch, as
|
|
9
|
+
* Filament and three.js do: a streak of sky across a brushed disc rather than a round spot, at the
|
|
10
|
+
* cost of a reflection that is not the exact anisotropic integral.
|
|
11
|
+
*
|
|
12
|
+
* Lamps widen both of the lobe's widths by the source's angle, as `sphereLobe` widens the standard
|
|
13
|
+
* lobe, with the energy kept; at strength 0 that is `sphereLobe` exactly.
|
|
14
|
+
*/
|
|
15
|
+
export const ANISOTROPIC_GLSL = /* glsl */ `
|
|
16
|
+
/* The stretch's frame and strength, gathered once with the surface. */
|
|
17
|
+
vec3 aAlong;
|
|
18
|
+
vec3 aAcross;
|
|
19
|
+
float aStrength;
|
|
20
|
+
|
|
21
|
+
void anisotropicSurface() {
|
|
22
|
+
vec2 direction = vec2(1.0, 0.0);
|
|
23
|
+
float strength = uModelParams[0].x;
|
|
24
|
+
if (uModelParams[1].w > 0.0) {
|
|
25
|
+
vec2 mapped = mMap.rg * 2.0 - 1.0;
|
|
26
|
+
direction = dot(mapped, mapped) > 1e-8 ? normalize(mapped) : direction;
|
|
27
|
+
strength *= mMap.b;
|
|
28
|
+
}
|
|
29
|
+
vec2 turned = vec2(
|
|
30
|
+
direction.x * uModelParams[0].y - direction.y * uModelParams[0].z,
|
|
31
|
+
direction.x * uModelParams[0].z + direction.y * uModelParams[0].y
|
|
32
|
+
);
|
|
33
|
+
aAlong = normalize(mTangent * turned.x + mBitangent * turned.y);
|
|
34
|
+
aAcross = cross(mNormal, aAlong);
|
|
35
|
+
aStrength = strength;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The stretched lobe at a half vector, 1 at the mirror direction: anisotropicLobe.ts. */
|
|
39
|
+
float anisotropicLobe(vec3 h, float sourceRadius, float dist) {
|
|
40
|
+
float alpha = max(mRoughness * mRoughness, MIN_LOBE_ALPHA);
|
|
41
|
+
float alongWidth = alpha + (1.0 - alpha) * aStrength * aStrength;
|
|
42
|
+
float grow = sourceRadius / max(2.0 * dist, 1e-3);
|
|
43
|
+
float alongWide = clamp(alongWidth + grow, alongWidth, 1.0);
|
|
44
|
+
float acrossWide = clamp(alpha + grow, alpha, 1.0);
|
|
45
|
+
float energy = (alongWidth / alongWide) * (alpha / acrossWide);
|
|
46
|
+
float x = dot(aAlong, h) / alongWide;
|
|
47
|
+
float y = dot(aAcross, h) / acrossWide;
|
|
48
|
+
float z = dot(mNormal, h);
|
|
49
|
+
float d = x * x + y * y + z * z;
|
|
50
|
+
return energy / max(d * d, 1e-8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void anisotropicLight(vec3 l, float sourceRadius, float dist) {
|
|
54
|
+
float ndl = max(dot(mNormal, l), 0.0);
|
|
55
|
+
vec3 h = normalize(l + mToEye);
|
|
56
|
+
vec3 tint = mix(mSpecColor, vec3(1.0), pow(1.0 - max(dot(mToEye, h), 0.0), 5.0) * mMetal);
|
|
57
|
+
mDiffuse = mAlbedo * ndl * (1.0 - mMetal);
|
|
58
|
+
mSpecular = ndl > 0.0 ? tint * anisotropicLobe(h, sourceRadius, dist) : vec3(0.0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* The environment's normal, bent toward the stretch by its strength and the surface's roughness. */
|
|
62
|
+
vec3 anisotropicReflectNormal(vec3 n) {
|
|
63
|
+
vec3 across = cross(aAcross, mToEye);
|
|
64
|
+
vec3 bent = cross(across, aAcross);
|
|
65
|
+
float bend = aStrength * clamp(5.0 * mRoughness, 0.0, 1.0);
|
|
66
|
+
return normalize(mix(n, bent, bend));
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The eye model in GLSL: `eyeRefraction.ts` is the refraction's arithmetic, and its tests hold it
|
|
3
|
+
* to no shift head-on, the refracted crossing at 30°, a bounded shift edge-on and the map-less iris.
|
|
4
|
+
*
|
|
5
|
+
* **The iris is moved before any map is read** (`modelSurfaceAt`), by how far the eye's ray crosses
|
|
6
|
+
* the iris plane after the cornea bends it, so the whole iris — colour, relief, the model's own
|
|
7
|
+
* channels — moves as one. The crossing is turned into texture coordinates by the tangent frame and
|
|
8
|
+
* by how many of them a metre spans here, which the screen's derivatives say: so it is in uniform
|
|
9
|
+
* control flow, as `modelSurfaceAt` is. **What it gives up** is an anisotropic mapping — a texture
|
|
10
|
+
* stretched one way more than the other moves by the average of the two.
|
|
11
|
+
*
|
|
12
|
+
* **The model map**: red is the iris (1 inside), green the cornea's height above the iris plane as a
|
|
13
|
+
* share of `irisDepth`. Without one, the iris is a disc of `irisRadius` about the texture's centre
|
|
14
|
+
* and the height is `irisDepth` throughout.
|
|
15
|
+
*
|
|
16
|
+
* **Lit as an eye is**: the iris by a normal mirrored about the axis — concave where the cornea is
|
|
17
|
+
* convex, so light pools on the side away from it — and the sclera by the surface's own; and over
|
|
18
|
+
* both, the cornea's highlight, normalised GGX at `corneaRoughness` with the Fresnel of index 1.336,
|
|
19
|
+
* in lamp units like skin's. The environment is reflected at the cornea's split-sum share.
|
|
20
|
+
*/
|
|
21
|
+
import { CORNEA_F0, EYE_MIN_DESCENT } from '../../eyeRefraction.ts';
|
|
22
|
+
|
|
23
|
+
export const EYE_GLSL = /* glsl */ `
|
|
24
|
+
const float CORNEA_F0 = ${CORNEA_F0.toFixed(9)};
|
|
25
|
+
const float EYE_MIN_DESCENT = ${EYE_MIN_DESCENT.toFixed(4)};
|
|
26
|
+
|
|
27
|
+
/* How much of this texel is iris, gathered with the surface. */
|
|
28
|
+
float eIris;
|
|
29
|
+
|
|
30
|
+
/** Where the iris is without a map: a disc of the model's radius about the centre, a soft limbus. */
|
|
31
|
+
float eyeIrisDisc(vec2 uv) {
|
|
32
|
+
float r = length(uv - 0.5);
|
|
33
|
+
return 1.0 - smoothstep(uModelParams[0].x - 0.01, uModelParams[0].x + 0.01, r);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The iris's texture coordinate, moved by the cornea: \`eyeRefraction.ts\`. Before any map is read. */
|
|
37
|
+
vec3 eyeRefract(vec3 at) {
|
|
38
|
+
vec3 n = normalize(vNormal);
|
|
39
|
+
vec3 axis = uModelParams[1].xyz;
|
|
40
|
+
vec4 map = texture(uModelMap, at);
|
|
41
|
+
bool mapped = uModelParams[1].w > 0.0;
|
|
42
|
+
float iris = mapped ? map.r : eyeIrisDisc(at.xy);
|
|
43
|
+
float height = uModelParams[0].y * (mapped ? map.g : 1.0);
|
|
44
|
+
vec3 ray = refract(-normalize(uCameraPos - vWorldPos), n, 1.0 / uModelParams[0].z);
|
|
45
|
+
float descent = max(-dot(ray, axis), EYE_MIN_DESCENT);
|
|
46
|
+
vec3 travel = ray * (height / descent);
|
|
47
|
+
vec3 across = travel - axis * dot(travel, axis);
|
|
48
|
+
mat3 frame = tangentFrame(n, vWorldPos, vUv.xy, vTangent, vHasTangents);
|
|
49
|
+
/* How many texture coordinates a metre spans here, from the screen's two derivatives. */
|
|
50
|
+
float perMetre = length(fwidth(vUv.xy)) / max(length(fwidth(vWorldPos)), 1e-9);
|
|
51
|
+
vec2 shift = vec2(dot(across, frame[0]), dot(across, frame[1])) * (perMetre * iris);
|
|
52
|
+
return vec3(at.xy + shift, at.z);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void eyeSurface(vec3 at) {
|
|
56
|
+
eIris = uModelParams[1].w > 0.0 ? mMap.r : eyeIrisDisc(at.xy);
|
|
57
|
+
mRoughness = uModelParams[0].w;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void eyeLight(vec3 l, float sourceRadius, float dist) {
|
|
61
|
+
vec3 axis = uModelParams[1].xyz;
|
|
62
|
+
/* The iris is concave where the cornea over it is convex: the surface's normal mirrored about the axis. */
|
|
63
|
+
vec3 irisNormal = normalize(2.0 * dot(mNormal, axis) * axis - mNormal);
|
|
64
|
+
vec3 lit = normalize(mix(mNormal, irisNormal, eIris));
|
|
65
|
+
mDiffuse = mAlbedo * max(dot(lit, l), 0.0) * (1.0 - mMetal);
|
|
66
|
+
|
|
67
|
+
float ndl = max(dot(mNormal, l), 0.0);
|
|
68
|
+
vec3 h = normalize(l + mToEye);
|
|
69
|
+
float ndh = max(dot(mNormal, h), 0.0);
|
|
70
|
+
float ndv = max(dot(mNormal, mToEye), 1e-4);
|
|
71
|
+
float grow = sourceRadius / max(2.0 * dist, 1e-3);
|
|
72
|
+
float alpha = min(max(mRoughness * mRoughness, MIN_LOBE_ALPHA) + grow, 1.0);
|
|
73
|
+
float f = CORNEA_F0 + (1.0 - CORNEA_F0) * pow(1.0 - max(dot(mToEye, h), 0.0), 5.0);
|
|
74
|
+
mSpecular = vec3(3.14159265 * f * smithMasking(ndl, ndv, alpha) * ggxLobe(ndh, alpha) * ndl);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The environment at the cornea's Fresnel, by the split sum's share at its own roughness. */
|
|
78
|
+
float eyeReflectance() {
|
|
79
|
+
vec2 dfg = envBrdfApprox(max(dot(mNormal, mToEye), 0.0), uModelParams[0].w);
|
|
80
|
+
return CORNEA_F0 * dfg.x + dfg.y;
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hair model in GLSL: `hairLobes.ts` is the same arithmetic, and its tests hold the lobes to
|
|
3
|
+
* their tilted peaks, the transmitted lobe to the backlight, the sum to what a white strand
|
|
4
|
+
* receives, and every term to a finite answer where the geometry degenerates.
|
|
5
|
+
*
|
|
6
|
+
* **The strand is the tangent, root to tip.** The model map's green turns each strand's tilt by up
|
|
7
|
+
* to half of it either way (0.5 is none) and its blue is occlusion, which the sky, DriftLight and
|
|
8
|
+
* the environment's reflection take and the lamps do not — their shadows are theirs to cast. The
|
|
9
|
+
* red is not read: a colour from root to tip is the albedo map's to carry.
|
|
10
|
+
*
|
|
11
|
+
* **The environment** is the reflected lobe's: mirrored about a normal turned toward the eye across
|
|
12
|
+
* the strand, which is the cone R lies on, at the strand's own Fresnel — the lobe integrates to it
|
|
13
|
+
* over every direction of arrival. **What it gives up** is the two transmitted lobes against the
|
|
14
|
+
* sky, which a head of hair lit from behind by an open sky would show as a rim; the lamps carry
|
|
15
|
+
* them.
|
|
16
|
+
*/
|
|
17
|
+
import { HAIR_F0, HAIR_MIN_BETA, HAIR_SCATTER_SPHERE } from '../../hairLobes.ts';
|
|
18
|
+
|
|
19
|
+
export const HAIR_GLSL = /* glsl */ `
|
|
20
|
+
const float HAIR_F0 = ${HAIR_F0.toFixed(9)};
|
|
21
|
+
const float HAIR_MIN_BETA = ${HAIR_MIN_BETA.toFixed(3)};
|
|
22
|
+
const float HAIR_SCATTER_SPHERE = ${HAIR_SCATTER_SPHERE.toFixed(3)};
|
|
23
|
+
|
|
24
|
+
/* This texel's tilt, gathered once with the surface. */
|
|
25
|
+
float hShift;
|
|
26
|
+
|
|
27
|
+
void hairSurface() {
|
|
28
|
+
hShift = uModelParams[0].x * (0.5 + mMap.g);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A longitudinal lobe: a normalised Gaussian of width \`beta\` at \`x\` from its centre. */
|
|
32
|
+
float hairLongitudinal(float beta, float x) {
|
|
33
|
+
return exp(-0.5 * x * x / (beta * beta)) / (2.5066283 * beta);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
float hairFresnel(float cosine) {
|
|
37
|
+
float c = 1.0 - clamp(cosine, 0.0, 1.0);
|
|
38
|
+
return HAIR_F0 + (1.0 - HAIR_F0) * c * c * c * c * c;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The normal turned toward the eye across the strand: the scatter's and the environment's. */
|
|
42
|
+
vec3 hairAcross() {
|
|
43
|
+
vec3 across = mToEye - mTangent * dot(mTangent, mToEye);
|
|
44
|
+
float length2 = dot(across, across);
|
|
45
|
+
return length2 > 1e-8 ? across * inversesqrt(length2) : mNormal;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void hairLight(vec3 l, float sourceRadius, float dist) {
|
|
49
|
+
vec3 t = mTangent;
|
|
50
|
+
float sinI = clamp(dot(t, l), -1.0, 1.0);
|
|
51
|
+
float sinO = clamp(dot(t, mToEye), -1.0, 1.0);
|
|
52
|
+
/* Floored for the half angle's limit, whose single-precision cosine is negative: hairLobes.ts. */
|
|
53
|
+
float cosD = max(cos(0.5 * abs(asin(sinO) - asin(sinI))), 1e-3);
|
|
54
|
+
vec3 lp = l - sinI * t;
|
|
55
|
+
vec3 vp = mToEye - sinO * t;
|
|
56
|
+
float cosPhi = dot(lp, vp) * inversesqrt(dot(lp, lp) * dot(vp, vp) + 1e-4);
|
|
57
|
+
float cosHalfPhi = sqrt(clamp(0.5 + 0.5 * cosPhi, 0.0, 1.0));
|
|
58
|
+
float beta = max(mRoughness * mRoughness, HAIR_MIN_BETA);
|
|
59
|
+
float grow = sourceRadius / max(2.0 * dist, 1e-3);
|
|
60
|
+
float x = sinI + sinO;
|
|
61
|
+
|
|
62
|
+
float r = hairLongitudinal(beta + grow, x + 2.0 * hShift) * 0.25 * cosHalfPhi
|
|
63
|
+
* hairFresnel(sqrt(clamp(0.5 + 0.5 * dot(l, mToEye), 0.0, 1.0)));
|
|
64
|
+
|
|
65
|
+
float a = 1.0 / (1.19 / cosD + 0.36 * cosD);
|
|
66
|
+
float h = cosHalfPhi * (1.0 + a * (0.6 - 0.8 * cosPhi));
|
|
67
|
+
float fTT = hairFresnel(cosD * sqrt(clamp(1.0 - h * h, 0.0, 1.0)));
|
|
68
|
+
float tt = hairLongitudinal(0.5 * beta + grow, x - hShift) * exp(-3.65 * cosPhi - 3.98)
|
|
69
|
+
* (1.0 - fTT) * (1.0 - fTT) * uModelParams[0].z;
|
|
70
|
+
float ttPower = 0.5 * sqrt(clamp(1.0 - h * h * a * a, 0.0, 1.0)) / cosD;
|
|
71
|
+
|
|
72
|
+
float fTRT = hairFresnel(0.5 * cosD);
|
|
73
|
+
float trt = hairLongitudinal(2.0 * beta + grow, x - 3.0 * hShift) * exp(17.0 * cosPhi - 16.78)
|
|
74
|
+
* (1.0 - fTRT) * (1.0 - fTRT) * fTRT;
|
|
75
|
+
|
|
76
|
+
/* Above zero, so pow never takes the logarithm of nothing: hairLobes.ts. */
|
|
77
|
+
vec3 fibre = max(mAlbedo, vec3(1e-4));
|
|
78
|
+
/* π: the published lobes are per unit irradiance, and a lamp here is π of that. */
|
|
79
|
+
mSpecular = 3.14159265 * (vec3(r) + tt * pow(fibre, vec3(ttPower)) + trt * pow(fibre, vec3(0.8 / cosD)));
|
|
80
|
+
float wrapped = clamp((dot(hairAcross(), l) + 1.0) * 0.25, 0.0, 1.0);
|
|
81
|
+
mDiffuse = sqrt(mAlbedo) * uModelParams[0].y * mix(wrapped, 1.0 - abs(sinI), 0.33);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A shadow on the scatter: light that reaches a strand in another's shadow has passed through
|
|
86
|
+
* hair to get there, so a penumbra deepens toward the fibre's own hue.
|
|
87
|
+
*/
|
|
88
|
+
vec3 hairShade(float shadow) {
|
|
89
|
+
vec3 hue = mAlbedo / max(dot(mAlbedo, vec3(0.2126, 0.7152, 0.0722)), 1e-4);
|
|
90
|
+
return shadow * pow(max(hue, vec3(1e-4)), vec3(1.0 - shadow));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Light from every side, through the scatter term: \`HAIR_SCATTER_SPHERE\` says why that number. */
|
|
94
|
+
vec3 hairAround(vec3 light) {
|
|
95
|
+
return sqrt(mAlbedo) * (uModelParams[0].y * HAIR_SCATTER_SPHERE * mMap.b) * light;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The reflected lobe's share of the environment: its Fresnel, which is what it integrates to. */
|
|
99
|
+
float hairReflectance() {
|
|
100
|
+
return hairFresnel(dot(hairAcross(), mToEye)) * mMap.b;
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
@@ -3,15 +3,18 @@ import { OCTAHEDRAL_GLSL } from '../octahedral.ts';
|
|
|
3
3
|
import { PROBEGRID_GLSL } from './probeGrid.ts';
|
|
4
4
|
import { TANGENT_FRAME_GLSL } from '../tangentFrame.ts';
|
|
5
5
|
import { CUTOUT_COVERAGE_GLSL } from '../cutoutCoverage.ts';
|
|
6
|
+
import { CUTOUT_DITHER_GLSL } from '../../cutoutDither.ts';
|
|
6
7
|
import { preambleGlsl } from './preamble.ts';
|
|
7
8
|
import { LOBES_GLSL } from './lobes.ts';
|
|
8
9
|
import { POINTSHADOW_GLSL } from './pointShadow.ts';
|
|
9
10
|
import { DIRECTIONALSHADOW_GLSL } from './directionalShadow.ts';
|
|
10
11
|
import { glassTintGlsl, litSwitchesGlsl } from './glassTint.ts';
|
|
11
12
|
import { MORPH_GLSL } from '../morph.ts';
|
|
13
|
+
import { CLOTH_BINDING_GLSL } from '../clothBinding.ts';
|
|
12
14
|
import { SKINNING_GLSL } from '../skinning.ts';
|
|
13
15
|
import { SURFACE_GLSL } from './surface.ts';
|
|
14
16
|
import { MAIN_GLSL } from './main.ts';
|
|
17
|
+
import { MODELS_GLSL } from './models.ts';
|
|
15
18
|
import { DRIFT_LIGHT_GLSL } from './driftLight.ts';
|
|
16
19
|
import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.ts';
|
|
17
20
|
import { CHANNEL_ATTRIBUTE, CHANNEL_BEND } from '../vertexChannel.ts';
|
|
@@ -62,6 +65,11 @@ uniform mat4 uLightViewProj;
|
|
|
62
65
|
* when no texture is bound.
|
|
63
66
|
*/
|
|
64
67
|
uniform vec2 uUvScale;
|
|
68
|
+
/**
|
|
69
|
+
* Where the texture starts, per axis, added after the scale: \`uv * uUvScale + uUvOffset\`. What
|
|
70
|
+
* picks a cell of a flipbook or an atlas through the material. See \`SurfaceMaterial.uOffset\`.
|
|
71
|
+
*/
|
|
72
|
+
uniform vec2 uUvOffset;
|
|
65
73
|
|
|
66
74
|
/*
|
|
67
75
|
* **Placement and tint are declared last of the shared uniforms, and the position is the
|
|
@@ -140,6 +148,10 @@ ${CHANNEL_BEND}
|
|
|
140
148
|
#if MORPHED
|
|
141
149
|
${MORPH_GLSL}
|
|
142
150
|
#endif
|
|
151
|
+
#if SKINNED
|
|
152
|
+
/* Samplers and a constant alone, so no field of the vertex block moves. See clothBinding.ts. */
|
|
153
|
+
${CLOTH_BINDING_GLSL}
|
|
154
|
+
#endif
|
|
143
155
|
|
|
144
156
|
out vec3 vNormal;
|
|
145
157
|
out vec3 vColor;
|
|
@@ -213,9 +225,11 @@ void main() {
|
|
|
213
225
|
mat4 skin = skinMatrix();
|
|
214
226
|
vec4 local = skin * vec4(basePosition, 1.0);
|
|
215
227
|
vec3 localNormal = mat3(skin) * aNormal;
|
|
228
|
+
vec3 localTangent = mat3(skin) * aTangent.xyz;
|
|
216
229
|
#else
|
|
217
230
|
vec4 local = vec4(basePosition, 1.0);
|
|
218
231
|
vec3 localNormal = aNormal;
|
|
232
|
+
vec3 localTangent = aTangent.xyz;
|
|
219
233
|
#endif
|
|
220
234
|
#if INSTANCED
|
|
221
235
|
mat4 model = mat4(aInstanceModel0, aInstanceModel1, aInstanceModel2, aInstanceModel3);
|
|
@@ -236,20 +250,42 @@ void main() {
|
|
|
236
250
|
vec3 bent = channelBend(world.xyz, aChannel.x);
|
|
237
251
|
#endif
|
|
238
252
|
world = vec4(bent, world.w);
|
|
239
|
-
vWorldPos = world.xyz;
|
|
240
253
|
// Rotation/translation-only models (our case): mat3 is a valid normal matrix.
|
|
241
254
|
vec3 worldNormal = mat3(model) * localNormal;
|
|
255
|
+
/*
|
|
256
|
+
* **The tangent turns as the normal does**, because the fragment stage crosses it with the
|
|
257
|
+
* world-space normal to build its frame. It went out in the mesh's own space until 4.8.4, which
|
|
258
|
+
* is world space only for a mesh that is moved and never turned: a turned or skinned mesh lit its
|
|
259
|
+
* normal map, and anything reading a direction along its surface, from the wrong side.
|
|
260
|
+
*/
|
|
261
|
+
vec3 worldTangent = mat3(model) * localTangent;
|
|
262
|
+
#if SKINNED
|
|
263
|
+
/*
|
|
264
|
+
* **Toward the cloth, after the skeleton and the bend.** The simulation already swings with the
|
|
265
|
+
* wind it was given, so the bend moves only the skinned part; the weight painted per vertex says
|
|
266
|
+
* how much of the cloth's place and turn the vertex takes. See clothBinding.ts.
|
|
267
|
+
*/
|
|
268
|
+
if (CLOTH_BOUND) {
|
|
269
|
+
vec3 clothPosition;
|
|
270
|
+
mat3 turn;
|
|
271
|
+
float follow = clothPlace(clothPosition, turn);
|
|
272
|
+
world = vec4(mix(world.xyz, clothPosition, follow), world.w);
|
|
273
|
+
worldNormal = normalize(mix(worldNormal, turn * aNormal, follow));
|
|
274
|
+
worldTangent = mix(worldTangent, turn * aTangent.xyz, follow);
|
|
275
|
+
}
|
|
276
|
+
#endif
|
|
277
|
+
vWorldPos = world.xyz;
|
|
242
278
|
vNormal = worldNormal;
|
|
243
279
|
vLightPos = uLightViewProj * world;
|
|
244
280
|
vColor = aColor * tint;
|
|
245
281
|
vEmissive = aEmissive;
|
|
246
282
|
vSpecular = aSpecular;
|
|
247
|
-
vUv = vec3(aUv.xy * uUvScale, aUv.z);
|
|
283
|
+
vUv = vec3(aUv.xy * uUvScale + uUvOffset, aUv.z);
|
|
248
284
|
vEmissiveColor = aEmissiveColor;
|
|
249
285
|
vRoughness = aRoughness;
|
|
250
286
|
vGrain = aGrain;
|
|
251
287
|
vRelief = aRelief;
|
|
252
|
-
vTangent = aTangent;
|
|
288
|
+
vTangent = vec4(worldTangent, aTangent.w);
|
|
253
289
|
#if INSTANCED
|
|
254
290
|
/* No channel on this path; the neutral values are what every mesh read before it existed. */
|
|
255
291
|
vSkyDirect = 1.0;
|
|
@@ -529,8 +565,11 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
529
565
|
TANGENT_FRAME_GLSL,
|
|
530
566
|
/* Unconditional for the same reason, and called only where a cutoff was asked for. */
|
|
531
567
|
CUTOUT_COVERAGE_GLSL,
|
|
568
|
+
CUTOUT_DITHER_GLSL,
|
|
532
569
|
DRIFT_LIGHT_GLSL,
|
|
533
570
|
SURFACE_EFFECTS_GLSL,
|
|
571
|
+
/* Depth zero and after the tangent frame and the lobes it calls; see models.ts. */
|
|
572
|
+
MODELS_GLSL,
|
|
534
573
|
MAIN_GLSL,
|
|
535
574
|
].join('\n'),
|
|
536
575
|
{
|