3

我一直在尝试将 Seefront 3D API 与 XNA 一起使用。Seefront 向 f.ex 提供 3D 面板。索尼提供裸眼3D。这些面板会折射光线,一束光射入您的一只眼睛,另一束射入另一只眼睛。

这基本上是如何工作的,API 中的眼动追踪软件会计算出你两只眼睛的位置。然后 API 调用接管渲染:对 sfdx_setTextures 的调用然后采用 2 个 IDirect3DTexture9* 纹理,然后由 API 中的像素着色器处理。

在较早的 C++ 项目中,我向 MPC-HC 添加了功能,正是这样做的:https ://github.com/atlaste/mpc-hc/tree/master/src/filters/renderer/VideoRenderers 。

现在我正试图用 XNA 做一些新奇的事情,结果遇到了告密者。不幸的是,我无法共享 Seefront DLL;他们不是我要放弃的......代码必须做(我在这里删除了检查等)。

DLL 首先使用 LoadLibrary 加载,并通过一些无聊的调用来启用互操作。

this.dllHandle = Win32NativeMethods.LoadLibrary(@"C:\Program Files (x86)\Sony\SeeFront3D\seefront_ilace_dx.dll");

// Initialize; LoadFunction basically calls GetProcAddress
this.CreateInstance = LoadFunction<CreateInstanceFunc>(dllHandle, "sfdx_createInstance");
this.StartTrackerUpdate = LoadFunction<StartTrackerUpdateFunc>(dllHandle, "sfdx_startTrackerUpdate");
this.SetTextures = LoadFunction<SetTexturesFunc>(dllHandle, "sfdx_setTextures");
this.SetTextureSize = LoadFunction<SetTextureSizeFunc>(dllHandle, "sfdx_setTextureSize");
// etc...

结果是一堆封装在委托中的调用。通过使用 IDirect3DDevice9* 和 StartTrackerUpdate 调用 CreateInstance 来进行初始化。这是 XNA 没有给我们需要的第一点,所以 API 初始化如下:

// Get pComPtr from device
var ptr = device.GetType().GetField("pComPtr", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var d3ddevice = (Pointer)ptr.GetValue(device);
var ptrValue = Pointer.Unbox(d3ddevice);

// Create seefront instance
this.instance = CreateInstance((IDirect3DDevice9*)ptrValue);
StartTrackerUpdate(instance);

调用这两个后,摄像头被启用,这表明它是活着的。这留下了最后一件事:对 SetTexture 的调用。因为它是传递给 API 并且可能未复制的指针数组,所以我在堆上分配了一些指针(使用 IDisposable 处理),并通过凌乱的 GetComPtr 调用再次获取 IDirect3DTexture9* 指针:

// initialization:
realTextures = (IDirect3DTexture9**)Marshal.AllocHGlobal(IntPtr.Size * 2);

// ...

private IDirect3DTexture9** realTextures;

public void RenderFrame(Texture texture1, Texture texture2)
{
    MethodInfo textureMethod = typeof(Texture).GetMethod("GetComPtr", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    var ptr = (Pointer)textureMethod.Invoke(texture1, null);
    realTextures[0] = (IDirect3DTexture9*)Pointer.Unbox(ptr);

    ptr = (Pointer)textureMethod.Invoke(texture2, null);
    realTextures[1] = (IDirect3DTexture9*)Pointer.Unbox(ptr);

    SetTextures(instance, realTextures, 2, 0, 0, 1, 1);
    Render(instance);
    Sync(instance);
}

上面所有的混乱都包含在一个名为 SeeFront3D 的小类中,该类从 XNA 应用程序中调用。

接下来是在 XNA 中渲染一些东西。因为 API 接受两个纹理,所以我基本上使用http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series3/Render_to_texture.php中描述的方法在“游戏”的绘制阶段创建这些纹理。

此代码如下所示:

private SeeFront3D sf3d;
private RenderTarget2D[] eyeTextures;
private int currentEyeTexture = 0;

protected override void LoadContent()
{
    InitializeModel();
    InitializeEffect();

    if (EnableSeefront3D)
    {
        sf3d = new SeeFront3D(GraphicsDevice, Window);
    }

    eyeTextures = new RenderTarget2D[4];
    for (int i = 0; i < 4; ++i)
    {
        eyeTextures[i] = new RenderTarget2D(GraphicsDevice, 1980, 1080, true, GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24Stencil8);
    }
}

// ...

protected override void Draw(GameTime gameTime)
{
    // render right eye; normally you render both eyes, but this is for testing.
    GraphicsDevice.SetRenderTarget(rightEye);
    GraphicsDevice.Clear(Color.CornflowerBlue);
    Render();
    GraphicsDevice.SetRenderTarget(null);

    GraphicsDevice.Clear(Color.CornflowerBlue);

    if (EnableSeefront3D)
    {
        sf3d.SetTextureDimensions(1980, 1080);
        sf3d.RenderFrame(rightEye, rightEye);
        // Thread.Sleep(TimeSpan.FromSeconds(1.0)); // used for debugging
    }
    else
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        using (SpriteBatch sprite = new SpriteBatch(GraphicsDevice))
        {
            sprite.Begin();
            sprite.Draw(rightEye, new Vector2(0, 0), null, Color.White, 0, new Vector2(0, 0), 0.4f, SpriteEffects.None, 1);
            sprite.End();
        }
    }

    base.Draw(gameTime);
}

private void Render()
{
    foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
    {
        pass.Apply();
    }

    GraphicsDevice.Indices = indexBuffer;
    GraphicsDevice.SetVertexBuffer(vertexBuffer);
    graphics.GraphicsDevice.DrawIndexedPrimitives(
        PrimitiveType.TriangleList, 0, 0, this.vertexBuffer.VertexCount, 0, this.indexBuffer.IndexCount / 3);
}

当然,我尝试过不同的变体,例如在左眼用一种颜色调用“清晰”,在右眼用另一种颜色调用,等等。

会发生以下情况:

  • 清除似乎工作正常。如果你用两种颜色填充两只眼睛,你就会得到完全正确的结果。
  • 如果你改变颜色,它仍然可以正常工作。
  • 你可以看到 Seefront 的像素着色器在做他们的工作。如果你改变位置,屏幕上的像素也会改变位置。
  • 如果将 EnableSeefront3D 标志设置为“false”,则可以看到渲染正常

但是(问题):

  • 绘制循环的第一次迭代,你会得到一个“灰色”屏幕。
  • 3D 模型仅在绘制循环的第二次迭代中呈现(我通过 'sleep' 调用解决了这个问题)
  • 在绘制循环的第 2 次迭代之后,仅渲染背景(来自 'clear');3D模型不见了。

我个人认为第一帧是 SeeFront 库中的一个小错误,绝对是我可以忍受的。

如果启动应用程序,它看起来像这样: 第二帧(正确)

后期帧(3d 模型消失了)

从乱码看,可以清楚的看到调用了Seefront库;3D 模型在第一帧之后也明显消失了。

有什么建议会出什么问题 cq 如何解决这个问题?

4

1 回答 1

3

灰框

如果您的灰色匹配#CDCDCDCDhttp://www.colorhexa.com/cdcdcd,(注意可能的 alpha 值))您的纹理(内存)数据尚未设置。如果是这样,变化很大,如果您将其编译为发布版本,您将获得一个非常随机的第一帧。

编译器在调试模式下使用此标记 (0xCD) 将内存标记为“干净”。

http://www.codeguru.com/cpp/wp/win32/tutorials/article.php/c9535/Inside-CRT-Debug-Heap-Management.htm

在发布模式下,这通常会被优化跳过,你会留下一大堆垃圾。

但是,如您所说,如果这是一个外部库,您可能需要检查您是否使用了某个debug版本。

要解决此问题,您需要在呈现之前先更新纹理。此外,它可能会导致您的代码存在重大缺陷。因此,值得一试的是,纹理如何或何时可用。

第二次迭代后 仍在处理这个,所以请温柔

所以你IDirect3DDevice9从你的GraphicsDevice. 那挺好的。

但是随着渲染:

GraphicsDevice.SetRenderTarget(rightEye);
GraphicsDevice.Clear(Color.CornflowerBlue);
Render();
GraphicsDevice.SetRenderTarget(null);

GraphicsDevice.Clear(Color.CornflowerBlue);

if (EnableSeefront3D)
{
    sf3d.SetTextureDimensions(1980, 1080);
    sf3d.RenderFrame(rightEye, rightEye);
}

这让我有点困惑:

  • 首先你设置并清除 rightEye 渲染目标并渲染一些东西。
  • 然后你把原来的backbuffer放回rendertarget,并清除它。
  • 之后,您将使用纹理作为输入让外部库渲染到本机设备。

这最后一件事与我有关,Seefront 3D 是渲染到纹理还是渲染到后缓冲区?如果它渲染到纹理,例如您提到的像素着色器,则没有任何东西实际呈现 rightEye 纹理。我的猜测是它会渲染到后台缓冲区,所以;仍在努力,但值得检查。

此外 // render right eye; normally you render both eyes, but this is for testing.

我强烈反对这种做法。如果内部渲染针对并行处理进行了优化(GPU 喜欢的东西),可能会出现一些locking问题dirty region,因为不同的读/写操作应用于相同的纹理。由于实际的实现是未知的,在这种情况下最好是安全的。

最后一点关于

sf3d.SetTextureDimensions(1980, 1080);

称呼。

在本机 DirectX 中,这是不行的。为纹理分配的内存是在创建纹理时确定的。创建纹理时会设置,FORMATMemoryPool内容。Size重新调整大小肯定会导致重新创建纹理。

在我看来:如果可能的话,看看 rightEye 渲染目标存在于哪个内存池中可能会很有趣。在本机 directX 库中,纹理更新有一些限制。请参阅:http: //msdn.microsoft.com/en-us/library/windows/desktop/bb172584%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb172625%28v=vs.85%29.aspx

D3DUSAGE_RENDERTARGET

The resource will be a render target. D3DUSAGE_RENDERTARGET can only be used with 
D3DPOOL_DEFAULT.
于 2013-11-21T09:51:48.687 回答