@woosh/meep-engine 2.138.13 → 2.138.15
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/build/bundle-worker-image-decoder.js +1 -1
- package/package.json +1 -1
- package/samples/terrain/from_image_2.js +2 -10
- package/src/engine/asset/loaders/image/ImageDecoderWorker.js +1 -1
- package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +6 -1
- package/src/engine/asset/loaders/image/png/PNGReader.d.ts +1 -99
- package/src/engine/asset/loaders/image/png/PNGReader.d.ts.map +1 -1
- package/src/engine/asset/loaders/image/png/PNGReader.js +31 -420
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.d.ts +12 -0
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.js +53 -0
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.d.ts +10 -0
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.js +42 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.d.ts +18 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterAverage.js +59 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts +17 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +55 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.d.ts +17 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterPaeth.js +74 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts +15 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +34 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts +16 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +46 -0
- package/src/engine/asset/loaders/image/png/inflate.d.ts +7 -0
- package/src/engine/asset/loaders/image/png/inflate.d.ts.map +1 -0
- package/src/engine/asset/loaders/image/png/inflate.js +20 -0
- package/src/engine/ecs/terrain/ecs/Terrain.js +1 -1
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +32 -9
- package/src/engine/graphics/impostors/octahedral/prototypeBaker.js +202 -8
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderDepthV0.d.ts +10 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderDepthV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderDepthV0.js +395 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderLitV0.d.ts +14 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderLitV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderLitV0.js +757 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderNormalsV0.d.ts +13 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderNormalsV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderNormalsV0.js +380 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderPerPixelV0.d.ts +6 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderPerPixelV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderPerPixelV0.js +406 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +8 -1
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderViewportDepthV0.d.ts +14 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderViewportDepthV0.d.ts.map +1 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderViewportDepthV0.js +356 -0
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.js +4 -1
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Color,
|
|
3
|
+
ShaderMaterial,
|
|
4
|
+
UniformsLib,
|
|
5
|
+
UniformsUtils,
|
|
6
|
+
Vector3
|
|
7
|
+
} from "three";
|
|
8
|
+
|
|
9
|
+
// Lit variant of ImpostorShaderV0 — runs three.js's STANDARD (physical) shading
|
|
10
|
+
// model so the impostor responds to scene lights and shadow maps.
|
|
11
|
+
//
|
|
12
|
+
// What this shader does differently from V0:
|
|
13
|
+
// - The atlas baked-view-space normal is reconstructed back to object
|
|
14
|
+
// space per-corner before blending. We need the actual surface normal
|
|
15
|
+
// in view space for the lighting equations; V0 didn't care.
|
|
16
|
+
// - We integrate three.js's lighting chunks (lights_fragment_begin /
|
|
17
|
+
// lights_physical_fragment / shadowmap_* etc.) instead of just writing
|
|
18
|
+
// out the sampled colour.
|
|
19
|
+
// - We don't write gl_FragDepth — the card sits at the bounding sphere
|
|
20
|
+
// centre, so depth from gl_Position is close enough for the lit
|
|
21
|
+
// impostor's own occlusion. (Receiving shadows needs vDirectionalShadowCoord
|
|
22
|
+
// etc., which we compute in the vertex shader from worldPosition.)
|
|
23
|
+
//
|
|
24
|
+
// Implementation choice: ShaderMaterial (not Raw) so three.js auto-injects
|
|
25
|
+
// position/normal/uv attributes, modelMatrix/viewMatrix uniforms, and the
|
|
26
|
+
// light/shadow uniform block, and so the chunk system Just Works. GLSL1-style
|
|
27
|
+
// (varying/attribute/texture2D) so the included three.js chunks compile
|
|
28
|
+
// without per-include rewrites; on WebGL2 three.js #defines these to their
|
|
29
|
+
// GLSL3 equivalents automatically.
|
|
30
|
+
const shader_vx = `
|
|
31
|
+
#define STANDARD
|
|
32
|
+
|
|
33
|
+
varying vec3 vViewPosition;
|
|
34
|
+
varying vec2 vImpUv;
|
|
35
|
+
|
|
36
|
+
// Card position and normal in OBJECT space — the fragment shader uses
|
|
37
|
+
// these (plus the atlas depth at this fragment's parallax-shifted UV)
|
|
38
|
+
// to reconstruct the actual surface position in WORLD space. Once we
|
|
39
|
+
// have that, the shadow coord we hand to getShadow() reflects where
|
|
40
|
+
// the pixel REALLY is rather than where its card-plane proxy is, so
|
|
41
|
+
// the lit impostor's shadow-receive agrees with the caster's
|
|
42
|
+
// gl_FragDepth and self-shadow goes away.
|
|
43
|
+
varying vec3 vCardPos_OS;
|
|
44
|
+
flat varying vec3 vCardNormal_OS;
|
|
45
|
+
|
|
46
|
+
flat varying vec2 vGridFloor;
|
|
47
|
+
flat varying vec4 vWeights;
|
|
48
|
+
flat varying vec3 vImpTangent;
|
|
49
|
+
flat varying vec3 vImpBinormal;
|
|
50
|
+
flat varying vec3 vImpNormal;
|
|
51
|
+
flat varying vec3 vRay00;
|
|
52
|
+
flat varying vec3 vRay10;
|
|
53
|
+
flat varying vec3 vRay01;
|
|
54
|
+
flat varying vec3 vRay11;
|
|
55
|
+
flat varying vec4 vFrameXform00;
|
|
56
|
+
flat varying vec4 vFrameXform10;
|
|
57
|
+
flat varying vec4 vFrameXform01;
|
|
58
|
+
flat varying vec4 vFrameXform11;
|
|
59
|
+
|
|
60
|
+
uniform vec3 uOffset;
|
|
61
|
+
uniform float uRadius;
|
|
62
|
+
uniform float uFrames;
|
|
63
|
+
uniform bool uIsFullSphere;
|
|
64
|
+
|
|
65
|
+
#include <common>
|
|
66
|
+
#include <fog_pars_vertex>
|
|
67
|
+
#include <shadowmap_pars_vertex>
|
|
68
|
+
#include <logdepthbuf_pars_vertex>
|
|
69
|
+
#include <clipping_planes_pars_vertex>
|
|
70
|
+
|
|
71
|
+
vec2 VecToSphereOct(vec3 pivotToCamera)
|
|
72
|
+
{
|
|
73
|
+
vec3 octant = sign(pivotToCamera);
|
|
74
|
+
float sum = dot(pivotToCamera, octant);
|
|
75
|
+
vec3 octahedron = pivotToCamera / sum;
|
|
76
|
+
if (octahedron.y < 0.0) {
|
|
77
|
+
vec3 absolute = abs(octahedron);
|
|
78
|
+
octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x);
|
|
79
|
+
}
|
|
80
|
+
return octahedron.xz;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
vec2 VecToHemiSphereOct(vec3 v)
|
|
84
|
+
{
|
|
85
|
+
v.y = max(v.y, 0.001);
|
|
86
|
+
v = normalize(v);
|
|
87
|
+
vec3 octant = sign(v);
|
|
88
|
+
float sum = dot(v, octant);
|
|
89
|
+
vec3 octahedron = v / sum;
|
|
90
|
+
return vec2(
|
|
91
|
+
octahedron.x + octahedron.z,
|
|
92
|
+
octahedron.z - octahedron.x
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
vec2 VectorToGrid(vec3 v)
|
|
97
|
+
{
|
|
98
|
+
return uIsFullSphere ? VecToSphereOct(v) : VecToHemiSphereOct(v);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
vec3 OctaSphereDec(vec2 coord)
|
|
102
|
+
{
|
|
103
|
+
coord = (coord - 0.5) * 2.0;
|
|
104
|
+
vec3 p = vec3(coord.x, 0.0, coord.y);
|
|
105
|
+
vec2 a = abs(p.xz);
|
|
106
|
+
p.y = 1.0 - a.x - a.y;
|
|
107
|
+
if (p.y < 0.0) {
|
|
108
|
+
p.xz = sign(p.xz) * vec2(1.0 - a.y, 1.0 - a.x);
|
|
109
|
+
}
|
|
110
|
+
return p;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
vec3 OctaHemiSphereDec(vec2 coord)
|
|
114
|
+
{
|
|
115
|
+
vec3 p = vec3(coord.x - coord.y, 0.0, -1.0 + coord.x + coord.y);
|
|
116
|
+
vec2 a = abs(p.xz);
|
|
117
|
+
p.y = 1.0 - a.x - a.y;
|
|
118
|
+
return p;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
vec3 GridToVector(vec2 coord)
|
|
122
|
+
{
|
|
123
|
+
return uIsFullSphere ? OctaSphereDec(coord) : OctaHemiSphereDec(coord);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
vec3 FrameToRay(vec2 frame, vec2 framesMinusOne)
|
|
127
|
+
{
|
|
128
|
+
vec2 f = clamp(frame / framesMinusOne, 0.0, 1.0);
|
|
129
|
+
return normalize(GridToVector(f));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
vec4 ComputeFrameXform(vec3 D_frame, vec3 tangent_OS, vec3 binormal_OS)
|
|
133
|
+
{
|
|
134
|
+
vec3 D = abs(D_frame.y) > 0.99999
|
|
135
|
+
? normalize(D_frame + vec3(0.0, 0.0, 0.0001))
|
|
136
|
+
: D_frame;
|
|
137
|
+
vec3 bake_right = normalize(cross(vec3(0.0, 1.0, 0.0), D));
|
|
138
|
+
vec3 bake_up = cross(D, bake_right);
|
|
139
|
+
return vec4(
|
|
140
|
+
dot(tangent_OS, bake_right),
|
|
141
|
+
dot(binormal_OS, bake_right),
|
|
142
|
+
dot(tangent_OS, bake_up),
|
|
143
|
+
dot(binormal_OS, bake_up)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
vec4 BilinearWeights(vec2 frac_uv)
|
|
148
|
+
{
|
|
149
|
+
vec2 omuv = vec2(1.0) - frac_uv;
|
|
150
|
+
return vec4(
|
|
151
|
+
omuv.x * omuv.y,
|
|
152
|
+
frac_uv.x * omuv.y,
|
|
153
|
+
omuv.x * frac_uv.y,
|
|
154
|
+
frac_uv.x * frac_uv.y
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
void main() {
|
|
159
|
+
vImpUv = uv;
|
|
160
|
+
|
|
161
|
+
vec3 cameraPos_OS = (inverse(modelViewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
|
162
|
+
// Atlas lookup must be relative to the bounding-sphere centre
|
|
163
|
+
// (uOffset), since the bake's D_frame is measured from the centre,
|
|
164
|
+
// not from the object origin.
|
|
165
|
+
vec3 pivotToCameraRay = normalize(cameraPos_OS - uOffset);
|
|
166
|
+
|
|
167
|
+
vec2 framesMinusOne = vec2(uFrames - 1.0);
|
|
168
|
+
vec2 octahedral_uv = clamp(VectorToGrid(pivotToCameraRay) * 0.5 + 0.5, 0.0, 1.0);
|
|
169
|
+
vec2 grid = octahedral_uv * framesMinusOne;
|
|
170
|
+
vec2 gridFloor = min(floor(grid), framesMinusOne - 1.0);
|
|
171
|
+
vec4 weights = BilinearWeights(grid - gridFloor);
|
|
172
|
+
|
|
173
|
+
vGridFloor = gridFloor;
|
|
174
|
+
vWeights = weights;
|
|
175
|
+
|
|
176
|
+
vec3 ray00 = FrameToRay(gridFloor + vec2(0.0, 0.0), framesMinusOne);
|
|
177
|
+
vec3 ray10 = FrameToRay(gridFloor + vec2(1.0, 0.0), framesMinusOne);
|
|
178
|
+
vec3 ray01 = FrameToRay(gridFloor + vec2(0.0, 1.0), framesMinusOne);
|
|
179
|
+
vec3 ray11 = FrameToRay(gridFloor + vec2(1.0, 1.0), framesMinusOne);
|
|
180
|
+
|
|
181
|
+
// Object-space corner ray directions — needed by the fragment shader
|
|
182
|
+
// to reconstruct each frame's bake_right / bake_up basis and decode
|
|
183
|
+
// the atlas normal back to object space.
|
|
184
|
+
vRay00 = ray00;
|
|
185
|
+
vRay10 = ray10;
|
|
186
|
+
vRay01 = ray01;
|
|
187
|
+
vRay11 = ray11;
|
|
188
|
+
|
|
189
|
+
vec3 projectedRay = normalize(
|
|
190
|
+
ray00 * weights.x +
|
|
191
|
+
ray10 * weights.y +
|
|
192
|
+
ray01 * weights.z +
|
|
193
|
+
ray11 * weights.w
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
vec3 normal_OS = projectedRay;
|
|
197
|
+
vec3 up_OS = abs(normal_OS.y) > 0.999
|
|
198
|
+
? vec3(0.0, 0.0, -1.0)
|
|
199
|
+
: vec3(0.0, 1.0, 0.0);
|
|
200
|
+
vec3 tangent_OS = normalize(cross(up_OS, normal_OS));
|
|
201
|
+
vec3 binormal_OS = cross(normal_OS, tangent_OS);
|
|
202
|
+
|
|
203
|
+
float card_diameter = uRadius * 2.0;
|
|
204
|
+
vec3 pos_OS = uOffset
|
|
205
|
+
+ position.x * card_diameter * tangent_OS
|
|
206
|
+
+ position.y * card_diameter * binormal_OS;
|
|
207
|
+
|
|
208
|
+
vCardPos_OS = pos_OS;
|
|
209
|
+
vCardNormal_OS = normal_OS;
|
|
210
|
+
|
|
211
|
+
vec4 mvPosition = modelViewMatrix * vec4(pos_OS, 1.0);
|
|
212
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
213
|
+
vViewPosition = -mvPosition.xyz;
|
|
214
|
+
|
|
215
|
+
mat3 m3 = mat3(modelViewMatrix);
|
|
216
|
+
vImpTangent = normalize(m3 * tangent_OS);
|
|
217
|
+
vImpBinormal = normalize(m3 * binormal_OS);
|
|
218
|
+
vImpNormal = normalize(m3 * normal_OS);
|
|
219
|
+
|
|
220
|
+
vFrameXform00 = ComputeFrameXform(ray00, tangent_OS, binormal_OS);
|
|
221
|
+
vFrameXform10 = ComputeFrameXform(ray10, tangent_OS, binormal_OS);
|
|
222
|
+
vFrameXform01 = ComputeFrameXform(ray01, tangent_OS, binormal_OS);
|
|
223
|
+
vFrameXform11 = ComputeFrameXform(ray11, tangent_OS, binormal_OS);
|
|
224
|
+
|
|
225
|
+
// Quantities the three.js chunks expect to be in scope.
|
|
226
|
+
// - 'worldPosition' is consumed by <shadowmap_vertex> to build the
|
|
227
|
+
// per-light shadow projection coords.
|
|
228
|
+
// - 'transformedNormal' is consumed by the same chunk for the
|
|
229
|
+
// shadow-acne normal-bias offset; it should be the view-space
|
|
230
|
+
// surface normal.
|
|
231
|
+
vec4 worldPosition = modelMatrix * vec4(pos_OS, 1.0);
|
|
232
|
+
vec3 transformedNormal = vImpNormal;
|
|
233
|
+
|
|
234
|
+
#include <logdepthbuf_vertex>
|
|
235
|
+
#include <clipping_planes_vertex>
|
|
236
|
+
#include <shadowmap_vertex>
|
|
237
|
+
#include <fog_vertex>
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
const shader_fg = `
|
|
241
|
+
#define STANDARD
|
|
242
|
+
|
|
243
|
+
uniform vec3 diffuse;
|
|
244
|
+
uniform vec3 emissive;
|
|
245
|
+
uniform float roughness;
|
|
246
|
+
uniform float metalness;
|
|
247
|
+
uniform float opacity;
|
|
248
|
+
|
|
249
|
+
varying vec3 vViewPosition;
|
|
250
|
+
varying vec2 vImpUv;
|
|
251
|
+
|
|
252
|
+
varying vec3 vCardPos_OS;
|
|
253
|
+
flat varying vec3 vCardNormal_OS;
|
|
254
|
+
|
|
255
|
+
flat varying vec2 vGridFloor;
|
|
256
|
+
flat varying vec4 vWeights;
|
|
257
|
+
flat varying vec3 vImpTangent;
|
|
258
|
+
flat varying vec3 vImpBinormal;
|
|
259
|
+
flat varying vec3 vImpNormal;
|
|
260
|
+
flat varying vec3 vRay00;
|
|
261
|
+
flat varying vec3 vRay10;
|
|
262
|
+
flat varying vec3 vRay01;
|
|
263
|
+
flat varying vec3 vRay11;
|
|
264
|
+
flat varying vec4 vFrameXform00;
|
|
265
|
+
flat varying vec4 vFrameXform10;
|
|
266
|
+
flat varying vec4 vFrameXform01;
|
|
267
|
+
flat varying vec4 vFrameXform11;
|
|
268
|
+
|
|
269
|
+
uniform sampler2D tBase;
|
|
270
|
+
uniform sampler2D tGeometry;
|
|
271
|
+
uniform sampler2D tMaterial;
|
|
272
|
+
uniform float uFrames;
|
|
273
|
+
uniform float uRadius;
|
|
274
|
+
uniform float uDepthScale;
|
|
275
|
+
|
|
276
|
+
// Needed in the fragment by the light-direction parallax block below
|
|
277
|
+
// (it has to compute its own octahedral grid lookup for the caster's
|
|
278
|
+
// sampling direction, not the camera's).
|
|
279
|
+
uniform vec3 uOffset;
|
|
280
|
+
uniform bool uIsFullSphere;
|
|
281
|
+
|
|
282
|
+
// three.js auto-injects normalMatrix in the VERTEX prefix only; in the
|
|
283
|
+
// fragment we have to declare it ourselves. WebGLRenderer.js still
|
|
284
|
+
// binds it from object.normalMatrix when the program exposes the
|
|
285
|
+
// location, so this is enough to transform our atlas-reconstructed
|
|
286
|
+
// object-space normal into view space.
|
|
287
|
+
uniform mat3 normalMatrix;
|
|
288
|
+
|
|
289
|
+
// Same story for modelMatrix. We need it to put the parallax surface
|
|
290
|
+
// into world space for the shadow-coord recomputation.
|
|
291
|
+
uniform mat4 modelMatrix;
|
|
292
|
+
|
|
293
|
+
#include <common>
|
|
294
|
+
#include <packing>
|
|
295
|
+
#include <dithering_pars_fragment>
|
|
296
|
+
#include <color_pars_fragment>
|
|
297
|
+
#include <bsdfs>
|
|
298
|
+
#include <cube_uv_reflection_fragment>
|
|
299
|
+
#include <envmap_common_pars_fragment>
|
|
300
|
+
#include <envmap_physical_pars_fragment>
|
|
301
|
+
#include <fog_pars_fragment>
|
|
302
|
+
#include <lights_pars_begin>
|
|
303
|
+
#include <lights_physical_pars_fragment>
|
|
304
|
+
#include <shadowmap_pars_fragment>
|
|
305
|
+
#include <logdepthbuf_pars_fragment>
|
|
306
|
+
#include <clipping_planes_pars_fragment>
|
|
307
|
+
|
|
308
|
+
// directionalShadowMatrix[] is declared in <shadowmap_pars_VERTEX> but
|
|
309
|
+
// not in the fragment-side pars chunk; declaring it here lets
|
|
310
|
+
// three.js's WebGLLights bind it (it's part of UniformsLib.lights
|
|
311
|
+
// which got merged into our material's uniforms via lights: true).
|
|
312
|
+
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
313
|
+
uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];
|
|
314
|
+
#endif
|
|
315
|
+
|
|
316
|
+
// Redirect the chunk-side reference to vDirectionalShadowCoord[i]
|
|
317
|
+
// into our per-fragment customShadowCoord array. The varying with the
|
|
318
|
+
// original name still exists (declared above by shadowmap_pars_fragment)
|
|
319
|
+
// and the vertex shader still writes to it — we just don't read it.
|
|
320
|
+
// We have to put the #define AFTER the pars include so the chunk's
|
|
321
|
+
// own "varying vec4 vDirectionalShadowCoord[...]" declaration isn't
|
|
322
|
+
// affected by the macro.
|
|
323
|
+
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
324
|
+
#define vDirectionalShadowCoord customShadowCoord
|
|
325
|
+
#endif
|
|
326
|
+
|
|
327
|
+
// ---- octahedral helpers duplicated from the vertex shader ----
|
|
328
|
+
// We need them in the fragment too because the shadow-coord block
|
|
329
|
+
// below recomputes the caster's parallax surface (with light-direction
|
|
330
|
+
// octahedral lookup) per-fragment. The vertex flat-varyings are for
|
|
331
|
+
// the CAMERA direction; here we need the same math evaluated for the
|
|
332
|
+
// LIGHT direction.
|
|
333
|
+
|
|
334
|
+
vec2 VecToSphereOct(vec3 pivotToCamera)
|
|
335
|
+
{
|
|
336
|
+
vec3 octant = sign(pivotToCamera);
|
|
337
|
+
float sum = dot(pivotToCamera, octant);
|
|
338
|
+
vec3 octahedron = pivotToCamera / sum;
|
|
339
|
+
if (octahedron.y < 0.0) {
|
|
340
|
+
vec3 absolute = abs(octahedron);
|
|
341
|
+
octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x);
|
|
342
|
+
}
|
|
343
|
+
return octahedron.xz;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
vec2 VecToHemiSphereOct(vec3 v)
|
|
347
|
+
{
|
|
348
|
+
v.y = max(v.y, 0.001);
|
|
349
|
+
v = normalize(v);
|
|
350
|
+
vec3 octant = sign(v);
|
|
351
|
+
float sum = dot(v, octant);
|
|
352
|
+
vec3 octahedron = v / sum;
|
|
353
|
+
return vec2(
|
|
354
|
+
octahedron.x + octahedron.z,
|
|
355
|
+
octahedron.z - octahedron.x
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
vec2 VectorToGrid(vec3 v)
|
|
360
|
+
{
|
|
361
|
+
return uIsFullSphere ? VecToSphereOct(v) : VecToHemiSphereOct(v);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
vec3 OctaSphereDec(vec2 coord)
|
|
365
|
+
{
|
|
366
|
+
coord = (coord - 0.5) * 2.0;
|
|
367
|
+
vec3 p = vec3(coord.x, 0.0, coord.y);
|
|
368
|
+
vec2 a = abs(p.xz);
|
|
369
|
+
p.y = 1.0 - a.x - a.y;
|
|
370
|
+
if (p.y < 0.0) {
|
|
371
|
+
p.xz = sign(p.xz) * vec2(1.0 - a.y, 1.0 - a.x);
|
|
372
|
+
}
|
|
373
|
+
return p;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
vec3 OctaHemiSphereDec(vec2 coord)
|
|
377
|
+
{
|
|
378
|
+
vec3 p = vec3(coord.x - coord.y, 0.0, -1.0 + coord.x + coord.y);
|
|
379
|
+
vec2 a = abs(p.xz);
|
|
380
|
+
p.y = 1.0 - a.x - a.y;
|
|
381
|
+
return p;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
vec3 GridToVector(vec2 coord)
|
|
385
|
+
{
|
|
386
|
+
return uIsFullSphere ? OctaSphereDec(coord) : OctaHemiSphereDec(coord);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
vec3 FrameToRay(vec2 frame, vec2 framesMinusOne)
|
|
390
|
+
{
|
|
391
|
+
vec2 f = clamp(frame / framesMinusOne, 0.0, 1.0);
|
|
392
|
+
return normalize(GridToVector(f));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
vec4 ComputeFrameXform(vec3 D_frame, vec3 tangent_OS, vec3 binormal_OS)
|
|
396
|
+
{
|
|
397
|
+
vec3 D = abs(D_frame.y) > 0.99999
|
|
398
|
+
? normalize(D_frame + vec3(0.0, 0.0, 0.0001))
|
|
399
|
+
: D_frame;
|
|
400
|
+
vec3 bake_right = normalize(cross(vec3(0.0, 1.0, 0.0), D));
|
|
401
|
+
vec3 bake_up = cross(D, bake_right);
|
|
402
|
+
return vec4(
|
|
403
|
+
dot(tangent_OS, bake_right),
|
|
404
|
+
dot(binormal_OS, bake_right),
|
|
405
|
+
dot(tangent_OS, bake_up),
|
|
406
|
+
dot(binormal_OS, bake_up)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
vec4 BilinearWeights(vec2 frac_uv)
|
|
411
|
+
{
|
|
412
|
+
vec2 omuv = vec2(1.0) - frac_uv;
|
|
413
|
+
return vec4(
|
|
414
|
+
omuv.x * omuv.y,
|
|
415
|
+
frac_uv.x * omuv.y,
|
|
416
|
+
omuv.x * frac_uv.y,
|
|
417
|
+
frac_uv.x * frac_uv.y
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
vec2 apply_frame_xform(vec2 card_uv, vec4 xform)
|
|
422
|
+
{
|
|
423
|
+
vec2 c = card_uv - 0.5;
|
|
424
|
+
return vec2(
|
|
425
|
+
xform.x * c.x + xform.y * c.y,
|
|
426
|
+
xform.z * c.x + xform.w * c.y
|
|
427
|
+
) + 0.5;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Sample one corner's atlas tile after rotating the card UV into that
|
|
431
|
+
// frame's bake-camera basis. Returns the diffuse and geometry+ORM
|
|
432
|
+
// texels along with the per-frame atlas UV (so the caller can do
|
|
433
|
+
// parallax in two passes — first depth, then a shifted re-sample).
|
|
434
|
+
void corner_atlas_uv(
|
|
435
|
+
vec2 card_uv, vec2 gridFloor, vec2 corner_offset,
|
|
436
|
+
vec4 xform, out vec2 atlas_uv
|
|
437
|
+
) {
|
|
438
|
+
vec2 frame_size = vec2(1.0 / uFrames);
|
|
439
|
+
vec2 uv = clamp(apply_frame_xform(card_uv, xform), 0.0, 1.0);
|
|
440
|
+
atlas_uv = (gridFloor + corner_offset + uv) * frame_size;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Decode the atlas-encoded normal (stored in bake-camera view space)
|
|
444
|
+
// back to OBJECT-LOCAL space. Inverse of the (right, up, D_frame)
|
|
445
|
+
// basis the baker used. See BakeShaderStandard.js for the encode side.
|
|
446
|
+
vec3 decode_normal_OS(vec3 encoded, vec3 D_frame_OS)
|
|
447
|
+
{
|
|
448
|
+
vec3 n_bv = encoded * 2.0 - 1.0;
|
|
449
|
+
vec3 D = abs(D_frame_OS.y) > 0.99999
|
|
450
|
+
? normalize(D_frame_OS + vec3(0.0, 0.0, 0.0001))
|
|
451
|
+
: D_frame_OS;
|
|
452
|
+
vec3 bake_right = normalize(cross(vec3(0.0, 1.0, 0.0), D));
|
|
453
|
+
vec3 bake_up = cross(D, bake_right);
|
|
454
|
+
return bake_right * n_bv.x + bake_up * n_bv.y + D * n_bv.z;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
void main() {
|
|
458
|
+
|
|
459
|
+
#include <clipping_planes_fragment>
|
|
460
|
+
|
|
461
|
+
// ---- pass 1: blended depth (for parallax shift) ----
|
|
462
|
+
vec2 base_uv = vImpUv;
|
|
463
|
+
|
|
464
|
+
vec2 atlas_uv00, atlas_uv10, atlas_uv01, atlas_uv11;
|
|
465
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(0.0, 0.0), vFrameXform00, atlas_uv00);
|
|
466
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(1.0, 0.0), vFrameXform10, atlas_uv10);
|
|
467
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(0.0, 1.0), vFrameXform01, atlas_uv01);
|
|
468
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(1.0, 1.0), vFrameXform11, atlas_uv11);
|
|
469
|
+
|
|
470
|
+
float d00 = texture2D(tGeometry, atlas_uv00).a;
|
|
471
|
+
float d10 = texture2D(tGeometry, atlas_uv10).a;
|
|
472
|
+
float d01 = texture2D(tGeometry, atlas_uv01).a;
|
|
473
|
+
float d11 = texture2D(tGeometry, atlas_uv11).a;
|
|
474
|
+
float blended_depth =
|
|
475
|
+
d00 * vWeights.x + d10 * vWeights.y +
|
|
476
|
+
d01 * vWeights.z + d11 * vWeights.w;
|
|
477
|
+
|
|
478
|
+
// Card-tangent-space view direction; same trick as V0 — the card
|
|
479
|
+
// is perpendicular to the projected ray (= vImpNormal), so projecting
|
|
480
|
+
// -vViewPosition onto (vImpTangent, vImpBinormal, vImpNormal) gives the
|
|
481
|
+
// view direction in the card's frame.
|
|
482
|
+
vec3 view_dir_view = normalize(vViewPosition);
|
|
483
|
+
vec3 view_dir = vec3(
|
|
484
|
+
dot(vImpTangent, view_dir_view),
|
|
485
|
+
dot(vImpBinormal, view_dir_view),
|
|
486
|
+
dot(vImpNormal, view_dir_view)
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
base_uv += (view_dir.xy / view_dir.z) * (blended_depth - 0.5) * uDepthScale;
|
|
490
|
+
base_uv = clamp(base_uv, 0.0, 1.0);
|
|
491
|
+
|
|
492
|
+
// ---- pass 2: full atlas sample at parallax-shifted UV ----
|
|
493
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(0.0, 0.0), vFrameXform00, atlas_uv00);
|
|
494
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(1.0, 0.0), vFrameXform10, atlas_uv10);
|
|
495
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(0.0, 1.0), vFrameXform01, atlas_uv01);
|
|
496
|
+
corner_atlas_uv(base_uv, vGridFloor, vec2(1.0, 1.0), vFrameXform11, atlas_uv11);
|
|
497
|
+
|
|
498
|
+
vec4 b00 = texture2D(tBase, atlas_uv00);
|
|
499
|
+
vec4 b10 = texture2D(tBase, atlas_uv10);
|
|
500
|
+
vec4 b01 = texture2D(tBase, atlas_uv01);
|
|
501
|
+
vec4 b11 = texture2D(tBase, atlas_uv11);
|
|
502
|
+
vec4 albedo =
|
|
503
|
+
b00 * vWeights.x + b10 * vWeights.y +
|
|
504
|
+
b01 * vWeights.z + b11 * vWeights.w;
|
|
505
|
+
|
|
506
|
+
if (albedo.a <= 0.5) {
|
|
507
|
+
discard;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// sRGB -> linear. The GLTFLoader marks color textures as
|
|
511
|
+
// sRGBEncoding, but BakeShaderStandard is a RawShaderMaterial and
|
|
512
|
+
// sampled them with a plain texture() call (no auto-conversion
|
|
513
|
+
// through <map_fragment>), so the bake atlas holds the raw sRGB
|
|
514
|
+
// bytes labelled LinearEncoding. The truth mesh's
|
|
515
|
+
// MeshStandardMaterial decodes its map in the shader before
|
|
516
|
+
// running PBR; we have to do the same here or the lighting math
|
|
517
|
+
// operates on gamma-compressed values and the result looks
|
|
518
|
+
// washed-out / too bright in the mid-tones relative to the
|
|
519
|
+
// truth. (sRGBToLinear is provided by three.js's fragment prefix
|
|
520
|
+
// via <encodings_pars_fragment>, which is auto-included.)
|
|
521
|
+
albedo.rgb = sRGBToLinear(vec4(albedo.rgb, 1.0)).rgb;
|
|
522
|
+
|
|
523
|
+
vec4 g00 = texture2D(tGeometry, atlas_uv00);
|
|
524
|
+
vec4 g10 = texture2D(tGeometry, atlas_uv10);
|
|
525
|
+
vec4 g01 = texture2D(tGeometry, atlas_uv01);
|
|
526
|
+
vec4 g11 = texture2D(tGeometry, atlas_uv11);
|
|
527
|
+
|
|
528
|
+
// Decode each corner's normal in its own bake basis, then blend.
|
|
529
|
+
// Blending the encoded RGB directly would mix incompatible bases
|
|
530
|
+
// (a normal pointing "right" in frame 00 is unrelated to the
|
|
531
|
+
// same RGB in frame 11).
|
|
532
|
+
vec3 n_OS_00 = decode_normal_OS(g00.rgb, vRay00);
|
|
533
|
+
vec3 n_OS_10 = decode_normal_OS(g10.rgb, vRay10);
|
|
534
|
+
vec3 n_OS_01 = decode_normal_OS(g01.rgb, vRay01);
|
|
535
|
+
vec3 n_OS_11 = decode_normal_OS(g11.rgb, vRay11);
|
|
536
|
+
vec3 normal_OS = normalize(
|
|
537
|
+
n_OS_00 * vWeights.x + n_OS_10 * vWeights.y +
|
|
538
|
+
n_OS_01 * vWeights.z + n_OS_11 * vWeights.w
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
vec4 m00 = texture2D(tMaterial, atlas_uv00);
|
|
542
|
+
vec4 m10 = texture2D(tMaterial, atlas_uv10);
|
|
543
|
+
vec4 m01 = texture2D(tMaterial, atlas_uv01);
|
|
544
|
+
vec4 m11 = texture2D(tMaterial, atlas_uv11);
|
|
545
|
+
vec4 orm =
|
|
546
|
+
m00 * vWeights.x + m10 * vWeights.y +
|
|
547
|
+
m01 * vWeights.z + m11 * vWeights.w;
|
|
548
|
+
|
|
549
|
+
// ---- parallax surface in world space ----
|
|
550
|
+
// Use the post-parallax depth (we just sampled g00..g11 at the
|
|
551
|
+
// shifted UV — their alpha is the depth at that surface point).
|
|
552
|
+
// Same convention as in the bake/depth shader: depth=1 -> +radius
|
|
553
|
+
// along the card normal, 0.5 -> on the card plane, 0 -> -radius.
|
|
554
|
+
float surface_depth_blended =
|
|
555
|
+
g00.a * vWeights.x + g10.a * vWeights.y +
|
|
556
|
+
g01.a * vWeights.z + g11.a * vWeights.w;
|
|
557
|
+
float height_OS = (surface_depth_blended - 0.5) * 2.0 * uRadius;
|
|
558
|
+
vec3 surface_OS_pos = vCardPos_OS + height_OS * vCardNormal_OS;
|
|
559
|
+
vec4 worldPosition_surface = modelMatrix * vec4(surface_OS_pos, 1.0);
|
|
560
|
+
|
|
561
|
+
// ---- standard three.js lit pipeline ----
|
|
562
|
+
// The chunks below expect these names to already be defined in
|
|
563
|
+
// scope: diffuseColor (vec4), reflectedLight, totalEmissiveRadiance,
|
|
564
|
+
// roughnessFactor, metalnessFactor, normal, geometryNormal.
|
|
565
|
+
vec4 diffuseColor = vec4(albedo.rgb * diffuse, 1.0);
|
|
566
|
+
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
|
|
567
|
+
|
|
568
|
+
float occlusionFactor = orm.r;
|
|
569
|
+
float roughnessFactor = roughness * orm.g;
|
|
570
|
+
float metalnessFactor = metalness * orm.b;
|
|
571
|
+
|
|
572
|
+
vec3 totalEmissiveRadiance = emissive;
|
|
573
|
+
|
|
574
|
+
vec3 normal = normalize(normalMatrix * normal_OS);
|
|
575
|
+
vec3 geometryNormal = normal;
|
|
576
|
+
|
|
577
|
+
// Recompute the directional-light shadow coord per-fragment.
|
|
578
|
+
//
|
|
579
|
+
// The naive approach — feed worldPosition_surface (which uses
|
|
580
|
+
// CAMERA-direction parallax) straight to directionalShadowMatrix —
|
|
581
|
+
// doesn't work because the CASTER's gl_FragDepth uses LIGHT-
|
|
582
|
+
// direction parallax. Single-step parallax along two different
|
|
583
|
+
// directions converges to two different surface points on a
|
|
584
|
+
// sloped height-field, so the receiver and caster disagree on
|
|
585
|
+
// "where this pixel actually is in world space" and the
|
|
586
|
+
// comparison creates self-shadow along the boundary.
|
|
587
|
+
//
|
|
588
|
+
// The fix is to do a SECOND parallax inside the lit shader that
|
|
589
|
+
// mirrors the caster: light-perpendicular caster card geometry,
|
|
590
|
+
// light-direction octahedral grid, and a sample at the caster's
|
|
591
|
+
// UV. The world-space surface that comes out matches what
|
|
592
|
+
// ImpostorShaderDepthV0 wrote at the same light_xy, so the
|
|
593
|
+
// receiver/caster depths line up.
|
|
594
|
+
//
|
|
595
|
+
// (We keep the camera-direction parallax surface for the rest of
|
|
596
|
+
// the lit pipeline — it's what the eye actually sees through this
|
|
597
|
+
// card pixel.)
|
|
598
|
+
//
|
|
599
|
+
// The vDirectionalShadowCoord macro defined above redirects the
|
|
600
|
+
// chunk's reference into this array.
|
|
601
|
+
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
|
602
|
+
vec4 customShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
|
|
603
|
+
{
|
|
604
|
+
vec3 shadowWorldNormal_lit = normalize( mat3( modelMatrix ) * normal_OS );
|
|
605
|
+
|
|
606
|
+
for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {
|
|
607
|
+
|
|
608
|
+
// ---- step 1: light direction in OBJECT space ----
|
|
609
|
+
// directionalLights[i].direction is "to-light" in VIEW
|
|
610
|
+
// space; bring it back to world then into OS. modelMatrix
|
|
611
|
+
// is uniform-scale + rotation for our impostor entities,
|
|
612
|
+
// so a plain inverseTransformDirection works.
|
|
613
|
+
vec3 light_dir_view = normalize( directionalLights[ i ].direction );
|
|
614
|
+
vec3 light_dir_world = inverseTransformDirection( light_dir_view, viewMatrix );
|
|
615
|
+
vec3 light_dir_OS = inverseTransformDirection( light_dir_world, modelMatrix );
|
|
616
|
+
|
|
617
|
+
// ---- step 2: caster card basis (perpendicular to light) ----
|
|
618
|
+
vec3 up_OS_L = abs( light_dir_OS.y ) > 0.999
|
|
619
|
+
? vec3( 0.0, 0.0, -1.0 )
|
|
620
|
+
: vec3( 0.0, 1.0, 0.0 );
|
|
621
|
+
vec3 tan_OS_L = normalize( cross( up_OS_L, light_dir_OS ) );
|
|
622
|
+
vec3 bin_OS_L = cross( light_dir_OS, tan_OS_L );
|
|
623
|
+
|
|
624
|
+
// ---- step 3: project the visible's parallax surface
|
|
625
|
+
// onto the caster card plane to find the
|
|
626
|
+
// caster-UV at this fragment ----
|
|
627
|
+
vec3 surf_OS = surface_OS_pos;
|
|
628
|
+
vec3 from_center = surf_OS - uOffset;
|
|
629
|
+
float u_caster = dot( from_center, tan_OS_L );
|
|
630
|
+
float v_caster = dot( from_center, bin_OS_L );
|
|
631
|
+
vec2 caster_card_uv = vec2( u_caster, v_caster ) / ( 2.0 * uRadius ) + 0.5;
|
|
632
|
+
caster_card_uv = clamp( caster_card_uv, 0.0, 1.0 );
|
|
633
|
+
|
|
634
|
+
// ---- step 4: atlas lookup for the light direction ----
|
|
635
|
+
vec2 fMinusOne_L = vec2( uFrames - 1.0 );
|
|
636
|
+
vec2 oct_uv_L = clamp(
|
|
637
|
+
VectorToGrid( light_dir_OS ) * 0.5 + 0.5,
|
|
638
|
+
0.0, 1.0
|
|
639
|
+
);
|
|
640
|
+
vec2 grid_L = oct_uv_L * fMinusOne_L;
|
|
641
|
+
vec2 gridFloor_L = min( floor( grid_L ), fMinusOne_L - 1.0 );
|
|
642
|
+
vec4 weights_L = BilinearWeights( grid_L - gridFloor_L );
|
|
643
|
+
|
|
644
|
+
vec3 ray00_L = FrameToRay( gridFloor_L + vec2( 0.0, 0.0 ), fMinusOne_L );
|
|
645
|
+
vec3 ray10_L = FrameToRay( gridFloor_L + vec2( 1.0, 0.0 ), fMinusOne_L );
|
|
646
|
+
vec3 ray01_L = FrameToRay( gridFloor_L + vec2( 0.0, 1.0 ), fMinusOne_L );
|
|
647
|
+
vec3 ray11_L = FrameToRay( gridFloor_L + vec2( 1.0, 1.0 ), fMinusOne_L );
|
|
648
|
+
|
|
649
|
+
vec4 xform00_L = ComputeFrameXform( ray00_L, tan_OS_L, bin_OS_L );
|
|
650
|
+
vec4 xform10_L = ComputeFrameXform( ray10_L, tan_OS_L, bin_OS_L );
|
|
651
|
+
vec4 xform01_L = ComputeFrameXform( ray01_L, tan_OS_L, bin_OS_L );
|
|
652
|
+
vec4 xform11_L = ComputeFrameXform( ray11_L, tan_OS_L, bin_OS_L );
|
|
653
|
+
|
|
654
|
+
// ---- step 5: sample the depth atlas at the caster UV.
|
|
655
|
+
// Caster card is perpendicular to light, so
|
|
656
|
+
// its view_dir.z == 1 and parallax shift is
|
|
657
|
+
// zero — same as in ImpostorShaderDepthV0. ----
|
|
658
|
+
vec2 fs_L = vec2( 1.0 / uFrames );
|
|
659
|
+
vec2 uv00_L = clamp( apply_frame_xform( caster_card_uv, xform00_L ), 0.0, 1.0 );
|
|
660
|
+
vec2 uv10_L = clamp( apply_frame_xform( caster_card_uv, xform10_L ), 0.0, 1.0 );
|
|
661
|
+
vec2 uv01_L = clamp( apply_frame_xform( caster_card_uv, xform01_L ), 0.0, 1.0 );
|
|
662
|
+
vec2 uv11_L = clamp( apply_frame_xform( caster_card_uv, xform11_L ), 0.0, 1.0 );
|
|
663
|
+
|
|
664
|
+
float d00_L = texture2D( tGeometry, ( gridFloor_L + vec2( 0.0, 0.0 ) + uv00_L ) * fs_L ).a;
|
|
665
|
+
float d10_L = texture2D( tGeometry, ( gridFloor_L + vec2( 1.0, 0.0 ) + uv10_L ) * fs_L ).a;
|
|
666
|
+
float d01_L = texture2D( tGeometry, ( gridFloor_L + vec2( 0.0, 1.0 ) + uv01_L ) * fs_L ).a;
|
|
667
|
+
float d11_L = texture2D( tGeometry, ( gridFloor_L + vec2( 1.0, 1.0 ) + uv11_L ) * fs_L ).a;
|
|
668
|
+
float depth_L =
|
|
669
|
+
d00_L * weights_L.x + d10_L * weights_L.y +
|
|
670
|
+
d01_L * weights_L.z + d11_L * weights_L.w;
|
|
671
|
+
|
|
672
|
+
// ---- step 6: build the surface position the CASTER
|
|
673
|
+
// would have written at this light_xy, then
|
|
674
|
+
// project through directionalShadowMatrix. ----
|
|
675
|
+
float height_OS_L = ( depth_L - 0.5 ) * 2.0 * uRadius;
|
|
676
|
+
vec3 caster_card_pos_OS =
|
|
677
|
+
uOffset + u_caster * tan_OS_L + v_caster * bin_OS_L;
|
|
678
|
+
vec3 surface_OS_L = caster_card_pos_OS + height_OS_L * light_dir_OS;
|
|
679
|
+
vec4 worldPosition_surface_L =
|
|
680
|
+
modelMatrix * vec4( surface_OS_L, 1.0 );
|
|
681
|
+
|
|
682
|
+
vec4 shadowWorldPosition = worldPosition_surface_L
|
|
683
|
+
+ vec4( shadowWorldNormal_lit * directionalLightShadows[ i ].shadowNormalBias, 0.0 );
|
|
684
|
+
customShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
#endif
|
|
688
|
+
|
|
689
|
+
#include <logdepthbuf_fragment>
|
|
690
|
+
|
|
691
|
+
#include <lights_physical_fragment>
|
|
692
|
+
#include <lights_fragment_begin>
|
|
693
|
+
#include <lights_fragment_maps>
|
|
694
|
+
#include <lights_fragment_end>
|
|
695
|
+
|
|
696
|
+
// Apply the atlas's baked AO directly to direct + indirect diffuse;
|
|
697
|
+
// standard <aomap_fragment> only touches indirect, but the impostor
|
|
698
|
+
// bake doesn't separate them.
|
|
699
|
+
reflectedLight.directDiffuse *= occlusionFactor;
|
|
700
|
+
reflectedLight.indirectDiffuse *= occlusionFactor;
|
|
701
|
+
reflectedLight.indirectSpecular *= occlusionFactor;
|
|
702
|
+
|
|
703
|
+
vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
|
|
704
|
+
vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;
|
|
705
|
+
vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;
|
|
706
|
+
|
|
707
|
+
gl_FragColor = vec4(outgoingLight, 1.0);
|
|
708
|
+
|
|
709
|
+
#include <tonemapping_fragment>
|
|
710
|
+
#include <encodings_fragment>
|
|
711
|
+
#include <fog_fragment>
|
|
712
|
+
#include <premultiplied_alpha_fragment>
|
|
713
|
+
#include <dithering_fragment>
|
|
714
|
+
}
|
|
715
|
+
`;
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* PBR-lit impostor material — applies three.js's standard (physical)
|
|
719
|
+
* lighting model to a baked impostor atlas. Casts/receives shadows like
|
|
720
|
+
* any MeshStandardMaterial-backed mesh.
|
|
721
|
+
*
|
|
722
|
+
* Same uniform names as ImpostorShaderV0 (tBase / tGeometry / tMaterial /
|
|
723
|
+
* uFrames / uRadius / uOffset / uIsFullSphere / uDepthScale) plus the
|
|
724
|
+
* standard PBR knobs (diffuse / emissive / roughness / metalness / opacity).
|
|
725
|
+
*/
|
|
726
|
+
export class ImpostorShaderLitV0 extends ShaderMaterial {
|
|
727
|
+
constructor() {
|
|
728
|
+
const uniforms = UniformsUtils.merge([
|
|
729
|
+
UniformsLib.common,
|
|
730
|
+
UniformsLib.fog,
|
|
731
|
+
UniformsLib.lights,
|
|
732
|
+
{
|
|
733
|
+
roughness: { value: 1.0 },
|
|
734
|
+
metalness: { value: 0.0 },
|
|
735
|
+
emissive: { value: new Color(0x000000) }
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
tBase: { value: null },
|
|
739
|
+
tGeometry: { value: null },
|
|
740
|
+
tMaterial: { value: null },
|
|
741
|
+
uFrames: { value: 0 },
|
|
742
|
+
uRadius: { value: 0 },
|
|
743
|
+
uOffset: { value: new Vector3(0, 0, 0) },
|
|
744
|
+
uIsFullSphere: { value: false },
|
|
745
|
+
uDepthScale: { value: 0.5 }
|
|
746
|
+
}
|
|
747
|
+
]);
|
|
748
|
+
|
|
749
|
+
super({
|
|
750
|
+
uniforms,
|
|
751
|
+
vertexShader: shader_vx,
|
|
752
|
+
fragmentShader: shader_fg,
|
|
753
|
+
lights: true,
|
|
754
|
+
fog: true
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|