Unity低级本地插件接口--Low-level Native Plugin Interface

1 篇文章 0 订阅
1 篇文章 0 订阅

除了本地的脚本代码,Unity的本地插件能够接收特定时间的回调。这个特性最常用被来实施低层次的插件渲染工作,使之能够和Unity的多线程渲染一起工作。

定义这些接口的头文件由编辑器提供。(什么意思,没看明白)

接口注册

一个插件应该导出UnityPluginLoad和UnityPluginUnload函数来处理unity事件。参考IUnityInterface.h来获取正确的签名(声明)。IUnityInterfaces被提供给插件继而进一步获取Unity APIs。

#include "IUnityInterface.h"
#include "IUnityGraphics.h"
// Unity 插件加载事件
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
    UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
    IUnityGraphics* graphics = unityInterfaces->Get<IUnityGraphics>();
}

访问图形设备

插件通过获取IUnityGraphics接口能够功能性地访问通常的图形设备。早期Unity版本中,为了接收图形设备上事件的通知,UnitySetGraphicsDevice函数必须被导出。从5.2版本以后新的IUnityGraphics接口提供了一种方式来注册一个回调。

#include "IUnityInterface.h"
#include "IUnityGraphics.h"

static IUnityInterfaces* s_UnityInterfaces = NULL;
static IUnityGraphics* s_Graphics = NULL;
static UnityGfxRenderer s_RendererType = kUnityGfxRendererNull;

// Unity插件加载事件
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
s_UnityInterfaces = unityInterfaces;
s_Graphics = unityInterfaces->Get<IUnityGraphics>();
s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);

// 插件加载时手动运行OnGraphicsDeviceEvent(initialize) 
// 为了不丢失/错过这个事件,以防图形设备已经被初始化过了
OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);
}

// Unity 插件卸载事件
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginUnload()
{
s_Graphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
}

static void UNITY_INTERFACE_API
OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
{
switch (eventType)
{
case kUnityGfxDeviceEventInitialize:
{
s_RendererType = s_Graphics->GetRenderer();
//TODO: 用户初始化代码
break;
	}
case kUnityGfxDeviceEventShutdown:
{
s_RendererType = kUnityGfxRendererNull;
//TODO: 用户关闭代码
break;
}
case kUnityGfxDeviceEventBeforeReset:
{
//TODO: user Direct3D 9 code
break;
}
case kUnityGfxDeviceEventAfterReset:
{
//TODO: user Direct3D 9 code
break;
}
};
}


渲染线程上的插件回调

如果平台或者CPU数量支持则Unity能够进行多线程渲染。当多线程渲染被使用时,一个线程上的渲染API接口命令完全独立于运行MonoBehaviours脚本的线程。所以,通常你的插件没法立即开始执行渲染,因为很可能跟主渲染线程正在做的事发生冲突。为了从插件做任何的渲染工作,你需要从你的代码中调用GL.IssuePluginEvent。这将会导致提供的本地函数从渲染线程调用。例如,如果你从camera的OnPostRender函数调用GL.IssuePluginEvent,你将会在摄像机完成渲染后马上得到一个插件回调。

UnityRenderingEvent回调的声明被提供在IUnityGraphics.h中。一个本地插件的示例:

// Plugin function to handle a specific rendering event    处理特定渲染事件的插件函数
static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
    //TODO: 用户渲染代码
}
    
// Freely defined function to pass a callback to plugin-specific scripts   自由定义的函数,用来传递给插件相关代码的回调
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
    GetRenderEventFunc()
{
    return OnRenderEvent;
}
托管的插件代码示例:

#if UNITY_IPHONE && !UNITY_EDITOR
[DllImport ("__Internal")]
#else
[DllImport("RenderingPlugin")]
#endif
private static extern IntPtr GetRenderEventFunc();
    
// Queue a specific callback to be called on the render thread
GL.IssuePluginEvent(GetRenderEventFunc(), 1);
这种回调也可以被加在CommandBUffers通过CommandBuffer.IssuePluginEvent。


使用openGL图形接口的插件

有两种openGL的对象:

openGL Context之间共享的对象(texture, buffer, renderbuffer, samplers, query, shader, program objects);

per-OpenGL context对象(vertex array, framebuffer, program pipeline, transform feedback, sync objects);

Unity使用多个OpenGL context。当初始化、关闭编辑器和player的时候,我们依赖一个Master context,但是渲染的时候我们用专门的context。

因此,你不能创建per-context 对象在 kUnityGfxDeviceEventInitialize 和 kUnityGfxDeviceEventShutdown事件时候。


例如,一个本地的插件不能在kUnityGfxDeviceEventInitialize事件中创建vertex array 然后在UnityRenderingEvent 中使用它,因为激活的context不是vertex array对象创建时被使用的context。

案例

https://bitbucket.org/Unity-Technologies/graphicsdemos

这个案例干了两个事:

1 所有通常的渲染完成后又用c++渲染了一个三角形。

2 填充了一个C++代码的 procedural texture,使用Texture.GetNativetexturePtr 来获取它。

实施

官方的文档,说白了,已经把需要的所有东西都告诉你了,然而,然而,对于没有接触过底层的人来说,就跟雾里看花一样。

首先,我们来解释下官方文档的例子。

官方文档做了很少的底层渲染的一部分,而且只做了通过Native Plugin Interface获取unity渲染线程的opengl(或者其他绘图api)资源和相应的textureID(也就是指针,新的unityapi教NativeTexturePtr),一是在本地通过cpp插件改变这个texture的值,官方例子是通过实时的time值来改变纹理数值,写到内存区,然后绑定textureID写入到texture中,所以我们能看到Unity中plane的texture实时变化。二是,获取texture的指针,改变它的顶点值之类的,这样就是我们看到的波浪的效果。三是,在unity 的绘制线程内,在协程中EndOfFrame,或者Lateupdate中,也就是每一帧渲染完成之后,再绘制自己的三角型,由于Unity本身接口就在一个循环里,所以每一次都是在接口函数OnrenderEvent里面绘制一次。

所以,综合的看,这个接口给了一个你直接进入unity渲染线程的接口,可以获取到某些可以线程共享的资源。所以你可以在本地插件代码中进行底层的opengl绘制操作。具体操作流程可以查看项目源码,由于是支持多平台的,所以定义了很多的接口,官方例子还写成了类。

由于工作中的问题,我需要的并不是通过本地代码改变或者做插件渲染,而是要获取unity的texture来进行自己的渲染。这块的知识一个就是本地插件的接口,另外一块就是opengl多线程的绘制,资源共享等。具体就不详细说了,感兴趣的留方式我们再交流,目前已经成功实现unity内部任意texture、rendertexture等的绘制输出,采用Win32-openGL外部窗口,并且添加了跟外部进程交互的内容。

相关代码:

C# Code

[DllImport("user32.dll")]
    static extern System.IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern System.IntPtr GetCurrentHinstance();

    [DllImport("unitydll")]
    static extern bool ReturnShareTrue();

    [DllImport("unitydll")]
    static extern void TellUnityNull();

    [DllImport("unitydll")]
    private static extern void StartDllThread();

    [DllImport("unitydll")]
    private static extern void PassTextureFromUnity(System.IntPtr texture, int w, int h);

    [DllImport("unitydll")]
    private static extern System.IntPtr GetRenderEventFunc();

    [DllImport("unitydll")]
    private static extern void PassUnityHwnd(System.IntPtr unityhwnd);

    bool flag2 = false;
    private StreamWriter DebugToFile;

    void Awake()
    {
    }

    IEnumerator Start()
    {
        _ARcamera = this.gameObject.AddComponent<Camera>();
        _ARcamera.enabled = false;
        StartCoroutine(WaitAndMakeCurrentNull());
        StartCoroutine(startDLL());
        StartCoroutine(CreateTextureAndPassToPlugin());
        yield return StartCoroutine("CallPluginAtEndOfFrames");
        Initial();
    }
    private void Update()
    {
        Debug.Log("--------update");
    }
    private void LateUpdate()
    {
        Debug.Log("--------Late update");
        if (flag)
        {
            Render();
        }

        if (!flag2)
        {
            GL.IssuePluginEvent(GetRenderEventFunc(), 1);
            flag2 = true;
        }
    }

    IEnumerator startDLL()
    {
        yield return new WaitForSeconds(0.2f);
        StartDllThread();
    }
        IEnumerator WaitAndMakeCurrentNull()
    {
        yield return new WaitForSeconds(0.2f);
        Debug.Log("makeNULL");
        GL.IssuePluginEvent(GetRenderEventFunc(), 2);//make null
        TellUnityNull();
    }

    private IEnumerator CallPluginAtEndOfFrames()
    {
        while (true)
        {
//你的代码,在lateupdate中也可以做同样的调用,机制相同
        }
    }

    IEnumerator CreateTextureAndPassToPlugin()
    {
        yield return new WaitForSeconds(0.2f);
        Debug.Log("Create Texture!");

        flag = true;
        tempRT = new RenderTexture(ARcamera.pixelWidth, ARcamera.pixelHeight, 24, RenderTextureFormat.ARGB32);
        tempRT.filterMode = FilterMode.Point;
        tempRT.Create();
        PassTextureFromUnity(tempRT.GetNativeTexturePtr(), tempRT.width, tempRT.height);
        Debug.Log(tempRT.GetNativeTexturePtr());
    }

    public void Render()
    {
        _ARcamera.CopyFrom(ARcamera);
        _ARcamera.targetTexture = tempRT;
        _ARcamera.Render();
    }

DLL Cpp

extern "C"	void DLL_API StartDllThread()
{
	g_hInstance = GetModuleHandle(NULL);
	_beginthread(LoadDllThread, NULL, NULL);//
}

extern "C"	void DLL_API PassTextureFromUnity(void* textureHandle, int w, int h)
{
	g_TextureHandle = textureHandle;
	g_texture_Width = w;
	g_texture_Height = h;
}

static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
        //渲染程序入口,可以参考官方例子
	switch (eventID)
	{
        //你的时间处理代码,基于eventID
	}
}

extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API GetRenderEventFunc()
{
	return OnRenderEvent;
       //返回渲染的接口函数
}




  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Odin Inspector是一个操作Unity对象的插件,可以增强Unity编辑器中的开发体验。Odin Inspector 3.0.4版本是该插件的一个更新版本,可以通过CSDN下载获得。 该插件通过注解的方式可以为Unity对象添加更多的编辑器控件,如:枚举选择器,拖拽选择器,开关按钮等。同时,该插件也提供了一些常用的数据类型,如:字典、序列化树等。 Odin Inspector使用起来非常方便,只需要在需要增强的类或属性前添加对应的注解即可,这样就可以在Unity编辑器中变得更加灵活和直观。此外,Odin Inspector还提供了一些自定义的标记属性,可用于美化编辑器界面或增加辅助功能。 总的来说,Odin Inspector插件是一个非常实用的Unity开发工具,可以为Unity开发者提高开发效率和开发体验。而CSDN下载的unity3d文档类资源,也为我们提供了方便的使用说明和示例代码,有助于我们更快地上手和运用该插件。 ### 回答2: Odin Inspector是Unity3D的插件,这个插件可以在Unity编辑器中为开发人员提供更多的调试、编辑和自定义选项。Odin Inspector的3.0.4版本是最新的版本,这个版本通过提供更多的功能来提高了编辑器的性能和效率。Odin Inspector 3.0.4支持C#生成代码,这使得开发者可以以更快的速度编写代码,而且还提供了一个强大的反射系统,能够在使用过程中自动为脚本创建编辑器。此外,Odin Inspector可以让开发者轻松地创建自定义的编辑器窗口和面板,并提供多种工具和快捷键以帮助开发者更加高效地开发Unity项目。 而在CSDN上可以下载Unity3D文档类资源,这些资源包括了Unity3D的官方文档以及其他一些非官方的教程和手册。这些资源可以帮助开发者学习Unity3D的基础知识和高级技巧,并且对于使用Odin Inspector来增强开发人员的编辑器体验来说也非常有用。在CSDN上下载这些资源非常容易,只需要注册并登录即可随时下载和使用。 总之,Odin Inspector版本3.0.4和CSDN中的Unity3D文档类资源都可以帮助Unity3D开发者提高效率和编写更好的代码。它们是Unity3D开发过程中的重要工具,能够帮助开发者更好地利用Unity编辑器的功能,并加速开发过程。 ### 回答3: Unity是一款流行的游戏引擎,为了方便开发者使用Unity,社区推出了许多插件,其中Odin Inspector 3.0.4版本是一个非常优秀的插件。这个插件可以用来简化Unity的开发流程,提高开发效率。 Odin Inspector 3.0.4版本主要提供了一些高级编辑器功能,如可定制化的Inspector面板、可扩展属性系统、强大的序列化和反序列化等。它还包含了一些常用的扩展功能,如资源管理器、GUI工具、命名空间工具等等。它的强大功能让开发者可以更加方便地设置和管理游戏中的各种对象和组件。 此外,Unity3D文档类资源在开发过程中也起到了不可替代的作用。这些文档提供了关于Unity3D的详细信息和指南,包括游戏开发、图形渲染、物理引擎、脚本编程等方面的内容。 Odin Inspector 3.0.4版本和Unity3D文档类资源可以通过CSDN进行下载,CSDN是一个技术社区,拥有大量的技术资源和开发者社区。通过下载Odin Inspector 3.0.4版本和Unity3D文档类资源,开发者可以更加高效地使用Unity进行游戏开发,同时也可以获得更多的技术帮助和指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值