Discard cutout

Discard cutout

A fragment helper discards outside a centred circle and returns the fill colour, called once as the fragment IO-struct's constructor argument.

Discard cutout shader, written in TypeScript and compiled by TypeShade, rendered at build time.
// ═══ typeshade example — discard cutout (#1840 GLSL-legalize coverage) ═══
//
// A fragment helper discards outside a centred circle and returns the fill colour;
// the fragment stage calls it ONCE, as the sole argument of the fragment IO-struct's
// `.construct({...})` — the exact shape ANGLE's D3D11 backend miscompiles when the
// discarding call is left inline in a GLSL struct constructor (glsl-legalize.ts,
// #1840). This example's GLSL golden pins the `_dh0` local the legalize pass
// hoists it into; the WGSL golden stays the untouched inline call (WGSL has no
// such backend bug, so it is byte-untouched by design).
import {
fn,
module,
ioStruct,
vec3,
vec4,
location,
vec2fT,
vec4fT,
If,
Discard,
Let,
length,
mix,
} from '../src/index.js';
import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.js';
import type { ShaderExample } from './_shared.js';
const U = fullscreenUniforms();
const FsOut = ioStruct('FsOut', { color: location(0, vec4fT) });
// Discards outside the unit circle; inside, a radial gradient from a bright
// centre to a dim rim.
const discardOutsideCircle = fn('discard_outside_circle', { p: vec2fT }, ({ p }) => {
const r = Let(length(p));
If(r.gt(1), () => {
Discard();
});
return vec4(mix(vec3(1, 1, 1), vec3(0.06, 0.1, 0.35), r), 1);
});
const fs = fn(
'fs',
{ vo: VsOut },
({ vo }) => {
const p = screenCoords(vo.uv, U.field.resolution);
return FsOut.construct({ color: discardOutsideCircle({ p }) });
},
{ stage: 'fragment' },
);
const discardCutoutModule = module({
structs: [U.struct, VsOut.decl, FsOut.decl],
bindings: [U.binding],
funcs: [vs, fs],
});
export const discardCutout: ShaderExample = {
id: 'discard-cutout',
title: 'Discard cutout',
blurb:
"A fragment helper discards outside a centred circle and returns the fill colour, called once as the fragment IO-struct's constructor argument — the exact shape ANGLE's D3D11 backend miscompiles when left inline (#1840). Renders a clean circular cutout with a radial centre-to-rim gradient.",
category: 'generic',
file: 'discard-cutout.ts',
module: discardCutoutModule,
renderable: true,
controls: {
time: { kind: 'time' },
resolution: { kind: 'resolution' },
},
};
struct Uniforms {
time: f32,
resolution: vec2<f32>,
}
struct VsOut {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
}
struct FsOut {
@location(0) color: vec4<f32>,
}
@group(0) @binding(0) var<uniform> U: Uniforms;
fn discard_outside_circle(p: vec2<f32>) -> vec4<f32> {
let _v0 = length(p);
if ((_v0 > 1.0)) {
discard;
}
return vec4<f32>(mix(vec3<f32>(1.0, 1.0, 1.0), vec3<f32>(0.06, 0.1, 0.35), _v0), 1.0);
}
@vertex
fn vs(@builtin(vertex_index) vi: u32) -> VsOut {
let _cse0 = ((f32((vi & 1u)) * 4.0) - 1.0);
let _cse1 = ((f32((vi >> 1u)) * 4.0) - 1.0);
return VsOut(vec4<f32>(_cse0, _cse1, 0.0, 1.0), vec2<f32>(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));
}
@fragment
fn fs(vo: VsOut) -> FsOut {
return FsOut(discard_outside_circle(vec2<f32>((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0))));
}
#version 300 es
precision highp float;
precision highp int;
out vec2 uv;
void main() {
uint vi = uint(gl_VertexID);
float _cse0 = ((float((vi & 1u)) * 4.0) - 1.0);
float _cse1 = ((float((vi >> 1u)) * 4.0) - 1.0);
gl_Position = vec4(_cse0, _cse1, 0.0, 1.0);
uv = vec2(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5));
}
#version 300 es
precision highp float;
precision highp int;
layout(std140) uniform Uniforms {
float time;
vec2 resolution;
} U;
vec4 discard_outside_circle(vec2 p) {
float _v0 = length(p);
if ((_v0 > 1.0)) {
discard;
}
return vec4(mix(vec3(1.0, 1.0, 1.0), vec3(0.06, 0.1, 0.35), _v0), 1.0);
}
in vec2 uv;
layout(location = 0) out vec4 color;
void main() {
vec4 _dh0 = discard_outside_circle(vec2((((uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((uv.y * 2.0) - 1.0)));
color = _dh0;
}

Discard cutout, drawn at build time.

The WGSL and GLSL tabs are the compiler's own output at commit 26de7be8, read from the goldens its emit suite bakes (emit-goldens.test.ts).

This example is written against the fn() builder API, which the editor in the Playground does not take.

File on GitHub

Edit this page Report a problem