Skip to content

Commit fa55ae5

Browse files
authored
Improve and simplify shadercode (#42)
* dynamic headers * add code construction method * simplify GLSL uniforms * document changes * update screenshot * naming consistency
1 parent 5e07690 commit fa55ae5

File tree

8 files changed

+202
-224
lines changed

8 files changed

+202
-224
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Added:
2626
Changed:
2727
* `ShadertoyChannel` is now more specific `ShadertoyChannelTexture` https://github.com/pygfx/shadertoy/pull/40
2828

29+
Removed:
30+
* Removed GLSL uniform aliases in favor of just Shadertoy syntax https://github.com/pygfx/shadertoy/pull/42
31+
2932
### [v0.1.0] - 2024-01-21
3033

3134
Fixed:

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@ To display a shader from the website, simply provide its ID or url.
8282
> wgpu-shadertoy tsXBzS --resolution 1024 640
8383
```
8484

85+
### Uniforms
86+
The Shadertoy uniform format is directly supported for GLSL. However for WGSL the syntax is a bit different.
87+
88+
| Shadertoy.com | GLSL | WGSL |
89+
|--- | --- | --- |
90+
| `vec4 iMouse` | `iMouse` | `i_mouse` |
91+
| `vec4 iDate` | `iDate` | `i_date` |
92+
| `vec3 iResolution` | `iResolution` | `i_resolution` |
93+
| `float iTime` | `iTime` | `i_time` |
94+
| `vec3 iChannelResolution[4]` | `iChannelResolution` | `i_channel_resolution` |
95+
| `float iTimeDelta` | `iTimeDelta` | `i_time_delta` |
96+
| `int iFrame` | `iFrame` | `i_frame` |
97+
| `float iFrameRate` | `iFrameRate` | `i_frame_rate` |
98+
| `sampler2D iChannel0..3` | `iChannel0..3` | `i_channel0..3` |
99+
| `sampler3D iChannel0..3` | not yet supported | not yet supported |
100+
| `samplerCube iChannel0..3` | not yet supported | not yet supported |
101+
| `float iChannelTime[4]` | not yet supported | not yet supported |
102+
| `float iSampleRate` | not yet supported | not yet supported |
103+
85104
## Status
86105

87106
This project is still in development. Some functionality from the Shadertoy [website is missing](https://github.com/pygfx/shadertoy/issues/4) and [new features](https://github.com/pygfx/shadertoy/issues/8) are being added. See the issues to follow the development or [contribute yourself](./CONTRIBUTING.md)! For progress see the [changelog](./CHANGELOG.md).
-12 Bytes
Loading

tests/test_textures.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_textures_glsl():
6060
vec2 uv = fragCoord/iResolution.xy;
6161
vec4 c0 = texture(iChannel0, 2.0*uv);
6262
vec4 c1 = texture(iChannel1, 3.0*uv);
63-
fragColor = mix(c0,c1,abs(sin(i_time)));
63+
fragColor = mix(c0,c1,abs(sin(iTime)));
6464
}
6565
"""
6666

tests/test_util_shadertoy.py

+15-12
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,19 @@ def test_shadertoy_glsl():
6060
from wgpu_shadertoy import Shadertoy
6161

6262
shader_code = """
63-
void shader_main(out vec4 fragColor, vec2 frag_coord) {
64-
vec2 uv = frag_coord / i_resolution.xy;
63+
void mainImage(out vec4 fragColor, vec2 fragCoord) {
64+
vec2 uv = fragCoord / iResolution.xy;
6565
66-
if ( length(frag_coord - i_mouse.xy) < 20.0 ) {
66+
if ( length(fragCoord - iMouse.xy) < 20.0 ) {
6767
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
6868
}else{
69-
fragColor = vec4( 0.5 + 0.5 * sin(i_time * vec3(uv, 1.0) ), 1.0);
69+
fragColor = vec4( 0.5 + 0.5 * sin(iTime * vec3(uv, 1.0) ), 1.0);
7070
}
7171
7272
}
7373
"""
7474

75+
# tests the shader_type detection base case we will most likely see.
7576
shader = Shadertoy(shader_code, resolution=(800, 450))
7677
assert shader.resolution == (800, 450)
7778
assert shader.image.shader_code == shader_code
@@ -97,6 +98,7 @@ def test_shadertoy_glsl2():
9798
}
9899
"""
99100

101+
# this tests if setting the shader_type to glsl works as expected
100102
shader = Shadertoy(shader_code, shader_type="glsl", resolution=(800, 450))
101103
assert shader.resolution == (800, 450)
102104
assert shader.image.shader_code == shader_code
@@ -122,6 +124,7 @@ def test_shadertoy_glsl3():
122124
}
123125
"""
124126

127+
# this tests glsl detection against the regular expression when using more than one whitespace between void and mainImage.
125128
shader = Shadertoy(shader_code, resolution=(800, 450))
126129
assert shader.resolution == (800, 450)
127130
assert shader.image.shader_code == shader_code
@@ -135,13 +138,13 @@ def test_shadertoy_offscreen():
135138
from wgpu_shadertoy import Shadertoy
136139

137140
shader_code = """
138-
void shader_main(out vec4 fragColor, vec2 frag_coord) {
139-
vec2 uv = frag_coord / i_resolution.xy;
141+
void mainImage(out vec4 fragColor, vec2 fragCoord) {
142+
vec2 uv = fragCoord / iResolution.xy;
140143
141-
if ( length(frag_coord - i_mouse.xy) < 20.0 ) {
144+
if ( length(fragCoord - iMouse.xy) < 20.0 ) {
142145
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
143146
}else{
144-
fragColor = vec4( 0.5 + 0.5 * sin(i_time * vec3(uv, 1.0) ), 1.0);
147+
fragColor = vec4( 0.5 + 0.5 * sin(iTime * vec3(uv, 1.0) ), 1.0);
145148
}
146149
147150
}
@@ -159,13 +162,13 @@ def test_shadertoy_snapshot():
159162
from wgpu_shadertoy import Shadertoy
160163

161164
shader_code = """
162-
void shader_main(out vec4 fragColor, vec2 frag_coord) {
163-
vec2 uv = frag_coord / i_resolution.xy;
165+
void mainImage(out vec4 fragColor, vec2 fragCoord) {
166+
vec2 uv = fragCoord / iResolution.xy;
164167
165-
if ( length(frag_coord - i_mouse.xy) < 20.0 ) {
168+
if ( length(fragCoord - iMouse.xy) < 20.0 ) {
166169
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
167170
}else{
168-
fragColor = vec4( 0.5 + 0.5 * sin(i_time * vec3(uv, 1.0) ), 1.0);
171+
fragColor = vec4( 0.5 + 0.5 * sin(iTime * vec3(uv, 1.0) ), 1.0);
169172
}
170173
171174
}

wgpu_shadertoy/inputs.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def sampler_settings(self) -> dict:
3535
wrap = "clamp-to-edge"
3636
settings["address_mode_u"] = wrap
3737
settings["address_mode_v"] = wrap
38-
# we don't do 3D textures yet, but I guess ssetting this too is fine.
38+
# we don't do 3D textures yet, but I guess setting this too is fine.
3939
settings["address_mode_w"] = wrap
4040
return settings
4141

@@ -131,6 +131,35 @@ def _bind_groups_layout_entries(self, texture_view, sampler) -> list:
131131
},
132132
]
133133

134+
def make_header(self, shader_type: str) -> str:
135+
"""
136+
Constructs the glsl or wgsl code snippet that for the sampler and texture bindings.
137+
"""
138+
# does this fallback ever happen?
139+
if not shader_type:
140+
shader_type = self.parent.shader_type
141+
shader_type = shader_type.lower()
142+
143+
input_idx = self.channel_idx
144+
binding_id = self.texture_binding
145+
sampler_id = self.sampler_binding
146+
if shader_type == "glsl":
147+
return f"""
148+
layout(binding = {binding_id}) uniform texture2D si_channel{input_idx};
149+
layout(binding = {sampler_id}) uniform sampler sampler{input_idx};
150+
151+
#define iChannel{input_idx} sampler2D(si_channel{input_idx}, sampler{input_idx})
152+
"""
153+
elif shader_type == "wgsl":
154+
return f"""
155+
@group(0) @binding({binding_id})
156+
var i_channel{input_idx}: texture_2d<f32>;
157+
@group(0) @binding({sampler_id})
158+
var sampler{input_idx}: sampler;
159+
"""
160+
else:
161+
raise ValueError(f"Unknown shader type: {shader_type}")
162+
134163
def __repr__(self):
135164
"""
136165
Convenience method to get a representation of this object for debugging.

0 commit comments

Comments
 (0)