看完this video后,我开始考虑如何在当前项目中实现类似的功能。我认为让我的大部分代码实时可编辑是很困难的,但我想我至少可以在玩游戏时使我的OpenGL着色器可编辑。
所以我设置了FileSystemWatcher
:
protected void WatchShaders()
{
_uiDispatcher = Dispatcher.CurrentDispatcher;
const string shaderDir = @"path\to\my\shaders";
_shaderFileWatcher = new FileSystemWatcher(shaderDir);
_shaderFileWatcher.NotifyFilter = NotifyFilters.LastWrite;
//fw.Filter = "*.frag;*.vert";
_shaderFileWatcher.Changed += ShaderChanged;
_shaderFileWatcher.EnableRaisingEvents = true;
}
现在我想在文件发生变化时更新着色器:
void ShaderChanged(object sender, FileSystemEventArgs e)
{
_shaderFileWatcher.EnableRaisingEvents = false; // prevent more/duplicate events from firing before we've finished processing the current one
lock (_bfShader)
{
_bfShader.AttachShader(Shader.FromFile(e.FullPath));
_bfShader.Link();
_bfShader.Use();
_bfProjUniform = new Uniform(_bfShader, "ProjectionMatrix");
_bfSampler = new Uniform(_bfShader, "TexSampler");
_bfSampler.Set1(0);
}
_shaderFileWatcher.EnableRaisingEvents = true;
}
问题是我编辑着色器文件后,一个异常就是说:
调用线程
中没有上下文
所以我做了一些挖掘,发现OpenGL上下文基本上绑定到一个线程。 AFAIK,有2个解决方法:
我不确定如何实现(1)因为主线程充满了OpenGL调用...我不知道在哪里启用和禁用它。
所以我留下了选项(2),除了我无法弄清楚如何将文件更改事件发送回主线程。
GLControl提供GLControl.BeginInvoke()方法,以简化从辅助线程到主System.Windows.Forms.Application线程的异步方法调用。 GameWindow没有提供类似的API。
不幸的是我使用的是GameWindow
,因此我不确定如何访问该功能。
那么将我的事件发送回主UI线程的最简单方法是什么?是使用OpenTK库还是其他优选非Windows专用库?
答案 0 :(得分:3)
想通了我可以使用一个队列来解决问题:
void ShaderChanged(object sender, FileSystemEventArgs e)
{
_shaderFileWatcher.EnableRaisingEvents = false; // prevent more/duplicate events from firing before we've finished processing the current one
lock (_renderQueue)
{
_renderQueue.Enqueue(() =>
{
switch(e.Name)
{
case "block.frag":
_bfShader.DetachShader(_blockFragShader);
_blockFragShader = Shader.FromFile(e.FullPath);
_bfShader.AttachShader(_blockFragShader);
break;
default:
return;
}
Trace.TraceInformation("Updating shader '{0}'", e.Name);
_bfShader.Link();
_bfShader.Use();
_bfProjUniform = new Uniform(_bfShader, "ProjectionMatrix");
_bfSampler = new Uniform(_bfShader, "TexSampler");
_bfSampler.Set1(0);
});
}
_shaderFileWatcher.EnableRaisingEvents = true;
}
然后我稍微修改了渲染循环:
lock(_renderQueue)
{
while(_renderQueue.Count > 0)
{
_renderQueue.Dequeue().Invoke();
}
}