mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-19 18:11:39 -05:00
Add animated shader samples (#9026)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request - Adds two simple animated shaders to the pixel shaders sample folder - Updates the readme in the pixel shaders sample folder to add a section explaining the animated shaders - Modifies some comments in existing shader samples <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist * [ ] Closes #xxx * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [ ] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #8994 <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments The two shaders I wrote are not especially pretty or interesting, but they should hopefully serve as simple examples for anyone looking to do animated effects. One simply draws a line of inverted pixels that scrolls down the screen, and the other fades the background back and forth between two colors. I've added a new section to the readme explaining how the shaders work to achieve animated effects. I've also updated the comments on the existing shaders to clear up a couple of things: 1. Be more explicit that `Time` represents seconds since the shader loaded. Though obvious in hindsight, this was not clear to me when I was first learning/experimenting 2. Explain that `tex` ranges from 0,0 to 1,1. This is important because, when trying to port GLSL shaders from shadertoy, I at first assumed `fragCoord` and `tex` are the same thing but the former actually ranges from 0,0 to the resolution of the canvas, so some of the math doesn't work out if you just substitute it with `tex`. Any and all feedback welcome. <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed I ran the shaders manually in a dev build of the Terminal. I auto-spellchecked and manually proofread my additions to the readme and verifed the markdown rendering on github.
This commit is contained in:
42
samples/PixelShaders/Animate_breathe.hlsl
Normal file
42
samples/PixelShaders/Animate_breathe.hlsl
Normal file
@@ -0,0 +1,42 @@
|
||||
// A simple animated shader that fades the terminal background back and forth between two colours
|
||||
|
||||
// The terminal graphics as a texture
|
||||
Texture2D shaderTexture;
|
||||
SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings
|
||||
{
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
// Resolution of the shaderTexture
|
||||
float2 Resolution;
|
||||
// Background color as rgba
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// pi and tau (2 * pi) are useful constants when using trigonometric functions
|
||||
#define TAU 6.28318530718
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
float4 sample = shaderTexture.Sample(samplerState, tex);
|
||||
|
||||
// The number of seconds the breathing effect should span
|
||||
float duration = 5.0;
|
||||
|
||||
float3 color1 = float3(0.3, 0.0, 0.5); // indigo
|
||||
float3 color2 = float3(0.1, 0.1, 0.44); // midnight blue
|
||||
|
||||
// Set background colour based on the time
|
||||
float4 backgroundColor = float4(lerp(color1, color2, 0.5 * cos(TAU / duration * Time) + 0.5), 1.0);
|
||||
|
||||
// Draw the terminal graphics over the background
|
||||
return lerp(backgroundColor, sample, sample.w);
|
||||
}
|
||||
45
samples/PixelShaders/Animate_scan.hlsl
Normal file
45
samples/PixelShaders/Animate_scan.hlsl
Normal file
@@ -0,0 +1,45 @@
|
||||
// A simple animated shader that draws an inverted line that scrolls down the screen
|
||||
|
||||
// The terminal graphics as a texture
|
||||
Texture2D shaderTexture;
|
||||
SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings
|
||||
{
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
// Resolution of the shaderTexture
|
||||
float2 Resolution;
|
||||
// Background color as rgba
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
float4 color = shaderTexture.Sample(samplerState, tex);
|
||||
|
||||
// Here we spread the animation over 5 seconds. We use time modulo 5 because we want
|
||||
// the timer to count to five repeatedly. We then divide the result by five again
|
||||
// to get a value between 0.0 and 1.0, which maps to our texture coordinate.
|
||||
float linePosition = Time % 5 / 5;
|
||||
|
||||
// Since TEXCOORD ranges from 0.0 to 1.0, we need to divide 1.0 by the height of the
|
||||
// texture to find out the size of a single pixel
|
||||
float lineWidth = 1.0 / Resolution.y;
|
||||
|
||||
// If the current texture coordinate is in the range of our line on the Y axis:
|
||||
if (tex.y > linePosition - lineWidth && tex.y < linePosition)
|
||||
{
|
||||
// Invert the sampled color
|
||||
color.rgb = 1.0 - color.rgb;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// Time since pixel shader was enabled
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
@@ -16,8 +16,9 @@ cbuffer PixelShaderSettings {
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color
|
||||
// Just ignore the pos parameter
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
|
||||
@@ -6,7 +6,7 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// Time since pixel shader was enabled
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
@@ -16,8 +16,9 @@ cbuffer PixelShaderSettings {
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color
|
||||
// Just ignore the pos parameter
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
|
||||
@@ -20,7 +20,7 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// Time since pixel shader was enabled
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
@@ -30,8 +30,9 @@ cbuffer PixelShaderSettings {
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color
|
||||
// Just ignore the pos parameter
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
@@ -82,7 +83,7 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// Time since pixel shader was enabled
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
@@ -92,8 +93,9 @@ cbuffer PixelShaderSettings {
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color
|
||||
// Just ignore the pos parameter
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
@@ -144,5 +146,75 @@ As a more complicated example, the Terminal's built-in `experimental.retroTermin
|
||||
|
||||

|
||||
|
||||
## Animated Effects
|
||||
|
||||
You can use the `Time` value in the shader input settings to drive animated effects. `Time` is the number of seconds since the shader first loaded. Here’s a simple example with a line of inverted pixels that scrolls down the terminal (`Animate_scan.hlsl`):
|
||||
|
||||
```hlsl
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
float4 color = shaderTexture.Sample(samplerState, tex);
|
||||
|
||||
// Here we spread the animation over 5 seconds. We use time modulo 5 because we want
|
||||
// the timer to count to five repeatedly. We then divide the result by five again
|
||||
// to get a value between 0.0 and 1.0, which maps to our texture coordinate.
|
||||
float linePosition = Time % 5 / 5;
|
||||
|
||||
// Since TEXCOORD ranges from 0.0 to 1.0, we need to divide 1.0 by the height of the
|
||||
// texture to find out the size of a single pixel
|
||||
float lineWidth = 1.0 / Resolution.y;
|
||||
|
||||
// If the current texture coordinate is in the range of our line on the Y axis:
|
||||
if (tex.y > linePosition - lineWidth && tex.y < linePosition)
|
||||
{
|
||||
// Invert the sampled color
|
||||
color.rgb = 1.0 - color.rgb;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
```
|
||||
What if we want an animation that goes backwards and forwards? In this example (`Animate_breathe.hlsl`), we'll make the background fade between two colours. Our `Time` value only ever goes up, so we need a way to generate a value that sweeps back and forth from `0.0` to `1.0`. Trigonometric functions like cosine are perfect for this and are very frequently used in shaders.
|
||||
|
||||
`cos()` outputs a value between `-1.0` and `1.0`. We can adjust the wave with the following formula:
|
||||
|
||||
```
|
||||
a * cos(b * (x - c)) + d
|
||||
```
|
||||
|
||||
Where `a` adjusts the amplitude, `b` adjusts the wavelength/frequency, `c` adjusts the offset along the x axis, and `d` adjusts the offset along the y axis. You can use a graphing calculator (such as the Windows Calculator) to help visualize the output and experiment:
|
||||
|
||||

|
||||
|
||||
As shown above, by halving the output and then adding `0.5`, we can shift the range of the function to `0.0` - `1.0`. Because `cos()` takes input in radians, if we multiply `x` (`Time`) by tau (`2*pi`), we are effectively setting the wavelength to `1.0`.
|
||||
|
||||
In other words, our full animation will be one second long. We can modify this duration by dividing tau by the number of seconds we want the animation to run for. In this case, we’ll go for five seconds.
|
||||
|
||||
Finally we use linear interpolation to achieve our breathing effect by selecting a color between our two chosen colors based on the output from our cosine.
|
||||
|
||||
```hlsl
|
||||
// pi and tau (2 * pi) are useful constants when using trigonometric functions
|
||||
#define TAU 6.28318530718
|
||||
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
float4 sample = shaderTexture.Sample(samplerState, tex);
|
||||
|
||||
// The number of seconds the breathing effect should span
|
||||
float duration = 5.0;
|
||||
|
||||
float3 color1 = float3(0.3, 0.0, 0.5); // indigo
|
||||
float3 color2 = float3(0.1, 0.1, 0.44); // midnight blue
|
||||
|
||||
// Set background colour based on the time
|
||||
float4 backgroundColor = float4(lerp(color1, color2, 0.5 * cos(TAU / duration * Time) + 0.5), 1.0);
|
||||
|
||||
// Draw the terminal graphics over the background
|
||||
return lerp(backgroundColor, sample, sample.w);
|
||||
}
|
||||
```
|
||||
|
||||
Feel free to modify and experiment!
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// Time since pixel shader was enabled
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
@@ -16,8 +16,9 @@ cbuffer PixelShaderSettings {
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color
|
||||
// Just ignore the pos parameter
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
// tex is an x,y tuple that ranges from 0,0 (top left) to 1,1 (bottom right).
|
||||
// Just ignore the pos parameter.
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
// Read the color value at the current texture coordinate (tex)
|
||||
|
||||
BIN
samples/PixelShaders/Screenshots/GraphCosine.png
Normal file
BIN
samples/PixelShaders/Screenshots/GraphCosine.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Reference in New Issue
Block a user