eml-stdlib 0.4.0__py3-none-any.whl
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.
- eml_stdlib/__init__.py +81 -0
- eml_stdlib/ballistics/air_density.eml +73 -0
- eml_stdlib/ballistics/ballistic_solver.eml +102 -0
- eml_stdlib/ballistics/coriolis.eml +60 -0
- eml_stdlib/ballistics/drag.eml +44 -0
- eml_stdlib/ballistics/gravity.eml +52 -0
- eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
- eml_stdlib/ballistics/spin_drift.eml +60 -0
- eml_stdlib/ballistics/time_of_flight.eml +79 -0
- eml_stdlib/ballistics/wind.eml +58 -0
- eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
- eml_stdlib/biology/abilities/bite_force.eml +48 -0
- eml_stdlib/biology/abilities/chromatophore.eml +50 -0
- eml_stdlib/biology/abilities/discharge.eml +45 -0
- eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
- eml_stdlib/biology/abilities/locomotion.eml +50 -0
- eml_stdlib/biology/abilities/muscle_power.eml +47 -0
- eml_stdlib/biology/abilities/sprint.eml +36 -0
- eml_stdlib/biology/abilities/texture_match.eml +49 -0
- eml_stdlib/biology/abilities/toxin.eml +44 -0
- eml_stdlib/biology/cone_l.eml +36 -0
- eml_stdlib/biology/cone_s.eml +32 -0
- eml_stdlib/biology/cryptochrome.eml +44 -0
- eml_stdlib/biology/geomagnetic_field.eml +32 -0
- eml_stdlib/biology/goldman_equation.eml +41 -0
- eml_stdlib/biology/hill_equation.eml +65 -0
- eml_stdlib/biology/hodgkin_huxley.eml +108 -0
- eml_stdlib/biology/michaelis_menten.eml +65 -0
- eml_stdlib/biology/plume_diffusion.eml +47 -0
- eml_stdlib/biology/receptor_binding.eml +35 -0
- eml_stdlib/biology/rod_sensitivity.eml +38 -0
- eml_stdlib/biology/tapetum.eml +70 -0
- eml_stdlib/carriers/acoustic_cloak.eml +56 -0
- eml_stdlib/carriers/ferron_emission.eml +55 -0
- eml_stdlib/carriers/ferron_logic.eml +45 -0
- eml_stdlib/carriers/ferron_propagation.eml +69 -0
- eml_stdlib/carriers/mach_zehnder.eml +52 -0
- eml_stdlib/carriers/magnon_dispersion.eml +57 -0
- eml_stdlib/carriers/magnon_logic.eml +45 -0
- eml_stdlib/carriers/optical_neuron.eml +55 -0
- eml_stdlib/carriers/phonon_bandgap.eml +54 -0
- eml_stdlib/carriers/ring_resonator.eml +48 -0
- eml_stdlib/carriers/spin_torque.eml +61 -0
- eml_stdlib/carriers/thermal_rectifier.eml +63 -0
- eml_stdlib/catalog.json +4209 -0
- eml_stdlib/circuits/buck_converter.eml +74 -0
- eml_stdlib/circuits/cmos_inverter.eml +75 -0
- eml_stdlib/circuits/h_bridge.eml +69 -0
- eml_stdlib/circuits/ldo.eml +66 -0
- eml_stdlib/circuits/mosfet_iv.eml +59 -0
- eml_stdlib/circuits/pll_loop.eml +63 -0
- eml_stdlib/circuits/rc_filter.eml +98 -0
- eml_stdlib/circuits/voltage_divider.eml +68 -0
- eml_stdlib/control/bangbang.eml +34 -0
- eml_stdlib/control/deadband.eml +28 -0
- eml_stdlib/control/hysteresis.eml +28 -0
- eml_stdlib/control/kalman_1d.eml +53 -0
- eml_stdlib/control/lqr_1d.eml +47 -0
- eml_stdlib/control/pid.eml +38 -0
- eml_stdlib/gaming/animation/bounce.eml +39 -0
- eml_stdlib/gaming/animation/ease_in.eml +27 -0
- eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
- eml_stdlib/gaming/animation/ease_out.eml +27 -0
- eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
- eml_stdlib/gaming/animation/spring.eml +39 -0
- eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
- eml_stdlib/gaming/audio/doppler.eml +39 -0
- eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
- eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
- eml_stdlib/gaming/camera/dof_blur.eml +30 -0
- eml_stdlib/gaming/camera/fov_projection.eml +47 -0
- eml_stdlib/gaming/camera/orbit.eml +48 -0
- eml_stdlib/gaming/camera/shake.eml +20 -0
- eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
- eml_stdlib/gaming/lighting/area_light.eml +50 -0
- eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
- eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
- eml_stdlib/gaming/lighting/point_light.eml +33 -0
- eml_stdlib/gaming/lighting/spot_light.eml +43 -0
- eml_stdlib/gaming/noise/fbm.eml +65 -0
- eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
- eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
- eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
- eml_stdlib/gaming/noise/turbulence.eml +46 -0
- eml_stdlib/gaming/noise/value_noise.eml +45 -0
- eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
- eml_stdlib/gaming/noise/white_noise.eml +47 -0
- eml_stdlib/gaming/noise/worley.eml +79 -0
- eml_stdlib/gaming/particles/drag.eml +30 -0
- eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
- eml_stdlib/gaming/particles/fade.eml +36 -0
- eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
- eml_stdlib/gaming/particles/size_over_life.eml +28 -0
- eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
- eml_stdlib/gaming/physics/buoyancy.eml +43 -0
- eml_stdlib/gaming/physics/circle_collision.eml +46 -0
- eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
- eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
- eml_stdlib/gaming/physics/spring_damper.eml +30 -0
- eml_stdlib/gaming/physics/verlet.eml +29 -0
- eml_stdlib/gaming/shading/fresnel.eml +57 -0
- eml_stdlib/gaming/shading/matcap.eml +34 -0
- eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
- eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
- eml_stdlib/gaming/shading/subsurface.eml +52 -0
- eml_stdlib/gaming/shading/toon.eml +30 -0
- eml_stdlib/gaming/terrain/biome_select.eml +41 -0
- eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
- eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
- eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
- eml_stdlib/gaming/terrain/heightmap.eml +33 -0
- eml_stdlib/gaming/textures/brick.eml +27 -0
- eml_stdlib/gaming/textures/caustics.eml +25 -0
- eml_stdlib/gaming/textures/checkerboard.eml +27 -0
- eml_stdlib/gaming/textures/fire.eml +26 -0
- eml_stdlib/gaming/textures/marble.eml +24 -0
- eml_stdlib/gaming/textures/rust.eml +23 -0
- eml_stdlib/gaming/textures/water_surface.eml +49 -0
- eml_stdlib/gaming/textures/wood.eml +41 -0
- eml_stdlib/math/abs.eml +45 -0
- eml_stdlib/math/atan.eml +32 -0
- eml_stdlib/math/atan2.eml +61 -0
- eml_stdlib/math/bessel_j0.eml +65 -0
- eml_stdlib/math/cos.eml +42 -0
- eml_stdlib/math/cosh.eml +24 -0
- eml_stdlib/math/erf.eml +66 -0
- eml_stdlib/math/exp.eml +49 -0
- eml_stdlib/math/gamma.eml +55 -0
- eml_stdlib/math/gaussian.eml +18 -0
- eml_stdlib/math/log.eml +59 -0
- eml_stdlib/math/pow.eml +51 -0
- eml_stdlib/math/relu.eml +63 -0
- eml_stdlib/math/sigmoid.eml +21 -0
- eml_stdlib/math/sin.eml +37 -0
- eml_stdlib/math/sinh.eml +24 -0
- eml_stdlib/math/softmax.eml +87 -0
- eml_stdlib/math/sqrt.eml +46 -0
- eml_stdlib/math/tan.eml +36 -0
- eml_stdlib/math/tanh.eml +54 -0
- eml_stdlib/ml/attention.eml +70 -0
- eml_stdlib/ml/cross_entropy.eml +85 -0
- eml_stdlib/ml/gelu.eml +51 -0
- eml_stdlib/ml/layernorm.eml +49 -0
- eml_stdlib/ml/leaky_relu.eml +51 -0
- eml_stdlib/ml/relu.eml +63 -0
- eml_stdlib/ml/rotary_embed.eml +64 -0
- eml_stdlib/ml/silu.eml +40 -0
- eml_stdlib/ml/softmax.eml +87 -0
- eml_stdlib/physics/coulomb.eml +48 -0
- eml_stdlib/physics/damped_oscillator.eml +20 -0
- eml_stdlib/physics/diffusion.eml +45 -0
- eml_stdlib/physics/gravity.eml +60 -0
- eml_stdlib/physics/hooke.eml +56 -0
- eml_stdlib/physics/navier_stokes_1d.eml +57 -0
- eml_stdlib/physics/planck_radiation.eml +77 -0
- eml_stdlib/physics/stefan_boltzmann.eml +75 -0
- eml_stdlib/physics/wave_propagation.eml +52 -0
- eml_stdlib/quantum/cnot.eml +72 -0
- eml_stdlib/quantum/grover_oracle.eml +69 -0
- eml_stdlib/quantum/hadamard.eml +73 -0
- eml_stdlib/quantum/pauli_x.eml +29 -0
- eml_stdlib/quantum/pauli_z.eml +29 -0
- eml_stdlib/quantum/phase_gate.eml +76 -0
- eml_stdlib/quantum/rotation_rz.eml +92 -0
- eml_stdlib/sensors/accelerometer.eml +59 -0
- eml_stdlib/sensors/hall_effect.eml +42 -0
- eml_stdlib/sensors/photodetector.eml +58 -0
- eml_stdlib/sensors/rtd.eml +57 -0
- eml_stdlib/sensors/strain_gauge.eml +63 -0
- eml_stdlib/sensors/thermistor.eml +71 -0
- eml_stdlib/signal/bandpass.eml +39 -0
- eml_stdlib/signal/chirp.eml +42 -0
- eml_stdlib/signal/dft_single.eml +30 -0
- eml_stdlib/signal/fft_butterfly.eml +36 -0
- eml_stdlib/signal/highpass.eml +45 -0
- eml_stdlib/signal/lowpass.eml +49 -0
- eml_stdlib/signal/matched_filter.eml +48 -0
- eml_stdlib/signal/notch_filter.eml +47 -0
- eml_stdlib/signal/window_hamming.eml +47 -0
- eml_stdlib/signal/window_hann.eml +50 -0
- eml_stdlib-0.4.0.dist-info/METADATA +453 -0
- eml_stdlib-0.4.0.dist-info/RECORD +185 -0
- eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
- eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
- eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// pbr_diffuse.eml -- Lambertian diffuse shading.
|
|
2
|
+
//
|
|
3
|
+
// L = albedo * max(0, n · l) / π
|
|
4
|
+
//
|
|
5
|
+
// The 1/π factor is the energy-conservation normalisation; many
|
|
6
|
+
// real-time shaders fold it into the light intensity instead.
|
|
7
|
+
// Both forms are provided.
|
|
8
|
+
|
|
9
|
+
module pbr_diffuse;
|
|
10
|
+
|
|
11
|
+
const PI: Real = 3.141592653589793
|
|
12
|
+
const INV_PI: Real = 0.31830988618379
|
|
13
|
+
|
|
14
|
+
@verify(lean, theorem = "lambert_in_unit_interval")
|
|
15
|
+
fn lambert(n_dot_l: Real) -> Real
|
|
16
|
+
where chain_order <= 0
|
|
17
|
+
ensures (result >= 0.0)
|
|
18
|
+
ensures (result <= 1.0)
|
|
19
|
+
{
|
|
20
|
+
clamp(n_dot_l, 0.0, 1.0)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@verify(lean, theorem = "lambert_normalised_nonneg")
|
|
24
|
+
fn lambert_normalised(n_dot_l: Real) -> Real
|
|
25
|
+
where chain_order <= 0
|
|
26
|
+
ensures (result >= 0.0)
|
|
27
|
+
{
|
|
28
|
+
clamp(n_dot_l, 0.0, 1.0) * INV_PI
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Half-Lambert (Valve trick): wrap-around term for soft skin /
|
|
32
|
+
// foliage shading. Maps n·l ∈ [-1, 1] to [0.25, 1].
|
|
33
|
+
@verify(lean, theorem = "half_lambert_in_unit_interval")
|
|
34
|
+
fn half_lambert(n_dot_l: Real) -> Real
|
|
35
|
+
where chain_order <= 0
|
|
36
|
+
ensures (result >= 0.0)
|
|
37
|
+
ensures (result <= 1.0)
|
|
38
|
+
{
|
|
39
|
+
let s: Real = 0.5 * n_dot_l + 0.5;
|
|
40
|
+
s * s
|
|
41
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// pbr_specular.eml -- Cook-Torrance microfacet specular BRDF.
|
|
2
|
+
//
|
|
3
|
+
// f_spec = (D * F * G) / (4 * (n·v) * (n·l))
|
|
4
|
+
//
|
|
5
|
+
// Where:
|
|
6
|
+
// D = GGX (Trowbridge-Reitz) normal distribution
|
|
7
|
+
// F = Schlick Fresnel
|
|
8
|
+
// G = Smith geometry term (height-correlated GGX visibility)
|
|
9
|
+
//
|
|
10
|
+
// All sub-terms are bounded ∈ [0, 1]; the caller multiplies by the
|
|
11
|
+
// light radiance and integrates over the hemisphere.
|
|
12
|
+
|
|
13
|
+
module pbr_specular;
|
|
14
|
+
|
|
15
|
+
use stdlib::gaming::shading::fresnel;
|
|
16
|
+
|
|
17
|
+
const PI: Real = 3.141592653589793
|
|
18
|
+
|
|
19
|
+
// GGX distribution.
|
|
20
|
+
@verify(lean, theorem = "ggx_in_unit_interval_for_unit_alpha")
|
|
21
|
+
fn d_ggx(n_dot_h: Real, alpha: Real) -> Real
|
|
22
|
+
where chain_order <= 0,
|
|
23
|
+
domain: alpha > 0.0
|
|
24
|
+
ensures (result >= 0.0)
|
|
25
|
+
{
|
|
26
|
+
let a2: Real = alpha * alpha;
|
|
27
|
+
let nh2: Real = clamp(n_dot_h, 0.0, 1.0) * clamp(n_dot_h, 0.0, 1.0);
|
|
28
|
+
let denom: Real = nh2 * (a2 - 1.0) + 1.0;
|
|
29
|
+
a2 / (PI * denom * denom + 1.0e-7)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Smith geometry term, height-correlated GGX visibility.
|
|
33
|
+
@verify(lean, theorem = "smith_visibility_in_unit_interval")
|
|
34
|
+
fn v_smith(n_dot_v: Real, n_dot_l: Real, alpha: Real) -> Real
|
|
35
|
+
where chain_order <= 1,
|
|
36
|
+
domain: alpha > 0.0
|
|
37
|
+
ensures (result >= 0.0)
|
|
38
|
+
{
|
|
39
|
+
let a2: Real = alpha * alpha;
|
|
40
|
+
let nv: Real = clamp(n_dot_v, 1.0e-4, 1.0);
|
|
41
|
+
let nl: Real = clamp(n_dot_l, 1.0e-4, 1.0);
|
|
42
|
+
let lambda_v: Real = nl * sqrt(nv * nv * (1.0 - a2) + a2);
|
|
43
|
+
let lambda_l: Real = nv * sqrt(nl * nl * (1.0 - a2) + a2);
|
|
44
|
+
0.5 / (lambda_v + lambda_l + 1.0e-7)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Composite Cook-Torrance specular.
|
|
48
|
+
@verify(lean, theorem = "cook_torrance_nonneg")
|
|
49
|
+
fn specular(n_dot_l: Real, n_dot_v: Real, n_dot_h: Real,
|
|
50
|
+
v_dot_h: Real, f0: Real, alpha: Real) -> Real
|
|
51
|
+
where chain_order <= 1,
|
|
52
|
+
domain: alpha > 0.0
|
|
53
|
+
requires (f0 >= 0.0)
|
|
54
|
+
requires (f0 <= 1.0)
|
|
55
|
+
ensures (result >= 0.0)
|
|
56
|
+
{
|
|
57
|
+
let d: Real = d_ggx(n_dot_h, alpha);
|
|
58
|
+
let f: Real = fresnel::schlick(f0, v_dot_h);
|
|
59
|
+
let v: Real = v_smith(n_dot_v, n_dot_l, alpha);
|
|
60
|
+
d * f * v
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Roughness → α = roughness² is the standard remap; clamped away
|
|
64
|
+
// from 0 to keep D_GGX well-defined.
|
|
65
|
+
@verify(lean, theorem = "roughness_to_alpha_in_band")
|
|
66
|
+
fn roughness_to_alpha(roughness: Real) -> Real
|
|
67
|
+
where chain_order <= 0
|
|
68
|
+
requires (roughness >= 0.0)
|
|
69
|
+
requires (roughness <= 1.0)
|
|
70
|
+
ensures (result >= 1.0e-4)
|
|
71
|
+
ensures (result <= 1.0)
|
|
72
|
+
{
|
|
73
|
+
let r: Real = clamp(roughness, 0.0, 1.0);
|
|
74
|
+
clamp(r * r, 1.0e-4, 1.0)
|
|
75
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// subsurface.eml -- Christensen-Burley subsurface scattering approx.
|
|
2
|
+
//
|
|
3
|
+
// For real-time skin / wax / leaves: light entering through a thin
|
|
4
|
+
// region scatters and emerges in a halo of the material's albedo.
|
|
5
|
+
// Burley's approximation is a sum of two exponentials in distance,
|
|
6
|
+
// integrated against the surface normal:
|
|
7
|
+
//
|
|
8
|
+
// sss(n, l, k_back) = k_back + (1 - k_back) * max(0, n · l)
|
|
9
|
+
//
|
|
10
|
+
// Plus a wrap-around term for the back-lit hemisphere. Chain order 1.
|
|
11
|
+
|
|
12
|
+
module subsurface;
|
|
13
|
+
|
|
14
|
+
@verify(lean, theorem = "sss_wrap_in_unit_interval")
|
|
15
|
+
fn wrap(n_dot_l: Real, wrap_amount: Real) -> Real
|
|
16
|
+
where chain_order <= 0
|
|
17
|
+
requires (wrap_amount >= 0.0)
|
|
18
|
+
requires (wrap_amount <= 1.0)
|
|
19
|
+
ensures (result >= 0.0)
|
|
20
|
+
ensures (result <= 1.0)
|
|
21
|
+
{
|
|
22
|
+
let s: Real = (n_dot_l + wrap_amount) / (1.0 + wrap_amount);
|
|
23
|
+
clamp(s, 0.0, 1.0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Burley back-scatter term: (l ∙ -v + 1) raised to a hardness.
|
|
27
|
+
@verify(lean, theorem = "sss_back_in_unit_interval")
|
|
28
|
+
fn back_scatter(l_dot_neg_v: Real, hardness: Real) -> Real
|
|
29
|
+
where chain_order <= 1,
|
|
30
|
+
domain: hardness >= 0.0
|
|
31
|
+
ensures (result >= 0.0)
|
|
32
|
+
ensures (result <= 1.0)
|
|
33
|
+
{
|
|
34
|
+
let s: Real = clamp(0.5 * l_dot_neg_v + 0.5, 0.0, 1.0);
|
|
35
|
+
pow(s, hardness)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Composite SSS contribution. albedo handled by caller.
|
|
39
|
+
@verify(lean, theorem = "sss_total_nonneg")
|
|
40
|
+
fn sss_total(n_dot_l: Real, l_dot_neg_v: Real,
|
|
41
|
+
wrap_amount: Real, hardness: Real,
|
|
42
|
+
back_strength: Real) -> Real
|
|
43
|
+
where chain_order <= 1,
|
|
44
|
+
domain: back_strength >= 0.0
|
|
45
|
+
requires (wrap_amount >= 0.0 && wrap_amount <= 1.0)
|
|
46
|
+
requires (hardness >= 0.0)
|
|
47
|
+
ensures (result >= 0.0)
|
|
48
|
+
{
|
|
49
|
+
let lit: Real = wrap(n_dot_l, wrap_amount);
|
|
50
|
+
let back: Real = back_scatter(l_dot_neg_v, hardness);
|
|
51
|
+
lit + back_strength * back
|
|
52
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// toon.eml -- step-based cel shading.
|
|
2
|
+
//
|
|
3
|
+
// Banded diffuse: take n·l, quantise into K levels.
|
|
4
|
+
// Output ∈ [0, 1].
|
|
5
|
+
|
|
6
|
+
module toon;
|
|
7
|
+
|
|
8
|
+
@verify(lean, theorem = "toon_in_unit_interval")
|
|
9
|
+
fn cel_diffuse(n_dot_l: Real, levels: Real) -> Real
|
|
10
|
+
where chain_order <= 0,
|
|
11
|
+
domain: levels >= 1.0
|
|
12
|
+
ensures (result >= 0.0)
|
|
13
|
+
ensures (result <= 1.0)
|
|
14
|
+
{
|
|
15
|
+
let v: Real = clamp(n_dot_l, 0.0, 1.0);
|
|
16
|
+
let q: Real = floor(v * levels) / levels;
|
|
17
|
+
clamp(q, 0.0, 1.0)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Rim-light for outline emphasis: high near grazing angles.
|
|
21
|
+
@verify(lean, theorem = "toon_rim_in_unit_interval")
|
|
22
|
+
fn rim_light(n_dot_v: Real, rim_power: Real) -> Real
|
|
23
|
+
where chain_order <= 1,
|
|
24
|
+
domain: rim_power >= 0.0
|
|
25
|
+
ensures (result >= 0.0)
|
|
26
|
+
ensures (result <= 1.0)
|
|
27
|
+
{
|
|
28
|
+
let edge: Real = clamp(1.0 - n_dot_v, 0.0, 1.0);
|
|
29
|
+
pow(edge, rim_power)
|
|
30
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// biome_select.eml -- temperature × moisture biome classification.
|
|
2
|
+
//
|
|
3
|
+
// Whittaker's biome diagram: each biome is a region of
|
|
4
|
+
// (temperature, moisture) space. Returns an integer biome id ∈
|
|
5
|
+
// [0, 7]:
|
|
6
|
+
//
|
|
7
|
+
// 0 = tundra (cold, dry)
|
|
8
|
+
// 1 = boreal (cold, wet)
|
|
9
|
+
// 2 = grassland (temperate, dry)
|
|
10
|
+
// 3 = temperate_forest (temperate, mid)
|
|
11
|
+
// 4 = rainforest (temperate/hot, wet)
|
|
12
|
+
// 5 = desert (hot, dry)
|
|
13
|
+
// 6 = savanna (hot, mid)
|
|
14
|
+
// 7 = tropical_dry (hot, very dry)
|
|
15
|
+
//
|
|
16
|
+
// Inputs in [0, 1]. Pure piecewise.
|
|
17
|
+
|
|
18
|
+
module biome_select;
|
|
19
|
+
|
|
20
|
+
@verify(lean, theorem = "biome_id_in_band")
|
|
21
|
+
fn biome_id(temperature: Real, moisture: Real) -> Real
|
|
22
|
+
where chain_order <= 0
|
|
23
|
+
requires (temperature >= 0.0)
|
|
24
|
+
requires (temperature <= 1.0)
|
|
25
|
+
requires (moisture >= 0.0)
|
|
26
|
+
requires (moisture <= 1.0)
|
|
27
|
+
ensures (result >= 0.0)
|
|
28
|
+
ensures (result <= 7.0)
|
|
29
|
+
{
|
|
30
|
+
if temperature < 0.30 {
|
|
31
|
+
if moisture < 0.30 { 0.0 } else { 1.0 } // tundra / boreal
|
|
32
|
+
} else if temperature < 0.65 {
|
|
33
|
+
if moisture < 0.30 { 2.0 } // grassland
|
|
34
|
+
else if moisture < 0.65 { 3.0 } // temperate_forest
|
|
35
|
+
else { 4.0 } // rainforest
|
|
36
|
+
} else {
|
|
37
|
+
if moisture < 0.20 { 5.0 } // desert
|
|
38
|
+
else if moisture < 0.55 { 6.0 } // savanna
|
|
39
|
+
else { 4.0 } // tropical rainforest
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// cliff_detect.eml -- gradient-magnitude cliff detection.
|
|
2
|
+
//
|
|
3
|
+
// Given a heightmap sampled at four neighbours of a centre point,
|
|
4
|
+
// the gradient magnitude is the slope. If it exceeds the threshold
|
|
5
|
+
// the cell is "cliff" (1.0); below, "non-cliff" (0.0).
|
|
6
|
+
//
|
|
7
|
+
// gradient_magnitude = sqrt((dh/dx)² + (dh/dy)²)
|
|
8
|
+
// is_cliff = gradient_magnitude > threshold
|
|
9
|
+
|
|
10
|
+
module cliff_detect;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "cliff_gradient_magnitude_nonneg")
|
|
13
|
+
fn gradient_magnitude(h_left: Real, h_right: Real,
|
|
14
|
+
h_down: Real, h_up: Real, dx_m: Real) -> Real
|
|
15
|
+
where chain_order <= 1,
|
|
16
|
+
domain: dx_m > 0.0
|
|
17
|
+
ensures (result >= 0.0)
|
|
18
|
+
{
|
|
19
|
+
let dhx: Real = (h_right - h_left) / (2.0 * dx_m);
|
|
20
|
+
let dhy: Real = (h_up - h_down) / (2.0 * dx_m);
|
|
21
|
+
sqrt(dhx * dhx + dhy * dhy)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@verify(lean, theorem = "cliff_classification_in_zero_one")
|
|
25
|
+
fn is_cliff(h_left: Real, h_right: Real,
|
|
26
|
+
h_down: Real, h_up: Real, dx_m: Real,
|
|
27
|
+
slope_threshold: Real) -> Real
|
|
28
|
+
where chain_order <= 1,
|
|
29
|
+
domain: dx_m > 0.0 && slope_threshold >= 0.0
|
|
30
|
+
ensures (result >= 0.0)
|
|
31
|
+
ensures (result <= 1.0)
|
|
32
|
+
{
|
|
33
|
+
let mag: Real = gradient_magnitude(h_left, h_right, h_down, h_up, dx_m);
|
|
34
|
+
if mag > slope_threshold { 1.0 } else { 0.0 }
|
|
35
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// erosion_hydraulic.eml -- water flow + sediment transport step.
|
|
2
|
+
//
|
|
3
|
+
// Per cell: water arrives, picks up some sediment (capacity =
|
|
4
|
+
// flow_speed * sediment_capacity), then flows to the lowest
|
|
5
|
+
// neighbour. Excess sediment beyond capacity deposits.
|
|
6
|
+
//
|
|
7
|
+
// The full sim iterates this on a grid; this kernel is one cell's
|
|
8
|
+
// per-step state update.
|
|
9
|
+
|
|
10
|
+
module erosion_hydraulic;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "sediment_capacity_nonneg")
|
|
13
|
+
fn capacity(flow_speed: Real, sediment_capacity_coeff: Real) -> Real
|
|
14
|
+
where chain_order <= 0,
|
|
15
|
+
domain: flow_speed >= 0.0 && sediment_capacity_coeff >= 0.0
|
|
16
|
+
ensures (result >= 0.0)
|
|
17
|
+
{
|
|
18
|
+
flow_speed * sediment_capacity_coeff
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Erosion: how much new sediment the water lifts this step.
|
|
22
|
+
// eroded = (capacity - sediment) * erosion_rate (when capacity > sediment)
|
|
23
|
+
@verify(lean, theorem = "erosion_step_nonneg")
|
|
24
|
+
fn eroded_amount(sediment: Real, capacity: Real, erosion_rate: Real) -> Real
|
|
25
|
+
where chain_order <= 0,
|
|
26
|
+
domain: erosion_rate >= 0.0
|
|
27
|
+
requires (sediment >= 0.0)
|
|
28
|
+
requires (capacity >= 0.0)
|
|
29
|
+
ensures (result >= 0.0)
|
|
30
|
+
{
|
|
31
|
+
clamp((capacity - sediment) * erosion_rate, 0.0, 1.0e6)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Deposition: when the water slows or sediment > capacity, drop the
|
|
35
|
+
// excess back to the terrain.
|
|
36
|
+
@verify(lean, theorem = "deposition_step_nonneg")
|
|
37
|
+
fn deposited_amount(sediment: Real, capacity: Real, deposition_rate: Real) -> Real
|
|
38
|
+
where chain_order <= 0,
|
|
39
|
+
domain: deposition_rate >= 0.0
|
|
40
|
+
requires (sediment >= 0.0)
|
|
41
|
+
requires (capacity >= 0.0)
|
|
42
|
+
ensures (result >= 0.0)
|
|
43
|
+
{
|
|
44
|
+
clamp((sediment - capacity) * deposition_rate, 0.0, 1.0e6)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Water-volume update with evaporation.
|
|
48
|
+
@verify(lean, theorem = "water_evaporation_in_band")
|
|
49
|
+
fn updated_water(water: Real, evap_rate: Real, dt: Real) -> Real
|
|
50
|
+
where chain_order <= 0,
|
|
51
|
+
domain: evap_rate >= 0.0 && dt >= 0.0
|
|
52
|
+
requires (water >= 0.0)
|
|
53
|
+
ensures (result >= 0.0)
|
|
54
|
+
ensures (result <= water)
|
|
55
|
+
{
|
|
56
|
+
clamp(water * (1.0 - evap_rate * dt), 0.0, water)
|
|
57
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// erosion_thermal.eml -- talus-angle thermal erosion step.
|
|
2
|
+
//
|
|
3
|
+
// Per cell: if the slope to the lowest neighbour exceeds the talus
|
|
4
|
+
// angle, transfer some material from this cell to the neighbour.
|
|
5
|
+
//
|
|
6
|
+
// delta = max(0, dh - talus_threshold) * fraction
|
|
7
|
+
//
|
|
8
|
+
// The full simulation iterates this for many cells; the kernel
|
|
9
|
+
// here is one (cell, neighbour) update.
|
|
10
|
+
|
|
11
|
+
module erosion_thermal;
|
|
12
|
+
|
|
13
|
+
@verify(lean, theorem = "thermal_step_signed_balance")
|
|
14
|
+
fn material_transfer(h_self: Real, h_neighbour: Real,
|
|
15
|
+
talus_threshold: Real, fraction: Real) -> Real
|
|
16
|
+
where chain_order <= 0,
|
|
17
|
+
domain: talus_threshold >= 0.0
|
|
18
|
+
requires (fraction >= 0.0)
|
|
19
|
+
requires (fraction <= 1.0)
|
|
20
|
+
ensures (result >= 0.0)
|
|
21
|
+
{
|
|
22
|
+
let dh: Real = h_self - h_neighbour;
|
|
23
|
+
let excess: Real = clamp(dh - talus_threshold, 0.0, 1.0e6);
|
|
24
|
+
excess * fraction
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Apply transfer to two cells: returns the new self height.
|
|
28
|
+
@verify(lean, theorem = "thermal_apply_self_decreases")
|
|
29
|
+
fn apply_self(h_self: Real, transfer: Real) -> Real
|
|
30
|
+
where chain_order <= 0
|
|
31
|
+
requires (transfer >= 0.0)
|
|
32
|
+
ensures (result <= h_self)
|
|
33
|
+
{
|
|
34
|
+
h_self - transfer
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@verify(lean, theorem = "thermal_apply_neighbour_increases")
|
|
38
|
+
fn apply_neighbour(h_neighbour: Real, transfer: Real) -> Real
|
|
39
|
+
where chain_order <= 0
|
|
40
|
+
requires (transfer >= 0.0)
|
|
41
|
+
ensures (result >= h_neighbour)
|
|
42
|
+
{
|
|
43
|
+
h_neighbour + transfer
|
|
44
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// heightmap.eml -- FBM-driven heightmap generation.
|
|
2
|
+
//
|
|
3
|
+
// h(x, y) = fbm(x * scale, y * scale) * amplitude + base_elevation
|
|
4
|
+
//
|
|
5
|
+
// Output is a scalar height in metres. Caller passes the FBM scale
|
|
6
|
+
// (lower = larger features) and amplitude (peak-to-trough range).
|
|
7
|
+
|
|
8
|
+
module heightmap;
|
|
9
|
+
|
|
10
|
+
use stdlib::gaming::noise::fbm;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "heightmap_in_band")
|
|
13
|
+
fn height(x: Real, y: Real, scale: Real,
|
|
14
|
+
amplitude: Real, base_elevation: Real) -> Real
|
|
15
|
+
where chain_order <= 1,
|
|
16
|
+
domain: scale > 0.0 && amplitude >= 0.0
|
|
17
|
+
ensures (result >= base_elevation - amplitude)
|
|
18
|
+
ensures (result <= base_elevation + amplitude)
|
|
19
|
+
{
|
|
20
|
+
base_elevation + amplitude * fbm::fbm2_default(x * scale, y * scale)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ridged variant: 1 - |fbm| produces sharp ridges (mountains).
|
|
24
|
+
@verify(lean, theorem = "ridged_heightmap_in_band")
|
|
25
|
+
fn ridged_height(x: Real, y: Real, scale: Real,
|
|
26
|
+
amplitude: Real, base_elevation: Real) -> Real
|
|
27
|
+
where chain_order <= 1,
|
|
28
|
+
domain: scale > 0.0 && amplitude >= 0.0
|
|
29
|
+
ensures (result >= base_elevation)
|
|
30
|
+
ensures (result <= base_elevation + amplitude)
|
|
31
|
+
{
|
|
32
|
+
base_elevation + amplitude * (1.0 - abs(fbm::fbm2_default(x * scale, y * scale)))
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// brick.eml -- modular brick grid with mortar gap + per-row offset.
|
|
2
|
+
//
|
|
3
|
+
// Returns 1.0 inside a brick, 0.0 inside the mortar joint. The
|
|
4
|
+
// offset alternates each row, which is the visual signature of
|
|
5
|
+
// running-bond brickwork.
|
|
6
|
+
|
|
7
|
+
module brick;
|
|
8
|
+
|
|
9
|
+
@verify(lean, theorem = "brick_pattern_in_unit_interval")
|
|
10
|
+
fn pattern(u: Real, v: Real, brick_w: Real, brick_h: Real,
|
|
11
|
+
mortar_thickness: Real) -> Real
|
|
12
|
+
where chain_order <= 0,
|
|
13
|
+
domain: brick_w > 0.0 && brick_h > 0.0 && mortar_thickness >= 0.0
|
|
14
|
+
ensures (result >= 0.0)
|
|
15
|
+
ensures (result <= 1.0)
|
|
16
|
+
{
|
|
17
|
+
let row: Real = floor(v / brick_h);
|
|
18
|
+
let row_offset: Real = if (row - 2.0 * floor(row * 0.5)) < 0.5 { 0.0 } else { brick_w * 0.5 };
|
|
19
|
+
let lx: Real = u + row_offset;
|
|
20
|
+
let frac_u: Real = lx / brick_w - floor(lx / brick_w);
|
|
21
|
+
let frac_v: Real = v / brick_h - floor(v / brick_h);
|
|
22
|
+
let edge_u: Real = mortar_thickness / brick_w;
|
|
23
|
+
let edge_v: Real = mortar_thickness / brick_h;
|
|
24
|
+
let in_brick_u: Real = if frac_u > edge_u && frac_u < 1.0 - edge_u { 1.0 } else { 0.0 };
|
|
25
|
+
let in_brick_v: Real = if frac_v > edge_v && frac_v < 1.0 - edge_v { 1.0 } else { 0.0 };
|
|
26
|
+
in_brick_u * in_brick_v
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// caustics.eml -- refracted-light interference pattern.
|
|
2
|
+
//
|
|
3
|
+
// Cheap caustics for pool / sea-floor effects: two perpendicular
|
|
4
|
+
// sine waves perturbed by Perlin noise, multiplied. Result ∈ [0, 1].
|
|
5
|
+
|
|
6
|
+
module caustics;
|
|
7
|
+
|
|
8
|
+
use stdlib::gaming::noise::perlin_2d;
|
|
9
|
+
|
|
10
|
+
const PI: Real = 3.141592653589793
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "caustics_in_unit_interval")
|
|
13
|
+
fn intensity(x: Real, y: Real, time_s: Real, scale: Real) -> Real
|
|
14
|
+
where chain_order <= 1,
|
|
15
|
+
domain: scale > 0.0
|
|
16
|
+
ensures (result >= 0.0)
|
|
17
|
+
ensures (result <= 1.0)
|
|
18
|
+
{
|
|
19
|
+
let n: Real = perlin_2d::perlin2(x * 0.5, y * 0.5);
|
|
20
|
+
let s_a: Real = sin(2.0 * PI * scale * (x + 0.3 * n) + time_s * 1.3);
|
|
21
|
+
let s_b: Real = sin(2.0 * PI * scale * (y - 0.3 * n) + time_s * 1.7);
|
|
22
|
+
let raw: Real = s_a * s_b;
|
|
23
|
+
let highlit: Real = raw * raw;
|
|
24
|
+
clamp(highlit, 0.0, 1.0)
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// checkerboard.eml -- canonical checkerboard pattern.
|
|
2
|
+
//
|
|
3
|
+
// checker(x, y) = mod(floor(x) + floor(y), 2)
|
|
4
|
+
//
|
|
5
|
+
// Returns 0.0 or 1.0 — caller maps to two colours. Chain order 0.
|
|
6
|
+
|
|
7
|
+
module checkerboard;
|
|
8
|
+
|
|
9
|
+
@verify(lean, theorem = "checker_returns_zero_or_one")
|
|
10
|
+
fn checker(x: Real, y: Real) -> Real
|
|
11
|
+
where chain_order <= 0
|
|
12
|
+
ensures (result == 0.0 || result == 1.0)
|
|
13
|
+
{
|
|
14
|
+
let s: Real = floor(x) + floor(y);
|
|
15
|
+
s - 2.0 * floor(s * 0.5)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Tile-size-parameterised variant.
|
|
19
|
+
@verify(lean, theorem = "checker_tiled_returns_zero_or_one")
|
|
20
|
+
fn checker_tiled(x: Real, y: Real, tile_size: Real) -> Real
|
|
21
|
+
where chain_order <= 0,
|
|
22
|
+
domain: tile_size > 0.0
|
|
23
|
+
ensures (result == 0.0 || result == 1.0)
|
|
24
|
+
{
|
|
25
|
+
let s: Real = floor(x / tile_size) + floor(y / tile_size);
|
|
26
|
+
s - 2.0 * floor(s * 0.5)
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// fire.eml -- noise-advection fire texture.
|
|
2
|
+
//
|
|
3
|
+
// Vertical advection of FBM noise produces flickering tongues of
|
|
4
|
+
// flame; remap to a temperature-to-colour gradient.
|
|
5
|
+
//
|
|
6
|
+
// Output is a normalised intensity ∈ [0, 1] that the caller maps
|
|
7
|
+
// to (orange, yellow, white, alpha) by table lookup.
|
|
8
|
+
|
|
9
|
+
module fire;
|
|
10
|
+
|
|
11
|
+
use stdlib::gaming::noise::fbm;
|
|
12
|
+
|
|
13
|
+
@verify(lean, theorem = "fire_intensity_in_unit_interval")
|
|
14
|
+
fn intensity(u: Real, v: Real, time_s: Real, rise_speed: Real) -> Real
|
|
15
|
+
where chain_order <= 1,
|
|
16
|
+
domain: rise_speed >= 0.0
|
|
17
|
+
ensures (result >= 0.0)
|
|
18
|
+
ensures (result <= 1.0)
|
|
19
|
+
{
|
|
20
|
+
// Advect upward with time.
|
|
21
|
+
let v_advected: Real = v + rise_speed * time_s;
|
|
22
|
+
let n: Real = fbm::fbm2_unit(u * 1.5, v_advected * 0.8);
|
|
23
|
+
// Vertical fall-off: hotter at the base, cools toward the tip.
|
|
24
|
+
let falloff: Real = clamp(1.0 - v, 0.0, 1.0);
|
|
25
|
+
clamp(n * falloff, 0.0, 1.0)
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// marble.eml -- turbulence-perturbed sine ramp.
|
|
2
|
+
//
|
|
3
|
+
// marble(x, y) = (sin(2π * f * (x + amplitude * turbulence(x, y))) + 1) / 2
|
|
4
|
+
//
|
|
5
|
+
// The turbulence (absolute-value FBM) bends the sine bands into
|
|
6
|
+
// the cusped vein patterns marble is known for. Caller maps the
|
|
7
|
+
// scalar to a colour gradient.
|
|
8
|
+
|
|
9
|
+
module marble;
|
|
10
|
+
|
|
11
|
+
use stdlib::gaming::noise::turbulence;
|
|
12
|
+
|
|
13
|
+
const PI: Real = 3.141592653589793
|
|
14
|
+
|
|
15
|
+
@verify(lean, theorem = "marble_in_unit_interval")
|
|
16
|
+
fn marble(x: Real, y: Real, frequency: Real, amplitude: Real) -> Real
|
|
17
|
+
where chain_order <= 1,
|
|
18
|
+
domain: frequency > 0.0 && amplitude >= 0.0
|
|
19
|
+
ensures (result >= 0.0)
|
|
20
|
+
ensures (result <= 1.0)
|
|
21
|
+
{
|
|
22
|
+
let t: Real = turbulence::turbulence2_default(x, y);
|
|
23
|
+
0.5 * (sin(2.0 * PI * frequency * (x + amplitude * t)) + 1.0)
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// rust.eml -- Voronoi-driven rust / oxidation mask.
|
|
2
|
+
//
|
|
3
|
+
// Combine Voronoi cells (irregular pitting) with FBM erosion (the
|
|
4
|
+
// flow patterns where moisture pools). Output ∈ [0, 1] — pipe into
|
|
5
|
+
// a brown-orange colour ramp.
|
|
6
|
+
|
|
7
|
+
module rust;
|
|
8
|
+
|
|
9
|
+
use stdlib::gaming::noise::voronoi_2d;
|
|
10
|
+
use stdlib::gaming::noise::fbm;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "rust_mask_in_unit_interval")
|
|
13
|
+
fn mask(x: Real, y: Real, pitting_strength: Real, flow_strength: Real) -> Real
|
|
14
|
+
where chain_order <= 1,
|
|
15
|
+
domain: pitting_strength >= 0.0 && flow_strength >= 0.0
|
|
16
|
+
ensures (result >= 0.0)
|
|
17
|
+
ensures (result <= 1.0)
|
|
18
|
+
{
|
|
19
|
+
let pit: Real = clamp(1.0 - voronoi_2d::distance_to_nearest(x * 0.5, y * 0.5),
|
|
20
|
+
0.0, 1.0);
|
|
21
|
+
let flow: Real = fbm::fbm2_unit(x * 0.7, y * 0.7);
|
|
22
|
+
clamp(pitting_strength * pit + flow_strength * flow, 0.0, 1.0)
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// water_surface.eml -- sum-of-sines water height field.
|
|
2
|
+
//
|
|
3
|
+
// Three layered sine waves with different frequencies, directions
|
|
4
|
+
// and phase speeds. Cheap, plausible water you can drop into any
|
|
5
|
+
// shader. For real-time rendering the caller animates the `time`
|
|
6
|
+
// parameter.
|
|
7
|
+
|
|
8
|
+
module water_surface;
|
|
9
|
+
|
|
10
|
+
const PI: Real = 3.141592653589793
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "water_height_signed")
|
|
13
|
+
fn height(x: Real, y: Real, time_s: Real,
|
|
14
|
+
amp_a: Real, amp_b: Real, amp_c: Real) -> Real
|
|
15
|
+
where chain_order <= 1
|
|
16
|
+
requires (amp_a >= 0.0)
|
|
17
|
+
requires (amp_b >= 0.0)
|
|
18
|
+
requires (amp_c >= 0.0)
|
|
19
|
+
{
|
|
20
|
+
let w_a: Real = sin(2.0 * PI * (0.10 * x + 0.05 * y) + 1.4 * time_s);
|
|
21
|
+
let w_b: Real = sin(2.0 * PI * (0.07 * x - 0.13 * y) + 0.9 * time_s);
|
|
22
|
+
let w_c: Real = sin(2.0 * PI * (0.21 * x + 0.18 * y) + 2.3 * time_s);
|
|
23
|
+
amp_a * w_a + amp_b * w_b + amp_c * w_c
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Default parameter set good enough for most water shaders.
|
|
27
|
+
@verify(lean, theorem = "water_height_default_signed")
|
|
28
|
+
fn height_default(x: Real, y: Real, time_s: Real) -> Real
|
|
29
|
+
where chain_order <= 1
|
|
30
|
+
{
|
|
31
|
+
height(x, y, time_s, 0.5, 0.3, 0.15)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Approximate normal of the height field for shading.
|
|
35
|
+
@verify(lean, theorem = "water_normal_unit_length")
|
|
36
|
+
fn normal_y(x: Real, y: Real, time_s: Real, eps: Real) -> Real
|
|
37
|
+
where chain_order <= 1,
|
|
38
|
+
domain: eps > 0.0
|
|
39
|
+
ensures (result >= 0.0)
|
|
40
|
+
{
|
|
41
|
+
let h_x_plus: Real = height_default(x + eps, y, time_s);
|
|
42
|
+
let h_x_minus: Real = height_default(x - eps, y, time_s);
|
|
43
|
+
let h_y_plus: Real = height_default(x, y + eps, time_s);
|
|
44
|
+
let h_y_minus: Real = height_default(x, y - eps, time_s);
|
|
45
|
+
let dhx: Real = (h_x_plus - h_x_minus) / (2.0 * eps);
|
|
46
|
+
let dhy: Real = (h_y_plus - h_y_minus) / (2.0 * eps);
|
|
47
|
+
let n_y: Real = 1.0 / sqrt(1.0 + dhx * dhx + dhy * dhy);
|
|
48
|
+
clamp(n_y, 0.0, 1.0)
|
|
49
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// wood.eml -- procedural wood-grain texture.
|
|
2
|
+
//
|
|
3
|
+
// ring(x, y) = sin(2π * frequency * (radial_distance + warp))
|
|
4
|
+
//
|
|
5
|
+
// Radial distance from a virtual trunk axis modulated by Perlin
|
|
6
|
+
// noise gives concentric tree rings. Output is a scalar grain
|
|
7
|
+
// intensity in [0, 1] suitable for piping into a colour ramp.
|
|
8
|
+
|
|
9
|
+
module wood;
|
|
10
|
+
|
|
11
|
+
use stdlib::gaming::noise::perlin_2d;
|
|
12
|
+
|
|
13
|
+
const PI: Real = 3.141592653589793
|
|
14
|
+
|
|
15
|
+
@verify(lean, theorem = "wood_grain_in_unit_interval")
|
|
16
|
+
fn grain(x: Real, y: Real, ring_frequency: Real, warp_amount: Real) -> Real
|
|
17
|
+
where chain_order <= 1,
|
|
18
|
+
domain: ring_frequency > 0.0
|
|
19
|
+
ensures (result >= 0.0)
|
|
20
|
+
ensures (result <= 1.0)
|
|
21
|
+
{
|
|
22
|
+
let r: Real = sqrt(x * x + y * y);
|
|
23
|
+
let warp: Real = warp_amount * perlin_2d::perlin2(x * 0.4, y * 0.4);
|
|
24
|
+
let s: Real = sin(2.0 * PI * ring_frequency * (r + warp));
|
|
25
|
+
0.5 * (s + 1.0)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Knot-mask: returns a contribution that goes to 1 inside random
|
|
29
|
+
// knot blobs scattered by Perlin noise. Multiplied with the grain
|
|
30
|
+
// for visual depth.
|
|
31
|
+
@verify(lean, theorem = "wood_knot_in_unit_interval")
|
|
32
|
+
fn knot_mask(x: Real, y: Real, threshold: Real) -> Real
|
|
33
|
+
where chain_order <= 1
|
|
34
|
+
requires (threshold >= 0.0)
|
|
35
|
+
requires (threshold <= 1.0)
|
|
36
|
+
ensures (result >= 0.0)
|
|
37
|
+
ensures (result <= 1.0)
|
|
38
|
+
{
|
|
39
|
+
let n: Real = perlin_2d::perlin2_unit(x * 0.15, y * 0.15);
|
|
40
|
+
clamp((n - threshold) / (1.0 - threshold + 1.0e-3), 0.0, 1.0)
|
|
41
|
+
}
|