ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Windows Terminal 像素着色器实战:experimental.pixelShaderPath 与 HLSL 着色器的完整编写、配置与动画指南

Windows Terminal 像素着色器实战:experimental.pixelShaderPath 与 HLSL 着色器的完整编写、配置与动画指南 Windows Terminal 像素着色器实战experimental.pixelShaderPath 与 HLSL 着色器的完整编写、配置与动画指南【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal本文基于 Windows TerminalWindows Terminal and the original Windows console host官方示例文档 samples/PixelShaders/README.md 整理并深度扩充完整讲解如何通过experimental.pixelShaderPath设置项为终端加载自定义 HLSL 像素着色器从最小反转着色器、扫描线/栅格条特效、内建复古效果的源码剖析到基于Time变量的一维滚动与往返呼吸动画。读完后你可以独立编写、配置并调试自己的终端像素着色器理解终端画面是如何被送入 GPU 纹理、再由着色器逐像素重采样的。特性总览一个实验性设置项把终端画面交给 GPUGPU 拥有巨大的并行算力像素着色器可以实时完成分形缩放、光线追踪、图像处理等运算。Windows Terminal 允许用户提供一个像素着色器文件由渲染引擎将其编译并应用于终端画面。启用方式是在任意一个 profile或全局 settings 段中加入experimental.pixelShaderPath: path to a .hlsl pixel shader两个关键行为均有官方 JSON Schema 佐证见 doc/cascadia/profiles.schema.json优先级一旦指定了experimental.pixelShaderPathTerminal 会用它替代内建的experimental.retroTerminalEffectretro 效果同样作用于失焦场景见 doc/cascadia/profiles.schema.json。生效时机Schema 中对该字段的描述是 Use to set a path to a pixel shader to use with the Terminalwhen unfocused即着色器在终端失去焦点时应用——这与内建复古效果失焦时才显示的行为一致。该字段为实验性特性官方注明其后续存续不做保证This is an experimental feature, and its continued existence is not guaranteed。该字段同时出现在 profile 级别与全局 settings 级别doc/cascadia/profiles.schema.json因此可以按 profile 定制各自的着色器。从源码结构看该设置经由 src/cascadia/TerminalSettingsModel/MTSMSettings.h 进入设置模型由 src/cascadia/ControlProperties.h 传给终端控件最终在 src/renderer/atlas/AtlasEngine.api.cpp 与 src/renderer/atlas/BackendD3D.cpp 的 D3D 后端中被加载与绑定——终端文本被先渲染成一张纹理着色器再对这张纹理做逐像素变换。内建 retro 着色器本身也以源文件形式随引擎编译src/renderer/atlas/custom_shader_ps.hlsl与下文示例 samples/PixelShaders/Retro.hlsl 内容一致。快速上手12 行的颜色反转着色器官方示例目录中最简单的起点是 samples/PixelShaders/Invert.hlsl。完整代码如下// A minimal pixel shader that inverts the colors // 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 is tuple of 4 floats, rgba float4 color shaderTexture.Sample(samplerState, tex); // Inverts the rgb values (xyz) but dont touch the alpha (w) color.xyz 1.0 - color.xyz; // Return the final color return color; }操作步骤照做即可把上面的代码保存为C:\temp\invert.hlsl在终端设置的某个 profile 中更新配置experimental.pixelShaderPath: C:\\temp\\invert.hlsl注意 JSON 中路径的反斜杠必须写成双反斜杠转义保存 settings 文件后用该 profile 打开一个新终端屏幕颜色即被反转。左侧默认终端右侧应用 Invert 着色器编译失败与重新加载机制如果着色器编译失败Terminal 会弹出一个警告对话框并临时忽略该着色器。修复后重新 touch 一下settings.json文件或者直接打开一个新标签页Terminal 就会再次尝试加载着色器——无需重启整个应用。着色器 API 契约终端向 HLSL 暴露了什么所有终端像素着色器共享同一套全局约定见 samples/PixelShaders/README.md全局符号类型含义shaderTextureTexture2D终端当前画面文本、背景作为纹理。这是你唯一输入画面的来源必须通过它采样samplerStateSamplerState配套的采样器shaderTexture.Sample(samplerState, uv)的标准搭档PixelShaderSettings.Timefloat着色器启用后经过的秒数只能递增驱动动画的核心变量PixelShaderSettings.ScalefloatUI 缩放比例。做按像素偏移的运算时要乘它以适配 DPIPixelShaderSettings.Resolutionfloat2shaderTexture的像素分辨率宽, 高。1.0/Resolution.y即单行像素在 UV 空间的高度PixelShaderSettings.Backgroundfloat4终端背景色rgba入口函数签名为float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET着色器是一个给定纹理坐标tex就产出一个颜色的纯函数。tex是 0,0左上角到 1,1右下角的归一化坐标pos参数可以直接忽略。关于 HLSL 语言本身它是一种类 C 语言但有若干限制——不能动态分配内存、不能使用指针、不能递归。作为交换你能得到体感为 teraflop 量级的并行算力实时光线追踪、分形等效果在近年 GPU 上完全可行。社区中大量 GLSL 写的像素着色器案例如 Shadertoy 上的 menger sponge 等分形/光线追踪作品在熟悉之后可以较容易地移植为 HLSL。进阶示例Retro 栅格条 文字投影Rasterbars接下来看官方给出的更复杂示例——80 年代 CRT 风格的栅格条raster bars背景源码为 samples/PixelShaders/Rasterbars.hlsl// A minimal pixel shader that shows some raster bars // 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 is tuple of 4 floats, rgba float4 color shaderTexture.Sample(samplerState, tex); // Read the color value at some offset, will be used as shadow float4 ocolor shaderTexture.Sample(samplerState, tex2.0*Scale*float2(-1.0, -1.0)/Resolution.y); // Thickness of raster const float thickness 0.1; float ny floor(tex.y/thickness); float my tex.y%thickness; const float pi 3.141592654; // ny is used to compute the rasterbar base color float cola ny*2.0*pi; float3 col 0.750.25*float3(sin(cola*0.111), sin(cola*0.222), sin(cola*0.333)); // my is used to compute the rasterbar brightness // smoothstep is a great little function: https://en.wikipedia.org/wiki/Smoothstep float brightness 1.0-smoothstep(0.0, thickness*0.5, abs(my - 0.5*thickness)); float3 rasterColor col*brightness; // lerp(x, y, a) is another very useful function: https://en.wikipedia.org/wiki/Linear_interpolation float3 final rasterColor; // Create the drop shadow of the terminal graphics // .w is the alpha channel, 0 is fully transparent and 1 is fully opaque final lerp(final, float(0.0), ocolor.w); // Draw the terminal graphics final lerp(final, color.xyz, color.w); // Return the final color, set alpha to 1 (ie opaque) return float4(final, 1.0); }这段代码是理解终端画面 带 alpha 的纹理这一关键模型的最佳范本color.w就是字形不透明度。终端画面纹理中背景区域 alpha 接近 0文字像素 alpha 接近 1。因此final lerp(final, color.xyz, color.w)一行就完成了把终端图形合成到自定义背景上——这也是你写背景替换类着色器的通用套路先用Time、tex等算出背景色再按 alpha 把原画面叠上去。偏移采样制造投影tex 2.0*Scale*float2(-1.0,-1.0)/Resolution.y在 UV 空间向左上偏移2 个像素。除以Resolution.y把像素数换算成 UV 距离乘以Scale抵消高 DPI 缩放这正是PixelShaderSettings中Scale与Resolution的用途。偏移位置采样到的 alphaocolor.w被用来先把目标色拉黑形成文字左上方 45° 的落影再叠回正文提升栅格条背景上的可读性。条带的基色ny floor(tex.y/thickness)把垂直方向按厚度0.1切成横条索引用三个频率错开的sin生成每条不同的底色条带亮度包络my tex.y%thickness是条内局部坐标1.0 - smoothstep(0.0, thickness*0.5, abs(my - 0.5*thickness))让亮度在条中心最亮、向边缘平滑衰减模拟 CRT 行的辉光边界。重载后你会看到背景出现复古栅格条且文字带投影、依旧清晰内建 Retro 效果的源码剖析高斯模糊 方波扫描线再复杂一档的例子是 Terminal 内建的experimental.retroTerminalEffect其完整实现即本目录的 samples/PixelShaders/Retro.hlsl并且与编译进 D3D 渲染引擎的内建版本 src/renderer/atlas/custom_shader_ps.hlsl 逐行一致——直接阅读引擎源码即可印证其行为。其main函数只有三步float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET { // TODO:GH#3930 Make these configurable in some way. float4 color shaderTexture.Sample(samplerState, tex); color Blur(shaderTexture, tex, SCALED_GAUSSIAN_SIGMA) * 0.3f; color Scanline(color, pos); return color; }辉光模糊Blur对当前纹素周围13×13sampleCount 13的邻域逐点采样每个样本乘以二维高斯权重Gaussian2D(dx, dy, sigma)sigma 取2.0f * scale即随 UI 缩放自适应。169 次带权采样叠加后以 30% 强度加回原色——这就是 CRT 磷光泛光的来源。从源码结构看这里用的是固定循环次数的朴素卷积而非多级 mipmap 或双线性近似属于典型的GPU 算力换画质写法。扫描线Scanline/SquareWaveSquareWave(y)返回1.0f - (floor(y / SCALED_SCANLINE_PERIOD) % 2.0f) * SCANLINE_FACTOR即周期为scale、占空比 50% 的方波SCANLINE_FACTOR 0.5f最终color * wave让隔行亮度减半形成细密扫描线。源码中还留有一处被 false关闭的实验分支标注 TODO:GH#3929意图是让扫描线只在暗背景上加亮而非全局乘暗说明该效果仍在迭代中。这两处TODOGH#3929 / GH#3930也解释了为何官方推荐自定义pixelShaderPath内建效果的强度、周期尚未开放配置想要只要辉光不要扫描线之类的变体最直接的途径就是复制 Retro.hlsl 改参数。动画效果一用 Time 驱动单向滚动扫描线Time着色器加载后的秒数是驱动动画的输入。官方示例 samples/PixelShaders/Animate_scan.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; }两个要点周期归一化Time % 5 / 5把只增不减的时间映射回[0.0, 1.0)即每 5 秒完整走一遍纹理正好对应 UV 坐标域像素级线宽UV 空间里一个物理像素的高度是1.0 / Resolution.y用它判定tex.y是否落在扫描线上可保证线宽恒为 1 行像素、不随窗口大小变化。动画效果二余弦调制实现往返呼吸背景如果希望动画往返往复而非单向循环Time只增不减的特性就需要借助三角函数。官方示例 samples/PixelShaders/Animate_breathe.hlsl 让背景在两种颜色之间呼吸式渐变。cos()输出[-1.0, 1.0]通用波形整形公式为a * cos(b * (x - c)) d其中a调振幅、b调波长/频率、c调 x 轴偏移、d调 y 轴偏移。可用图形计算器如 Windows 自带计算器可视化实验。输出减半再加0.5即a 0.5, d 0.5把值域平移到[0.0, 1.0]正好可以直接作为lerp的插值因子cos()以弧度为输入将x这里是Time乘以 tau2*pi等效于把波长设为 1 秒——整个动画周期即为 1 秒把 tau 除以期望的秒数即可任意调整时长。本例取 5 秒。完整实现// 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); }最后lerp(backgroundColor, sample, sample.w)按字形 alpha 把终端图形合成在呼吸背景之上——与 Rasterbars 中背景 alpha 合成的手法完全同构是写自定义背景类着色器的标准收尾。示例着色器全览与其他可用素材samples/PixelShaders 目录中还提供了更多可直接运行的.hlsl起点覆盖了从验证管线到故障演示的各种场景文件用途Invert.hlsl最小示例整体反色用于验证着色器链路打通Nop.hlsl直通no-op采样后原样返回适合做调试基线Grayscale.hlsl灰度化Outlines.hlsl边缘/描边效果BackgroundImage.hlsl背景图替换类着色器配合自定义纹理的思路可参考 Rasterbars 的 alpha 合成写法Rasterbars.hlsl彩色栅格条 文字投影Retro.hlsl内建复古效果的完整实现高斯辉光 扫描线Animate_scan.hlsl单向滚动反色扫描线Animate_breathe.hlsl余弦往返的背景呼吸动画Broken.hlsl故意写坏的着色器可用来验证编译失败→警告对话框→临时忽略的降级行为Error.hlsl编译错误演示同属调试参考配合官方 Schemadoc/cascadia/profiles.schema.json可确认该设置项在 profile 与全局 settings 两级均受支持、为字符串类型的文件路径、且明确标注为实验性特性。实践清单与注意事项配置experimental.pixelShaderPath: C:\\path\\to\\shader.hlslJSON 内反斜杠需双写可放在 profile 内按配置定制优先级高于experimental.retroTerminalEffect。生效时机按 Schema 描述着色器在终端失焦unfocused时应用失焦即见效果聚焦恢复原画面。重载着色器编译失败会弹警告并临时忽略修复后重新 touchsettings.json或开新标签页即可重试无需重启。编写约束HLSL 无动态内存分配、无指针、无递归所有画面信息只能来自shaderTexture采样所有窗口信息来自PixelShaderSettingsTime/Scale/Resolution/Background。合成套路自定义背景类着色器 算出背景色 →lerp(背景, 采样色, 采样色.w)做像素偏移一律用像素数 * Scale / Resolution.y换算到 UV 空间。调试手段拿 Nop.hlsl 确认管线、拿 Broken.hlsl 验证错误降级路径再逐步加入自己的逻辑。从12 行反色到169 样本高斯辉光这套机制把终端每一帧都变成了一张可任意二次加工的纹理。以上示例均可在仓库中直接对照源码阅读动手改参数是最快的学习方式——欢迎基于这些起点继续实验。【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表