Skip to content

Commit

Permalink
Add iDate builtin to Shadertoy utility (#428)
Browse files Browse the repository at this point in the history
* Add iDate to glsl builtins

* add iDate to wgsl builtins

* Fix memory offset error

* Fix order of fields in Uniform array

* Fix style

* Add example for iDate

* Replace tabs with spaces

---------

Co-authored-by: Korijn van Golen <[email protected]>
  • Loading branch information
Vipitis and Korijn authored Nov 21, 2023
1 parent 257143a commit 031a766
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 10 deletions.
94 changes: 94 additions & 0 deletions examples/shadertoy_glsl_clock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from wgpu.utils.shadertoy import Shadertoy

shader_code = """
// source: https://www.shadertoy.com/view/MdVcRd
// See https://www.shadertoy.com/view/ldKGRR
// Computes a smooth-edged diamond pixel value (Manhattan distance)
#define P(i, j, b) \
vec2(.1, b).xyxy * smoothstep(0., 9. / R.y, .1 - abs(i) - abs(j))
// Computes a segment value (length = 0.5)
#define S(i, j, b) \
P(i - clamp(i, 0., .5), j, b & 1)
// Colon render
#define C \
x += .5; O += P(x, y + .3, i.w / 50) + P(x, y - .3, i.w / 50); t /= 60
// Hyphen render
#define H(b) \
++x; O += S(x, y, b)
// Computes the horizontal and vertical segments based on a denary digit
#define X(i, j, b) \
S(x - i, y - j, b)
#define Y(i, j, b) \
S(y - j, x - i, b)
#define D(n) \
H(892>>n) \
+ X(0., .7, 1005>>n) \
+ X(0., -.7, 877>>n) \
+ Y(-.1, .1, 881>>n) \
+ Y(.6, .1, 927>>n) \
+ Y(-.1, -.6, 325>>n) \
+ Y(.6, -.6, 1019>>n);
// Two-digit render
#define Z(n) ; D(n % 10) D(n / 10)
void mainImage(out vec4 O, vec2 U)
{
vec2 R = iResolution.xy;
U += U - R;
U /= R.y / 3.; // Global scaling with aspect ratio correction
O-=O; // Zero the pixel
float x = U.x - U.y * .2 - 2.8, // Slight skew to slant the digits
y = --U.y;
ivec4 i = ivec4(iDate); // Convert everything to integers
int t = i.w;
i.w = int(iDate.w * 100.) % 100 // Replace with centiseconds
// Seconds (preceded by a colon)
Z(t % 60)
C
// Minutes (preceded by a colon)
Z(t % 60)
C
// Hours
Z(t)
// Smaller digits
x /= .6;
y /= .6;
R *= .6;
// Centiseconds
x -= 14.;
y += .53
Z(i.w)
// Day (preceded by a hyphen)
x -= .8;
y += 3.
Z(i.z)
H(1)
// Month (preceded by a hyphen)
Z((i.y + 1)) // Is it a bug in shadertoy that we have to add one?
H(1)
// Year
Z(i.x % 100)
Z(i.x / 100)
}
""" # noqa
shader = Shadertoy(shader_code)

if __name__ == "__main__":
shader.show()
41 changes: 31 additions & 10 deletions wgpu/utils/shadertoy.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@
builtin_variables_glsl = """
#version 450 core
vec3 i_resolution;
vec4 i_mouse;
vec4 i_date;
vec3 i_resolution;
float i_time;
float i_time_delta;
int i_frame;
// Shadertoy compatibility, see we can use the same code copied from shadertoy website
#define iTime i_time
#define iMouse i_mouse
#define iDate i_date
#define iResolution i_resolution
#define iTime i_time
#define iTimeDelta i_time_delta
#define iMouse i_mouse
#define iFrame i_frame
#define mainImage shader_main
Expand All @@ -52,6 +54,7 @@
struct ShadertoyInput {
vec4 mouse;
vec4 date;
vec3 resolution;
float time;
float time_delta;
Expand All @@ -62,13 +65,13 @@
out vec4 FragColor;
void main(){
i_time = input.time;
i_mouse = input.mouse;
i_date = input.date;
i_resolution = input.resolution;
i_time = input.time;
i_time_delta = input.time_delta;
i_mouse = input.mouse;
i_frame = input.frame;
vec2 uv = vec2(uv.x, 1.0 - uv.y);
vec2 frag_coord = uv * i_resolution.xy;
Expand Down Expand Up @@ -107,8 +110,9 @@

builtin_variables_wgsl = """
var<private> i_resolution: vec3<f32>;
var<private> i_mouse: vec4<f32>;
var<private> i_date: vec4<f32>;
var<private> i_resolution: vec3<f32>;
var<private> i_time_delta: f32;
var<private> i_time: f32;
var<private> i_frame: u32;
Expand All @@ -123,6 +127,7 @@
struct ShadertoyInput {
mouse: vec4<f32>,
date: vec4<f32>,
resolution: vec3<f32>,
time: f32,
time_delta: f32,
Expand All @@ -141,10 +146,11 @@
@fragment
fn main(in: Varyings) -> @location(0) vec4<f32> {
i_time = input.time;
i_mouse = input.mouse;
i_date = input.date;
i_resolution = input.resolution;
i_time = input.time;
i_time_delta = input.time_delta;
i_mouse = input.mouse;
i_frame = input.frame;
Expand All @@ -169,6 +175,8 @@ class UniformArray:
"""Convenience class to create a uniform array.
Maybe we can make it a public util at some point.
Ensure that the order matches structs in the shader code.
See https://www.w3.org/TR/WGSL/#alignment-and-size for reference on alignment.
"""

def __init__(self, *args):
Expand Down Expand Up @@ -239,8 +247,9 @@ class Shadertoy:
* ``i_frame``: the frame number
* ``i_resolution``: the resolution of the shadertoy
* ``i_mouse``: the mouse position in pixels
* ``i_date``: the current date and time as a vec4 (year, month, day, seconds)
For GLSL, you can also use the aliases ``iTime``, ``iTimeDelta``, ``iFrame``, ``iResolution``, and ``iMouse`` of these built-in variables,
For GLSL, you can also use the aliases ``iTime``, ``iTimeDelta``, ``iFrame``, ``iResolution``, ``iMouse`` and ``iDate`` of these built-in variables,
the entry point function also has an alias ``mainImage``, so you can use the shader code copied from shadertoy website without making any changes.
"""

Expand All @@ -251,6 +260,7 @@ class Shadertoy:
def __init__(self, shader_code, resolution=(800, 450), offscreen=False) -> None:
self._uniform_data = UniformArray(
("mouse", "f", 4),
("date", "f", 4),
("resolution", "f", 3),
("time", "f", 1),
("time_delta", "f", 1),
Expand Down Expand Up @@ -429,6 +439,17 @@ def _update(self):
if not hasattr(self, "_frame"):
self._frame = 0

time_struct = time.localtime()
self._uniform_data["date"] = (
float(time_struct.tm_year),
float(time_struct.tm_mon - 1),
float(time_struct.tm_mday),
time_struct.tm_hour * 3600
+ time_struct.tm_min * 60
+ time_struct.tm_sec
+ now % 1,
)

self._uniform_data["frame"] = self._frame
self._frame += 1

Expand Down

0 comments on commit 031a766

Please sign in to comment.