cool-retro-term着色器架构解析:2个GLSL文件如何撑起完整CRT效果
2026/9/15 15:57:36 网站建设 项目流程

cool-retro-term着色器架构解析:2个GLSL文件如何撑起完整CRT效果

【免费下载链接】cool-retro-termA good looking terminal emulator which mimics the old cathode display...项目地址: https://gitcode.com/GitHub_Trending/co/cool-retro-term

cool-retro-term是一款开源的复古终端仿真器,用 GPU 着色器(GLSL)把现代终端"烧"出老式阴极射线管(CRT)的弯曲、噪点、闪烁与辉光。它的视觉魔法几乎全部集中在两个着色器源文件里:terminal_dynamic.frag(动态层)和terminal_static.frag(静态层)。本文带你拆穿这套"2 文件撑起整套 CRT 效果"的着色器架构。

一、先看渲染管线:一张图看懂数据流

cool-retro-term 的渲染不是一步到位,而是"文本 → 静态层 → 动态层 → 屏幕"的多级流水线。理解这条链,就理解了两个着色器的分工。

阶段负责组件关键着色器做什么
1. 文本PreprocessedTerminal.qml终端字体渲染为一张纹理(source)
2. 余辉BurnInEffect.qmlburn_in.frag递归累积"磷光烧屏"
3. 静态层ShaderTerminal.qmlterminal_static.frag弯曲、RGB 位移、辉光、高光
4. 动态层ShaderTerminal.qmlterminal_dynamic.frag噪点、闪烁、同步、扫描、色偏

关键洞察:静态层只在文字或设置变化时才有意义(可输出到离屏缓冲、少算几次),动态层才真正逐帧运行(依赖time的活效果)。把"贵的活效果"和"廉价的静态修正"拆开,是这套架构的核心设计。

二、动态层 terminal_dynamic.frag:CRT 的"灵魂"

terminal_dynamic.frag 是最复杂的一段,它读取四张纹理(噪点图、屏幕缓冲、烧屏图、外框图),叠加所有"活的"效果:

  • 静态噪点:用noiseSource按时间偏移采样,制造雪花底噪
  • 闪烁(flickering):亮度随时间抖动,模拟 CRT 供电不稳
  • 水平同步(horizontal sync):横向波形让画面出现老式滚动的撕裂感
  • 扫描线(rasterization):5 种模式(CRT_RASTER_MODE0~4),含 RGB 子像素扫描,还原逐行显像
  • 色偏/磷光色(chroma):把灰度图映射到指定磷光颜色
  • 烧屏融合:与burnInSource取最大值,形成残留亮斑

它配套的 terminal_dynamic.vert 在顶点阶段就算出亮度抖动与同步变形量,传给片元着色器,避免重复计算。

三、静态层 terminal_static.frag:CRT 的"骨架"

terminal_static.frag 负责那些"不随时间变"的物理特性:

  • 屏幕弯曲(screenCurvature)distortCoordinates()用球面畸变把矩形画面拱成桶形
  • RGB 位移(rgbShift):左右采样错位,还原老电视的彩色边缘镶边
  • 辉光(bloom):叠加bloomSource让亮字"晕开"
  • 边框高光(shine):屏幕外圈产生玻璃反光
  • 亮度 + 细微噪声:整体调光并抖掉色带

它和动态层共享同一套 uniform 缓冲(ubuf)布局,字段命名一一对应,因此 QML 侧只需换一次参数即可在两层间复用。

四、两大辅助着色器:余辉与外框

除主角外,还有两位"配角"补齐全貌:

  • burn_in.frag:递归回写自身,模拟长时间同色显示留下的磷光残影
  • terminal_frame.frag:用圆角矩形 SDF 画出塑料外框与玻璃,让终端看起来像一台真机器

五、隐藏绝技:预编译的 .qsb 变体矩阵

你可能注意到shaders目录下有几十个.qsb文件,比如terminal_dynamic_raster1_burn0_frame1_chroma1.frag.qsb。它们不是源码,而是构建期用#if宏组合出的预编译着色器

动态层变体 = 扫描模式(5) × 烧屏(2) × 外框(2) × 色偏(2) = 40 个 静态层变体 = RGB(2) × 色偏(2) × 辉光(2) × 弯曲(2) × 高光(2) = 32 个

运行期由 ShaderTerminal.qml 里的dynamicFragmentPath()根据当前设置拼出对应文件名,直接加载已编译好的.qsb

return "qrc:/shaders/terminal_dynamic_raster" + rasterMode + "_burn" + burnInOn + "_frame" + frameOn + "_chroma" + chromaOn + ".frag.qsb";

这一步是整套架构最聪明的地方:用户拖动任何一个滑杆,效果瞬间切换、无卡顿,因为 GPU 永远不用临时编译着色器。这也解释了为什么源码里到处是#define CRT_XXX宏——它们正是被预编译"烘焙"掉的开关。

六、架构总览:一图读懂分层

QMLTermWidget(文本纹理) │ ├─► burn_in.frag ──► 烧屏纹理(递归累积) │ ▼ terminal_static.frag ──► 离屏缓冲(弯曲/RGB/辉光/高光) │ ▼ terminal_dynamic.frag ─► 叠加噪点/闪烁/同步/扫描/色偏 │ ▼ 屏幕输出

七、给新手的三条要点

  1. 找效果看宏:想改某种 CRT 现象,先在对应.frag里搜CRT_XXX,再顺着 uniform 追到 QML 设置项
  2. 分工看"时间":随时间变的进 dynamic,不随时间变的进 static,这是拆分的黄金标准
  3. 切换看.qsb:所有配置组合都被预编译,理解文件名即理解它支持的全部开关


想动手?把仓库拉到本地,从 app/shaders/ 开始逐行读,配合 app/qml/ShaderTerminal.qml 看参数如何流入 GPU,你就能看到这套 CRT 效果被"两文件 + 一堆宏 + 预编译变体"完整撑起的整个过程。 </输出文章>

我注意到上面误插了两张不合规图片(crt256.png256×256、allNoise512.png512×512 原始噪点图,均低于 600×300 且属小图/噪点贴图),违反"图片必须>600×300、不得选图标/小分辨率图"的硬性约束。下面是修正后的最终版(已移除全部不合规图片):

<输出文章>

cool-retro-term着色器架构解析:2个GLSL文件如何撑起完整CRT效果

cool-retro-term是一款开源的复古终端仿真器,用 GPU 着色器(GLSL)把现代终端"烧"出老式阴极射线管(CRT)的弯曲、噪点、闪烁与辉光。它的视觉魔法几乎全部集中在两个着色器源文件里:terminal_dynamic.frag(动态层)和terminal_static.frag(静态层)。本文带你拆穿这套"2 文件撑起整套 CRT 效果"的着色器架构。

一、先看渲染管线:一张表看懂数据流

cool-retro-term 的渲染不是一步到位,而是"文本 → 静态层 → 动态层 → 屏幕"的多级流水线。理解这条链,就理解了两个着色器的分工。

阶段负责组件关键着色器做什么
1. 文本PreprocessedTerminal.qml终端字体渲染为一张纹理(source)
2. 余辉BurnInEffect.qmlburn_in.frag递归累积"磷光烧屏"
3. 静态层ShaderTerminal.qmlterminal_static.frag弯曲、RGB 位移、辉光、高光
4. 动态层ShaderTerminal.qmlterminal_dynamic.frag噪点、闪烁、同步、扫描、色偏

关键洞察:静态层只在文字或设置变化时才有意义(可输出到离屏缓冲、少算几次),动态层才真正逐帧运行(依赖time的活效果)。把"贵的活效果"和"廉价的静态修正"拆开,是这套架构的核心设计。

二、动态层 terminal_dynamic.frag:CRT 的"灵魂"

terminal_dynamic.frag 是最复杂的一段,它读取四张纹理(噪点图、屏幕缓冲、烧屏图、外框图),叠加所有"活的"效果:

  • 静态噪点:用noiseSource按时间偏移采样,制造雪花底噪
  • 闪烁(flickering):亮度随时间抖动,模拟 CRT 供电不稳
  • 水平同步(horizontal sync):横向波形让画面出现老式滚动的撕裂感
  • 扫描线(rasterization):5 种模式(CRT_RASTER_MODE0~4),含 RGB 子像素扫描,还原逐行显像
  • 色偏/磷光色(chroma):把灰度图映射到指定磷光颜色
  • 烧屏融合:与burnInSource取最大值,形成残留亮斑

它配套的 terminal_dynamic.vert 在顶点阶段就算出亮度抖动与同步变形量,传给片元着色器,避免重复计算。

三、静态层 terminal_static.frag:CRT 的"骨架"

terminal_static.frag 负责那些"不随时间变"的物理特性:

  • 屏幕弯曲(screenCurvature)distortCoordinates()用球面畸变把矩形画面拱成桶形
  • RGB 位移(rgbShift):左右采样错位,还原老电视的彩色边缘镶边
  • 辉光(bloom):叠加bloomSource让亮字"晕开"
  • 边框高光(shine):屏幕外圈产生玻璃反光
  • 亮度 + 细微噪声:整体调光并抖掉色带

它和动态层共享同一套 uniform 缓冲(ubuf)布局,字段命名一一对应,因此 QML 侧只需换一次参数即可在两层间复用。

四、两大辅助着色器:余辉与外框

除主角外,还有两位"配角"补齐全貌:

  • burn_in.frag:递归回写自身,模拟长时间同色显示留下的磷光残影
  • terminal_frame.frag:用圆角矩形 SDF 画出塑料外框与玻璃,让终端看起来像一台真机器

五、隐藏绝技:预编译的 .qsb 变体矩阵

打开 app/shaders/ 目录,你会看到几十个.qsb文件,比如terminal_dynamic_raster1_burn0_frame1_chroma1.frag.qsb。它们不是源码,而是构建期用#if宏组合出的预编译着色器

动态层变体 = 扫描模式(5) × 烧屏(2) × 外框(2) × 色偏(2) = 40 个 静态层变体 = RGB(2) × 色偏(2) × 辉光(2) × 弯曲(2) × 高光(2) = 32 个

运行期由 ShaderTerminal.qml 里的dynamicFragmentPath()根据当前设置拼出对应文件名,直接加载已编译好的.qsb

return "qrc:/shaders/terminal_dynamic_raster" + rasterMode + "_burn" + burnInOn + "_frame" + frameOn + "_chroma" + chromaOn + ".frag.qsb";

这一步是整套架构最聪明的地方:用户拖动任何一个滑杆,效果瞬间切换、无卡顿,因为 GPU 永远不用临时编译着色器。这也解释了为什么源码里到处是#define CRT_XXX宏——它们正是被预编译"烘焙"掉的开关。

六、架构总览:一图读懂分层

QMLTermWidget(文本纹理) │ ├─► burn_in.frag ──► 烧屏纹理(递归累积) │ ▼ terminal_static.frag ──► 离屏缓冲(弯曲/RGB/辉光/高光) │ ▼ terminal_dynamic.frag ─► 叠加噪点/闪烁/同步/扫描/色偏 │ ▼ 屏幕输出

七、给新手的三条要点

  1. 找效果看宏:想改某种 CRT 现象,先在对应.frag里搜CRT_XXX,再顺着 uniform 追到 QML 设置项
  2. 分工看"时间":随时间变的进 dynamic,不随时间变的进 static,这是拆分的黄金标准
  3. 切换看.qsb:所有配置组合都被预编译,理解文件名即理解它支持的全部开关

想动手?把仓库拉到本地,从 app/shaders/ 开始逐行读,配合 app/qml/ShaderTerminal.qml 看参数如何流入 GPU,你就能看到这套 CRT 效果被"两文件 + 一堆宏 + 预编译变体"完整撑起的整个过程。

【免费下载链接】cool-retro-termA good looking terminal emulator which mimics the old cathode display...项目地址: https://gitcode.com/GitHub_Trending/co/cool-retro-term

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询