我想为完整的 Windows 桌面添加动态视频效果。
我希望能够将屏幕变成灰色,斜切边缘并添加一些扫描线,使它看起来像一个旧的 CRT 屏幕,或者使屏幕出现故障,就像他们在电影中显示被黑系统一样了解技术等
效果本身超出了这个问题的范围。这个问题是如何应用它们,即如何获取 Windows 生成的桌面图像,应用我的效果,并在同一显示器上显示结果。
我知道几种可行的方法。
- 连接到绘制内容的 WinAPI 调用。
- 创建一个假的辅助显示器,使其成为主要显示设备,从中获取视频流,应用我的效果,并在真实显示器上呈现
- 创建应用效果的自定义显示驱动程序。
它们都有缺点:复杂性、驱动程序签名要求、复杂的设置。有没有更好的方法来实现我想要的?
1 很难,太多不同的 API。
2 困难的部分是制作假显示器。如果您改为购买一个 10 美元的设备,称为“HDMI 虚拟插头”,它将变得相对简单,具有 100% 记录的 API。使用桌面复制 API 获取显示器 1 的纹理,应用任何你想要的效果并在显示器 2 上显示。如果你想要好的性能,你最好完全在 GPU 上实现处理,例如使用自定义像素着色器渲染四边形。
3 会工作,但很难做到。
还有一个办法。实现和使用未记录的 API 很棘手,但根据我的经验,它非常可靠,至少在 Windows 7 上是这样。编写一个 DLL,将其自身注入(inject) dwm.exe。那是一个 windows 进程“桌面 windows 管理器”,它组成了桌面上可见的任何东西。 DLL 注入(inject)后,创建一个新线程,在该线程中调用 D3D11CreateDeviceAndSwapChain,然后使用例如MinHook拦截 Present
,理想情况下还拦截 IDXGISwapChain
接口(interface)的 ResizeBuffers
方法。如果成功,dwm.exe 将在每次显示帧或桌面分辨率更改时调用 DLL 中的函数。然后,在目前的功能中你可以做你的事情,例如添加另一个实现您的效果的渲染 channel ,然后调用原始实现以实际将结果呈现到桌面。
这在理论上很容易,但在实践中实现起来却相当棘手。例如。很难调试 dwm.exe,你必须依赖日志记录,或者使用带有远程调试器的虚拟机。这也不能跨 Windows 版本移植。另一个限制是,它不适用于视频游戏等全屏应用程序,它们会绕过 dwm.exe。对于视频游戏,仅适用于“无边框全屏窗口”游戏内设置。
更新:另一种方法,简单得多。创建一个具有逐像素透明度的最顶层全屏窗口。操作系统支持它们数十年,设置 WS_EX_LAYERED
和 WS_EX_TRANSPARENT
扩展样式位。你将无法进行灰度,因为你只能覆盖你的东西而不能读取下面的内容,但边缘、扫描线和毛刺是完全可行的。为了获得最佳性能,请使用一些以 GPU 为中心的 API 来渲染该窗口,例如C++ 中的 Direct2D 或 D3D。它也更容易调试,要么使用 2 个显示器,要么放置透明窗口,使其在角落占据一个矩形,为 IDE 留出足够的屏幕空间。 Here's an example (不是我的)。