mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
struct Viewpoint {
    view_projection: mat4x4<f32>,
    // What the light drawing this map is: a lamp's position, or a sun's
    // direction. The last lane holds 1.0 for a position and 0.0 for a
    // direction; the camera's slot leaves it empty.
    towards: vec4<f32>,
    // What a world plane's own depth is read through: the plane taken
    // through this comes back as the numbers `plane_depth` divides.
    read_plane: mat4x4<f32>,
}

struct Frame {
    // The light the frame's sky lands on a surface, as nine coefficients
    // the direction a surface faces is read through.
    irradiance: array<vec4<f32>, 9>,
    // What a clip position is taken through to give the direction the sky
    // is read along: the inverse of the camera with its viewpoint left at
    // the origin.
    sky_from_clip: mat4x4<f32>,
    // The camera a faced draw resolves its depth against: `eye` is where its
    // rays leave, `foreshortened` whether they leave that point at all
    // rather than running level along `looking`, the direction it looks.
    eye: vec3<f32>,
    foreshortened: u32,
    looking: vec3<f32>,
    lights: u32,
    // The mip level a fully rough surface reflects that sky from: the
    // smallest mip's, counting `0.0` at the largest.
    top_mip: f32,
    // What a surface's reflection of the sky is scaled by; the coefficients
    // already hold it.
    sky_share: f32,
}

struct Light {
    position: vec3<f32>,
    kind: u32,
    direction: vec3<f32>,
    range: f32,
    color: vec3<f32>,
    cone: f32,
    shadow: i32,
}

struct ShadowMap {
    view_projection: mat4x4<f32>,
    texel: f32,
    layer: u32,
    world_texel: vec2<f32>,
}

const DIRECTIONAL: u32 = 0u;
const POINT: u32 = 1u;
const SPOT: u32 = 2u;

// The fraction of a spotlight's cone its edge fades over.
const PENUMBRA: f32 = 0.1;

// A light with no depth map of its own.
const NO_SHADOW: i32 = -1;

// Map count a sun is drawn into, which `shadows.rs` fits densest first, and
// the texels a surface must be inside one by for its 3x3 to be
// taken from that map rather than from past its edge.
const CASCADES: u32 = 3u;
const MARGIN: f32 = 2.0;

// The power a surface of no roughness at all takes its highlight to; one
// fully rough takes it to 1.0, and the highlight covers less of a surface
// as the roughness falls between them.
const TIGHTEST: f32 = 256.0;

// The share of the sky a surface of no metallic at all reflects where the
// viewpoint faces it straight on. A fully metallic one reflects its own
// color in that share's place.
const SKY_DIELECTRIC: f32 = 0.04;

// The angle the sky's image covers across it and down it, in radians, and
// the depth the sky itself is drawn at. Every name the sky's own code
// declares starts with `sky`, so a style declaring `PI` still compiles.
const SKY_AROUND: f32 = 6.28318531;
const SKY_DOWN: f32 = 3.14159265;
const SKY_FAR: f32 = 1.0;

// A cutout instance writes its litness under this, past anything a plain
// one writes, since the instance layout has no lane left for it alone. Its
// texels are drawn only where the alpha is at least THRESHOLD.
const CUTOUT: f32 = -1.0;
const THRESHOLD: f32 = 0.5;

// The window written for a draw with no frame of its own: the whole texture,
// which the sampler's own repeat is left to resolve.
const WHOLE: vec4<f32> = vec4<f32>(0.0, 0.0, 1.0, 1.0);

// Lift distance for a surface along its normal before its depth is
// compared, in texels of the map it is compared against: a fixed lift,
// plus one per unit of slope away from the light, which is itself held to
// STEEPEST.
const LIFT: f32 = 1.0;
const LIFT_SLOPE: f32 = 2.0;
const STEEPEST: f32 = 2.0;

// The least squared length of level look direction a view must have to
// place a faced draw in an upright plane, and the furthest a corner slides
// along its own ray onto that plane, as a fraction of the ray.
const LEVEL: f32 = 1e-6;
const SLIDE: f32 = 0.5;

// What relief a draw's part has, in the order `pipelines.rs` numbers
// them: none at all, one holding a normal for its texels, and one that also
// moves them off the plane — a lane only a faced draw writes. The depth a
// draw with no solid is given follows, in sprite widths.
const NO_RELIEF: u32 = 0u;
const NORMALS: u32 = 1u;
const SOLID: u32 = 2u;
const UNMAPPED_DEPTH: f32 = 1.0;

// The sprite is at the middle of the volume a relief holds, so the surface
// it shows is half that depth out of the plane.
const HALF: f32 = 0.5;

@group(0) @binding(0) var<uniform> viewpoint: Viewpoint;
@group(0) @binding(1) var<uniform> frame: Frame;
@group(0) @binding(2) var<storage, read> lights: array<Light>;
@group(0) @binding(3) var<storage, read> maps: array<ShadowMap>;

// The frame's own sky, with every mip of it: what the frame draws where
// nothing was drawn, and what a surface reflects.
@group(0) @binding(4) var sky: texture_2d<f32>;
@group(0) @binding(5) var sky_sampler: sampler;

// The joint matrices of every posed draw of the frame, one run per draw
// record; an instance holds where its own run starts. The skinned stages
// alone read it, and every one of them reads the same run, so a posed draw
// casts the shadow of the pose it is drawn in.
@group(0) @binding(6) var<storage, read> palette: array<mat4x4<f32>>;

// One white texel is bound wherever a mesh slot holds no texture, no
// shading map, or no emissive map — leaving those lanes at what the material
// alone set — and one flat texel wherever it holds no relief.
@group(1) @binding(0) var base_color: texture_2d<f32>;
@group(1) @binding(1) var base_color_sampler: sampler;
@group(1) @binding(2) var relief: texture_2d<f32>;
@group(1) @binding(3) var shading: texture_2d<f32>;
@group(1) @binding(4) var emissive_map: texture_2d<f32>;

// A sun's or a cone's maps, a lamp's six faces at half their side, and
// the sampler that returns how much of a map a depth is in front of.
@group(2) @binding(0) var wide_maps: texture_depth_2d_array;
@group(2) @binding(1) var face_maps: texture_depth_2d_array;
@group(2) @binding(2) var map_sampler: sampler_comparison;

struct VertexInput {
    @location(0) position: vec3<f32>,
    @location(1) normal: vec3<f32>,
    @location(2) uv: vec2<f32>,
}

struct InstanceInput {
    @location(3) model_row_x: vec4<f32>,
    @location(4) model_row_y: vec4<f32>,
    @location(5) model_row_z: vec4<f32>,
    @location(6) tint: vec4<f32>,
    @location(7) params: vec4<f32>,
    @location(8) window: vec4<f32>,
    @location(10) relief: u32,
    @location(11) plane: vec4<f32>,
    @location(14) roughness: f32,
    @location(15) metallic: f32,
    // Where this draw's run of the palette starts. A draw of a mesh with no
    // joints holds zero here and no stage reads it.
    @location(9) palette: u32,
}

// The second stream a skinned mesh holds: the joints one corner takes,
// by their place in its model, and how much of each it takes. Its two
// lanes are the last a pipeline has left, so a skinned one reads all
// sixteen.
struct SkinInput {
    @location(12) joints: vec4<u32>,
    @location(13) weights: vec4<f32>,
}

struct Fragment {
    @builtin(position) clip_position: vec4<f32>,
    // Where the texel is, which the lights, the shadow comparison and a
    // style's own SURFACE hook read it at: the corner its transform placed
    // for a placed draw, and the point on the plane it lies in for a faced
    // one.
    @location(0) world: vec3<f32>,
    @location(1) normal: vec3<f32>,
    @location(2) tint: vec4<f32>,
    @location(3) litness: f32,
    @location(4) uv: vec2<f32>,
    @location(5) emissive: vec3<f32>,
    @location(6) @interpolate(flat) cutout: u32,
    @location(7) @interpolate(flat) window: vec4<f32>,
    // The drawn sprite's own across, with its width in meters, and its
    // own up: the axes a relief's normals and depths are read in.
    @location(8) @interpolate(flat) across: vec4<f32>,
    @location(9) @interpolate(flat) upward: vec3<f32>,
    @location(10) @interpolate(flat) relief: u32,
    // The plane the draw takes its depth from, which a draw that takes its
    // own corners' depth leaves empty.
    @location(11) @interpolate(flat) plane: vec4<f32>,
    @location(12) @interpolate(flat) roughness: f32,
    @location(13) @interpolate(flat) metallic: f32,
}

// The values a style's vertex code is passed: the position the transform
// placed the vertex at, and what it was built with.
struct Placed {
    world: vec3<f32>,
    normal: vec3<f32>,
    uv: vec2<f32>,
    local: vec3<f32>,
}

// The values a style's fragment code is passed and returns, before the
// surface is lit: the engine draws with the first three fields.
struct Surface {
    color: vec4<f32>,
    normal: vec3<f32>,
    emissive: vec3<f32>,
    world: vec3<f32>,
    uv: vec2<f32>,
}

// The cofactor matrix turns normals the way the inverse transpose does, up to
// a positive scale that normalizing drops — so no normal matrix has to be
// computed or uploaded per draw.
fn cofactor(m: mat3x3<f32>) -> mat3x3<f32> {
    return mat3x3<f32>(cross(m[1], m[2]), cross(m[2], m[0]), cross(m[0], m[1]));
}

// The instance holds its transform's affine rows; the (0, 0, 0, 1) row it drops
// makes the world position three dot products.
fn placed(local: vec3<f32>, instance: InstanceInput) -> vec3<f32> {
    let point = vec4<f32>(local, 1.0);
    return vec3<f32>(
        dot(instance.model_row_x, point),
        dot(instance.model_row_y, point),
        dot(instance.model_row_z, point),
    );
}

// The transform's own axes, which the instance holds transposed.
fn columns(instance: InstanceInput) -> mat3x3<f32> {
    return transpose(mat3x3<f32>(
        instance.model_row_x.xyz,
        instance.model_row_y.xyz,
        instance.model_row_z.xyz,
    ));
}

// Where a corner lands on the screen.
fn on_screen(world: vec3<f32>) -> vec4<f32> {
    return viewpoint.view_projection * vec4<f32>(world, 1.0);
}

// The depth of `plane` at the pixel `position` names, which is where the
// viewpoint's ray through that pixel intersects it. The plane and the pixel decide
// it and the drawn corners do not, so every draw of one plane writes one
// depth at a pixel and the later of them draws over the earlier, whole.
//
// A draw with no plane, and one whose plane the viewpoint meets edge-on,
// keep the depth their own corners left.
fn plane_depth(plane: vec4<f32>, position: vec4<f32>) -> f32 {
    let read = viewpoint.read_plane * plane;
    let depth = -(read.x * position.x + read.y * position.y + read.w) / read.z;
    return select(position.z, depth, read.z != 0.0);
}

// Slides a corner along the camera's own ray through it, onto the upright
// plane its draw lies in. That leaves the corner where it is on the screen
// and places it where the draw is, so the view clips a turned draw, the
// lights land on it there and every depth map records it there, however far
// the turn moved it back.
//
// A draw that lies in no plane, and one whose plane the camera meets
// edge-on, keep the corners they were drawn with. A slide past SLIDE is held
// there rather than dropped, so a view nearly level with the plane moves
// every corner to that bound instead of leaving some of them behind.
fn onto_plane(plane: vec4<f32>, world: vec3<f32>) -> vec3<f32> {
    if dot(plane.xyz, plane.xyz) < LEVEL {
        return world;
    }
    let ray = select(frame.looking, world - frame.eye, frame.foreshortened == 1u);
    let along = dot(plane.xyz, ray);
    if along == 0.0 {
        return world;
    }
    let slide = clamp(-(dot(plane.xyz, world) + plane.w) / along, -SLIDE, SLIDE);
    return world + ray * slide;
}

// The four matrices a corner takes of its draw's own run of the palette,
// each at the weight that corner holds of it.
fn skin_blend(skin: SkinInput, at: u32) -> mat4x4<f32> {
    return palette[at + skin.joints.x] * skin.weights.x
        + palette[at + skin.joints.y] * skin.weights.y
        + palette[at + skin.joints.z] * skin.weights.z
        + palette[at + skin.joints.w] * skin.weights.w;
}

// The same corner moved by that blend, and its normal turned by the
// blend's own cofactor, which is what a stage past this one is passed: a
// skinned corner goes through the stages a placed one does, from here on.
fn skinned(vertex: VertexInput, skin: SkinInput, instance: InstanceInput) -> VertexInput {
    let blend = skin_blend(skin, instance.palette);
    let turns = cofactor(mat3x3<f32>(blend[0].xyz, blend[1].xyz, blend[2].xyz));

    var posed: VertexInput;
    posed.position = (blend * vec4<f32>(vertex.position, 1.0)).xyz;
    posed.normal = turns * vertex.normal;
    posed.uv = vertex.uv;
    return posed;
}

// mirage-engine:style

@vertex
fn vertex(vertex: VertexInput, instance: InstanceInput) -> Fragment {
    var fragment = drawn(vertex, instance);
    fragment.clip_position = on_screen(fragment.world);
    return fragment;
}

// The same for a draw the viewpoint turned: its corners slide onto the
// upright plane it lies in, so the view clips it and the lights land on it
// there, however far the turn moved it back.
@vertex
fn faced_vertex(vertex: VertexInput, instance: InstanceInput) -> Fragment {
    var fragment = drawn(vertex, instance);
    fragment.world = onto_plane(instance.plane, fragment.world);
    fragment.clip_position = on_screen(fragment.world);
    return fragment;
}

// Both of those again for a draw of a mesh with joints, whose corners are
// posed before its own transform places them.
@vertex
fn skinned_vertex(
    vertex: VertexInput,
    skin: SkinInput,
    instance: InstanceInput,
) -> Fragment {
    var fragment = drawn(skinned(vertex, skin, instance), instance);
    fragment.clip_position = on_screen(fragment.world);
    return fragment;
}

@vertex
fn faced_skinned_vertex(
    vertex: VertexInput,
    skin: SkinInput,
    instance: InstanceInput,
) -> Fragment {
    var fragment = drawn(skinned(vertex, skin, instance), instance);
    fragment.world = onto_plane(instance.plane, fragment.world);
    fragment.clip_position = on_screen(fragment.world);
    return fragment;
}

// Everything one corner passes into the fragment stage but where it lands
// on the screen, which each vertex stage decides for itself.
fn drawn(vertex: VertexInput, instance: InstanceInput) -> Fragment {
    let orientation = columns(instance);
    let normal = cofactor(orientation) * vertex.normal;
    let uv = instance.window.xy + vertex.uv * instance.window.zw;
    let world = displaced(Placed(
        placed(vertex.position, instance),
        normal,
        uv,
        vertex.position,
    ));

    let shading = instance.params.x;
    let cuts = shading < 0.0;

    var fragment: Fragment;
    fragment.world = world;
    fragment.normal = normal;
    fragment.tint = instance.tint;
    fragment.litness = select(shading, CUTOUT - shading, cuts);
    fragment.uv = uv;
    fragment.emissive = instance.params.yzw;
    fragment.cutout = select(0u, 1u, cuts);
    fragment.window = instance.window;
    let width = length(orientation[0]);
    fragment.across = vec4<f32>(orientation[0] / max(width, 1e-6), width);
    fragment.upward = orientation[1] / max(length(orientation[1]), 1e-6);
    fragment.relief = instance.relief;
    fragment.plane = instance.plane;
    fragment.roughness = instance.roughness;
    fragment.metallic = instance.metallic;
    return fragment;
}

// Everything a caster leaves in one depth map: the same placement, from
// the light's viewpoint, with no color to write.
@vertex
fn caster(vertex: VertexInput, instance: InstanceInput) -> @builtin(position) vec4<f32> {
    return depth_of(vertex, instance);
}

// The same for a draw of a mesh with joints: the pose it is drawn in is the
// pose it casts.
@vertex
fn skinned_caster(
    vertex: VertexInput,
    skin: SkinInput,
    instance: InstanceInput,
) -> @builtin(position) vec4<f32> {
    return depth_of(skinned(vertex, skin, instance), instance);
}

fn depth_of(vertex: VertexInput, instance: InstanceInput) -> vec4<f32> {
    return viewpoint.view_projection * vec4<f32>(placed(vertex.position, instance), 1.0);
}

// What a caster that samples its slot passes into the fragment stage:
// where it reads its textures, what it drops by, where it is positioned, and
// how wide the sprite is.
struct Casting {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) uv: vec2<f32>,
    @location(1) @interpolate(flat) window: vec4<f32>,
    @location(2) @interpolate(flat) alpha: f32,
    @location(3) @interpolate(flat) cutout: u32,
    @location(4) world: vec3<f32>,
    @location(5) @interpolate(flat) relief: u32,
    @location(6) @interpolate(flat) width: f32,
    @location(7) @interpolate(flat) out: vec3<f32>,
    @location(8) @interpolate(flat) plane: vec4<f32>,
}

@vertex
fn sampling_caster(vertex: VertexInput, instance: InstanceInput) -> Casting {
    return casting(vertex, instance);
}

// The same for a skinned draw, which casts what its pose covers.
@vertex
fn skinned_sampling_caster(
    vertex: VertexInput,
    skin: SkinInput,
    instance: InstanceInput,
) -> Casting {
    return casting(skinned(vertex, skin, instance), instance);
}

// The axis a draw's relief holds its depth along: out of the sprite, towards
// the viewpoint that turned it.
fn out_of(orientation: mat3x3<f32>) -> vec3<f32> {
    let out = cross(orientation[0], orientation[1]);
    return out / max(length(out), 1e-6);
}

// How far towards a light a texel is moved, for one whose relief is
// `height` out of the plane along `out`: as far along the light as that
// surface is, and no further — nothing where the light is behind the
// sprite and the surface it meets is the plane itself. The caster and the
// forward pass both take it, so the two land on one point of the texel's own
// ray however the light crosses the sprite.
fn moved_towards(height: f32, out: vec3<f32>, towards: vec3<f32>) -> f32 {
    return height * max(dot(out, towards), 0.0);
}

// A draw casting the solid its relief holds records that solid texel by
// texel, so its corners take the same slide the camera lit them through.
// Every other caster writes the depth of the plane at the pixel it lands
// on, which no slide moves, and its corners stay where they were placed.
fn casting(vertex: VertexInput, instance: InstanceInput) -> Casting {
    let corner = placed(vertex.position, instance);
    let world = select(
        corner,
        onto_plane(instance.plane, corner),
        instance.relief == SOLID,
    );

    var casting: Casting;
    casting.clip_position = on_screen(world);
    casting.uv = instance.window.xy + vertex.uv * instance.window.zw;
    casting.window = instance.window;
    casting.alpha = instance.tint.a;
    casting.cutout = select(0u, 1u, instance.params.x < 0.0);
    casting.world = world;
    casting.relief = instance.relief;
    casting.plane = instance.plane;
    let orientation = columns(instance);
    casting.width = length(orientation[0]);
    casting.out = out_of(orientation);
    return casting;
}

// The direction from `world` to the light this map is drawn from.
fn toward_map(world: vec3<f32>) -> vec3<f32> {
    if viewpoint.towards.w == 0.0 {
        return viewpoint.towards.xyz;
    }
    let offset = viewpoint.towards.xyz - world;
    return offset / max(length(offset), 1e-6);
}

// The alpha one texel of a caster covers with: the crisp shape a cutout
// draw keeps, scaled by a translucent draw's own tint, and the whole texel
// for an opaque one, whose slot ignores texture alpha as it does in every
// pass. A draw casting the solid its relief holds takes its shape from that
// relief instead, so only its tint scales what it casts.
fn covered(casting: Casting, sampled: f32) -> f32 {
    if casting.relief == SOLID {
        return casting.alpha;
    }
    if casting.cutout == 1u {
        return casting.alpha * select(0.0, 1.0, sampled >= THRESHOLD);
    }
    return select(1.0, casting.alpha * sampled, casting.alpha < 1.0);
}

// The value partial coverage is dropped against at one texel of a map: the
// 4x4 Bayer matrix, in `(0, 1)`, so coverage of one keeps every texel and
// coverage of zero keeps none. It is laid over the map's own grid, not the
// camera's, so what a translucent caster blocks holds still as the camera
// turns; bit arithmetic is used in place of the table WGSL cannot index at a
// texel that varies.
fn dithered(at: vec2<f32>) -> f32 {
    let cell = vec2<u32>(at) & vec2<u32>(3u);
    let diagonal = cell.x ^ cell.y;
    let rung = ((diagonal & 1u) << 3u)
        | ((cell.y & 1u) << 2u)
        | (diagonal & 2u)
        | ((cell.y & 2u) >> 1u);
    return (f32(rung) + 0.5) / 16.0;
}

// A caster's depth wherever its draw covers the texel it lands on: the
// whole of an opaque one, the shape a cutout draw keeps, and a dithered
// fraction of a translucent one, so what it takes out of a light is the
// alpha it covers with; a caster writes no color.
//
// A draw lying in a plane records that plane's own depth, so its record and
// the records of the draws of its plane land at one depth and it casts
// nothing of its own past its edges.
//
// A draw whose relief holds a depth casts the solid that relief holds
// instead: depth wherever it holds any, the base it holds under the
// sprite's own texels included, so what a sprite casts meets the ground
// under it.
// The depth written is the relief's surface, moved from the plane towards
// this map's light as far as that surface is along it, which is the point
// the forward pass rebuilds it at.
@fragment
fn caster_sampled(casting: Casting) -> @builtin(frag_depth) f32 {
    let uv = held_inside(casting.uv, casting.window, textureDimensions(base_color));
    let sampled = textureSample(base_color, base_color_sampler, uv).a;
    let depth = textureSample(
        relief,
        base_color_sampler,
        held_inside(casting.uv, casting.window, textureDimensions(relief)),
    ).a;
    if covered(casting, sampled) < dithered(casting.clip_position.xy) {
        discard;
    }
    if casting.relief != SOLID {
        return plane_depth(casting.plane, casting.clip_position);
    }
    if depth <= 0.0 {
        discard;
    }

    let towards = toward_map(casting.world);
    let met = casting.world
        + towards * moved_towards(depth * casting.width * HALF, casting.out, towards);
    let clip = viewpoint.view_projection * vec4<f32>(met, 1.0);
    if clip.w <= 0.0 {
        return casting.clip_position.z;
    }
    return clamp(clip.z / clip.w, 0.0, 1.0);
}

fn toward(light: Light, world: vec3<f32>) -> vec3<f32> {
    if light.kind == DIRECTIONAL {
        return -light.direction;
    }
    let offset = light.position - world;
    return offset / max(length(offset), 1e-6);
}

// A point as the viewpoint reads it: the direction from it back to the
// viewpoint, and how far along the view it is, in meters. The rays
// leave `eye` under a foreshortened camera and run level along `looking`
// under one that is not.
struct Seen {
    towards: vec3<f32>,
    distance: f32,
}

fn seen_from(world: vec3<f32>) -> Seen {
    let offset = frame.eye - world;
    if frame.foreshortened == 0u {
        return Seen(-frame.looking, -dot(offset, frame.looking));
    }
    let span = length(offset);
    return Seen(offset / max(span, 1e-6), span);
}

// The fraction of one light a surface returns towards the viewpoint: none
// at a roughness of 1.0, and a highlight that covers less of the surface as
// the roughness falls. It is measured from the halfway direction between the
// light and the viewpoint: a normal facing along that direction returns the
// light straight back. A surface with no normal of its own returns none.
fn highlight_of(roughness: f32, normal: vec3<f32>, towards: vec3<f32>, seen: Seen) -> f32 {
    let sharpness = 1.0 - roughness;
    let halved = towards + seen.towards;
    let halfway = halved * inverseSqrt(max(dot(halved, halved), LEVEL));
    return sharpness * pow(max(dot(normal, halfway), 0.0), pow(TIGHTEST, sharpness));
}

// How a fragment meets one light: the fraction of it the surface's angle
// takes, the point the light's map is compared at, the direction that point
// is lifted along before the comparison, and how much further from the
// light the compared surface is over one step across it.
struct Incidence {
    landed: f32,
    compared: vec3<f32>,
    lift: vec3<f32>,
    slope: f32,
}

// That slope for a surface meeting the light at `squarely`, held to
// STEEPEST so a surface nearly edge-on is not lifted out of its own map.
fn slope_of(squarely: f32) -> f32 {
    let straight = sqrt(max(1.0 - squarely * squarely, 0.0));
    return min(straight / max(squarely, 1e-3), STEEPEST);
}

fn contribution(light: Light, world: vec3<f32>, landed: f32, towards: vec3<f32>) -> vec3<f32> {
    let lambert = light.color * landed;
    if light.kind == DIRECTIONAL {
        return lambert;
    }

    let span = length(light.position - world);
    let falloff = pow(clamp(1.0 - span / light.range, 0.0, 1.0), 2.0);

    var cone = 1.0;
    if light.kind == SPOT {
        let alignment = dot(-towards, light.direction);
        cone = smoothstep(light.cone, mix(light.cone, 1.0, PENUMBRA), alignment);
    }

    return lambert * falloff * cone;
}

// The one of a lamp's six cube faces an offset from it falls on, in the
// order `shadows.rs` draws them.
fn face_of(offset: vec3<f32>) -> u32 {
    let reach = abs(offset);
    if reach.x >= reach.y && reach.x >= reach.z {
        return select(1u, 0u, offset.x > 0.0);
    }
    if reach.y >= reach.z {
        return select(3u, 2u, offset.y > 0.0);
    }
    return select(5u, 4u, offset.z > 0.0);
}

fn compared(kind: u32, at: vec2<f32>, layer: u32, depth: f32) -> f32 {
    if kind == POINT {
        return textureSampleCompareLevel(face_maps, map_sampler, at, layer, depth);
    }
    return textureSampleCompareLevel(wide_maps, map_sampler, at, layer, depth);
}

// The fraction of a light one map allows through, and whether it holds the
// surface at all; one that does not is left to the map past it.
struct Through {
    reached: f32,
    held: bool,
}

// Everything a fixed 3x3 of `map` is in front of, taken no
// nearer its edge than `margin` texels.
fn through(light: Light, map: ShadowMap, met: Incidence, margin: f32) -> Through {
    let texel = map.world_texel.x + map.world_texel.y * distance(met.compared, light.position);
    let lifted = met.compared + met.lift * texel * (LIFT + LIFT_SLOPE * met.slope);

    let clip = map.view_projection * vec4<f32>(lifted, 1.0);
    if clip.w <= 0.0 {
        return Through(1.0, false);
    }
    let ndc = clip.xyz / clip.w;
    let landed = ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
    let edge = margin * map.texel;
    let escaped = any(landed < vec2<f32>(edge)) || any(landed > vec2<f32>(1.0 - edge));
    if ndc.z <= 0.0 || ndc.z >= 1.0 || (escaped && light.kind != POINT) {
        return Through(1.0, false);
    }
    // A lamp's faces tile every direction: one the lift moved past this
    // face is held at its edge, the next face being another map.
    let at = clamp(landed, vec2<f32>(0.0), vec2<f32>(1.0));

    var reached = 0.0;
    for (var y = -1; y <= 1; y++) {
        for (var x = -1; x <= 1; x++) {
            let step = vec2<f32>(f32(x), f32(y)) * map.texel;
            reached += compared(light.kind, at + step, map.layer, ndc.z);
        }
    }
    return Through(reached / 9.0, true);
}

// The fraction of a light that lands on a surface: all of it wherever the light
// has no map holding the surface, and whatever the densest map that does
// returns.
fn fraction_landing(light: Light, met: Incidence) -> f32 {
    if light.shadow == NO_SHADOW {
        return 1.0;
    }
    let first = u32(light.shadow);

    if light.kind == DIRECTIONAL {
        for (var cascade = 0u; cascade < CASCADES; cascade++) {
            let taken = through(light, maps[first + cascade], met, MARGIN);
            if taken.held {
                return taken.reached;
            }
        }
        return 1.0;
    }

    var index = first;
    if light.kind == POINT {
        index += face_of(met.compared - light.position);
    }
    return through(light, maps[index], met, 0.0).reached;
}

// The coordinate a windowed draw reads a texture `across` texels wide at:
// within the interval its two edges span, whichever way round they run, no
// nearer either than half a texel — or its middle, where it is narrower
// than that. A draw of the whole texture is left to the sampler, which
// repeats.
fn held_inside(uv: vec2<f32>, window: vec4<f32>, across: vec2<u32>) -> vec2<f32> {
    if all(window == WHOLE) {
        return uv;
    }
    let edge = window.xy + window.zw;
    let low = min(window.xy, edge);
    let high = max(window.xy, edge);
    let inset = min(0.5 / vec2<f32>(across), (high - low) / 2.0);
    return clamp(uv, low + inset, high - inset);
}

// The surface facing `normal` before it is lit: its tint over the
// part of its texture the instance samples, with its own emissive light
// scaled by its emissive map, as the style it is drawn with leaves it.
fn surface_of(fragment: Fragment, normal: vec3<f32>) -> Surface {
    let uv = held_inside(fragment.uv, fragment.window, textureDimensions(base_color));
    let base = fragment.tint * textureSample(base_color, base_color_sampler, uv);
    let light = textureSample(
        emissive_map,
        base_color_sampler,
        held_inside(fragment.uv, fragment.window, textureDimensions(emissive_map)),
    );
    return styled(Surface(
        base,
        normal,
        fragment.emissive * light.rgb,
        fragment.world,
        uv,
    ));
}

// The slot's shading map over this texel: the occlusion it takes of the
// sky's own light in `R`, and, in `G` and `B`, the scale it applies to the
// roughness and metallic lanes.
fn shading_of(fragment: Fragment) -> vec3<f32> {
    return textureSample(
        shading,
        base_color_sampler,
        held_inside(fragment.uv, fragment.window, textureDimensions(shading)),
    ).rgb;
}

// The same fragment with those two lanes scaled by `texel`.
fn scaled_by(fragment: Fragment, texel: vec3<f32>) -> Fragment {
    var scaled = fragment;
    scaled.roughness *= texel.g;
    scaled.metallic *= texel.b;
    return scaled;
}

// Where `direction` lands on the sky: the whole way around across the
// image, and zenith to nadir down it. `skybox.rs` writes a texel at the
// same place.
fn sky_at(direction: vec3<f32>) -> vec2<f32> {
    let around = atan2(direction.x, -direction.z) / SKY_AROUND + 0.5;
    let down = acos(clamp(direction.y, -1.0, 1.0)) / SKY_DOWN;
    return vec2<f32>(around, down);
}

// The light the sky lands on a surface facing `normal`: the frame's nine
// coefficients, each scaled by its own shape at that direction, in the
// order `skybox.rs` lays them out.
//
// Nine coefficients cannot hold a sky that changes over a short angle, and
// what they return dips under zero here and there, so nothing under zero is
// returned.
fn sky_light(normal: vec3<f32>) -> vec3<f32> {
    let light = frame.irradiance[0].rgb
        + frame.irradiance[1].rgb * normal.y
        + frame.irradiance[2].rgb * normal.z
        + frame.irradiance[3].rgb * normal.x
        + frame.irradiance[4].rgb * (normal.x * normal.y)
        + frame.irradiance[5].rgb * (normal.y * normal.z)
        + frame.irradiance[6].rgb * (3.0 * normal.z * normal.z - 1.0)
        + frame.irradiance[7].rgb * (normal.x * normal.z)
        + frame.irradiance[8].rgb * (normal.x * normal.x - normal.y * normal.y);
    return max(light, vec3<f32>(0.0));
}

// What the sky leaves on a surface that reflects it: the sky at
// `reflect(-towards, normal)`, read from the mip `roughness` chooses, the
// largest at `0.0` and the smallest at `1.0`, scaled by the share the
// surface reflects at this angle and by the fraction of its light the sky
// lands. No draw is reflected in another.
//
// The roughness holds what is returned where the viewpoint meets the surface
// along it: the whole sky where smooth, the base share where fully rough.
fn sky_reflection(
    color: vec3<f32>,
    normal: vec3<f32>,
    towards: vec3<f32>,
    roughness: f32,
    metallic: f32,
) -> vec3<f32> {
    let base = mix(vec3<f32>(SKY_DIELECTRIC), color, metallic);
    let squarely = clamp(dot(normal, towards), 0.0, 1.0);
    let along = max(vec3<f32>(1.0 - roughness), base);
    let returned = base + (along - base) * pow(1.0 - squarely, 5.0);
    let level = clamp(roughness, 0.0, 1.0) * frame.top_mip;
    let reflected = sky_at(reflect(-towards, normal));

    return textureSampleLevel(sky, sky_sampler, reflected, level).rgb * returned
        * frame.sky_share;
}

// What one surface takes from the frame: the light landing on it from
// every direction, and the highlight the frame's lights leave over that.
struct Received {
    reached: vec3<f32>,
    highlight: vec3<f32>,
}

// The light received by a surface placed by its own transform: what the sky
// lands on a surface facing its way and what the sky leaves reflected in
// it, both darkened by `occlusion` — the share its slot's shading map holds
// for this texel — and every light scaled by the angle its normal makes
// with it, darkened where that light's map holds something nearer. Each
// light leaves a highlight scaled by what it landed with.
fn received(fragment: Fragment, surface: Surface, occlusion: f32) -> Received {
    let seen = seen_from(fragment.world);
    var taken = Received(
        sky_light(surface.normal) * occlusion,
        sky_reflection(
            surface.color.rgb,
            surface.normal,
            seen.towards,
            fragment.roughness,
            fragment.metallic,
        ) * occlusion,
    );
    for (var i = 0u; i < frame.lights; i++) {
        let light = lights[i];
        let towards = toward(light, fragment.world);
        let landed = clamp(dot(surface.normal, towards), 0.0, 1.0);
        let met = Incidence(landed, fragment.world, surface.normal, slope_of(landed));
        let reached = contribution(light, fragment.world, met.landed, towards)
            * fraction_landing(light, met);
        taken.reached += reached;
        taken.highlight += reached * highlight_of(fragment.roughness, surface.normal, towards, seen);
    }
    return taken;
}

// The relief texel a fragment reads, held inside the window it samples.
fn relief_at(fragment: Fragment) -> vec4<f32> {
    return textureSample(
        relief,
        base_color_sampler,
        held_inside(fragment.uv, fragment.window, textureDimensions(relief)),
    );
}

// The normal one relief texel holds, in the basis its three channels are
// read in: across the map, up it, and out of the surface. A texel with no
// normal at all faces out, so a relief a source left empty there is drawn
// instead of black.
fn facing_in(basis: mat3x3<f32>, sampled: vec3<f32>) -> vec3<f32> {
    let facing = basis * (sampled * 2.0 - 1.0);
    let reach = dot(facing, facing);
    return select(basis[2], facing * inverseSqrt(reach), reach > LEVEL);
}

// `direction` at unit length, or the zero vector where `direction` itself
// has zero length.
fn unit(direction: vec3<f32>) -> vec3<f32> {
    let reach = dot(direction, direction);
    return select(vec3<f32>(0.0), direction * inverseSqrt(reach), reach > 0.0);
}

// The derivative basis: the one the fragment's position and UV derivatives
// span, which is what a draw placed by its own transform reads its relief
// in, since no mesh holds a tangent stream. Across the texels, up them, and
// out along the surface's own normal.
fn derivative_basis(world: vec3<f32>, uv: vec2<f32>, normal: vec3<f32>) -> mat3x3<f32> {
    let step_x = dpdx(world);
    let step_y = dpdy(world);
    let uv_x = dpdx(uv);
    let uv_y = dpdy(uv);
    let winding = sign(uv_x.x * uv_y.y - uv_y.x * uv_x.y);
    let across = (step_x * uv_y.y - step_y * uv_x.y) * winding;
    let down = (step_y * uv_x.x - step_x * uv_y.x) * winding;
    return mat3x3<f32>(
        unit(across - normal * dot(normal, across)),
        unit(normal * dot(normal, down) - down),
        normal,
    );
}

// The normal a draw placed by its own transform shades with: the one its
// relief holds, read through that basis, or the mesh's own where its slot
// holds no relief. The basis is built whether or not the draw has a
// relief: a derivative is read only where every pixel beside this one reads
// it too.
fn placed_normal(fragment: Fragment) -> vec3<f32> {
    let normal = normalize(fragment.normal);
    let basis = derivative_basis(fragment.world, fragment.uv, normal);
    let facing = facing_in(basis, relief_at(fragment).xyz);
    return select(normal, facing, fragment.relief >= NORMALS);
}

// What one texel of a faced draw holds: the normal of the surface it
// shows, whether that normal came from the draw's own relief, how far the
// surface is offset from the plane, in meters, and whether the relief holds
// that offset at all.
struct Standing {
    facing: vec3<f32>,
    mapped: bool,
    height: f32,
    out: vec3<f32>,
    solid: bool,
}

// The relief read in the drawn sprite's own axes: across it, up it, and out
// of it towards the viewpoint that turned it. A draw whose relief holds no
// depth, and one with no relief at all, are given UNMAPPED_DEPTH of the
// sprite's width; one with no relief has no normal either, and takes every
// light whole.
fn standing_of(fragment: Fragment) -> Standing {
    let sampled = relief_at(fragment);
    let out = cross(fragment.across.xyz, fragment.upward);
    let solid = fragment.relief == SOLID;
    let height = select(UNMAPPED_DEPTH, sampled.a, solid) * fragment.across.w * HALF;
    if fragment.relief == NO_RELIEF {
        return Standing(vec3<f32>(0.0), false, height, out, false);
    }

    let turned = mat3x3<f32>(fragment.across.xyz, fragment.upward, out);
    return Standing(facing_in(turned, sampled.xyz), true, height, out, solid);
}

// Where a texel of a faced draw meets one light: its place on that plane,
// moved towards the light as far as the surface its relief shows is
// along that light.
//
// A mapped draw's own record is that same surface, texel for texel: the
// light's ray through this point crosses the plane where this fragment
// lies, and the caster moved that crossing off by the same depth. The two
// match, so only the depth format's own steps separate them and a faced
// draw cannot shadow itself. A draw without a relief is recorded flat and
// met half a sprite width towards the light, which clears its record by
// that much.
fn met_at(fragment: Fragment, standing: Standing, towards: vec3<f32>) -> vec3<f32> {
    let reach = select(
        standing.height,
        moved_towards(standing.height, standing.out, towards),
        standing.solid,
    );
    return fragment.world + towards * reach;
}

// The light received by a draw the viewpoint turned: the volume its relief
// holds, offset from the plane its record was drawn in, lit by the normals
// that relief holds. A draw without a relief takes every light whole; its
// own normal is the viewpoint's, which no light or highlight can be
// measured against. The sky is read and reflected through that normal.
fn received_facing(fragment: Fragment, surface: Surface, occlusion: f32) -> Received {
    let standing = standing_of(fragment);
    let upright = unit(fragment.plane.xyz);
    let seen = seen_from(fragment.world);
    let facing = select(seen.towards, standing.facing, standing.mapped);
    var taken = Received(
        sky_light(facing) * occlusion,
        sky_reflection(
            surface.color.rgb,
            facing,
            seen.towards,
            fragment.roughness,
            fragment.metallic,
        ) * occlusion,
    );
    for (var i = 0u; i < frame.lights; i++) {
        let light = lights[i];
        let towards = toward(light, fragment.world);
        let landed = select(1.0, clamp(dot(standing.facing, towards), 0.0, 1.0), standing.mapped);
        let met = Incidence(
            landed,
            met_at(fragment, standing, towards),
            towards,
            slope_of(abs(dot(upright, towards))),
        );
        let reached = contribution(light, met.compared, met.landed, towards)
            * fraction_landing(light, met);
        taken.reached += reached;
        taken.highlight += reached * highlight_of(fragment.roughness, standing.facing, towards, seen);
    }
    return taken;
}

// What a surface draws once `received` light has landed on it. The
// highlight and the sky it reflects land with the rest of the light, so an
// unlit surface takes none of them.
//
// A metal takes less of the light that lands on it as its own color, until
// at 1.0 the sky it reflects and its highlight are all it draws.
fn lit(fragment: Fragment, surface: Surface, received: Received) -> vec4<f32> {
    let taken = surface.color.rgb * received.reached * (1.0 - fragment.metallic);
    let received_color = mix(surface.color.rgb, taken + received.highlight, fragment.litness);
    return vec4<f32>(received_color + surface.emissive, surface.color.a);
}

// Whether a cutout instance leaves this texel out: every draw of the cutout
// pipeline is one, and a blended draw is where its material requested it.
fn dropped(fragment: Fragment, surface: Surface) -> bool {
    return fragment.cutout == 1u && surface.color.a < THRESHOLD;
}

// What a draw leaves at a pixel, before the stage that calls it decides the
// depth; its slot's shading map scales the fragment's roughness and
// metallic lanes.
fn shaded(fragment: Fragment) -> vec4<f32> {
    let texel = shading_of(fragment);
    let scaled = scaled_by(fragment, texel);
    let surface = surface_of(scaled, placed_normal(scaled));
    return lit(scaled, surface, received(scaled, surface, texel.r));
}

// The same, dropping the texels a cutout instance leaves out.
fn shaded_tested(fragment: Fragment) -> vec4<f32> {
    let texel = shading_of(fragment);
    let scaled = scaled_by(fragment, texel);
    let surface = surface_of(scaled, placed_normal(scaled));
    if dropped(scaled, surface) {
        discard;
    }
    return lit(scaled, surface, received(scaled, surface, texel.r));
}

// The same two, lit by the relief the draw's slot holds.
fn faced_shaded(fragment: Fragment) -> vec4<f32> {
    let texel = shading_of(fragment);
    let scaled = scaled_by(fragment, texel);
    let surface = surface_of(scaled, normalize(scaled.normal));
    return lit(scaled, surface, received_facing(scaled, surface, texel.r));
}

fn faced_shaded_tested(fragment: Fragment) -> vec4<f32> {
    let texel = shading_of(fragment);
    let scaled = scaled_by(fragment, texel);
    let surface = surface_of(scaled, normalize(scaled.normal));
    if dropped(scaled, surface) {
        discard;
    }
    return lit(scaled, surface, received_facing(scaled, surface, texel.r));
}

// What a stage that takes its depth from the plane its draw lies in leaves:
// the color, and that depth in place of the one the corners left.
struct Lying {
    @location(0) color: vec4<f32>,
    @builtin(frag_depth) depth: f32,
}

@fragment
fn fragment(fragment: Fragment) -> @location(0) vec4<f32> {
    return shaded(fragment);
}

// The same, dropping the texels a cutout instance leaves out.
@fragment
fn fragment_tested(fragment: Fragment) -> @location(0) vec4<f32> {
    return shaded_tested(fragment);
}

// The depth a draw lying in a plane leaves at this pixel.
fn lying_depth(fragment: Fragment) -> f32 {
    return plane_depth(fragment.plane, fragment.clip_position);
}

// The same three for a draw lying in a plane, which takes that plane's
// depth.
@fragment
fn flat_fragment(fragment: Fragment) -> Lying {
    return Lying(shaded(fragment), lying_depth(fragment));
}

@fragment
fn flat_fragment_tested(fragment: Fragment) -> Lying {
    return Lying(shaded_tested(fragment), lying_depth(fragment));
}

// The same three again, for a draw the viewpoint turned, which lies in the
// upright plane it was placed in.
@fragment
fn faced_fragment(fragment: Fragment) -> Lying {
    return Lying(faced_shaded(fragment), lying_depth(fragment));
}

@fragment
fn faced_fragment_tested(fragment: Fragment) -> Lying {
    return Lying(faced_shaded_tested(fragment), lying_depth(fragment));
}


// What the sky's vertex stage leaves: where the pixel lies across the
// frame, in the same `-1..1` the corners are placed at.
struct SkyCovered {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) clip: vec2<f32>,
}

// One triangle large enough to cover the frame, at the far depth, so no
// vertex buffer is bound and the sky lands only where nothing nearer was
// drawn.
@vertex
fn sky_cover(@builtin(vertex_index) index: u32) -> SkyCovered {
    let corner = vec2<f32>(f32((index << 1u) & 2u), f32(index & 2u)) * 2.0 - 1.0;

    var covered: SkyCovered;
    covered.clip_position = vec4<f32>(corner, SKY_FAR, 1.0);
    covered.clip = corner;
    return covered;
}

// The direction the camera reads the sky along at `clip`. A lens that
// foreshortens takes a ray of its own through every pixel; one that does not
// takes the direction it looks through all of them, which draws one texel
// over the whole frame.
fn sky_along(clip: vec2<f32>) -> vec3<f32> {
    if frame.foreshortened == 0u {
        return normalize(frame.looking);
    }
    let along = frame.sky_from_clip * vec4<f32>(clip, SKY_FAR, 1.0);
    return normalize(along.xyz / along.w);
}

@fragment
fn sky_draw(covered: SkyCovered) -> @location(0) vec4<f32> {
    let along = sky_at(sky_along(covered.clip));
    return vec4<f32>(textureSampleLevel(sky, sky_sampler, along, 0.0).rgb, 1.0);
}