@@ -40,8 +40,6 @@ uniform float uIntensity;
4040uniform vec2 uScreenSize;
4141uniform float uBrightness;
4242uniform float uContrast;
43- uniform float uRoundness;
44- uniform float uPixelSize;
4543
4644// --- constants, in the original's 960x540 present space ---
4745const float CURVE = 9.0; // px the image is squeezed at the edges
@@ -62,23 +60,6 @@ const float VIGNETTE_INNER = 0.45;
6260const float VIGNETTE_OUTER = 0.95;
6361const float VIGNETTE_ALPHA = 0.5;
6462
65- // Superellipse exponents the roundness slider sweeps between. 8 still reads as
66- // a square - only the very corners are off - and 2 is a true circle.
67- const float SQUIRCLE_SQUARE = 8.0;
68- const float SQUIRCLE_ROUND = 2.0;
69- // How dark the gap between dots goes at full roundness. Below 1 so the picture
70- // keeps some floor rather than dropping to black between pixels.
71- const float DOT_DEPTH = 0.85;
72- // Average of the dot mask over one cell, which is what the brightness
73- // compensation has to undo. Straight lines in the feather width, fitted to the
74- // mask integrated numerically at each end - 1 - pi/4 is the right answer only
75- // for a hard-edged circle, and the feather widens it by more than it looks:
76- // at the widest setting a square already averages 0.29, not 0.02.
77- const float GAP_SQUARE_BASE = 0.026;
78- const float GAP_SQUARE_SLOPE = 0.876;
79- const float GAP_ROUND_BASE = 0.218;
80- const float GAP_ROUND_SLOPE = 0.703;
81-
8263vec3 sampleScene(vec2 frameUv) {
8364 // Outside the tube there is nothing behind the glass.
8465 if (frameUv.x < 0.0 || frameUv.x > 1.0 || frameUv.y < 0.0 || frameUv.y > 1.0) return vec3(0.0);
@@ -144,36 +125,6 @@ void main() {
144125 float grilleAlpha = col < 2.0 ? GRILLE_ALPHA : GRILLE_ALPHA_B;
145126 color = mix(color, tint, grilleAlpha);
146127
147- // --- pixel roundness --------------------------------------------------
148- // Shapes each *game* pixel, so it needs uPixelSize rather than the grille's
149- // uGridPeriod - those are the same thing only at zoom 3 and above.
150- //
151- // A superellipse, |x|^n + |y|^n = 1: n large is a square with the corners
152- // barely off, n = 2 is a circle. Sweeping the exponent rather than mixing
153- // in a circle keeps every intermediate value a real shape instead of a
154- // cross-fade between two, so the slider reads as one dial being turned.
155- //
156- // Carving gaps costs light, so the level is put back below - see there.
157- if (uRoundness > 0.0 && uPixelSize >= 2.0) {
158- vec2 cell = fract(frag / uPixelSize) * 2.0 - 1.0;
159- float n = mix(SQUIRCLE_SQUARE, SQUIRCLE_ROUND, uRoundness);
160- float d = pow(pow(abs(cell.x), n) + pow(abs(cell.y), n), 1.0 / n);
161- // Feather over roughly a device pixel so a dot has an edge rather than
162- // a staircase, but kept narrow: a wide ramp shades most of the cell
163- // instead of just its corners, and the picture goes out with it.
164- float feather = clamp(1.2 / uPixelSize, 0.05, 0.3);
165- float mask = smoothstep(1.0 - feather, 1.0, d);
166-
167- // Carving gaps removes light, and a control labelled "round" that also
168- // dims is two controls. Divide the mask by its own average so the cell
169- // keeps the level it came in with, and only its shape changes.
170- float gap = mix(
171- GAP_SQUARE_BASE + GAP_SQUARE_SLOPE * feather,
172- GAP_ROUND_BASE + GAP_ROUND_SLOPE * feather,
173- uRoundness);
174- color *= (1.0 - uRoundness * DOT_DEPTH * mask) / (1.0 - uRoundness * DOT_DEPTH * gap);
175- }
176-
177128 // --- slow rolling band ------------------------------------------------
178129 float bandSpan = size.y + 160.0 * px;
179130 float bandY = mod(uTime * BAND_SPEED * px, bandSpan) - 160.0 * px;
@@ -223,10 +174,6 @@ export interface CrtOptions {
223174 brightness ?: number
224175 /** 1 = unchanged, pivoting around mid grey. Applied after the CRT pass. */
225176 contrast ?: number
226- /** 0 square, 1 round. See the shader's pixel-roundness block. */
227- roundness ?: number
228- /** One game pixel in device pixels - `zoom * resolution`. */
229- pixelSize ?: number
230177}
231178
232179export class CrtFilter extends Filter {
@@ -240,8 +187,6 @@ export class CrtFilter extends Filter {
240187 uScreenSize : { value : new Float32Array ( [ 960 , 540 ] ) , type : 'vec2<f32>' } ,
241188 uBrightness : { value : options . brightness ?? 1.05 , type : 'f32' } ,
242189 uContrast : { value : options . contrast ?? 1.21 , type : 'f32' } ,
243- uRoundness : { value : options . roundness ?? 0 , type : 'f32' } ,
244- uPixelSize : { value : options . pixelSize ?? 3 , type : 'f32' } ,
245190 } )
246191
247192 super ( {
@@ -264,8 +209,6 @@ export class CrtFilter extends Filter {
264209 uGridPeriod : number
265210 uGlow : number
266211 uIntensity : number
267- uRoundness : number
268- uPixelSize : number
269212 uScreenSize : Float32Array
270213 uBrightness : number
271214 uContrast : number
@@ -286,27 +229,6 @@ export class CrtFilter extends Filter {
286229 this . uniforms . uGridPeriod = value
287230 }
288231
289- /**
290- * One game pixel in device pixels - `zoom * resolution`.
291- *
292- * Not `gridPeriod`: that is a scanline cycle, which is grown to at least
293- * three device pixels so the mask can draw at all. Rounding wants the real
294- * pixel, and switches itself off below two device pixels because there is
295- * nothing there to shape.
296- */
297- set pixelSize ( value : number ) {
298- this . uniforms . uPixelSize = value
299- }
300-
301- /** 0 leaves pixels square; 1 makes them round. */
302- get roundness ( ) : number {
303- return this . uniforms . uRoundness
304- }
305-
306- set roundness ( value : number ) {
307- this . uniforms . uRoundness = Math . max ( 0 , Math . min ( 1 , value ) )
308- }
309-
310232 /**
311233 * How much bloom and convergence ghosting to add. 1 is the amount the
312234 * original effect was authored with; 0 leaves the picture sharp.
0 commit comments