0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひとりで完走_C# is GODAdvent Calendar 2024

Day 7

Windowsターミナルのカスタマイズ with hlsl

Last updated at Posted at 2024-12-06

Windowsターミナルhlsl拡張

Windowsターミナルでは外観をシェーダーを使ってアニメーションさせることができるようになっていて、かなり自由度の高い外観にすることが可能です。
今回は前回作成した画面横揺れエフェクトを元にWindowsターミナルをレトロ感あふれる砂嵐風の外観にしてみたいと思います。

横揺れエフェクトについて

今回使用するシェーダーは以下の動画を参考にしたものとなっています。

hlslが使えることにより、Unityなどの記事を参考にできるのは非常に便利だと思います。 Unityのシェーダー記事にはいろいろなものがあるので、気に入ったものから試してみてはいかがでしょうか。

WindowsTerminal組み込みエフェクト

WindowsTerminalの設定をいろいろいじった人ならわかるかもしれませんが、WindowsTerminalには既存のレトロエフェクトがあります。
実はこれもhlslのシェーダーにより効果がつけられていて、Microsoftが無料で公開しています。
スクリーンショット 2024-12-06 211413.png
以下のリンクがそのシェーダーのソースコードになります。
WindowsTerminal PixelShaders

今回はこれを元に改造して横揺れエフェクトを作るようにします。

シェーダー作成

hlslは以下の通りになります。

// The original retro pixel shader
Texture2D shaderTexture;
SamplerState samplerState;

cbuffer PixelShaderSettings {
    float time;
    float scale;
    float2 resolution;
    float4 background;
};

#define SCANLINE_FACTOR 0.5f
#define SCALED_SCANLINE_PERIOD scale
#define SCALED_GAUSSIAN_SIGMA (2.0f * scale)

static const float M_PI = 3.14159265f;

// ブラー処理(レトロエフェクトに既存)
float Gaussian2D(float x, float y, float sigma) {
    return 1 / (sigma * sqrt(2 * M_PI)) * exp(-0.5 * (x * x + y * y) / sigma / sigma);
}

// ブラー処理(レトロエフェクトに既存)
float4 Blur(Texture2D input, float2 tex_coord, float sigma) {
    float width, height;
    shaderTexture.GetDimensions(width, height);

    float texelWidth = 1.0f / width;
    float texelHeight = 1.0f / height;

    float4 color = {
        0, 0, 0, 0
    };

    float sampleCount = 13;

    for (float x = 0; x < sampleCount; x++) {
        float2 samplePos = {
            0, 0
        };
        samplePos.x = tex_coord.x + (x - sampleCount / 2.0f) * texelWidth;

        for (float y = 0; y < sampleCount; y++) {
            samplePos.y = tex_coord.y + (y - sampleCount / 2.0f) * texelHeight;
            color += input.Sample(samplerState, samplePos) * Gaussian2D(x - sampleCount / 2.0f, y - sampleCount / 2.0f, sigma);
        }
    }

    return color;
}

float SquareWave(float y) {
    return 1.0f - (floor(y + time*10 / SCALED_SCANLINE_PERIOD) % 2.0f) * SCANLINE_FACTOR;
}

// 走査線を作成する関数
float4 Scanline(float4 color, float4 pos) {
    float wave = SquareWave(pos.y);

    if (length(color.rgb) < 0.2f && false) {
        return color + wave * 0.1f;
    }
    else {
        return color * wave;
    }
}

// ノイズを作成する関数
float2 gradientNoise_dir(float2 p) {
    p = p % 289;
    float x = (34 * p.x + 1) * p.x % 289 + p.y;
    x = (34 * x + 1) * x % 289;
    x = frac(x / 41) * 2 - 1;
    return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
}

// ノイズを作成する関数
float gradientNoise_func(float2 p) {
    float2 ip = floor(p);
    float2 fp = frac(p);
    float d00 = dot(gradientNoise_dir(ip), fp);
    float d01 = dot(gradientNoise_dir(ip + float2(0, 1)), fp - float2(0, 1));
    float d10 = dot(gradientNoise_dir(ip + float2(1, 0)), fp - float2(1, 0));
    float d11 = dot(gradientNoise_dir(ip + float2(1, 1)), fp - float2(1, 1));
    fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
    return lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x);
}

// ノイズを作成する関数
float GradientNoise(float2 UV, float Scale) {
    return gradientNoise_func(UV * Scale) + 0.5;
}

// 入力をリマップする関数
float4 Remap_Float(float4 In, float2 inMinMax, float2 outMinMax) {
    return outMinMax.x + (In - inMinMax.x) * (outMinMax.y - outMinMax.x) / (inMinMax.y - inMinMax.x);
}

// 揺れをランダムに起こさせるための関数
float StepLighting() {
    float step = GradientNoise(time * 10, 1.0f);
    return step * step;
}

// main関数
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
{
    float gradientNoise = Remap_Float(GradientNoise(tex.y * time * 10, 1.0f), float2(0,1), float2(-0.005f,0.005f)) * StepLighting();
    float2 uv = tex - gradientNoise;
    // TODO:GH#3930 Make these configurable in some way.
    float4 color = shaderTexture.Sample(samplerState, uv);
    color += Blur(shaderTexture, uv, SCALED_GAUSSIAN_SIGMA) * 0.3f;
    color = Scanline(color, pos);

    return color;
}

WindowsTerminalでは自身の現在映している画面がテクスチャになり、上部のsamplerStateで入力されます。
shaderTexture.Sample(samplerState, TEXCOORD)で画面のテクスチャが入手できます。
今回はTEXCOORDにノイズを加えてやることで、横揺れの動きを表現します。
ですので、main()に与えられたtexにノイズを加えてSample()TEXCOORDに入れている形となります。
その後既存のブラー処理とScanline処理を加えて出力している形となります。

ノイズ処理等について

今回ノイズを作成するにあたって、Unityが開発したShaderGraphのマニュアルに載ってある関数をコピペしてノイズ処理を行っています。
その他、RemapについてもUnityのマニュアルに載っていた関数になります。
参考としてノイズ関数のリンクを貼っておきます。
Unity ShaderGraph GradientNoise
UnityのマニュアルではOut float outで結果を返していますが、戻り値の型を記述し、returnで値を返す形としていますが何でもよいです。

あとはmain()で上記の動画と同様の処理を行えば、まったく同じようなエフェクトがWindowsTerminalで使えるようになります。

WindowsTerminalに登録

それでは最後に作成したhlslをWindowsTerminalに登録する方法です。
WidowsTerminalの設定を開き、メニューの一番下に「Jsonファイルを開く」があると思うのでこれをクリックして開きます。
そのファイルのprofiles,defaultsの中に"experimental.pixelShaderPath": "{先ほど作成したhlslのパス}",と書き足せば反映されます。
一応その箇所だけ載せておきます

"profiles": 
    {
        "defaults": 
        {
            "antialiasingMode": "cleartype",
            "colorScheme": "Pro Light",
+           "experimental.pixelShaderPath": "C:\\Users\\VScode\\RetroPixel.hlsl",
            "experimental.retroTerminalEffect": false,
            "font": 
            {
                "face": "Consolas"
            },
            "intenseTextStyle": "none",
            "opacity": 30,
            "padding": "12",
            "useAcrylic": true
        },
        "list": 
        [
            {
                "commandline": "%SystemRoot%\\System32\\cmd.exe",
                "elevate": false,
                "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
                "hidden": false,
                "name": "CommandPrompt"
            },
            ...
        ]
    },
    "schemes": ...

以上でWindowsターミナルの外観変更ができました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?