@bitefoundry/retrieviewer 0.0.0-stage → 0.0.1
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/ASSET-PROVENANCE.md +43 -0
- package/ASSETS-LICENSE.md +58 -0
- package/ATTRIBUTION.md +54 -0
- package/EMBEDDING.md +96 -0
- package/INSTALLING.md +107 -0
- package/LICENSE +16 -0
- package/LICENSE-CODE +21 -0
- package/PLUGIN.md +110 -0
- package/PUPPY-MEMORY.md +57 -0
- package/PUPPY-Q5-COMPARISON.md +73 -0
- package/README.md +61 -2
- package/RELEASING.md +61 -0
- package/THIRD-PARTY-NOTICES.md +17 -0
- package/TRADEMARKS.md +21 -0
- package/braille.mjs +45 -0
- package/examples/bridge.py +62 -0
- package/examples/embed.mjs +15 -0
- package/examples/live.mjs +40 -0
- package/examples/plugin.mjs +21 -0
- package/integrations/opencode/runtime.mjs +195 -0
- package/integrations/opencode/tui.tsx +59 -0
- package/locomotion-core.mjs +21 -0
- package/locomotion-puppy.animal +0 -0
- package/package.json +125 -4
- package/pet-q5-manifest.json +118 -0
- package/puppy-clean.mjs +51 -0
- package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
- package/puppy-companion-interactions-v3.json +1 -0
- package/puppy-companion-life.mjs +182 -0
- package/puppy-companion-objects.mjs +30 -0
- package/puppy-companion-sprites.mjs +14 -0
- package/puppy-continuous.mjs +57 -0
- package/puppy-face.mjs +46 -0
- package/puppy-frame-predictive.mjs +147 -0
- package/puppy-frame-store.mjs +108 -0
- package/puppy-mouth-anchor.json +1 -0
- package/puppy-mouth-anchor.mjs +33 -0
- package/puppy-paper-actions-v9-q5.bin.gz +0 -0
- package/puppy-paper-actions-v9.json +1 -0
- package/puppy-paper-actions.mjs +48 -0
- package/puppy-paper-approach-q5.bin.gz +0 -0
- package/puppy-paper-approach.json +1 -0
- package/puppy-paper-area.mjs +15 -0
- package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
- package/puppy-paper-arrival-seat.json +1 -0
- package/puppy-paper-behaviors-q5.bin.gz +0 -0
- package/puppy-paper-behaviors.json +1 -0
- package/puppy-paper-behaviors.mjs +8 -0
- package/puppy-paper-contact.mjs +103 -0
- package/puppy-paper-director.mjs +21 -0
- package/puppy-paper-paw-transport.mjs +76 -0
- package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
- package/puppy-paper-rear-gallop.json +1 -0
- package/puppy-paper-seat-q5.bin.gz +0 -0
- package/puppy-paper-seat.json +14 -0
- package/puppy-paper-seat.mjs +13 -0
- package/puppy-paper-trick-timing.mjs +58 -0
- package/puppy-paper-world.mjs +108 -0
- package/puppy-paper.mjs +271 -0
- package/puppy-stable-meta.json +1606 -0
- package/puppy-stable.mjs +72 -0
- package/puppy-stride-meta.json +343 -0
- package/puppy-stride-tweens-q5.bin.gz +0 -0
- package/puppy-zoomies.mjs +27 -0
- package/puppy-zoomies30.mjs +12 -0
- package/retrieviewer-cli.mjs +81 -0
- package/retrieviewer-config.mjs +49 -0
- package/retrieviewer-install.mjs +157 -0
- package/retrieviewer-plugin.d.mts +44 -0
- package/retrieviewer-plugin.mjs +97 -0
- package/retrieviewer-stdio.mjs +38 -0
- package/retrieviewer.d.mts +85 -0
- package/retrieviewer.mjs +142 -0
- package/vendor/procedural-animals/LICENSE +21 -0
- package/vendor/procedural-animals/package.json +40 -0
- package/vendor/procedural-animals/src/animal.js +146 -0
- package/vendor/procedural-animals/src/bake.js +51 -0
- package/vendor/procedural-animals/src/build.js +81 -0
- package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
- package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
- package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
- package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
- package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
- package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
- package/vendor/procedural-animals/src/core/build/seams.js +183 -0
- package/vendor/procedural-animals/src/core/build/warp.js +112 -0
- package/vendor/procedural-animals/src/core/build/weights.js +393 -0
- package/vendor/procedural-animals/src/core/math/vec.js +81 -0
- package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
- package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
- package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
- package/vendor/procedural-animals/src/core/motion/common.js +158 -0
- package/vendor/procedural-animals/src/core/motion/index.js +22 -0
- package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
- package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
- package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
- package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
- package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
- package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
- package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
- package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
- package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
- package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
- package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
- package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
- package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
- package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
- package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
- package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
- package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
- package/vendor/procedural-animals/src/index.d.ts +98 -0
- package/vendor/procedural-animals/src/index.js +29 -0
- package/vendor/procedural-animals/src/registry.js +48 -0
- package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
- package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
- package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
- package/vendor/procedural-animals/src/species/bear/index.js +139 -0
- package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
- package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
- package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
- package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
- package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
- package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
- package/vendor/procedural-animals/src/species/boar/index.js +110 -0
- package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
- package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
- package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
- package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
- package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
- package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
- package/vendor/procedural-animals/src/species/cat/index.js +211 -0
- package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
- package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
- package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
- package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
- package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
- package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
- package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
- package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
- package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
- package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
- package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
- package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
- package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
- package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
- package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
- package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
- package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
- package/vendor/procedural-animals/src/species/cow/index.js +118 -0
- package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
- package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
- package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
- package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
- package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
- package/vendor/procedural-animals/src/species/crow/index.js +70 -0
- package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
- package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
- package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
- package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
- package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
- package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
- package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
- package/vendor/procedural-animals/src/species/deer/index.js +106 -0
- package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
- package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
- package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
- package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
- package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
- package/vendor/procedural-animals/src/species/dog/index.js +144 -0
- package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
- package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
- package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
- package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
- package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
- package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
- package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
- package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
- package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
- package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
- package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
- package/vendor/procedural-animals/src/species/fish/index.js +88 -0
- package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
- package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
- package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
- package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
- package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
- package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
- package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
- package/vendor/procedural-animals/src/species/fox/index.js +120 -0
- package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
- package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
- package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
- package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
- package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
- package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
- package/vendor/procedural-animals/src/species/frog/index.js +97 -0
- package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
- package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
- package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
- package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
- package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
- package/vendor/procedural-animals/src/species/goat/index.js +149 -0
- package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
- package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
- package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
- package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
- package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
- package/vendor/procedural-animals/src/species/horse/index.js +80 -0
- package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
- package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
- package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
- package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
- package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
- package/vendor/procedural-animals/src/species/lion/index.js +111 -0
- package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
- package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
- package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
- package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
- package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
- package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
- package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
- package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
- package/vendor/procedural-animals/src/species/pig/index.js +161 -0
- package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
- package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
- package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
- package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
- package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
- package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
- package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
- package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
- package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
- package/vendor/procedural-animals/src/species/rat/index.js +111 -0
- package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
- package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
- package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
- package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
- package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
- package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
- package/vendor/procedural-animals/src/species/shark/index.js +104 -0
- package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
- package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
- package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
- package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
- package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
- package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
- package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
- package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
- package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
- package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
- package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
- package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
- package/vendor/procedural-animals/src/species/snake/index.js +73 -0
- package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
- package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
- package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
- package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
- package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
- package/vendor/procedural-animals/src/species/spider/index.js +78 -0
- package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
- package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
- package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
- package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
- package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
- package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
- package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
- package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
- package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
- package/vendor/procedural-animals/src/worker.js +29 -0
|
@@ -0,0 +1,1301 @@
|
|
|
1
|
+
// Coverings: fur, bare skin, leather, keratin, scales, wet skin, chitin, feathers and fin membranes
|
|
2
|
+
// (material 11: translucent, rays; drawn as a separate transparent pass, see membranes.js), all lit by the
|
|
3
|
+
// scene's own three.js lights (directional + shadows, point + shadows, spot + shadows, hemisphere,
|
|
4
|
+
// ambient, light probes), fog and tone mapping.
|
|
5
|
+
//
|
|
6
|
+
// Three passes share one shading model:
|
|
7
|
+
// - base: the opaque, dual-quaternion skinned surface. Every material id (tint.w) gets its own
|
|
8
|
+
// surface detail here (pebbled leather, pores, growth striations, overlapping scales, granular
|
|
9
|
+
// wet skin, chitin segments, feather shingles with rachis and barbs). Without shells (crowd tier,
|
|
10
|
+
// coverings hidden) fur still reads as fur on the base: hair-flow streaks and anisotropic sheen.
|
|
11
|
+
// - shells: ONE instanced draw; gl_InstanceID = shell index. Strands are hashed from bind-space
|
|
12
|
+
// position (so they stick to the skin under any pose) and grouped into clumps: going up the
|
|
13
|
+
// shells, strands converge toward their clump centre, so the top of the coat breaks into tufts
|
|
14
|
+
// with dark gaps between them. Clumps vary in length, shade and lean, and a clump either belongs
|
|
15
|
+
// to a spot or not, which rags the spot edges. Shells draw wherever furLen > 0 on fur (0), bare
|
|
16
|
+
// skin (4: sparse bristles) and feathers (9: a soft down fringe).
|
|
17
|
+
// - fins: one camera-facing strand card per skin vertex, shown only near the silhouette, so the
|
|
18
|
+
// outline is made of individual hairs instead of a smooth edge.
|
|
19
|
+
// Depth: the shells write depth (drawn inner layer to outer), so a coat hides what lies behind it however
|
|
20
|
+
// the draws are ordered: the far legs behind a belly fringe, another part's fins (drawn after the shells,
|
|
21
|
+
// they used to draw the outlines of every part behind the coat across it). Between the strands each
|
|
22
|
+
// layer carries an undercoat (render hint undercoat: the hair the strand grid does not resolve), an
|
|
23
|
+
// optical depth taken along the line of sight and spread over the shell count, so the coat is as
|
|
24
|
+
// opaque at every tier and where one looks along it, shaded like the deep coat it stands in for.
|
|
25
|
+
//
|
|
26
|
+
// Hair lighting per light: wrapped diffuse + two-lobe Kajiya-Kay (R / TRT) + back-scatter through
|
|
27
|
+
// thin fur, depth-in-fur and between-tuft occlusion. Surfaces use wrapped / subsurface diffuse,
|
|
28
|
+
// normalised Blinn-Phong, an optional clear coat and anisotropic lobes. Units follow
|
|
29
|
+
// MeshStandardMaterial: a light of intensity I lights a covering of albedo a like a standard
|
|
30
|
+
// material of colour a (radiance = a * E / PI), so animals sit correctly next to your scenery.
|
|
31
|
+
//
|
|
32
|
+
// Every animal owns its uniforms (three.js shares the compiled programs). Species render hints,
|
|
33
|
+
// `species.render = { markColor, strandDensity, clumpDensity, shellScale, finWidth }`:
|
|
34
|
+
// markColor linear rgb of crisp marks (tear lines, eye rims, lips) default cheetah black
|
|
35
|
+
// strandDensity strands per metre (planar hash grid) default 620
|
|
36
|
+
// clumpDensity multiplier on tuft density default 1
|
|
37
|
+
// shellScale multiplier on hair length for shells and fins default 1
|
|
38
|
+
// finWidth silhouette card width as a fraction of hair length default 0.5
|
|
39
|
+
// fins false: no silhouette fins (very long coats: bear) default true
|
|
40
|
+
// finMask true: on fur (material 0) the coat's surf.y (unused by fur) thins the silhouette fins,
|
|
41
|
+
// 0 = full fins .. 1 = none (the cat's head: the head's silhouette ring of fins stood
|
|
42
|
+
// in front of the neck fur as a hood round the face) default false
|
|
43
|
+
// finMinLen hair length (m) below which no silhouette fins are drawn default 0.0058 (a species with a
|
|
44
|
+
// short face coat and a long mane raises it: fins on the mane only)
|
|
45
|
+
// strandBlend 0: the shells' strands are hashed on one axis plane picked by the rest normal's largest
|
|
46
|
+
// component (default); > 0: near a switch of that axis the second plane's strands are
|
|
47
|
+
// cross-faded in over this band of normal components (e.g. 0.12), so no seam line shows where
|
|
48
|
+
// the plane switches (a short-coated broad face tilted ~45 deg: a lion's forehead drew a
|
|
49
|
+
// rectangle); costs a second strand evaluation per shell fragment
|
|
50
|
+
// scaleEdge contrast of the scale rims, overlap shadows and relief (material 6) default 1 (fish 0.15)
|
|
51
|
+
// featherEdge contrast of the contour-feather shingles (material 9) default 1 (pale birds ~0.5)
|
|
52
|
+
// denticles 0..1: material 6 is shark skin instead of overlapping scales: dermal denticles ridged
|
|
53
|
+
// along the comb (surf.y = denticle size), a matte fine grain streaked along the body, a
|
|
54
|
+
// soft broad anisotropic sheen across the ridges, no clear coat default 0
|
|
55
|
+
// raiseLen [from, full] hair length (m) over which piloerection (state.furRaise) lifts the hair
|
|
56
|
+
// default [0.025, 0.05] (wolf hackles); short-coated animals lower it (cat [0.006, 0.014])
|
|
57
|
+
// leatherTint 0..1: nose leather (material 1) takes its colour from the tint (pink or brick noses and
|
|
58
|
+
// pads as leather, cat); default 0: the dark leather of every other species
|
|
59
|
+
// leatherRelief 0..1: depth of the leather's pebbling (crease darkening, cobble shade, bump); default 1.
|
|
60
|
+
// A small nose in daylight (the cat's) read speckled at full relief
|
|
61
|
+
// skinScatter bare skin (material 4): [r, g, b, wrap], the tint of the light wrapped past the terminator
|
|
62
|
+
// (subsurface) and how far it wraps default [0.85, 0.35, 0.25, 0.55]
|
|
63
|
+
// skinTrans bare skin: [base, perVertex], light through the skin lit from behind = base + perVertex x
|
|
64
|
+
// surf.y (per vertex, unused otherwise by skin: thin ears and tails glow, a thick body
|
|
65
|
+
// does not) default [0.5, 0]
|
|
66
|
+
// noseRoughness roughness of the nose leather (material 1) default 0.42
|
|
67
|
+
// noseMarks 1: marks (coat mark < 0) are drawn on the nose leather too, as openings (the nostrils:
|
|
68
|
+
// mark colour, no sheen from inside); default 0 (the leather hides marks)
|
|
69
|
+
// noseTint 1: the nose leather takes its albedo from the coat's per-vertex colour (pale rims, a
|
|
70
|
+
// septum painted by the species) instead of the fixed dark grey; default 0. The same
|
|
71
|
+
// blend as leatherTint 1 (both read the per-vertex tint): the larger of the two applies
|
|
72
|
+
// clumpLen [from, full] hair length (m) over which the tufts form (clumping strength 0 -> 1)
|
|
73
|
+
// default [0.0055, 0.016]; a long coat with a sleek face raises it, so 1-3 cm facial and
|
|
74
|
+
// ear hair lies smooth instead of curling into round tufts (bear [0.012, 0.045])
|
|
75
|
+
// shellShadowRoot 0..1: where the shells look up cast shadows, from each layer's own position (0) to the hair's
|
|
76
|
+
// root (1). At 0 every layer of a long coat crosses a cast shadow's edge at its own place, and seen
|
|
77
|
+
// at a grazing angle the stack shows as torn dark sheets (bear: the neck's 5-10 cm hair in the
|
|
78
|
+
// head's shadow behind the jaw); at 1 a strand is lit or shadowed as one. default 0
|
|
79
|
+
// undercoat optical depth per unit of coat height of the hair between the strands, at the roots (fading
|
|
80
|
+
// to none at 0.9 of the hair's height): 0 = strands only (what lay under a long coat showed between
|
|
81
|
+
// them: a leg under a fringe, a folded wing in down) default 3
|
|
82
|
+
// shellDepthBias [k, from]: a long coat's shells are depth-tested k x (hair length - from) closer to the camera
|
|
83
|
+
// than they are (a strand's every layer, constant along the strand). The shells slide along the comb
|
|
84
|
+
// as they rise (up to 0.85 x the hair length), so where the skin creases (a sheep's throat when the
|
|
85
|
+
// head drops to graze or turns aside) the layers of one side dive under the skin of the other and the
|
|
86
|
+
// crease shows as a jagged bald line; the bias keeps them in front of a fold that shallow. Shorter hair
|
|
87
|
+
// than `from` is untouched (face, lids). default [0, 0] (off)
|
|
88
|
+
// Nose leather (material 1) takes its roughness from -surf.y when surf.y < 0 (a matte nose or paw pad:
|
|
89
|
+
// bear nose -0.62, paw pads -0.75); otherwise from the render hint noseRoughness (satin 0.42 by default).
|
|
90
|
+
// matBorders true: a material border runs through the middle of the edges between the two
|
|
91
|
+
// materials (interpolated one-hot weights) instead of rounding the interpolated id,
|
|
92
|
+
// which shades a strip of nose / dark skin / mouth (ids 1-3) between fur (0) and bare
|
|
93
|
+
// skin (4) or keratin (5): black-and-red rims round pink noses, udders and hooves.
|
|
94
|
+
// default false (the rounding); one more varying (vec4)
|
|
95
|
+
// flatMaterial true: each triangle takes one material (its provoking vertex's) instead of rounding
|
|
96
|
+
// the interpolated material id; default false. The id is a smooth varying, so a
|
|
97
|
+
// triangle from fur (0) to bare skin (4), keratin (5) or wet skin (7) passes through
|
|
98
|
+
// thin bands of the ids in between, whose albedos are fixed (nose 1 dark, lids 2 black,
|
|
99
|
+
// mouth 3 red): a dark / red rim along every bare-skin border (a cow's pink muzzle,
|
|
100
|
+
// udder, lid rims, coronets). Species whose bare skin meets fur opt in.
|
|
101
|
+
// matBorders (goat) answers the same bands by splitting each triangle through its edge
|
|
102
|
+
// middles instead; a species picks one (with both set, flatMaterial applies)
|
|
103
|
+
import * as THREE from 'three';
|
|
104
|
+
import { DQS_GLSL, DQS_ATTR_GLSL } from './dqs.js';
|
|
105
|
+
|
|
106
|
+
export const RENDER_DEFAULTS = {
|
|
107
|
+
markColor: [0.016, 0.0105, 0.0075],
|
|
108
|
+
strandDensity: 620,
|
|
109
|
+
clumpDensity: 1,
|
|
110
|
+
shellScale: 1,
|
|
111
|
+
finWidth: 0.5,
|
|
112
|
+
finMinLen: 0.0058,
|
|
113
|
+
strandBlend: 0,
|
|
114
|
+
scaleEdge: 1,
|
|
115
|
+
featherEdge: 1,
|
|
116
|
+
denticles: 0,
|
|
117
|
+
raiseLen: [0.025, 0.05],
|
|
118
|
+
leatherTint: 0,
|
|
119
|
+
leatherRelief: 1,
|
|
120
|
+
skinScatter: [0.85, 0.35, 0.25, 0.55],
|
|
121
|
+
skinTrans: [0.5, 0],
|
|
122
|
+
noseRoughness: 0.42,
|
|
123
|
+
noseMarks: 0,
|
|
124
|
+
noseTint: 0,
|
|
125
|
+
clumpLen: [0.0055, 0.016],
|
|
126
|
+
shellShadowRoot: 0,
|
|
127
|
+
shellDepthBias: [0, 0],
|
|
128
|
+
undercoat: 3,
|
|
129
|
+
flatMaterial: false,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const MAX_PAINTED_EYES = 8;
|
|
133
|
+
|
|
134
|
+
const common = /* glsl */ `
|
|
135
|
+
uniform float uShells;
|
|
136
|
+
uniform float uShellsOn;
|
|
137
|
+
uniform float uFinFade; // fins: opacity of the whole fin pass (tier transitions, animalObject setQuality)
|
|
138
|
+
uniform float uUnder; // shells: the undercoat's optical depth per unit coat height at the roots (render hint undercoat)
|
|
139
|
+
uniform float uFurScale;
|
|
140
|
+
uniform vec3 uFurForce;
|
|
141
|
+
uniform float uFurRaise;
|
|
142
|
+
uniform vec2 uRaiseLen; // hair lengths over which piloerection sets in (render hint raiseLen)
|
|
143
|
+
uniform vec2 uClumpLen; // hair lengths over which the tufts form (render hint clumpLen)
|
|
144
|
+
uniform float uTime;
|
|
145
|
+
uniform float uStrandDensity;
|
|
146
|
+
uniform float uClumpDensity;
|
|
147
|
+
uniform float uFinWidth;
|
|
148
|
+
uniform float uFinMask;
|
|
149
|
+
uniform float uFinMinLen;
|
|
150
|
+
uniform float uStrandBlend;
|
|
151
|
+
uniform float uWind;
|
|
152
|
+
uniform float uDebug;
|
|
153
|
+
uniform float uForceMat;
|
|
154
|
+
uniform float uFlatMat; // render hint flatMaterial: the material id from the provoking vertex (vMatF)
|
|
155
|
+
uniform vec3 uMarkColor;
|
|
156
|
+
uniform float uNoseRough; // render hint noseRoughness
|
|
157
|
+
uniform float uNoseMarks; // render hint noseMarks
|
|
158
|
+
uniform float uNoseTint; // render hint noseTint
|
|
159
|
+
uniform float uFill;
|
|
160
|
+
uniform float uScaleEdge;
|
|
161
|
+
uniform float uFeatherEdge;
|
|
162
|
+
uniform float uLeatherTint;
|
|
163
|
+
uniform float uLeatherRelief;
|
|
164
|
+
uniform vec4 uSkinScatter; // bare skin: subsurface tint (rgb) and wrap (render hint skinScatter)
|
|
165
|
+
uniform vec2 uSkinTrans; // bare skin: transmission = x + y * surf.y (render hint skinTrans)
|
|
166
|
+
uniform float uDenticle;
|
|
167
|
+
uniform float uPaintEyes;
|
|
168
|
+
// environment light (setEnvironmentLight): the scene environment's diffuse irradiance as 9 world-space
|
|
169
|
+
// SH coefficients (three.js SphericalHarmonics3 / LightProbe convention), scaled; uEnvOn 0 = none (default)
|
|
170
|
+
uniform vec3 uEnvSH[9];
|
|
171
|
+
uniform float uEnvOn;
|
|
172
|
+
uniform vec3 uBaseTone; // fur albedo gain of the base when it is seen without shells (crowd tier)
|
|
173
|
+
uniform float uShellShadowRoot; // shells: cast shadows looked up toward the hair's root (render hint shellShadowRoot)
|
|
174
|
+
uniform vec2 uShellBias; // shells: depth pulled toward the camera by x * (hair length - y) (render hint shellDepthBias)
|
|
175
|
+
uniform vec2 uSeam; // cross-fade seam skirt (build/seams.js): x = sink depth (m), y = fade where the base stops
|
|
176
|
+
uniform vec4 uEyes[${MAX_PAINTED_EYES}];
|
|
177
|
+
uniform vec3 uEyeTint[${MAX_PAINTED_EYES}];
|
|
178
|
+
varying vec3 vRest;
|
|
179
|
+
varying vec3 vRestN;
|
|
180
|
+
varying vec3 vRestC;
|
|
181
|
+
varying vec3 vWorldPos;
|
|
182
|
+
varying vec3 vN;
|
|
183
|
+
varying vec3 vT;
|
|
184
|
+
varying vec4 vTint;
|
|
185
|
+
flat varying float vMatF;
|
|
186
|
+
varying vec4 vCoat;
|
|
187
|
+
varying vec4 vPat;
|
|
188
|
+
varying vec4 vSurf;
|
|
189
|
+
varying vec4 vFin; // x across 0..1 (fins), y up 0..1 (fins), z silhouette visibility, w = height in coat
|
|
190
|
+
#ifdef PA_DEBUG
|
|
191
|
+
varying vec3 vDbg;
|
|
192
|
+
#endif
|
|
193
|
+
// which material ids grow hair in the shells / fins: fur, bare skin (bristles), chitin (setae), feathers (down)
|
|
194
|
+
bool hairy(float m) { return m < 0.5 || abs(m - 4.0) < 0.5 || abs(m - 8.0) < 0.5 || abs(m - 9.0) < 0.5; }
|
|
195
|
+
float matId(float a) { return uForceMat >= 0.0 ? uForceMat : floor(a + 0.5); }
|
|
196
|
+
#ifdef PA_MAT_BORDERS
|
|
197
|
+
// (render hint matBorders) the material of a fragment is the one whose interpolated one-hot weight is the
|
|
198
|
+
// largest (ids 0..3 in vMatW; the rest, ids >= 4, recovered from the interpolated id), so the border
|
|
199
|
+
// between two materials runs through the middle of the edges between them. Rounding the interpolated
|
|
200
|
+
// id shaded a strip of every id in between: nose, dark skin and mouth (1..3) round a pink nose, an
|
|
201
|
+
// udder or a hoof (fur 0 next to skin 4 / keratin 5).
|
|
202
|
+
varying vec4 vMatW;
|
|
203
|
+
vec4 matOneHot(float a) { return vec4(equal(vec4(floor(a + 0.5)), vec4(0.0, 1.0, 2.0, 3.0))); }
|
|
204
|
+
float matIdFrag(float a) {
|
|
205
|
+
if (uForceMat >= 0.0) return uForceMat;
|
|
206
|
+
vec4 w = vMatW;
|
|
207
|
+
float r = 1.0 - (w.x + w.y + w.z + w.w), best = w.x, id = 0.0;
|
|
208
|
+
if (w.y > best) { best = w.y; id = 1.0; }
|
|
209
|
+
if (w.z > best) { best = w.z; id = 2.0; }
|
|
210
|
+
if (w.w > best) { best = w.w; id = 3.0; }
|
|
211
|
+
if (r > best) id = floor((a - (w.y + 2.0 * w.z + 3.0 * w.w)) / max(r, 1e-4) + 0.5);
|
|
212
|
+
return id;
|
|
213
|
+
}
|
|
214
|
+
#else
|
|
215
|
+
float matIdFrag(float a) { return matId(a); }
|
|
216
|
+
#endif
|
|
217
|
+
`;
|
|
218
|
+
|
|
219
|
+
// how a hair rises from the skin: lifted along the normal, laid down along the comb, bent by force
|
|
220
|
+
const hairProfile = /* glsl */ `
|
|
221
|
+
const float LIFT = 0.5;
|
|
222
|
+
const float SLIDE = 0.85;
|
|
223
|
+
float hash31(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.yzx + 33.33); return fract((p.x + p.y) * p.z); }
|
|
224
|
+
vec3 furForce(vec3 rest, vec3 sN) {
|
|
225
|
+
float ph = hash31(floor(rest * 60.0)) * 6.2831;
|
|
226
|
+
vec3 flutter = vec3(sin(uTime * 7.0 + ph), 0.0, cos(uTime * 5.3 + ph * 1.3)) * uWind * 0.25;
|
|
227
|
+
vec3 force = uFurForce + flutter;
|
|
228
|
+
if (any(isnan(force)) || any(isinf(force))) force = vec3(0.0, -0.2, 0.0);
|
|
229
|
+
// remove the part of the force that would push the hair into the skin
|
|
230
|
+
force -= sN * min(0.0, dot(force, sN)) * 0.9;
|
|
231
|
+
return force;
|
|
232
|
+
}
|
|
233
|
+
// piloerection (hackles): long hair stands up off the skin
|
|
234
|
+
float hairRaise(float L) { return uFurRaise * smoothstep(uRaiseLen.x, uRaiseLen.y, L / max(uFurScale, 1e-3)); }
|
|
235
|
+
vec3 hairOffset(vec3 sN, vec3 sC, vec3 force, float L, float h) {
|
|
236
|
+
float r = hairRaise(L);
|
|
237
|
+
return sN * (L * (LIFT + 0.45 * r) * h) + sC * (L * SLIDE * (1.0 - 0.55 * r) * (h * h * 0.6 + h * 0.4)) + force * (L * h * h * (1.0 - 0.6 * r));
|
|
238
|
+
}
|
|
239
|
+
vec3 hairDir(vec3 sN, vec3 sC, vec3 force, float L, float h) {
|
|
240
|
+
float r = hairRaise(L);
|
|
241
|
+
return normalize(sN * (LIFT + 0.45 * r) + sC * SLIDE * (1.0 - 0.55 * r) * (0.4 + 1.2 * h) + force * (2.0 * h * (1.0 - 0.6 * r)));
|
|
242
|
+
}
|
|
243
|
+
void debugColour(vec4 si, vec4 sw) {
|
|
244
|
+
#ifdef PA_DEBUG
|
|
245
|
+
vec3 bc0 = fract(vec3(0.13, 0.37, 0.71) * si.x * 7.31 + vec3(0.1, 0.5, 0.3));
|
|
246
|
+
vec3 bc1 = fract(vec3(0.13, 0.37, 0.71) * si.y * 7.31 + vec3(0.1, 0.5, 0.3));
|
|
247
|
+
vec3 bc2 = fract(vec3(0.13, 0.37, 0.71) * si.z * 7.31 + vec3(0.1, 0.5, 0.3));
|
|
248
|
+
vec3 bc3 = fract(vec3(0.13, 0.37, 0.71) * si.w * 7.31 + vec3(0.1, 0.5, 0.3));
|
|
249
|
+
vDbg = bc0 * sw.x + bc1 * sw.y + bc2 * sw.z + bc3 * sw.w;
|
|
250
|
+
#endif
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
|
|
254
|
+
const vertex = /* glsl */ `
|
|
255
|
+
#include <common>
|
|
256
|
+
#include <shadowmap_pars_vertex>
|
|
257
|
+
#include <fog_pars_vertex>
|
|
258
|
+
${common}
|
|
259
|
+
${DQS_GLSL}
|
|
260
|
+
${DQS_ATTR_GLSL}
|
|
261
|
+
attribute vec3 aComb;
|
|
262
|
+
attribute vec4 aTint;
|
|
263
|
+
attribute vec4 aCoat;
|
|
264
|
+
attribute vec4 aPat;
|
|
265
|
+
attribute vec4 aSurf;
|
|
266
|
+
${hairProfile}
|
|
267
|
+
|
|
268
|
+
void main() {
|
|
269
|
+
vec3 sP = position, sN = normal, sC = aComb;
|
|
270
|
+
dqsSkin(sP, sN, sC);
|
|
271
|
+
sP = (modelMatrix * vec4(sP, 1.0)).xyz;
|
|
272
|
+
sN = normalize(mat3(modelMatrix) * sN);
|
|
273
|
+
sC = mat3(modelMatrix) * sC;
|
|
274
|
+
sC = normalize(sC - sN * dot(sC, sN) + 1e-6);
|
|
275
|
+
#ifndef FUR_SHELL
|
|
276
|
+
// seams (build/seams.js): two overlapping surfaces hand over at fade 0.5 but do not coincide there.
|
|
277
|
+
// Past the midline the base does not stop at a step one could look through: it sinks along its
|
|
278
|
+
// normal under the surface taking over (which wins the depth test), down to fade uSeam.y.
|
|
279
|
+
// (quadratic: tangent to the skin at the midline, so coarse triangles across the band do not fold)
|
|
280
|
+
float seamT = clamp((0.5 - aCoat.w) / max(0.5 - uSeam.y, 1e-3), 0.0, 1.0);
|
|
281
|
+
sP -= sN * (uSeam.x * seamT * seamT);
|
|
282
|
+
#endif
|
|
283
|
+
|
|
284
|
+
float h = 0.0;
|
|
285
|
+
vec3 wp = sP;
|
|
286
|
+
vec3 strand = sC;
|
|
287
|
+
#ifdef FUR_SHELL
|
|
288
|
+
float L = aCoat.z * uFurScale;
|
|
289
|
+
vec3 force = furForce(position, sN);
|
|
290
|
+
h = (float(gl_InstanceID) + 1.0) / uShells;
|
|
291
|
+
// (a fractional shell count during a tier transition: the topmost partial layer lies at the tips and
|
|
292
|
+
// fades in by the fraction)
|
|
293
|
+
float layerFade = clamp(1.0 - (h - 1.0) * uShells, 0.0, 1.0);
|
|
294
|
+
h = min(h, 1.0);
|
|
295
|
+
wp = sP + hairOffset(sN, sC, force, L, h);
|
|
296
|
+
strand = hairDir(sN, sC, force, L, h);
|
|
297
|
+
#endif
|
|
298
|
+
debugColour(skinIndex, skinWeight);
|
|
299
|
+
vRest = position;
|
|
300
|
+
vRestN = normal;
|
|
301
|
+
vRestC = aComb;
|
|
302
|
+
vWorldPos = wp;
|
|
303
|
+
vN = sN;
|
|
304
|
+
vT = strand;
|
|
305
|
+
vTint = aTint;
|
|
306
|
+
#ifdef PA_MAT_BORDERS
|
|
307
|
+
vMatW = matOneHot(aTint.a);
|
|
308
|
+
#endif
|
|
309
|
+
vMatF = aTint.a;
|
|
310
|
+
vCoat = aCoat;
|
|
311
|
+
vPat = aPat;
|
|
312
|
+
vSurf = aSurf;
|
|
313
|
+
vFin = vec4(0.0, 0.0, 0.0, h);
|
|
314
|
+
#ifdef FUR_SHELL
|
|
315
|
+
vFin.z = layerFade;
|
|
316
|
+
#endif
|
|
317
|
+
|
|
318
|
+
vec4 worldPosition = vec4(wp, 1.0);
|
|
319
|
+
vec4 mvPosition = viewMatrix * worldPosition;
|
|
320
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
321
|
+
#ifdef FUR_SHELL
|
|
322
|
+
// (render hint shellDepthBias: the layer's depth taken from a point that much nearer the camera, so a layer that
|
|
323
|
+
// slid into a crease of its own skin still shows; its place on screen is unchanged)
|
|
324
|
+
float depthBias = uShellBias.x * max(0.0, L - uShellBias.y);
|
|
325
|
+
if (depthBias > 0.0) {
|
|
326
|
+
vec4 q = projectionMatrix * viewMatrix * vec4(wp + normalize(cameraPosition - wp) * depthBias, 1.0);
|
|
327
|
+
gl_Position.z = q.z / q.w * gl_Position.w;
|
|
328
|
+
}
|
|
329
|
+
#endif
|
|
330
|
+
vec3 transformedNormal = (viewMatrix * vec4(sN, 0.0)).xyz;
|
|
331
|
+
#ifdef FUR_SHELL
|
|
332
|
+
// (render hint shellShadowRoot: the cast shadow of a layer taken toward its strand's root, 4 mm off the skin)
|
|
333
|
+
if (uShellShadowRoot > 0.0) worldPosition = vec4(mix(wp, sP + sN * 0.004, uShellShadowRoot), 1.0);
|
|
334
|
+
#endif
|
|
335
|
+
#include <shadowmap_vertex>
|
|
336
|
+
#include <fog_vertex>
|
|
337
|
+
}
|
|
338
|
+
`;
|
|
339
|
+
|
|
340
|
+
// Fins: the template quad (position.x in [-0.5, 0.5] across, position.y in [0, 1] up) is instanced
|
|
341
|
+
// once per skin vertex; all per-vertex data arrives as instanced attributes.
|
|
342
|
+
const finVertex = /* glsl */ `
|
|
343
|
+
#include <common>
|
|
344
|
+
#include <shadowmap_pars_vertex>
|
|
345
|
+
#include <fog_pars_vertex>
|
|
346
|
+
${common}
|
|
347
|
+
${DQS_GLSL}
|
|
348
|
+
attribute vec3 iPos;
|
|
349
|
+
attribute vec3 iNrm;
|
|
350
|
+
attribute vec3 iComb;
|
|
351
|
+
attribute vec4 iTint;
|
|
352
|
+
attribute vec4 iCoat;
|
|
353
|
+
attribute vec4 iPat;
|
|
354
|
+
attribute vec4 iSurf;
|
|
355
|
+
attribute vec4 iSkinIndex;
|
|
356
|
+
attribute vec4 iSkinWeight;
|
|
357
|
+
${hairProfile}
|
|
358
|
+
|
|
359
|
+
void main() {
|
|
360
|
+
vec3 sP = iPos, sN = iNrm, sC = iComb;
|
|
361
|
+
dqsSkinW(sP, sN, sC, iSkinIndex, iSkinWeight);
|
|
362
|
+
sP = (modelMatrix * vec4(sP, 1.0)).xyz;
|
|
363
|
+
sN = normalize(mat3(modelMatrix) * sN);
|
|
364
|
+
sC = mat3(modelMatrix) * sC;
|
|
365
|
+
sC = normalize(sC - sN * dot(sC, sN) + 1e-6);
|
|
366
|
+
float L = iCoat.z * uFurScale;
|
|
367
|
+
vec3 V = normalize(cameraPosition - sP);
|
|
368
|
+
float ndv = dot(sN, V);
|
|
369
|
+
// only near the silhouette; everything else is collapsed before rasterisation
|
|
370
|
+
float vis = 1.0 - smoothstep(0.16, 0.42, abs(ndv));
|
|
371
|
+
float m = matId(iTint.a);
|
|
372
|
+
if (uFinMask > 0.5 && m < 0.5) vis *= clamp(1.0 - iSurf.y, 0.0, 1.0);
|
|
373
|
+
// (no fins on the short sleek face fur: they would blur the tear lines and eye rims)
|
|
374
|
+
if (vis < 0.01 || L < uFinMinLen || !hairy(m) || iCoat.w < 0.05) {
|
|
375
|
+
gl_Position = vec4(0.0, 0.0, -2.0, 1.0);
|
|
376
|
+
vFin = vec4(0.0);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
vec3 force = furForce(iPos, sN);
|
|
380
|
+
float y = position.y;
|
|
381
|
+
vec3 tipDir = hairDir(sN, sC, force, L, 1.0);
|
|
382
|
+
vec3 side = cross(tipDir, V);
|
|
383
|
+
side = dot(side, side) > 1e-8 ? normalize(side) : normalize(cross(sN, V) + 1e-4);
|
|
384
|
+
float w = clamp(L * uFinWidth, 0.0028, 0.012) * (abs(m - 4.0) < 0.5 ? 0.45 : 1.0);
|
|
385
|
+
// hairs on the silhouette stand a little prouder than the lying coat
|
|
386
|
+
vec3 wp = sP + hairOffset(sN, sC, force, L * 1.08, y) + sN * (L * 0.08 * y) + side * (position.x * w);
|
|
387
|
+
debugColour(iSkinIndex, iSkinWeight);
|
|
388
|
+
vRest = iPos;
|
|
389
|
+
vRestN = iNrm;
|
|
390
|
+
vRestC = iComb;
|
|
391
|
+
vWorldPos = wp;
|
|
392
|
+
vN = sN;
|
|
393
|
+
vT = hairDir(sN, sC, force, L, y);
|
|
394
|
+
vTint = iTint;
|
|
395
|
+
#ifdef PA_MAT_BORDERS
|
|
396
|
+
vMatW = matOneHot(iTint.a);
|
|
397
|
+
#endif
|
|
398
|
+
vMatF = iTint.a;
|
|
399
|
+
vCoat = iCoat;
|
|
400
|
+
vPat = iPat;
|
|
401
|
+
vSurf = iSurf;
|
|
402
|
+
vFin = vec4(position.x + 0.5, y, vis, y);
|
|
403
|
+
|
|
404
|
+
vec4 worldPosition = vec4(wp, 1.0);
|
|
405
|
+
vec4 mvPosition = viewMatrix * worldPosition;
|
|
406
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
407
|
+
// (the shells write depth, so a coat in front hides what lies behind it, fins included; a fin is
|
|
408
|
+
// depth-tested from a point 0.6 hair lengths nearer the camera (the coat it stands in rises 0.5), so its
|
|
409
|
+
// own coat does not hide it, while a part in front of it (a leg, a belly fringe) still does, and so does
|
|
410
|
+
// the skin of a limb as thick as its hair: a tarantula's leg seen end-on hid the cards round its far side)
|
|
411
|
+
vec4 qF = projectionMatrix * viewMatrix * vec4(wp + V * (L * 0.6), 1.0);
|
|
412
|
+
gl_Position.z = qF.z / qF.w * gl_Position.w;
|
|
413
|
+
vec3 transformedNormal = (viewMatrix * vec4(sN, 0.0)).xyz;
|
|
414
|
+
#include <shadowmap_vertex>
|
|
415
|
+
#include <fog_vertex>
|
|
416
|
+
}
|
|
417
|
+
`;
|
|
418
|
+
|
|
419
|
+
const fragment = /* glsl */ `
|
|
420
|
+
#include <common>
|
|
421
|
+
#include <packing>
|
|
422
|
+
#include <lights_pars_begin>
|
|
423
|
+
#include <shadowmap_pars_fragment>
|
|
424
|
+
#include <fog_pars_fragment>
|
|
425
|
+
${common}
|
|
426
|
+
|
|
427
|
+
float hash12(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); }
|
|
428
|
+
vec2 hash22(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973)); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.xx + p3.yz) * p3.zy); }
|
|
429
|
+
float hash13(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.zyx + 31.32); return fract((p.x + p.y) * p.z); }
|
|
430
|
+
vec3 hash33(vec3 p) { p = fract(p * vec3(0.1031, 0.1030, 0.0973)); p += dot(p, p.yxz + 33.33); return fract((p.xxy + p.yxx) * p.zyx); }
|
|
431
|
+
float vnoise(vec3 p) {
|
|
432
|
+
vec3 i = floor(p), f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
433
|
+
return mix(mix(mix(hash13(i), hash13(i + vec3(1, 0, 0)), f.x), mix(hash13(i + vec3(0, 1, 0)), hash13(i + vec3(1, 1, 0)), f.x), f.y),
|
|
434
|
+
mix(mix(hash13(i + vec3(0, 0, 1)), hash13(i + vec3(1, 0, 1)), f.x), mix(hash13(i + vec3(0, 1, 1)), hash13(i + vec3(1, 1, 1)), f.x), f.y), f.z);
|
|
435
|
+
}
|
|
436
|
+
#ifdef FUR_SHELL
|
|
437
|
+
// strands and clumps of one planar projection (uv on the axis plane axisId): the coverage at shell height h
|
|
438
|
+
// (strand x clump footprint, before the coat's fade), and the strand / clump randoms for the shading
|
|
439
|
+
float strandLayer(vec2 uv, float axisId, float h, float furLen, float dens, float cDens, float clumpK, bool bristle,
|
|
440
|
+
out float sharp, out float sv, out vec4 ch, out float fromC, out float inClump) {
|
|
441
|
+
vec2 cq = uv * cDens;
|
|
442
|
+
vec2 cb = floor(cq) - 1.0;
|
|
443
|
+
float bd2 = 1e9; vec2 cc = cq; vec2 cId = cb;
|
|
444
|
+
if (clumpK > 0.001) for (int j = 0; j < 3; j++) for (int i = 0; i < 3; i++) {
|
|
445
|
+
vec2 id = cb + vec2(float(i), float(j));
|
|
446
|
+
vec2 c = id + 0.5 + (hash22(id + axisId * 57.3) - 0.5) * 0.9;
|
|
447
|
+
vec2 dv = cq - c;
|
|
448
|
+
float dd = dot(dv, dv);
|
|
449
|
+
if (dd < bd2) { bd2 = dd; cc = c; cId = id; }
|
|
450
|
+
}
|
|
451
|
+
// short sleek fur (face, paws, lower legs) barely clumps; the body coat tufts properly
|
|
452
|
+
ch = mix(vec4(0.5), vec4(hash22(cId + 11.3 + axisId * 57.3), hash22(cId + 23.9 + axisId * 57.3)), clumpK);
|
|
453
|
+
float clumpLen = mix(0.6, 1.12, ch.x);
|
|
454
|
+
// strands converge toward the clump centre as they rise: invert that to find the root seen here
|
|
455
|
+
float pull = 0.7 * pow(h, 1.35) * clumpK;
|
|
456
|
+
vec2 rootC = cc + (cq - cc) / max(0.25, 1.0 - pull);
|
|
457
|
+
fromC = length(rootC - cc);
|
|
458
|
+
vec2 q = rootC / cDens * dens;
|
|
459
|
+
vec2 ci = floor(q);
|
|
460
|
+
vec2 f = fract(q) - 0.5;
|
|
461
|
+
vec2 cid = ci + axisId * 131.7;
|
|
462
|
+
vec2 jit = (hash22(cid) - 0.5) * 0.5;
|
|
463
|
+
sv = hash12(cid + 7.1);
|
|
464
|
+
float sl = mix(0.55, 1.0, hash12(cid + 3.3)) * clumpLen;
|
|
465
|
+
float hr = h / sl;
|
|
466
|
+
float radius = 0.5 * (1.0 - 0.8 * min(hr * hr, 1.0)) * (bristle ? 0.3 : 1.0);
|
|
467
|
+
float d = length(f - jit);
|
|
468
|
+
float aa = fwidth(d) * 0.9 + 0.015;
|
|
469
|
+
sharp = (hr > 1.0) ? 0.0 : smoothstep(radius + aa, radius - aa, d);
|
|
470
|
+
// expected coverage when strands shrink below a pixel
|
|
471
|
+
float pLong = clamp((clumpLen - h) / (0.5 * clumpLen), 0.0, 1.0);
|
|
472
|
+
float expected = pLong * 3.14159 * 0.25 * (1.0 - 0.7 * h * h) * 0.9 * (bristle ? 0.06 : 1.0);
|
|
473
|
+
float pix = length(fwidth(q));
|
|
474
|
+
float cov = mix(sharp, expected, smoothstep(0.45, 1.4, pix));
|
|
475
|
+
// outside the clump's shrinking footprint there is no hair at this height: gaps between tufts
|
|
476
|
+
float ea = fwidth(fromC) * 0.8 + 0.03;
|
|
477
|
+
inClump = mix(1.0, smoothstep(0.56 + ea, 0.56 - ea, fromC), clumpK);
|
|
478
|
+
inClump = mix(inClump, 0.8, smoothstep(0.5, 1.5, fwidth(cq.x) + fwidth(cq.y)));
|
|
479
|
+
return cov * inClump;
|
|
480
|
+
}
|
|
481
|
+
#endif
|
|
482
|
+
float lum(vec3 c) { return dot(c, vec3(0.2126, 0.7152, 0.0722)); }
|
|
483
|
+
// fades a procedural detail out once it gets smaller than a pixel (no shimmer at a distance)
|
|
484
|
+
float detailFade(float k) { return 1.0 - smoothstep(0.35, 0.9, length(fwidth(vRest)) * k); }
|
|
485
|
+
// noise stretched along a rest-space direction. The along-flow coordinate is measured from anchors
|
|
486
|
+
// snapped to 4 cm cells, so curving flow lines do not shear the pattern; two anchor lattices offset by
|
|
487
|
+
// half a cell are cross-faded so the cell borders do not show.
|
|
488
|
+
float edgeDist(vec3 f) { f = min(f, 1.0 - f); return min(min(f.x, f.y), f.z); }
|
|
489
|
+
float streak(vec3 P, vec3 Tr, float k, float s) {
|
|
490
|
+
vec3 g = P * 25.0;
|
|
491
|
+
float ea = edgeDist(fract(g)), eb = edgeDist(fract(g + 0.5));
|
|
492
|
+
vec3 aA = floor(g) * 0.04, aB = (floor(g + 0.5) - 0.5) * 0.04;
|
|
493
|
+
float st = k * (1.0 - 1.0 / s);
|
|
494
|
+
float nA = vnoise(P * k - Tr * (dot(P - aA, Tr) * st));
|
|
495
|
+
float nB = vnoise(P * k - Tr * (dot(P - aB, Tr) * st) + 17.3);
|
|
496
|
+
float w = ea * ea / (ea * ea + eb * eb + 1e-6);
|
|
497
|
+
return (mix(nB, nA, w) - 0.5) / sqrt(w * w + (1.0 - w) * (1.0 - w)) + 0.5;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Overlapping shingles (scales, contour feathers): jittered cell centres in bind space, each one an
|
|
501
|
+
// ellipse attached at its front and free toward +comb; where several overlap, the most anterior one
|
|
502
|
+
// lies on top. Returns along (-1 front .. 1 free edge), across (-1..1), edge (ellipse radius 0..1),
|
|
503
|
+
// id, and shadow from the free edges of the scales in front.
|
|
504
|
+
struct Shingle { float along; float across; float edge; float id; float shadow; };
|
|
505
|
+
Shingle shingles(vec3 P, vec3 N, vec3 T, vec3 B, float s, float elong, float wid) {
|
|
506
|
+
Shingle r; r.along = 0.0; r.across = 0.0; r.edge = 1.0; r.id = 0.0; r.shadow = 0.0;
|
|
507
|
+
vec3 g = P / s; vec3 gi = floor(g);
|
|
508
|
+
float ords[27]; float es[27];
|
|
509
|
+
float bestOrd = 1e9;
|
|
510
|
+
for (int k = 0; k < 27; k++) {
|
|
511
|
+
vec3 id = gi + vec3(float(k % 3) - 1.0, float((k / 3) % 3) - 1.0, float(k / 9) - 1.0);
|
|
512
|
+
vec3 c = id + 0.5 + (hash33(id) - 0.5) * 0.75;
|
|
513
|
+
vec3 d = g - c;
|
|
514
|
+
es[k] = 9.0; ords[k] = 1e9;
|
|
515
|
+
if (abs(dot(d, N)) > 0.8) continue;
|
|
516
|
+
float a = dot(d, T), b = dot(d, B);
|
|
517
|
+
float al = (a - 0.35) / (0.85 * elong);
|
|
518
|
+
float e = length(vec2(al, b / wid));
|
|
519
|
+
float ord = dot(c, T) + (hash13(id + 3.1) - 0.5) * 0.4;
|
|
520
|
+
es[k] = e; ords[k] = ord;
|
|
521
|
+
if (e < 1.0 && ord < bestOrd) { bestOrd = ord; r.along = al; r.across = b / wid; r.edge = e; r.id = hash13(id + 7.7); }
|
|
522
|
+
}
|
|
523
|
+
for (int k = 0; k < 27; k++) if (ords[k] < bestOrd - 1e-4) r.shadow = max(r.shadow, smoothstep(1.45, 1.0, es[k]));
|
|
524
|
+
if (bestOrd > 1e8) { r.edge = 1.0; r.shadow = 1.0; }
|
|
525
|
+
return r;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ---------------------------------------------------------------- light accumulation
|
|
529
|
+
vec3 gN, gNc, gT, gV, gAlb, gF0, gSSS, gAT, gDirect;
|
|
530
|
+
float gMemA = 1.0; // fin membrane opacity (material 11)
|
|
531
|
+
float gH, gOccl, gSv, gHair, gRough, gWrap, gCoat, gAniso, gEdge, gLit, gLitW, gTrans;
|
|
532
|
+
vec3 toWorld(vec3 dirView) { return normalize((vec4(dirView, 0.0) * viewMatrix).xyz); }
|
|
533
|
+
vec3 fresnel(vec3 f0, float c) { return f0 + (1.0 - f0) * pow(1.0 - clamp(c, 0.0, 1.0), 5.0); }
|
|
534
|
+
|
|
535
|
+
void shadeLight(vec3 L, vec3 C, float sh) {
|
|
536
|
+
C *= RECIPROCAL_PI;
|
|
537
|
+
float NdL = dot(gN, L);
|
|
538
|
+
vec3 H = normalize(L + gV);
|
|
539
|
+
float NdH = dot(gN, H);
|
|
540
|
+
float wrap = clamp((NdL + 0.4) / 1.4, 0.0, 1.0);
|
|
541
|
+
float w = lum(C);
|
|
542
|
+
gLitW += w; gLit += w * wrap * sh;
|
|
543
|
+
if (gHair > 0.5) {
|
|
544
|
+
float TdL = dot(gT, L);
|
|
545
|
+
float kk = sqrt(max(0.0, 1.0 - TdL * TdL));
|
|
546
|
+
float diff = wrap * mix(1.0, 0.55 + 0.45 * kk, 0.6);
|
|
547
|
+
// fur self shadowing: deep layers only lit when facing the light
|
|
548
|
+
float selfSh = mix(smoothstep(-0.25, 0.55, NdL), 1.0, gH * 0.85 + 0.15);
|
|
549
|
+
float shift = (gSv - 0.5) * 0.25;
|
|
550
|
+
vec3 T1 = normalize(gT + gN * (-0.08 + shift));
|
|
551
|
+
vec3 T2 = normalize(gT + gN * (0.14 + shift));
|
|
552
|
+
float t1 = dot(T1, H), t2 = dot(T2, H);
|
|
553
|
+
float s1 = pow(sqrt(max(0.0, 1.0 - t1 * t1)), 110.0);
|
|
554
|
+
float s2 = pow(sqrt(max(0.0, 1.0 - t2 * t2)), 26.0);
|
|
555
|
+
vec3 spec = (vec3(0.035) * s1 + gAlb * 0.4 * s2) * smoothstep(0.0, 0.6, NdH);
|
|
556
|
+
spec *= smoothstep(-0.1, 0.35, NdL) * gOccl;
|
|
557
|
+
// back-scatter through thin fur at silhouettes
|
|
558
|
+
float backLit = pow(clamp(dot(-L, gV), 0.0, 1.0), 2.5);
|
|
559
|
+
vec3 scatter = (gAlb + 0.04) * backLit * gEdge * 3.2 * (0.25 + 0.75 * gH);
|
|
560
|
+
gDirect += (gAlb * diff * selfSh * gOccl + spec) * C * sh + scatter * C * mix(0.35, 1.0, sh);
|
|
561
|
+
} else {
|
|
562
|
+
float ndl = max(NdL, 0.0);
|
|
563
|
+
// wrapped diffuse; the wrapped part is tinted like light scattered under the skin
|
|
564
|
+
float dw = clamp((NdL + gWrap) / (1.0 + gWrap), 0.0, 1.0) / (1.0 + gWrap);
|
|
565
|
+
vec3 diff = gAlb * (ndl + gSSS * max(dw - ndl, 0.0) * 2.0);
|
|
566
|
+
// normalised Blinn-Phong
|
|
567
|
+
float n = exp2(12.0 * (1.0 - gRough) + 1.0);
|
|
568
|
+
float VdH = max(dot(gV, H), 0.0);
|
|
569
|
+
vec3 F = fresnel(gF0, VdH);
|
|
570
|
+
vec3 spec = F * ((n + 2.0) / 8.0) * pow(max(NdH, 0.0), n) * ndl;
|
|
571
|
+
// anisotropic lobe along gAT (feather barbs, keratin striations, hair-like base fur)
|
|
572
|
+
if (gAniso > 0.0) {
|
|
573
|
+
float th = dot(gAT, H);
|
|
574
|
+
float an = n * 0.5 + 8.0;
|
|
575
|
+
spec += gAniso * F * ((an + 2.0) / 8.0) * pow(sqrt(max(0.0, 1.0 - th * th)), an) * smoothstep(-0.1, 0.3, NdL);
|
|
576
|
+
}
|
|
577
|
+
// clear coat (wet skin, polished keratin, fish scales)
|
|
578
|
+
// (the coat lies over the bumps: it follows the smooth normal)
|
|
579
|
+
if (gCoat > 0.0) { float cH = max(dot(gNc, H), 0.0); spec += gCoat * fresnel(vec3(0.04), VdH) * (802.0 / 8.0) * pow(cH, 800.0) * max(dot(gNc, L), 0.0); }
|
|
580
|
+
// light through thin edges (ear tips, fins, horn tips)
|
|
581
|
+
float backLit = pow(clamp(dot(-L, gV), 0.0, 1.0), 3.0) * gTrans;
|
|
582
|
+
gDirect += (diff + spec) * C * sh + gAlb * gSSS * backLit * C * mix(0.3, 1.0, sh);
|
|
583
|
+
// thin membranes (fins) glow through when lit from behind
|
|
584
|
+
if (gTrans > 0.9) gDirect += gSSS * max(-NdL, 0.0) * C * mix(0.35, 1.0, sh) * 0.9;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
void main() {
|
|
589
|
+
float h = vFin.w;
|
|
590
|
+
float furLen = vCoat.z * uFurScale;
|
|
591
|
+
// (render hint flatMaterial: the triangle's provoking-vertex material; otherwise matIdFrag, which with
|
|
592
|
+
// matBorders splits the triangle through the edge middles; both off: the rounded interpolated id)
|
|
593
|
+
float mat = uFlatMat > 0.5 ? matId(vMatF) : matIdFrag(vTint.a);
|
|
594
|
+
vec4 surf = vSurf;
|
|
595
|
+
|
|
596
|
+
// ---------- strand pattern (planar hashing, axis picked from rest normal)
|
|
597
|
+
vec3 an = abs(vRestN);
|
|
598
|
+
float axisId; vec2 uv;
|
|
599
|
+
if (an.x > an.y && an.x > an.z) { uv = vRest.yz; axisId = 0.0; }
|
|
600
|
+
else if (an.y > an.z) { uv = vRest.xz; axisId = 1.0; }
|
|
601
|
+
else { uv = vRest.xy; axisId = 2.0; }
|
|
602
|
+
float dens = uStrandDensity * mix(1.3, 1.0, smoothstep(0.004, 0.014, furLen));
|
|
603
|
+
|
|
604
|
+
float alpha = 1.0;
|
|
605
|
+
float sv = 0.5; // per-strand random
|
|
606
|
+
vec4 ch = vec4(0.5); // per-clump randoms
|
|
607
|
+
float fromC = 0.0; // distance from the clump centre (0 .. ~0.7)
|
|
608
|
+
float clumpK = 0.0; // clumping strength
|
|
609
|
+
float hazeF = 0.0, hazeNV = 1.0; // shells: the undercoat's share of this fragment's opacity, |N.V|
|
|
610
|
+
bool bristle = abs(mat - 4.0) < 0.5, down = abs(mat - 9.0) < 0.5;
|
|
611
|
+
#ifdef FUR_SHELL
|
|
612
|
+
if (furLen < 0.0006 || !hairy(mat)) discard;
|
|
613
|
+
if (bristle) dens *= 0.2;
|
|
614
|
+
if (down && h > 0.3) discard; // down: a short haze near the skin, not a pelt
|
|
615
|
+
// ---- clumps: nearest strongly-jittered centre (3x3 cell search) -> organic, non-grid tufts
|
|
616
|
+
float cDens = mix(300.0, 118.0, smoothstep(0.004, 0.03, furLen)) * uClumpDensity;
|
|
617
|
+
clumpK = (bristle || down) ? 0.0 : smoothstep(uClumpLen.x, uClumpLen.y, furLen);
|
|
618
|
+
float sharp, inClump;
|
|
619
|
+
alpha = strandLayer(uv, axisId, h, furLen, dens, cDens, clumpK, bristle, sharp, sv, ch, fromC, inClump);
|
|
620
|
+
if (uStrandBlend > 0.0) {
|
|
621
|
+
// (opt-in, render hint strandBlend) the second-largest axis plane cross-faded in near a switch of the largest:
|
|
622
|
+
// half and half where the two components are equal, none beyond uStrandBlend
|
|
623
|
+
float a0 = axisId == 0.0 ? an.x : (axisId == 1.0 ? an.y : an.z);
|
|
624
|
+
float ax2 = axisId == 0.0 ? (an.y > an.z ? 1.0 : 2.0) : (axisId == 1.0 ? (an.x > an.z ? 0.0 : 2.0) : (an.x > an.y ? 0.0 : 1.0));
|
|
625
|
+
float b0 = ax2 == 0.0 ? an.x : (ax2 == 1.0 ? an.y : an.z);
|
|
626
|
+
vec2 uv2 = ax2 == 0.0 ? vRest.yz : (ax2 == 1.0 ? vRest.xz : vRest.xy);
|
|
627
|
+
float w2 = 0.5 * smoothstep(uStrandBlend, 0.0, a0 - b0);
|
|
628
|
+
float sharp2, sv2, fromC2, inClump2; vec4 ch2;
|
|
629
|
+
float alpha2 = strandLayer(uv2, ax2, h, furLen, dens, cDens, clumpK, bristle, sharp2, sv2, ch2, fromC2, inClump2);
|
|
630
|
+
alpha = mix(alpha, alpha2, w2);
|
|
631
|
+
sharp = mix(sharp, sharp2, w2); sv = mix(sv, sv2, w2); ch = mix(ch, ch2, w2); fromC = mix(fromC, fromC2, w2); inClump = mix(inClump, inClump2, w2);
|
|
632
|
+
}
|
|
633
|
+
// down: a soft haze of barbules rather than individual strands
|
|
634
|
+
if (bristle && sv > 0.45) alpha = 0.0; // bristles are sparse
|
|
635
|
+
if (down) alpha = (0.05 + 0.12 * sharp) * (1.0 - h / 0.3);
|
|
636
|
+
// thin out the very outer shells
|
|
637
|
+
alpha *= 1.0 - smoothstep(0.85, 1.0, h) * 0.5;
|
|
638
|
+
// undercoat: the hair the strand grid does not resolve (a pelt has thousands of hairs per cm2, the grid
|
|
639
|
+
// a few dozen strands). An optical depth per unit of coat height, dense at the roots and none at the
|
|
640
|
+
// tips, taken through this layer's slab (1 / uShells of the coat) along the line of sight (Beer-Lambert:
|
|
641
|
+
// the coat's opacity is the same at every shell count, and a coat seen along its lie, a fringe hanging
|
|
642
|
+
// over a leg or down round a folded wing, is as dense as it is). Without it the gaps between strands
|
|
643
|
+
// showed whatever lay under the outer layers: another part's lit skin, a wing, a leg's outline.
|
|
644
|
+
if (!bristle && !down && uUnder > 0.0) {
|
|
645
|
+
hazeNV = abs(dot(normalize(vN), normalize(cameraPosition - vWorldPos)));
|
|
646
|
+
float tau = uUnder * (1.0 - smoothstep(0.3, 0.9, h)) * mix(1.0, inClump, 0.6 * clumpK);
|
|
647
|
+
float fill = 1.0 - exp(-tau / (max(uShells, 1.0) * max(hazeNV, 0.2)));
|
|
648
|
+
float aS = alpha;
|
|
649
|
+
alpha = 1.0 - (1.0 - alpha) * (1.0 - fill);
|
|
650
|
+
hazeF = alpha > 1e-4 ? (1.0 - aS) * fill / alpha : 0.0;
|
|
651
|
+
}
|
|
652
|
+
// cross-fade of two overlapping surfaces (vCoat.w): each takes its share of the coat's optical depth, so the
|
|
653
|
+
// pair composites to one whole coat. Scaling the opacity instead left the middle of the band a quarter thinner
|
|
654
|
+
// (two half-opaque layers cover 75 %): a pale or dark line where a head and a neck surface meet (a dog's ruff)
|
|
655
|
+
alpha = vCoat.w > 1e-4 ? 1.0 - pow(max(1.0 - alpha, 1e-4), vCoat.w) : 0.0;
|
|
656
|
+
alpha *= vFin.z;
|
|
657
|
+
if (alpha < 0.02) discard;
|
|
658
|
+
#elif defined( FUR_FIN )
|
|
659
|
+
// ---- a fin carries a handful of tapered strands side by side
|
|
660
|
+
float nS = bristle ? 2.0 : 5.0;
|
|
661
|
+
float u = vFin.x * nS;
|
|
662
|
+
float y = vFin.y;
|
|
663
|
+
float cov = 0.0;
|
|
664
|
+
float i0 = floor(u);
|
|
665
|
+
vec2 fid = vRest.xy * 1733.1 + vRest.z * 917.3;
|
|
666
|
+
float uw = fwidth(u) * 0.8 + 0.02;
|
|
667
|
+
for (int k = -1; k <= 1; k++) {
|
|
668
|
+
float i = i0 + float(k);
|
|
669
|
+
if (i < 0.0 || i >= nS) continue;
|
|
670
|
+
vec2 hid = fid + vec2(i * 7.13, 0.0);
|
|
671
|
+
float r1 = hash12(hid), r2 = hash12(hid + 3.7), r3 = hash12(hid + 9.1);
|
|
672
|
+
float slen = mix(0.38, 1.1, r1 * r1 * (3.0 - 2.0 * r1));
|
|
673
|
+
if (y > slen) continue;
|
|
674
|
+
float cx = i + 0.5 + (r3 - 0.5) * 0.6 + (r2 - 0.5) * 1.4 * y * y;
|
|
675
|
+
float wdt = 0.2 * (1.0 - y / slen) + 0.03;
|
|
676
|
+
float c = smoothstep(wdt + uw, wdt - uw, abs(u - cx));
|
|
677
|
+
if (c > cov) { cov = c; sv = r2; ch = vec4(r1, r3, r2, r1 * r3); }
|
|
678
|
+
}
|
|
679
|
+
alpha = cov * vFin.z * vCoat.w * (1.0 - 0.35 * smoothstep(0.6, 1.0, y)) * uFinFade;
|
|
680
|
+
// the root half of a long hair's card lies inside its own coat, which the shells draw: drawn there too it
|
|
681
|
+
// traced the base surface's outline, deep inside the coat, as a dark line (a long tail's core, a leg
|
|
682
|
+
// under a fringe); the card shows the hair where it leaves the coat
|
|
683
|
+
alpha *= mix(1.0, smoothstep(0.08, 0.5, y), smoothstep(0.008, 0.03, furLen));
|
|
684
|
+
if (down) alpha *= 0.5;
|
|
685
|
+
if (alpha < 0.02) discard;
|
|
686
|
+
#else
|
|
687
|
+
// overlap band of two regions: each surface keeps its half, plus the sunk skirt past the midline
|
|
688
|
+
if (vCoat.w < uSeam.y) discard;
|
|
689
|
+
#endif
|
|
690
|
+
|
|
691
|
+
// ---------- frames: rest space (for procedural detail) and world space (for lighting)
|
|
692
|
+
vec3 Nw = normalize(vN);
|
|
693
|
+
vec3 Tw = dot(vT, vT) > 1e-12 ? normalize(vT) : Nw;
|
|
694
|
+
vec3 V = normalize(cameraPosition - vWorldPos);
|
|
695
|
+
vec3 N = Nw;
|
|
696
|
+
vec3 T = Tw;
|
|
697
|
+
vec3 Nr = normalize(vRestN);
|
|
698
|
+
vec3 Tr = vRestC - Nr * dot(vRestC, Nr);
|
|
699
|
+
Tr = dot(Tr, Tr) > 1e-10 ? normalize(Tr) : normalize(cross(Nr, vec3(0.31, 0.83, 0.47)));
|
|
700
|
+
vec3 Br = cross(Nr, Tr);
|
|
701
|
+
vec3 Tb = normalize(Tw - Nw * dot(Tw, Nw) + 1e-6);
|
|
702
|
+
vec3 Bw = cross(Nw, Tb);
|
|
703
|
+
|
|
704
|
+
// ---------- albedo: base colour, pattern, marks
|
|
705
|
+
vec3 base = vTint.rgb;
|
|
706
|
+
bool furBase = false;
|
|
707
|
+
// hair-level raggedness of pattern edges: whole tufts are in or out, single strands stray
|
|
708
|
+
float jitS = (sv - 0.5) * 0.0036 + (ch.y - 0.5) * 0.0042;
|
|
709
|
+
#if !defined( FUR_SHELL ) && !defined( FUR_FIN )
|
|
710
|
+
furBase = mat < 0.5;
|
|
711
|
+
if (furBase) {
|
|
712
|
+
// no strands here: jitter pattern edges with hair-flow streaks instead
|
|
713
|
+
float st = streak(vRest, Tr, 900.0, 6.0) * 0.6 + streak(vRest + 7.1, Tr, 300.0, 4.0) * 0.4;
|
|
714
|
+
jitS = (st - 0.5) * 0.014 * detailFade(300.0);
|
|
715
|
+
} else jitS = 0.0;
|
|
716
|
+
#endif
|
|
717
|
+
float pw = max(fwidth(vCoat.x), 0.0011);
|
|
718
|
+
// (a negative pattern intensity is a flight-feather vane's bar count, see the vane branch below)
|
|
719
|
+
float spot = smoothstep(pw, -pw, vCoat.x + jitS) * max(vPat.a, 0.0);
|
|
720
|
+
// facial marks (tear lines, eye rims, lips, tail rings) are crisp lines: almost no strand jitter
|
|
721
|
+
float mw = max(fwidth(vCoat.y), 0.0007);
|
|
722
|
+
float mark = smoothstep(mw, -mw, vCoat.y + jitS * 0.08);
|
|
723
|
+
vec3 alb = mix(base, vPat.rgb, spot);
|
|
724
|
+
alb = mix(alb, uMarkColor, mark);
|
|
725
|
+
|
|
726
|
+
// ---------- hair: strand / tuft shade variation, agouti banding, root darkening
|
|
727
|
+
#if defined( FUR_SHELL ) || defined( FUR_FIN )
|
|
728
|
+
if (bristle) alb = mix(base * 1.1 + 0.03, alb, 0.3);
|
|
729
|
+
if (down) alb = mix(alb, vec3(lum(alb)) * 1.2 + 0.02, 0.35);
|
|
730
|
+
alb *= (0.8 + 0.4 * sv) * mix(0.86, 1.12, ch.z);
|
|
731
|
+
// agouti: light roots, a dark band just below the tips (grizzle)
|
|
732
|
+
float agouti = surf.z;
|
|
733
|
+
if (agouti > 0.0) {
|
|
734
|
+
float bc = 0.74 + (sv - 0.5) * 0.16;
|
|
735
|
+
float band = smoothstep(0.14, 0.02, abs(h - bc)) * step(0.35, fract(sv * 7.3));
|
|
736
|
+
alb = mix(alb, alb * 0.12 + vec3(0.006, 0.005, 0.004), band * agouti);
|
|
737
|
+
}
|
|
738
|
+
// undercoat: lighter, greyer roots
|
|
739
|
+
alb = mix(alb, mix(alb, vec3(lum(alb)) * 1.35, 0.6), surf.w * (1.0 - smoothstep(0.1, 0.5, h)));
|
|
740
|
+
// root darkening / tip lightening
|
|
741
|
+
// (sparse bristles on bare skin: isolated hairs lit like the skin they grow from, no dark roots)
|
|
742
|
+
alb *= bristle ? mix(0.9, 1.05, h) : mix(0.42, 1.08, pow(h, 0.55));
|
|
743
|
+
#endif
|
|
744
|
+
|
|
745
|
+
// ---------- per material surface (base pass)
|
|
746
|
+
float occl = 1.0;
|
|
747
|
+
gHair = 1.0; gRough = 0.5; gWrap = 0.0; gCoat = 0.0; gAniso = 0.0; gTrans = 0.0;
|
|
748
|
+
gF0 = vec3(0.04); gSSS = vec3(0.0); gAT = Tb;
|
|
749
|
+
float aoAmb = 1.0;
|
|
750
|
+
vec3 pert = vec3(0.0); // normal perturbation in (T, B, N) of the rest frame
|
|
751
|
+
#if !defined( FUR_SHELL ) && !defined( FUR_FIN )
|
|
752
|
+
gHair = 0.0;
|
|
753
|
+
float fd = 1.0;
|
|
754
|
+
// crowd tier: no eye meshes, the eyes are painted into the base
|
|
755
|
+
float painted = 0.0; vec3 eyeAlb = vec3(0.0);
|
|
756
|
+
if (uPaintEyes > 0.5) {
|
|
757
|
+
for (int i = 0; i < ${MAX_PAINTED_EYES}; i++) {
|
|
758
|
+
if (uEyes[i].w <= 0.0) continue;
|
|
759
|
+
float de = length(vRest - uEyes[i].xyz) / uEyes[i].w;
|
|
760
|
+
if (de < 1.25) { painted = 1.0; eyeAlb = mix(uEyeTint[i], vec3(0.004), smoothstep(0.55, 0.3, de)); }
|
|
761
|
+
}
|
|
762
|
+
if (painted > 0.5) mat = 2.0;
|
|
763
|
+
}
|
|
764
|
+
if (mat < 0.5) {
|
|
765
|
+
// fur seen from the base
|
|
766
|
+
gHair = 1.0;
|
|
767
|
+
if (uShellsOn > 0.5) {
|
|
768
|
+
occl = 0.32; // deep in the coat, under the shells
|
|
769
|
+
} else {
|
|
770
|
+
// no shells: hair-flow streaks, per-lock shade and an anisotropic sheen make it read as fur
|
|
771
|
+
alb *= uBaseTone;
|
|
772
|
+
fd = detailFade(1200.0);
|
|
773
|
+
float fd2 = detailFade(450.0), fd3 = detailFade(140.0);
|
|
774
|
+
float s1 = streak(vRest, Tr, 1200.0, 8.0), s2 = streak(vRest + 3.7, Tr, 450.0, 5.0), s3 = streak(vRest + 9.2, Tr, 140.0, 3.0);
|
|
775
|
+
sv = mix(0.5, s1, fd);
|
|
776
|
+
// strands, locks and tufts: brighter crests, darker partings
|
|
777
|
+
alb *= mix(1.0, 0.62 + 0.76 * s1, fd) * mix(1.0, 0.72 + 0.56 * s2, fd2) * mix(1.0, 0.8 + 0.4 * s3, fd3);
|
|
778
|
+
alb *= mix(1.0, 1.0 - 0.6 * smoothstep(0.5, 0.75, s2), surf.z * fd2); // agouti speckle
|
|
779
|
+
occl = 0.8;
|
|
780
|
+
h = 0.6;
|
|
781
|
+
ch = vec4(s3, s2, 0.5, s2);
|
|
782
|
+
pert = vec3(0.0, ((s2 - 0.5) * 0.6 + (s3 - 0.5) * 0.5) * fd2, 0.0);
|
|
783
|
+
}
|
|
784
|
+
} else if (mat < 1.5) {
|
|
785
|
+
// nose leather: pebbled cobbles with dark creases, satin sheen
|
|
786
|
+
float k = 1.0 / 0.0007;
|
|
787
|
+
fd = detailFade(k);
|
|
788
|
+
vec3 g = vRest * k; vec3 gi = floor(g);
|
|
789
|
+
float d1 = 9.0, d2 = 9.0; vec3 best = vec3(0.0);
|
|
790
|
+
for (int kk = 0; kk < 27; kk++) {
|
|
791
|
+
vec3 o = vec3(float(kk % 3) - 1.0, float((kk / 3) % 3) - 1.0, float(kk / 9) - 1.0);
|
|
792
|
+
vec3 c = gi + o + hash33(gi + o);
|
|
793
|
+
vec3 dv = g - c; dv -= Nr * dot(dv, Nr) * 0.7;
|
|
794
|
+
float dd = length(dv);
|
|
795
|
+
if (dd < d1) { d2 = d1; d1 = dd; best = dv; } else if (dd < d2) d2 = dd;
|
|
796
|
+
}
|
|
797
|
+
float crease = smoothstep(0.18, 0.0, d2 - d1) * fd;
|
|
798
|
+
// (leatherTint: pink / brick leather in the tint colour, e.g. a cat's nose and pads; the tint keeps the
|
|
799
|
+
// material ids next to fur 0 -> 1, where a pink bare-skin nose (4) interpolated through 1, 2, 3 at its rim)
|
|
800
|
+
// (noseTint 1, the wolf's pale rims and painted septum, is the same blend at full strength: both take
|
|
801
|
+
// the coat's per-vertex colour vTint, so the larger of the two wins)
|
|
802
|
+
// (uLeatherRelief scales the pebbling: crease darkening, cobble shade and bump; 1 = the default)
|
|
803
|
+
crease *= uLeatherRelief;
|
|
804
|
+
alb = mix(vec3(0.05, 0.042, 0.04), vTint.rgb, max(uLeatherTint, uNoseTint)) * (1.0 + (0.4 * hash13(floor(g)) - 0.2) * uLeatherRelief) * (1.0 - 0.6 * crease);
|
|
805
|
+
pert = vec3(dot(best, Tr), dot(best, Br), 0.0) * 0.5 * fd * uLeatherRelief;
|
|
806
|
+
// (surf.y < 0: the roughness, -surf.y, of a drier, matte leather per vertex (bear); unset (>= 0) takes the
|
|
807
|
+
// render hint noseRoughness, satin 0.42 by default)
|
|
808
|
+
gRough = surf.y < 0.0 ? -surf.y : uNoseRough; gF0 = vec3(0.035); gWrap = 0.2; gSSS = vec3(0.3, 0.1, 0.08);
|
|
809
|
+
aoAmb = 1.0 - 0.5 * crease;
|
|
810
|
+
// (render hint noseMarks: marks on the leather are openings, the nostrils: dark, no sheen from inside)
|
|
811
|
+
float nm = mark * uNoseMarks;
|
|
812
|
+
alb = mix(alb, uMarkColor, nm); pert *= 1.0 - nm; gF0 *= 1.0 - nm; aoAmb *= 1.0 - 0.6 * nm;
|
|
813
|
+
} else if (mat < 2.5) {
|
|
814
|
+
// bare dark skin (lids, lips): smooth, slightly moist
|
|
815
|
+
alb = vec3(0.012, 0.01, 0.01);
|
|
816
|
+
gRough = 0.32; gWrap = 0.25; gSSS = vec3(0.5, 0.15, 0.1); gF0 = vec3(0.035);
|
|
817
|
+
if (painted > 0.5) { alb = eyeAlb; gRough = 0.1; gCoat = 1.0; gWrap = 0.0; }
|
|
818
|
+
} else if (mat < 3.5) {
|
|
819
|
+
// mouth: wet, pink-red mucosa
|
|
820
|
+
alb = vec3(0.25, 0.08, 0.08);
|
|
821
|
+
gRough = 0.22; gWrap = 0.6; gSSS = vec3(0.9, 0.25, 0.2); gCoat = 0.5;
|
|
822
|
+
} else if (mat < 4.5) {
|
|
823
|
+
// bare skin: colour from rgb, soft subsurface wrap, fine pores and creases
|
|
824
|
+
float k = 1.0 / 0.0009;
|
|
825
|
+
fd = detailFade(k);
|
|
826
|
+
float n1 = vnoise(vRest * k), n2 = vnoise(vRest * k * 0.23 + 5.1);
|
|
827
|
+
float pore = smoothstep(0.72, 0.9, n1) * fd;
|
|
828
|
+
alb *= (1.0 - 0.25 * pore) * (0.92 + 0.16 * n2);
|
|
829
|
+
pert = vec3((n1 - 0.5) * 0.12, (vnoise(vRest * k + 9.3) - 0.5) * 0.12, 0.0) * fd;
|
|
830
|
+
gRough = mix(0.62, 0.3, surf.x); gWrap = uSkinScatter.w; gSSS = uSkinScatter.rgb; gTrans = uSkinTrans.x + (uSkinTrans.y > 0.0 ? uSkinTrans.y * surf.y : 0.0);
|
|
831
|
+
aoAmb = 1.0 - 0.3 * pore;
|
|
832
|
+
} else if (mat < 5.5) {
|
|
833
|
+
// keratin (hoof, horn, beak, claw): glossy, growth striations running along the comb
|
|
834
|
+
float k = 1.0 / 0.0006;
|
|
835
|
+
fd = detailFade(k);
|
|
836
|
+
float fd2 = detailFade(k * 0.2);
|
|
837
|
+
float s1 = streak(vRest, Tr, k, 14.0), s2 = streak(vRest + 1.3, Tr, k * 0.2, 10.0);
|
|
838
|
+
float rings = 0.5 + 0.5 * sin(dot(vRest, Tr) * 700.0 + s2 * 4.0);
|
|
839
|
+
alb *= mix(1.0, 0.8 + 0.4 * s1, fd) * mix(1.0, (0.7 + 0.6 * s2) * (0.92 + 0.08 * rings), fd2);
|
|
840
|
+
pert = vec3(0.0, (s1 - 0.5) * 0.35 * fd + (s2 - 0.5) * 0.5 * fd2, 0.0);
|
|
841
|
+
// (surf.x < 0, opt-in: duller than gloss 0, -1 matte: the highlight and the grazing sheen are the same on any
|
|
842
|
+
// albedo, and on dark horn they read as chrome; 0 keeps the default 0.6)
|
|
843
|
+
float gl = surf.x > 0.0 ? surf.x : (surf.x < 0.0 ? 0.0 : 0.6);
|
|
844
|
+
float matte = clamp(-surf.x, 0.0, 1.0);
|
|
845
|
+
gRough = mix(mix(0.55, 0.12, gl), 0.85, matte); gCoat = 0.25 * gl; gAniso = 0.6 * (1.0 - matte); gAT = Tb; gWrap = 0.15;
|
|
846
|
+
gSSS = alb * 0.8 + 0.02; gTrans = 0.35;
|
|
847
|
+
} else if (mat < 6.5) {
|
|
848
|
+
// scales laid out along the comb: overlapping, domed, dark edges, optional keel and iridescence
|
|
849
|
+
float sz = surf.y > 0.0 ? surf.y : 0.004;
|
|
850
|
+
fd = detailFade(1.0 / sz * 1.5);
|
|
851
|
+
if (surf.z < -0.5) {
|
|
852
|
+
// ventral scutes (snakes): plates spanning the belly, one per surf.y along the comb, each one's
|
|
853
|
+
// free (posterior) edge lying over the next: a bright lip, a dark gap, then its shadow
|
|
854
|
+
float a = dot(vRest, Tr) / sz, f = fract(a);
|
|
855
|
+
float gap = smoothstep(0.035, 0.0, f) + smoothstep(0.965, 1.0, f);
|
|
856
|
+
float lip = smoothstep(0.78, 0.94, f) * (1.0 - smoothstep(0.94, 0.97, f));
|
|
857
|
+
float sh = smoothstep(0.3, 0.035, f);
|
|
858
|
+
alb *= mix(1.0, (0.92 + 0.12 * hash13(vec3(floor(a), 1.7, 3.1))) * (1.0 - 0.55 * gap) * (1.0 + 0.12 * lip) * (1.0 - 0.28 * sh), fd);
|
|
859
|
+
pert = vec3(-0.3 - 0.35 * (f - 0.5), 0.0, 0.0) * fd;
|
|
860
|
+
float gl = surf.x > 0.0 ? surf.x : 0.6;
|
|
861
|
+
gRough = mix(0.6, 0.16, gl); gCoat = 0.4 * gl; gWrap = 0.12;
|
|
862
|
+
aoAmb = 1.0 - 0.4 * max(gap, sh) * fd;
|
|
863
|
+
} else if (uDenticle > 0.0) {
|
|
864
|
+
// shark skin (render hint denticles): tiny keeled denticles in rows along the comb. Single
|
|
865
|
+
// denticles, rows of them and broader streaks fade out in turn with distance; the ridges tilt the
|
|
866
|
+
// normal across the comb, so the light breaks into a fine grain along the body. Matte: a soft,
|
|
867
|
+
// broad sheen stretched across the ridges (anisotropic lobe), no clear coat.
|
|
868
|
+
float kd = 1.0 / sz;
|
|
869
|
+
float fd2 = detailFade(kd * 0.25), fd3 = detailFade(kd * 0.06);
|
|
870
|
+
float g1 = streak(vRest, Tr, kd, 6.0), g2 = streak(vRest + 3.1, Tr, kd * 0.25, 5.0), g3 = streak(vRest + 8.7, Tr, kd * 0.06, 4.0);
|
|
871
|
+
float grain = mix(1.0, 0.8 + 0.4 * g1, fd) * mix(1.0, 0.86 + 0.28 * g2, fd2) * mix(1.0, 0.93 + 0.14 * g3, fd3);
|
|
872
|
+
alb *= mix(1.0, grain, uDenticle);
|
|
873
|
+
pert = vec3(0.0, (g1 - 0.5) * 0.5 * fd + (g2 - 0.5) * 0.35 * fd2 + (g3 - 0.5) * 0.18 * fd3, 0.0) * uDenticle;
|
|
874
|
+
float gl = surf.x > 0.0 ? surf.x : 0.2;
|
|
875
|
+
gRough = mix(0.78, 0.38, gl); gCoat = 0.0; gWrap = 0.3; gSSS = alb * 0.6;
|
|
876
|
+
gAniso = 0.55 * uDenticle; gAT = Tb;
|
|
877
|
+
aoAmb = 1.0 - 0.18 * (1.0 - g2) * fd2 * uDenticle;
|
|
878
|
+
} else {
|
|
879
|
+
// surf.w < 0: lower relief and edge contrast (smooth plates, clean satin scales), -1 = flat
|
|
880
|
+
float scC = surf.w < 0.0 ? clamp(1.0 + surf.w, 0.0, 1.0) : 1.0;
|
|
881
|
+
Shingle S = shingles(vRest, Nr, Tr, Br, sz * 0.62, 1.25, 0.7);
|
|
882
|
+
// free edge: a thin bright lip, then the dark gap under it; base of the scale in shadow
|
|
883
|
+
float rim = smoothstep(0.8, 0.98, S.edge);
|
|
884
|
+
float lip = smoothstep(0.62, 0.8, S.edge) * (1.0 - rim) * step(0.0, S.along);
|
|
885
|
+
float keel = surf.z * exp(-S.across * S.across / 0.02);
|
|
886
|
+
alb *= mix(1.0, (0.82 + 0.3 * S.id) * (1.0 - 0.6 * rim) * (1.0 + 0.25 * lip) * (1.0 - 0.7 * S.shadow) * mix(0.72, 1.0, smoothstep(-0.8, 0.4, S.along)) * (1.0 + 0.35 * keel), fd * uScaleEdge * scC);
|
|
887
|
+
// the scale rises toward its free edge and is domed across; the keel is a ridge down the middle
|
|
888
|
+
pert = vec3(-0.35 - 0.3 * S.along, -S.across * 0.45 - sign(S.across) * keel * 0.6, 0.0) * fd * scC * (0.35 + 0.65 * uScaleEdge);
|
|
889
|
+
float gl = surf.x > 0.0 ? surf.x : 0.45;
|
|
890
|
+
gRough = mix(0.6, 0.18, gl); gCoat = 0.35 * gl; gWrap = 0.1;
|
|
891
|
+
aoAmb = 1.0 - 0.5 * S.shadow * fd * scC * uScaleEdge;
|
|
892
|
+
if (surf.w > 0.0) {
|
|
893
|
+
float nv = clamp(dot(Nw, V), 0.0, 1.0);
|
|
894
|
+
vec3 film = 0.5 + 0.5 * cos(6.2831 * (nv * 1.6 + S.id * 0.25 + vec3(0.0, 0.33, 0.67)));
|
|
895
|
+
gF0 = mix(vec3(0.04), film * 0.35, surf.w);
|
|
896
|
+
alb = mix(alb, alb * 0.8 + film * lum(alb) * 0.25, surf.w * 0.4);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
} else if (mat < 7.5) {
|
|
900
|
+
// wet skin (frog): granular bumps, glossy clear coat, strong subsurface
|
|
901
|
+
float sz = surf.y > 0.0 ? surf.y : 0.0015;
|
|
902
|
+
float k = 1.0 / sz;
|
|
903
|
+
fd = detailFade(k);
|
|
904
|
+
float b1 = vnoise(vRest * k), b2 = vnoise(vRest * k * 2.7 + 3.3);
|
|
905
|
+
float bump = smoothstep(0.45, 0.85, b1) * 0.7 + b2 * 0.3;
|
|
906
|
+
float e = 0.12;
|
|
907
|
+
float bx = vnoise((vRest + Tr * sz * e) * k), by = vnoise((vRest + Br * sz * e) * k);
|
|
908
|
+
pert = vec3(-(bx - b1), -(by - b1), 0.0) / e * 0.18 * fd;
|
|
909
|
+
alb *= mix(1.0, 0.85 + 0.3 * bump, fd);
|
|
910
|
+
float gl = surf.x > 0.0 ? surf.x : 0.85;
|
|
911
|
+
gRough = mix(0.5, 0.2, gl); gCoat = gl; gWrap = 0.6; gSSS = vec3(0.6, 0.75, 0.4) * 0.8; gTrans = 0.6;
|
|
912
|
+
} else if (mat < 8.5) {
|
|
913
|
+
// chitin: hard, glossy, dark, fine segment sutures and micro-pitting. A negative feature scale
|
|
914
|
+
// (surf.y < 0) means smooth cuticle without sutures (spiders), |surf.y| = pitting scale
|
|
915
|
+
float sz = surf.y > 0.0 ? surf.y : 0.003;
|
|
916
|
+
float pitK = surf.y < 0.0 ? 1.0 / max(-surf.y, 1e-5) : 2500.0;
|
|
917
|
+
fd = detailFade(surf.y < 0.0 ? pitK : 1.0 / 0.0004);
|
|
918
|
+
float seg = fract(dot(vRest, Tr) / sz + streak(vRest, Tr, 90.0, 3.0) * 0.3);
|
|
919
|
+
float suture = surf.y < 0.0 ? 0.0 : smoothstep(0.1, 0.0, seg) + smoothstep(0.93, 1.0, seg);
|
|
920
|
+
float pit = vnoise(vRest * pitK);
|
|
921
|
+
alb *= (1.0 - 0.7 * suture) * mix(1.0, 0.85 + 0.3 * pit, fd);
|
|
922
|
+
pert = vec3((seg - 0.5) * 0.5 + (pit - 0.5) * 0.15 * fd, 0.0, 0.0);
|
|
923
|
+
float gl = surf.x > 0.0 ? surf.x : 0.75;
|
|
924
|
+
gRough = mix(0.5, 0.15, gl); gCoat = 0.4 * gl; gWrap = 0.05;
|
|
925
|
+
gF0 = mix(vec3(0.045), vec3(0.08, 0.06, 0.03) + alb, surf.w);
|
|
926
|
+
aoAmb = 1.0 - 0.5 * suture;
|
|
927
|
+
} else if (mat > 10.5) {
|
|
928
|
+
// fin membrane (11): thin translucent skin stretched between bony rays. surf.y = ray phase (rays
|
|
929
|
+
// at integer values), surf.z = position along the ray (0 base .. 1 margin), surf.w = opacity.
|
|
930
|
+
// Rays are jointed and fork toward the margin; the membrane between them is clearer.
|
|
931
|
+
float along = clamp(surf.z, 0.0, 1.2);
|
|
932
|
+
float fork = smoothstep(0.5, 0.62, along);
|
|
933
|
+
float pr = surf.y * (1.0 + fork);
|
|
934
|
+
float dRay = abs(fract(pr + 0.5) - 0.5);
|
|
935
|
+
float fwR = fwidth(pr) * 0.8 + 1e-4;
|
|
936
|
+
float rw = mix(0.16, 0.1, along) * mix(1.0, 0.7, fork);
|
|
937
|
+
float rayFade = 1.0 - smoothstep(0.35, 0.7, fwR);
|
|
938
|
+
float ray = smoothstep(rw + fwR, rw - fwR, dRay) * rayFade;
|
|
939
|
+
float seg = abs(fract(along * 16.0 + surf.y * 0.37) - 0.5);
|
|
940
|
+
float joint = smoothstep(0.06, 0.0, abs(seg - 0.47)) * ray * detailFade(1.0 / 0.0015);
|
|
941
|
+
alb = mix(alb, alb * 0.72 + vec3(0.004), ray * 0.8);
|
|
942
|
+
alb = mix(alb, alb * 1.25 + 0.02, joint * 0.5);
|
|
943
|
+
gMemA = clamp(mix(surf.w, min(1.0, surf.w + 0.38), ray) * (1.0 - 0.3 * smoothstep(0.85, 1.05, along)), 0.0, 1.0);
|
|
944
|
+
pert = vec3(0.0, sign(fract(pr + 0.5) - 0.5) * ray * 0.22 * rayFade, 0.0);
|
|
945
|
+
float gl = surf.x > 0.0 ? surf.x : 0.7;
|
|
946
|
+
gRough = mix(0.55, 0.22, gl); gCoat = 0.3 * gl; gWrap = 0.45; gSSS = alb * 0.8 + 0.01; gTrans = 1.0;
|
|
947
|
+
} else if (mat < 9.5) {
|
|
948
|
+
// feathers: contour-feather shingles with a rachis, barbs and a barb-anisotropic sheen
|
|
949
|
+
float sz = surf.y > 0.0 ? surf.y : 0.012;
|
|
950
|
+
fd = detailFade(1.0 / sz * 2.0);
|
|
951
|
+
Shingle S = shingles(vRest, Nr, Tr, Br, sz * 0.55, 1.35, 0.8);
|
|
952
|
+
float rachis = smoothstep(0.06, 0.0, abs(S.across)) * smoothstep(1.0, 0.2, S.along);
|
|
953
|
+
float barbPh = (abs(S.across) * 0.9 - S.along * 0.55) * 22.0;
|
|
954
|
+
float fdB = detailFade(1.0 / sz * 30.0);
|
|
955
|
+
float barbs = (0.5 + 0.5 * sin(barbPh * 6.2831)) * fdB;
|
|
956
|
+
float tipFray = smoothstep(0.85, 1.0, S.edge) * smoothstep(0.0, 0.6, S.along);
|
|
957
|
+
if (surf.z < 0.0) {
|
|
958
|
+
// per-feather markings (poultry and game birds): surf.z = -(mode + amount), marking colour in
|
|
959
|
+
// the pattern colour (its intensity unused): 0 lacing (a band along the free edge), 1 bars across
|
|
960
|
+
// the feather, 2 a shaft streak down the middle, 3 spangle (a dark bar and a light tip). Far away
|
|
961
|
+
// the marking blends to its mean coverage instead of vanishing.
|
|
962
|
+
float mm = -surf.z, mode = floor(mm + 1e-3), amt = clamp(mm - mode, 0.0, 1.0);
|
|
963
|
+
float mk = 0.0, mMean = 0.3;
|
|
964
|
+
if (mode < 0.5) mk = smoothstep(0.6, 0.78, S.edge) * smoothstep(-0.4, 0.0, S.along);
|
|
965
|
+
else if (mode < 1.5) { mk = smoothstep(-0.3, 0.3, sin((S.along * 2.3 + S.id * 0.35) * 6.2831)); mMean = 0.5; }
|
|
966
|
+
else if (mode < 2.5) { mk = smoothstep(0.4, 0.16, abs(S.across)) * smoothstep(-0.7, 0.1, S.along); mMean = 0.18; }
|
|
967
|
+
else {
|
|
968
|
+
float tip = smoothstep(0.52, 0.68, S.along) * smoothstep(0.98, 0.85, S.edge);
|
|
969
|
+
float bar = smoothstep(0.15, 0.3, S.along) * (1.0 - smoothstep(0.5, 0.6, S.along));
|
|
970
|
+
alb = mix(alb, alb * 0.1, mix(0.15, bar, fd) * amt);
|
|
971
|
+
mk = tip; mMean = 0.15;
|
|
972
|
+
}
|
|
973
|
+
alb = mix(alb, vPat.rgb, mix(mMean, mk, fd) * amt);
|
|
974
|
+
}
|
|
975
|
+
alb *= mix(1.0, (0.85 + 0.25 * S.id) * (1.0 - 0.35 * S.shadow) * (0.9 + 0.2 * barbs) * (1.0 - 0.3 * tipFray), fd * uFeatherEdge);
|
|
976
|
+
alb = mix(alb, alb * 1.25 + 0.03, rachis * 0.6 * fd);
|
|
977
|
+
pert = vec3(-0.25 - 0.25 * S.along, -S.across * 0.3, 0.0) * fd;
|
|
978
|
+
vec3 barbDir = normalize(Tr * 0.55 + Br * sign(S.across) * 0.84);
|
|
979
|
+
gAT = normalize(Tb * dot(barbDir, Tr) + Bw * dot(barbDir, Br));
|
|
980
|
+
gAniso = 0.5 * fd; gRough = mix(0.8, 0.45, surf.x); gWrap = 0.3; gSSS = alb * 0.5; gTrans = 0.3;
|
|
981
|
+
aoAmb = 1.0 - 0.45 * S.shadow * fd;
|
|
982
|
+
if (surf.w > 0.0) {
|
|
983
|
+
// structural colour (crow / starling gloss): a thin-film tint of the specular lobe
|
|
984
|
+
float nv = clamp(dot(Nw, V), 0.0, 1.0);
|
|
985
|
+
vec3 film = mix(vec3(0.42, 0.3, 1.0), vec3(0.12, 0.62, 0.72), pow(1.0 - nv, 1.4) + (S.id - 0.5) * 0.25);
|
|
986
|
+
gF0 = mix(vec3(0.04), film * 0.14, surf.w);
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
// flight-feather vane (feather cards, core/build/featherCards.js): surf = [gloss, iridescence,
|
|
990
|
+
// across (-1 outer edge .. 0 rachis .. 1 inner edge), along (0 root .. 1 tip)], vCoat.x = signed
|
|
991
|
+
// metres from the rachis. Rachis, barbs leaving it toward the tip, barb splits, worn edges, a
|
|
992
|
+
// barb-anisotropic sheen, structural colour and light through the thin vane.
|
|
993
|
+
float across = surf.z, along = surf.w, ac = abs(across);
|
|
994
|
+
float xm = abs(vCoat.x), ym = dot(vRest, Tr), sgn = vCoat.x < 0.0 ? -1.0 : 1.0;
|
|
995
|
+
float aw = fwidth(across) + 1e-4;
|
|
996
|
+
float rw = mix(0.07, 0.018, along);
|
|
997
|
+
float rach = smoothstep(rw + aw, rw - aw, ac) * (1.0 - smoothstep(0.8, 1.0, along));
|
|
998
|
+
float bc = (ym - xm * 1.5) / 0.00032;
|
|
999
|
+
float fdB = detailFade(1.0 / 0.00032);
|
|
1000
|
+
float barb = (0.5 + 0.5 * sin(bc * 6.2831)) * fdB;
|
|
1001
|
+
float grp = floor((ym - xm * 1.5) / 0.0045);
|
|
1002
|
+
float split = step(0.88, hash12(vec2(grp, sgn * 7.0))) * smoothstep(0.35, 0.85, ac) * detailFade(220.0);
|
|
1003
|
+
float edge = smoothstep(0.8, 1.0, ac);
|
|
1004
|
+
fd = detailFade(220.0);
|
|
1005
|
+
if (vPat.a < 0.0) {
|
|
1006
|
+
// barred vanes (opt-in, featherCards colour().bar): -vPat.a bars from root to tip in vPat.rgb, bent
|
|
1007
|
+
// into shallow chevrons toward the edges; far away they blend to their mean
|
|
1008
|
+
float ph = along * -vPat.a + ac * 0.22;
|
|
1009
|
+
float fdBar = 1.0 - smoothstep(0.25, 0.5, fwidth(ph));
|
|
1010
|
+
float bar = smoothstep(-0.25, 0.25, sin(ph * 6.2831));
|
|
1011
|
+
alb = mix(alb, vPat.rgb, mix(0.5, bar, fdBar));
|
|
1012
|
+
}
|
|
1013
|
+
alb *= (0.9 + 0.2 * barb) * (1.0 - 0.4 * split * fd) * (1.0 - 0.22 * edge) * (0.94 + 0.12 * along);
|
|
1014
|
+
alb = mix(alb, alb * 1.35 + 0.012, rach * 0.55);
|
|
1015
|
+
pert = vec3((barb - 0.5) * 0.1, sgn * 0.2 * smoothstep(0.08, 1.0, ac) - sgn * 0.25 * split * fd, 0.0);
|
|
1016
|
+
vec3 barbDir = normalize(Tr * 0.83 + Br * sgn * 0.56);
|
|
1017
|
+
gAT = normalize(Tb * dot(barbDir, Tr) + Bw * dot(barbDir, Br));
|
|
1018
|
+
// light through the vane and the barb sheen scale with the pigment: melanin-dark feathers stay
|
|
1019
|
+
// dark from below and against the light, pale feathers glow
|
|
1020
|
+
float vLum = max(alb.r, max(alb.g, alb.b)), vTk = smoothstep(0.02, 0.3, vLum);
|
|
1021
|
+
gAniso = 0.45 * mix(0.12, 1.0, vTk); gRough = mix(0.8, 0.45, surf.x); gWrap = 0.2 * vTk; gSSS = alb * 0.6 + 0.004 * vTk; gTrans = 0.6 * vTk;
|
|
1022
|
+
if (surf.y > 0.0) {
|
|
1023
|
+
float nv = clamp(dot(Nw, V), 0.0, 1.0);
|
|
1024
|
+
vec3 film = mix(vec3(0.42, 0.3, 1.0), vec3(0.12, 0.62, 0.72), clamp(pow(1.0 - nv, 1.4) + along * 0.15, 0.0, 1.0));
|
|
1025
|
+
gF0 = mix(vec3(0.04), film * 0.09, surf.y);
|
|
1026
|
+
}
|
|
1027
|
+
aoAmb = 1.0 - 0.25 * edge - 0.2 * split * fd;
|
|
1028
|
+
}
|
|
1029
|
+
if (gHair < 0.5) N = normalize(Nw + Tb * pert.x + Bw * pert.y);
|
|
1030
|
+
else if (uShellsOn < 0.5) N = normalize(Nw + Bw * pert.y);
|
|
1031
|
+
#endif
|
|
1032
|
+
|
|
1033
|
+
// ---------- hair lighting frame
|
|
1034
|
+
if (gHair > 0.5) {
|
|
1035
|
+
// each tuft leans a little its own way: breaks the smooth shading into fur texture
|
|
1036
|
+
vec3 Bt = normalize(cross(N, T) + 1e-6);
|
|
1037
|
+
N = normalize(N + (T * (ch.w - 0.5) + Bt * (ch.z - 0.5)) * 0.55 * h);
|
|
1038
|
+
#if defined( FUR_SHELL ) || defined( FUR_FIN )
|
|
1039
|
+
occl = bristle ? mix(0.85, 1.0, h) : mix(0.3, 1.0, pow(h, 0.7));
|
|
1040
|
+
// the valleys between tufts are darker than the tufts themselves
|
|
1041
|
+
occl *= mix(1.0, 0.7, smoothstep(0.22, 0.56, fromC) * (1.0 - h) * clumpK);
|
|
1042
|
+
#endif
|
|
1043
|
+
}
|
|
1044
|
+
gN = N; gNc = normalize(mix(Nw, N, 0.25)); gT = T; gV = V; gAlb = alb; gH = h; gOccl = occl; gSv = sv;
|
|
1045
|
+
gEdge = pow(1.0 - abs(dot(Nw, V)), 1.6);
|
|
1046
|
+
#ifdef FUR_FIN
|
|
1047
|
+
gEdge = 1.0;
|
|
1048
|
+
#endif
|
|
1049
|
+
gDirect = vec3(0.0); gLit = 0.0; gLitW = 0.0;
|
|
1050
|
+
|
|
1051
|
+
// ---------- scene lights (three.js uniforms are in view space)
|
|
1052
|
+
IncidentLight directLight;
|
|
1053
|
+
float shD, shS, shP, shT; // (declared outside the unrolled loops)
|
|
1054
|
+
vec3 geoPos = (viewMatrix * vec4(vWorldPos, 1.0)).xyz;
|
|
1055
|
+
#if NUM_DIR_LIGHTS > 0
|
|
1056
|
+
DirectionalLight directionalLight;
|
|
1057
|
+
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
1058
|
+
DirectionalLightShadow directionalLightShadow;
|
|
1059
|
+
#endif
|
|
1060
|
+
#pragma unroll_loop_start
|
|
1061
|
+
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
|
|
1062
|
+
directionalLight = directionalLights[ i ];
|
|
1063
|
+
getDirectionalLightInfo( directionalLight, directLight );
|
|
1064
|
+
shD = 1.0;
|
|
1065
|
+
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
|
|
1066
|
+
directionalLightShadow = directionalLightShadows[ i ];
|
|
1067
|
+
shD = receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
|
|
1068
|
+
#endif
|
|
1069
|
+
shadeLight( toWorld( directLight.direction ), directLight.color, shD );
|
|
1070
|
+
}
|
|
1071
|
+
#pragma unroll_loop_end
|
|
1072
|
+
#endif
|
|
1073
|
+
#if NUM_SUN_LIGHTS > 0
|
|
1074
|
+
SunLight sunLight;
|
|
1075
|
+
#if defined( USE_SHADOWMAP ) && NUM_SUN_LIGHT_SHADOWS > 0
|
|
1076
|
+
SunLightShadow sunLightShadow;
|
|
1077
|
+
#endif
|
|
1078
|
+
#pragma unroll_loop_start
|
|
1079
|
+
for ( int i = 0; i < NUM_SUN_LIGHTS; i ++ ) {
|
|
1080
|
+
sunLight = sunLights[ i ];
|
|
1081
|
+
getSunLightInfo( sunLight, directLight );
|
|
1082
|
+
shS = 1.0;
|
|
1083
|
+
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SUN_LIGHT_SHADOWS )
|
|
1084
|
+
sunLightShadow = sunLightShadows[ i ];
|
|
1085
|
+
shS = receiveShadow ? getSunShadow( sunShadowMap[ i ], sunLightShadow, UNROLLED_LOOP_INDEX ) : 1.0;
|
|
1086
|
+
#endif
|
|
1087
|
+
shadeLight( toWorld( directLight.direction ), directLight.color, shS );
|
|
1088
|
+
}
|
|
1089
|
+
#pragma unroll_loop_end
|
|
1090
|
+
#endif
|
|
1091
|
+
#if NUM_POINT_LIGHTS > 0
|
|
1092
|
+
PointLight pointLight;
|
|
1093
|
+
#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
|
|
1094
|
+
PointLightShadow pointLightShadow;
|
|
1095
|
+
#endif
|
|
1096
|
+
#pragma unroll_loop_start
|
|
1097
|
+
for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
|
|
1098
|
+
pointLight = pointLights[ i ];
|
|
1099
|
+
getPointLightInfo( pointLight, geoPos, directLight );
|
|
1100
|
+
shP = 1.0;
|
|
1101
|
+
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) )
|
|
1102
|
+
pointLightShadow = pointLightShadows[ i ];
|
|
1103
|
+
shP = ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;
|
|
1104
|
+
#endif
|
|
1105
|
+
if ( directLight.visible ) shadeLight( toWorld( directLight.direction ), directLight.color, shP );
|
|
1106
|
+
}
|
|
1107
|
+
#pragma unroll_loop_end
|
|
1108
|
+
#endif
|
|
1109
|
+
#if NUM_SPOT_LIGHTS > 0
|
|
1110
|
+
SpotLight spotLight;
|
|
1111
|
+
#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
|
|
1112
|
+
SpotLightShadow spotLightShadow;
|
|
1113
|
+
#endif
|
|
1114
|
+
#pragma unroll_loop_start
|
|
1115
|
+
for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
|
|
1116
|
+
spotLight = spotLights[ i ];
|
|
1117
|
+
getSpotLightInfo( spotLight, geoPos, directLight );
|
|
1118
|
+
shT = 1.0;
|
|
1119
|
+
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
|
|
1120
|
+
spotLightShadow = spotLightShadows[ i ];
|
|
1121
|
+
shT = ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;
|
|
1122
|
+
#endif
|
|
1123
|
+
if ( directLight.visible ) shadeLight( toWorld( directLight.direction ), directLight.color, shT );
|
|
1124
|
+
}
|
|
1125
|
+
#pragma unroll_loop_end
|
|
1126
|
+
#endif
|
|
1127
|
+
|
|
1128
|
+
// ---------- ambient: hemisphere + ambient lights (+ light probes)
|
|
1129
|
+
vec3 irr = ambientLightColor;
|
|
1130
|
+
vec3 fillIrr = ambientLightColor * 2.0;
|
|
1131
|
+
vec3 R = reflect(-V, N);
|
|
1132
|
+
vec3 envIrr = ambientLightColor;
|
|
1133
|
+
#if NUM_HEMI_LIGHTS > 0
|
|
1134
|
+
for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
|
|
1135
|
+
vec3 hd = toWorld( hemisphereLights[ i ].direction );
|
|
1136
|
+
irr += mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, 0.5 * dot( N, hd ) + 0.5 );
|
|
1137
|
+
envIrr += mix( hemisphereLights[ i ].groundColor, hemisphereLights[ i ].skyColor, smoothstep( -0.3, 0.3, dot( R, hd ) ) );
|
|
1138
|
+
fillIrr += hemisphereLights[ i ].skyColor + hemisphereLights[ i ].groundColor;
|
|
1139
|
+
}
|
|
1140
|
+
#endif
|
|
1141
|
+
// the scene environment's diffuse light (sky and ground of an image-based environment, which three.js
|
|
1142
|
+
// feeds only to its own standard materials): irradiance at the normal, the reflection's blurred
|
|
1143
|
+
// radiance, and the fill's sky + ground like the hemisphere lights' (setEnvironmentLight)
|
|
1144
|
+
if (uEnvOn > 0.0) {
|
|
1145
|
+
irr += shGetIrradianceAt( N, uEnvSH );
|
|
1146
|
+
envIrr += shGetIrradianceAt( R, uEnvSH );
|
|
1147
|
+
fillIrr += shGetIrradianceAt( vec3(0.0, 1.0, 0.0), uEnvSH ) + shGetIrradianceAt( vec3(0.0, -1.0, 0.0), uEnvSH );
|
|
1148
|
+
}
|
|
1149
|
+
#if defined( USE_LIGHT_PROBES )
|
|
1150
|
+
vec3 pN = ( viewMatrix * vec4( N, 0.0 ) ).xyz;
|
|
1151
|
+
irr += getLightProbeIrradiance( lightProbe, pN );
|
|
1152
|
+
envIrr += getLightProbeIrradiance( lightProbe, ( viewMatrix * vec4( R, 0.0 ) ).xyz );
|
|
1153
|
+
#endif
|
|
1154
|
+
irr *= RECIPROCAL_PI; fillIrr *= RECIPROCAL_PI; envIrr *= RECIPROCAL_PI;
|
|
1155
|
+
// soft camera-side fill (the "bounce card" of wildlife cinematography): lifts the side turned away
|
|
1156
|
+
// from the key light so it reads as warm tan instead of mud, without flattening the lit side
|
|
1157
|
+
float litFrac = gLitW > 0.0 ? gLit / gLitW : 0.0;
|
|
1158
|
+
vec3 fill = fillIrr * uFill * clamp(dot(N, V) * 0.6 + 0.4, 0.0, 1.0) * (1.0 - litFrac);
|
|
1159
|
+
vec3 col;
|
|
1160
|
+
if (gHair > 0.5) {
|
|
1161
|
+
col = alb * irr * pow(occl, 0.75) + alb * fill + gDirect;
|
|
1162
|
+
} else {
|
|
1163
|
+
float nv = max(dot(N, V), 0.0);
|
|
1164
|
+
float gloss = (1.0 - gRough) * (1.0 - gRough);
|
|
1165
|
+
vec3 envSpec = envIrr * (fresnel(gF0, nv) * gloss + gCoat * fresnel(vec3(0.04), max(dot(gNc, V), 0.0)));
|
|
1166
|
+
col = (alb * irr + alb * fill) * aoAmb + gDirect + envSpec * aoAmb;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
#ifdef PA_DEBUG
|
|
1170
|
+
#if !defined(FUR_SHELL) && !defined(FUR_FIN) && !defined(FIN_MEMBRANE)
|
|
1171
|
+
// 'holes': the inside of the skin seen through a gap (not the seam skirt, sunk under the other surface on purpose)
|
|
1172
|
+
if (uDebug > 3.5 && !gl_FrontFacing && vCoat.w >= 0.5) col = vec3(1.0, 0.0, 0.0);
|
|
1173
|
+
#endif
|
|
1174
|
+
if (uDebug > 0.5 && uDebug < 1.5) col = alb;
|
|
1175
|
+
if (uDebug > 1.5 && uDebug < 2.5) col = vDbg * 0.8;
|
|
1176
|
+
if (uDebug > 2.5 && uDebug < 3.5) {
|
|
1177
|
+
float m = mat;
|
|
1178
|
+
col = m < 0.5 ? vec3(0.8, 0.55, 0.25) : m < 1.5 ? vec3(0.9, 0.1, 0.5) : m < 2.5 ? vec3(0.15, 0.15, 0.15) : m < 3.5 ? vec3(1.0, 0.2, 0.2)
|
|
1179
|
+
: m < 4.5 ? vec3(1.0, 0.7, 0.6) : m < 5.5 ? vec3(0.9, 0.9, 0.3) : m < 6.5 ? vec3(0.2, 0.8, 0.3) : m < 7.5 ? vec3(0.2, 0.6, 1.0)
|
|
1180
|
+
: m < 8.5 ? vec3(0.5, 0.2, 0.9) : m < 9.5 ? vec3(0.3, 0.9, 0.9) : m < 10.5 ? vec3(0.1, 0.4, 0.9) : vec3(0.95, 0.55, 0.9);
|
|
1181
|
+
col *= 0.6;
|
|
1182
|
+
}
|
|
1183
|
+
#endif
|
|
1184
|
+
#ifdef FUR_SHELL
|
|
1185
|
+
// the undercoat stands for what the line of sight met between the strands before it existed: looking
|
|
1186
|
+
// into the coat, the deep coat (the base's dark fur, occlusion 0.32); along it, more hair at this
|
|
1187
|
+
// height. So the coat keeps its tone and its dark partings, and only what lay beneath is covered.
|
|
1188
|
+
if (hazeF > 0.0) {
|
|
1189
|
+
float shellShade = mix(0.42, 1.08, pow(h, 0.55)) * occl;
|
|
1190
|
+
float kH = mix(1.0, clamp(0.32 / max(shellShade, 1e-3), 0.3, 1.0), smoothstep(0.2, 0.7, hazeNV));
|
|
1191
|
+
col *= mix(1.0, kH, hazeF);
|
|
1192
|
+
}
|
|
1193
|
+
#endif
|
|
1194
|
+
col = max(col, vec3(0.0));
|
|
1195
|
+
#ifdef FIN_MEMBRANE
|
|
1196
|
+
alpha = gMemA;
|
|
1197
|
+
#endif
|
|
1198
|
+
gl_FragColor = vec4(col, alpha);
|
|
1199
|
+
#include <tonemapping_fragment>
|
|
1200
|
+
#include <colorspace_fragment>
|
|
1201
|
+
#include <fog_fragment>
|
|
1202
|
+
}
|
|
1203
|
+
`;
|
|
1204
|
+
|
|
1205
|
+
// shadow casters: the same dual-quaternion deformation as the visible surface
|
|
1206
|
+
function dqsPatch(material, uniforms, key) {
|
|
1207
|
+
material.onBeforeCompile = (sh) => {
|
|
1208
|
+
sh.uniforms.uDQ = uniforms.uDQ;
|
|
1209
|
+
sh.vertexShader = sh.vertexShader
|
|
1210
|
+
.replace('#include <common>', '#include <common>\n' + DQS_GLSL + DQS_ATTR_GLSL)
|
|
1211
|
+
.replace('#include <begin_vertex>', '#include <begin_vertex>\n { vec3 dn = vec3(0.0, 1.0, 0.0), dt = vec3(1.0, 0.0, 0.0); dqsSkin(transformed, dn, dt); }');
|
|
1212
|
+
};
|
|
1213
|
+
material.customProgramCacheKey = () => key;
|
|
1214
|
+
return material;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
export function createShadowMaterials(uniforms) {
|
|
1218
|
+
return {
|
|
1219
|
+
depth: dqsPatch(new THREE.MeshDepthMaterial(), uniforms, 'pa-dqs-depth'),
|
|
1220
|
+
distance: dqsPatch(new THREE.MeshDistanceMaterial(), uniforms, 'pa-dqs-distance'),
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
export function createCoatMaterials(uniforms) {
|
|
1225
|
+
const make = (params) => {
|
|
1226
|
+
const u = THREE.UniformsUtils.merge([THREE.UniformsLib.lights, THREE.UniformsLib.fog]);
|
|
1227
|
+
// this animal's own uniform objects, shared by its three passes
|
|
1228
|
+
Object.assign(u, uniforms);
|
|
1229
|
+
return new THREE.ShaderMaterial({ uniforms: u, lights: true, fog: true, ...params });
|
|
1230
|
+
};
|
|
1231
|
+
// (render hint matBorders: material borders through the edge middles, see matIdFrag; off by default)
|
|
1232
|
+
const mb = uniforms.uMatBorders && uniforms.uMatBorders.value > 0 ? { PA_MAT_BORDERS: 1 } : {};
|
|
1233
|
+
const base = make({ vertexShader: vertex, fragmentShader: fragment, defines: { ...mb } });
|
|
1234
|
+
const shells = make({ vertexShader: vertex, fragmentShader: fragment, transparent: true, depthWrite: true, defines: { FUR_SHELL: 1, ...mb } });
|
|
1235
|
+
const fins = make({ vertexShader: finVertex, fragmentShader: fragment, transparent: true, depthWrite: false, side: THREE.DoubleSide, defines: { FUR_FIN: 1, ...mb } });
|
|
1236
|
+
// translucent fin membranes (material 11), drawn from their own index subset (membranes.js)
|
|
1237
|
+
const membrane = make({ vertexShader: vertex, fragmentShader: fragment, transparent: true, depthWrite: false, defines: { FIN_MEMBRANE: 1, ...mb } });
|
|
1238
|
+
return { base, shells, fins, membrane };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// debug colours compile only on demand
|
|
1242
|
+
export function setMaterialsDebug(mats, on) {
|
|
1243
|
+
for (const m of Object.values(mats)) {
|
|
1244
|
+
const has = !!m.defines.PA_DEBUG;
|
|
1245
|
+
if (has === on) continue;
|
|
1246
|
+
if (on) m.defines.PA_DEBUG = 1;
|
|
1247
|
+
else delete m.defines.PA_DEBUG;
|
|
1248
|
+
m.needsUpdate = true;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
export function makeCoatUniforms(hints = {}) {
|
|
1253
|
+
const r = { ...RENDER_DEFAULTS, ...hints };
|
|
1254
|
+
return {
|
|
1255
|
+
uShells: { value: 28 },
|
|
1256
|
+
uShellsOn: { value: 1 },
|
|
1257
|
+
uUnder: { value: r.undercoat },
|
|
1258
|
+
uFinFade: { value: 1 },
|
|
1259
|
+
uFurScale: { value: r.shellScale },
|
|
1260
|
+
uFurForce: { value: new THREE.Vector3(0, -0.15, 0) },
|
|
1261
|
+
uFurRaise: { value: 0 },
|
|
1262
|
+
uRaiseLen: { value: new THREE.Vector2(...r.raiseLen) },
|
|
1263
|
+
uClumpLen: { value: new THREE.Vector2(...r.clumpLen) },
|
|
1264
|
+
uTime: { value: 0 },
|
|
1265
|
+
uStrandDensity: { value: r.strandDensity },
|
|
1266
|
+
uClumpDensity: { value: r.clumpDensity },
|
|
1267
|
+
uFinWidth: { value: r.finWidth },
|
|
1268
|
+
uFinMask: { value: r.finMask ? 1 : 0 },
|
|
1269
|
+
uFinMinLen: { value: r.finMinLen },
|
|
1270
|
+
uStrandBlend: { value: r.strandBlend || 0 },
|
|
1271
|
+
uMarkColor: { value: new THREE.Color(...r.markColor) },
|
|
1272
|
+
uNoseRough: { value: r.noseRoughness },
|
|
1273
|
+
uNoseMarks: { value: r.noseMarks ? 1 : 0 },
|
|
1274
|
+
uNoseTint: { value: r.noseTint ? 1 : 0 },
|
|
1275
|
+
uFill: { value: 0.45 },
|
|
1276
|
+
uScaleEdge: { value: r.scaleEdge },
|
|
1277
|
+
uFeatherEdge: { value: r.featherEdge },
|
|
1278
|
+
uLeatherTint: { value: +r.leatherTint || 0 },
|
|
1279
|
+
uLeatherRelief: { value: r.leatherRelief ?? 1 },
|
|
1280
|
+
uSkinScatter: { value: new THREE.Vector4(...r.skinScatter) },
|
|
1281
|
+
uSkinTrans: { value: new THREE.Vector2(...r.skinTrans) },
|
|
1282
|
+
uDenticle: { value: r.denticles },
|
|
1283
|
+
uEnvSH: { value: Array.from({ length: 9 }, () => new THREE.Vector3()) },
|
|
1284
|
+
uEnvOn: { value: 0 },
|
|
1285
|
+
uWind: { value: 0.1 },
|
|
1286
|
+
uDebug: { value: 0 },
|
|
1287
|
+
uForceMat: { value: -1 },
|
|
1288
|
+
uFlatMat: { value: r.flatMaterial ? 1 : 0 },
|
|
1289
|
+
uPaintEyes: { value: 0 },
|
|
1290
|
+
uBaseTone: { value: new THREE.Vector3(1, 1, 1) },
|
|
1291
|
+
uShellShadowRoot: { value: r.shellShadowRoot },
|
|
1292
|
+
uShellBias: { value: new THREE.Vector2(...r.shellDepthBias) },
|
|
1293
|
+
// no skirt (older bakes without data.seamSink): each surface stops at its midline
|
|
1294
|
+
uSeam: { value: new THREE.Vector2(0, 0.5) },
|
|
1295
|
+
// (not read by the shaders: createCoatMaterials compiles the matBorders variant when it is set)
|
|
1296
|
+
uMatBorders: { value: r.matBorders ? 1 : 0 },
|
|
1297
|
+
uEyes: { value: Array.from({ length: MAX_PAINTED_EYES }, () => new THREE.Vector4(0, 0, 0, 0)) },
|
|
1298
|
+
uEyeTint: { value: Array.from({ length: MAX_PAINTED_EYES }, () => new THREE.Color(0.1, 0.05, 0.01)) },
|
|
1299
|
+
uDQ: { value: null },
|
|
1300
|
+
};
|
|
1301
|
+
}
|