C#Opengl钩子Swapbuffer添加插值帧
我试着以60帧而不是30帧的速度运行游戏。游戏锁定为每秒30帧,所以我唯一能达到每秒60帧的方法就是使用帧插值 对于插值部分,我将使用OpenCV,我发现 这个游戏使用OpenGL。经过一些研究,我发现抓取屏幕的最好方法是钩住SwapBuffer函数。因此,我选择挂接游戏以抓取屏幕并将其发送到另一个应用程序,该应用程序将实时播放游戏,顺便说一句,将添加插值帧以达到60 FPS(如果你有更好的想法,我完全开放!) 我开始写我的新dll。我正在用C#编写代码,我选择EasyHook注入我的DLL 钩子很好用,现在很酷 然而,我现在陷入了困境,因为我完全不知道如何从游戏中抓取屏幕 我试图使用和,但我不知道我应该如何抓住实际的OpenGL上下文来使用glReadPixel 这是我的实际代码:C#Opengl钩子Swapbuffer添加插值帧,c#,opengl,hook,screenshot,interpolation,C#,Opengl,Hook,Screenshot,Interpolation,我试着以60帧而不是30帧的速度运行游戏。游戏锁定为每秒30帧,所以我唯一能达到每秒60帧的方法就是使用帧插值 对于插值部分,我将使用OpenCV,我发现 这个游戏使用OpenGL。经过一些研究,我发现抓取屏幕的最好方法是钩住SwapBuffer函数。因此,我选择挂接游戏以抓取屏幕并将其发送到另一个应用程序,该应用程序将实时播放游戏,顺便说一句,将添加插值帧以达到60 FPS(如果你有更好的想法,我完全开放!) 我开始写我的新dll。我正在用C#编写代码,我选择EasyHook注入我的DLL 钩
using System;
using System.Runtime.InteropServices;
namespace Hook
{
public class ServerInterface : MarshalByRefObject
{
public void IsInstalled(int clientPID)
{
Console.WriteLine("This programm has injected dll into process {0}.\r\n", clientPID);
}
public void ReportMessage(int clientPID, string message)
{
Console.WriteLine(message);
}
public void ReportException(Exception e)
{
Console.WriteLine("The target process has reported an error:\r\n" + e.ToString());
}
public void Ping()
{
Console.WriteLine("Ping !");
}
}
//Point d'entrée
public class InjectionEntryPoint : EasyHook.IEntryPoint
{
//Serveur :
ServerInterface server = null;
public InjectionEntryPoint(EasyHook.RemoteHooking.IContext context, string channelName)
{
//Constructeur
//Objectif : vérifier si la communication entre les deux programmes peut etre établit
server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);
server.Ping();
}
public void Run(EasyHook.RemoteHooking.IContext context, string channelName)
{
try
{
var SwapBuffersHook = EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress("opengl32.dll", "wglSwapBuffers"), new SwapBuffers_Delegate(SwapBuffers_Hook), this);
SwapBuffersHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "SwapBuffers Hooked");
}
catch (Exception ExtInfo)
{
server.ReportException(ExtInfo);
return;
}
server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
EasyHook.RemoteHooking.WakeUpProcess();
//Waiting years
while( true)
{
System.Threading.Thread.Sleep(10000);
}
// Finalise cleanup of hooks
EasyHook.LocalHook.Release();
}
//Deleguate
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate IntPtr SwapBuffers_Delegate(IntPtr hdc);
//Original
[DllImport("opengl32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr wglSwapBuffers(IntPtr hdc);
//Hook function
public IntPtr SwapBuffers_Hook(IntPtr hdc)
{
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "I'm in SwapBuffers =DDDD");
//Here I need to grab frames
//Return
return wglSwapBuffers(hdc);
}
}
}
使用系统;
使用System.Runtime.InteropServices;
名称空间挂钩
{
公共类服务器接口:MarshalByRefObject
{
已安装public void(int clientPID)
{
WriteLine(“此程序已将dll注入进程{0}.\r\n”,clientPID);
}
公共void报告消息(int clientPID,字符串消息)
{
控制台写入线(消息);
}
公共无效报告例外(例外e)
{
Console.WriteLine(“目标进程报告了一个错误:\r\n”+e.ToString());
}
公开作废
{
控制台。WriteLine(“Ping!”);
}
}
//恩特雷角酒店
公共类InjectionEntryPoint:EasyHook.IEntryPoint
{
//服务员:
服务器接口服务器=null;
公共InjectionEntryPoint(EasyHook.RemoteHooking.IContext上下文,字符串channelName)
{
//建设者
//目标:为实现这一目标而进行的双向交流项目
server=EasyHook.RemoteHooking.IpcConnectClient(channelName);
Ping();
}
公共无效运行(EasyHook.RemoteHooking.IContext上下文,字符串channelName)
{
尝试
{
var swapbuffershake=EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress(“opengl32.dll”,“wglSwapBuffers”),新的SwapBuffers_委托(SwapBuffers_Hook),这是);
swapBuffershake.ThreadACL.SetExclusiveACL(新的Int32[]{0});
ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(),“SwapBuffers Hooked”);
}
捕获(例外情况)
{
server.ReportException(exemefo);
返回;
}
已安装server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
EasyHook.RemoteHooking.WakeUpProcess();
//等待年
while(true)
{
系统线程线程睡眠(10000);
}
//完成吊钩的清理工作
EasyHook.LocalHook.Release();
}
//消除
[非托管函数指针(CallingConvention.StdCall,CharSet=CharSet.Unicode,SetLastError=true)]
IntPtr SwapBuffers_代表(IntPtr hdc);
//原创的
[DllImport(“opengl32.dll”,CharSet=CharSet.Unicode,SetLastError=true,CallingConvention=CallingConvention.StdCall)]
静态外部IntPtr wglSwapBuffers(IntPtr hdc);
//钩子函数
公共IntPtr SwapBuffers_Hook(IntPtr hdc)
{
ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(),“我在SwapBuffers=ddddd中”);
//这里我需要抓取框架
//返回
返回wglSwapBuffers(hdc);
}
}
}
我怀疑你能否提高游戏的FPS。顺便说一下,有一些可能性
你所做的是正确的方式。您只是错过了创建要显示的中间帧的GL代码(我建议使用当前GL上下文在过程中创建它)
问题是执行SwapBuffers
命令时会显示视频帧。这就是你钩住的一个,但是问问你自己和程序:为什么SwapBuffers
每秒被调用30次
如果答案是“因为应用程序使用计时器暂停执行”,那么您无法轻松解决它:钩子必须与应用程序一起工作,因此您需要了解应用程序是如何休眠的
如果answe是“因为应用程序正在利用WGL_EXT_swap_control”,则您有一种可能性:
- 设置wglSwapIntervalEXT(1)(因为应用程序调用了wglSwapIntervalEXT(2)以获得与V-Sync同步的30 FPS。这可能只需要一次
- 在钩子中,抓取两个帧,然后每隔一帧对它们进行插值(这就是你的问题)。在每次钩子执行时,你需要渲染插值帧,然后进行交换;然后渲染下一个原始帧,然后进行交换
如何创建插值帧?如果您是通过CPU进行操作,请使用glReadPixels和后缓冲区(GL_back)作为读取帧缓冲区(使用glDrawBuffers)。这将下载帧缓冲区绑定的内容。然后使用混合方程获得插值像素。您也可以连接到上下文创建过程并存储信息。但是,既然您已经连接到缓冲区交换过程,那么正确的OpenGL上下文不应该已经绑定到执行此操作的线程中吗他在交换缓冲区?