-
-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add initial shader module #1196
base: main
Are you sure you want to change the base?
Conversation
When I run pnpm install, the lockfile is updated to version 9 with a massive diff. I tried to setup pnpm version 6, but pnpm install failed with |
a58f1a4
to
30de163
Compare
This change adds the `loadShader` and `shader` function, to be used like this: ```strudel await loadShader` // The modulation targets uniform float iColor; void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; vec3 col = 0.5 + 0.5*cos(iColor+uv.xyx+vec3(0,2,4)); fragColor = vec4(col, 0); } ` $: s("bd").shader({uniform: 'iColor'}) ```
30de163
to
f0ddb2d
Compare
hey, thanks for the PR, being able to directly control shaders is a great feature imo. I am trying to test it, but for some reason, the canvas is not visible, without getting an error.. is there anything else i need to do? I've cloned your branch and tried to test with your first basic example. |
yes the lockfile really needs updating, probably best to do it in a separate PR, i can do that |
The attribute for the draw call is called drawFrame, not draw.
@felixroos That's great to hear! I just pushed a fix that prevented the code to be reloaded (though refreshing the page should have displayed the canvas). I guess this should be marked as draft until the shader parameters support pattern strings (the values presently need to be single quoted). Also, it would be nice to figure out how to control the modulations, like the decay value which is presently hardcoded. Though I think that PR is already quite usable. The shader module should also support texture input, for now it's just a plain fragment shader. |
ah turns out the first example did set the alpha channel to 0 so it was always transparent :) I'm still not sure how the pattern interacts with the uniforms defined in the shader function, could you clarify that? The |
Interesting, on Firefox the alpha doesn't seem to be enabled. The iColor should be incremented by one each time a note is played, the effect is a bit dim, you can increase it by adding I picked 1 because the haps in onTrigger don't seem to have a velocity, but it would be great to use the sound intensity. Also note that this PR only reacts to noteOn events, I think we would need to analyze the sound to get a more accurate modulation source, though this simple solution works great for percussion sounds. |
Lastly, something I haven't done yet but I think would be really nice, is to pass the cpm as uniform to sync to sin values in the shader to better match the composition. Perhaps we could define some useful macros in the shader headers... |
This idea might be half baked, but wouldn't it be more flexible to allow the user to fill in the mapping between hap values and uniforms? Something like |
some more ideas:
what do you think? |
As explained in the tidal club post, I'm not really familiar with strudel, so I'm looking forward your feedback to improve the implementations. About the mapping, well, I couldn't find examples for sending events from one track to another, so I just re-used the code from the Midi API. I guess we could also attach the shader destination directly to a signal or pattern like so: Your other ideas sound great to me, I can update the PR tomorrow. I picked picogl because it handle the context loss, it's not implemented yet here, but it might be useful to avoid blank screen. Otherwise you are right, we could totally do it by hand. |
would that make it possible to call $: note("c a f e").pan(sine.uniform("iPan"))
$: s("hh*8").gain(".5 1".uniform("iGain")) The $: note("c a f e").pan(sine)
$: s("hh*8").gain(".5 1")
all(x=>x.uniforms({iPan: "pan", iGain: "gain")) In this notation, the
Maybe let's keep it for now. If it's a separate package the dependency is fine i think |
bf3a095
to
b0149f8
Compare
just formatted the code (using |
With the latest commit, the pretty demo now looks like this:
In the uniform.mjs module you can find the uniform param documentation. I think this is starting to look great. Lastly, I would like to expose the "smoothing" function to pre-process the value, I think we should have:
I though about adding an extra component to the uniform string syntax, e.g. PS: Sorry I erased the codeformat, I'll setup the an auto formatter next. |
I've been studying the pattern and signal module and I'll update the PR with with the following pattern methods:
It doesn't seem like we can check in advance if a pattern is numerical, so using two methods looks cleaner. To smooth the value, it looks easier to use an extra argument, e.g. Then I'll look into the |
Here is a new example:
|
I am struggling to understand the syntax passed to uniform / uniformTrigger, could you maybe elaborate more on what the different options do? Some functionality could maybe also expressed with patterns, which would add less complexity. Moving logic to pattern land would also allow people to implement their own functions from within the repl. |
Yeah, I'm not happy with the uniform syntax, but I couldn't figure out a better way to expose the following options:
Here are some example usages:
|
I meant I couldn't figure how to use the existing functions like |
I think I get how to handle the uniform's option as pattern. So the API would look like this: With the last commit 1baa6da, here is how the demo looks like:
|
d536807
to
f8a2414
Compare
This change makes the following demo works: `s("bd").uniform("rot")`
looks pretty neat! I think the difficulty for using pattern methods is that the uniform values are stateful, also expected to retain state between evaluations. It is theoretically possible with patterns to do a similar thing: I'm not sure if it's a good idea though.. Just playing a bit with the idea, you could maybe also pass uniforms as the second argument of loadShader: let iColorOffset = 1;
await loadShader(`
uniform float iColorOffset;
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord / iResolution.xy;
vec3 bcol = vec3(0.1,2.1,4.2);
vec3 col = 0.5 + 0.5*cos(iColorOffset+uv.xyx+bcol);
fragColor = vec4(col, 1);
}
`, () => ({
iColorOffset
}))
$: s("bd*<4 6>")
.onTrigger(() => iColorOffset++, false) This would allow implement any custom stateful logic in userland. What do you think? |
Thank you for following up on this feature; I appreciate your feedback! I think there is room for experimentation as it feels like we are in uncharted territory. I am not sure how many uniform variables could be used in a meaningful way, and perhaps a skillful shader artist could use every pattern to create pleasing visuals. So I hope this feature will be as flexible as possible to avoid limiting explorations. What I have proposed so far is based on my work with animation-fractal which mostly uses NoteOn events. In this project, I found that it is important to control the rate of change to avoid unpleasing stroboscopic effects and this is why I made this the default behavior with the Using a regular strudel pattern to activate variables sounds ideal because we can leverage the existing modifiers. For example: I like how explicit your uniform argument var example looks like. I guess we can also |
Right, using So I removed the |
2cf77b0
to
77bbb96
Compare
I started to remove picogl, that's better indeed, and that should let us try the feature directly on the strudel's version currently deployed. |
Trying to keep the ball rolling, I made the module standalone and uploaded a demo here: https://strudel.cc/?ErKvnkz3z_3p . Next I'll address the todo list, please let me know if the new design looks good to you. |
This change adds the
loadShader
, to be used like this:… as presented in https://club.tidalcycles.org/t/adding-webgl-shader-uniform-modulation/5481