Skip to content
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

feat: webgl shaders #920

Merged
merged 1 commit into from
Feb 3, 2024
Merged

Conversation

aarthificial
Copy link
Contributor

Introduces WebGL shaders.

Shaders can be assigned to any node using the shaders property.
See PossibleShaderConfig for all accepted values.
In the simplest case, you can just pass a string containing the fragment shader.

The general concept follows the globalCompositeOperation from 2D Canvas API.
The shader gets access to two textures:

  • source - contains the contents rendered by the node (including its children)
  • destination - contains what's already been drawn onto the screen (can be used to achieve a "backdrop blur" effect, for example)

With that in mind, the simplest shader that does nothing would look as follows:

#version 300 es
precision highp float;

#include "@motion-canvas/core/shaders/common.glsl"

void main() {
    out_color = texture(core_source_tx, source_uv);
}

#include is a directive for our custom pre-processor. It resolves the specified module using the same rules as javascript and includes its content in the file.
We generate all the necessary source maps so that the errors correctly link back to the original files:
image

Side note: WebGL error logs don't include a column number so we try to guess it. If our log points to a column 0, it means we couldn't guess it. (The line number is always correct though)

Side note 2: Our preprocessor runs at compile time for all .glsl files. It doesn't process strings inlined in typescript.
To use #include you need to import the shader from a standalone file:

import shader from './fragment.glsl';
// ...
<Rect shaders={shader} />

So the following directive:

#include "@motion-canvas/core/shaders/common.glsl"

...includes the common uniforms and inputs used in a fragment shader.
This is what they look like right now:

in vec2 screen_uv;  // screen space coordinates
in vec2 source_uv; // source texture coordinates
in vec2 destination_uv; // destination texture coordinates

out vec4 out_color;

uniform float core_time; // absolute time of the animation
uniform vec2 core_resolution; // resolution of the canvas
uniform sampler2D core_source_tx; // source texture
uniform sampler2D core_destination_tx; // destination texture
uniform mat4 core_source_matrix; // transforms from clip space to source texture coordinates
uniform mat4 core_destination_matrix; // transforms from clip space to destination texture coordinates

With that in mind, the example from before should now be clear:

out_color = texture(core_source_tx, source_uv);

We use the source uv to sample the source texture and output the resulting color.
The effect is the same as if we render the node without any shader.

Somewhat of the opposite would be doing this:

out_color = texture(core_destination_tx, destination_uv);

Here we sample the destination texture and return that.
As a result, nothing changes on our screen, we just take what's already there and output it back.

It becomes a bit more interesting when we manipulate the uv coordinates and/or color:

#version 300 es
precision highp float;

#include "@motion-canvas/core/shaders/common.glsl"

void main() {
  vec2 uv = destination_uv;
  uv.x = sin(uv.y * 50.0 + core_time * 10.0) * 0.01 + uv.x;

  out_color = texture(core_destination_tx, uv);
  out_color.r = 1.0 - out_color.r;
  out_color.g = 1.0 - out_color.g;
  out_color.b = 1.0 - out_color.b;
}

image

Side note 3: When using the destination texture, it may be helpful to set the fill color of the view itself. This way the destination texture will also be filled. Normally, the scene is considered transparent and gets layered on top of the project's background. So the destination texture is also transparent and so the originally rendered things may be seen through.

@aarthificial aarthificial merged commit 849216e into motion-canvas:main Feb 3, 2024
9 checks passed
@aarthificial aarthificial deleted the webgl-shaders branch February 3, 2024 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant