Search Unity

Display a Unity RenderTexture in an OpenGL plugin

Discussion in 'Windows' started by bugmagnet, May 6, 2016.

  1. bugmagnet

    bugmagnet

    Joined:
    Apr 16, 2013
    Posts:
    48
    Hi, my goal is to display a RenderTexture full screen on a separate window from Unity.

    I have built a plugin for Unity where I make an OpenGL window and try to draw a RenderTexture on it. If I build my own application around this code, I am able to display my own (dynamic) texture on it. However I am stuck when I try to display a RenderTexture using GetNativeTexturePtr().
    In fact, I am not even sure exactly what is coming out of GetNativeTexturePtr():
    • If I run Unity normally, I can sometimes see garbage pixels (that do not seem related to the RenderTexture.GetNativeTexturePtr() that I sent). These pixels seem to be texture related, but not to the given RenderTexture. My guess is I am looking at random data in the GPU memory. Code below.
    • If I run Unity as OpenGL, the best I can get is for it to not crash but I presume that this doesn't work because according to the docs, Unity sends the gl texture "name" in this case, but because I am creating my own OpenGL context my understanding is that each context creates its own list of texture names hence the texture names I get are not meaningful to my external window.
    Below is my attempt that at least displays garbage pixels, but sometimes crashes. I know something is definitely wrong but I am at a loss of how to actually USE the data inside GetNativeTexturePtr(). I have looked at the Unity plugin example but that only shows how to write to the texture. Is it even possible to read from it? Or is the pointer only meant for writing?
    I am a novice in OpenGL, any help or pointers (har har) are greatly appreciated.

    Code (CSharp):
    1. public RenderTexture rTex;
    2.  
    3. [DllImport("unityCaster")]
    4. public static extern void update(int w, int h, System.IntPtr texPtr);
    5.  
    6. //...
    7. void Update()
    8. {
    9.      update(rTex.width, rTex.height, rTex.GetNativeTexturePtr());
    10. }
    Code (C++):
    1. void unityCaster::update(int w, int h, unsigned char * texData)
    2. {
    3.     if (!instance)
    4.         return;
    5.  
    6.     if (texData == 0)
    7.     {
    8.         glClear(GL_COLOR_BUFFER_BIT);
    9.     }
    10.     else
    11.     {
    12.  
    13.         glEnable(GL_TEXTURE_2D);
    14.         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    15.                  
    16.         glBindTexture(GL_TEXTURE_2D, instance->texHandle);
    17.  
    18.         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
    19.        
    20.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    21.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    22.  
    23.         // Draw a textured quad to the whole screen
    24.         glBegin(GL_QUADS);
    25.         glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
    26.         glTexCoord2f(0, 1); glVertex3f(0, instance->height, 0);
    27.         glTexCoord2f(1, 1); glVertex3f(instance->width, instance->height, 0);
    28.         glTexCoord2f(1, 0); glVertex3f(instance->width, 0, 0);
    29.         glEnd();
    30.  
    31.         glFlush();
    32.         glDisable(GL_TEXTURE_2D);
    33.     }
    34.  
    35.     SDL_GL_SwapWindow(instance->window); //im using SDL to get my openGL context
    36.     glFinish(); //draw to screen
    37. }
    38.  
     
  2. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,919
    If I am not mistaken, you get texture handle. So basically you don't need to call glTexImage2D.

    Code (csharp):
    1.  
    2. void unityCaster::update(int w, int h, unsigned char * texData)
    3. {
    4. glBindTexture(GL_TEXTURE_2D, (GLuint)texData);
    5. ...
    6. }
    7.  
     
  3. bugmagnet

    bugmagnet

    Joined:
    Apr 16, 2013
    Posts:
    48
    Hi Tomas, thank you for the reply
    In this case, the result is simply a white (blank?) texture.
    It does not work, but I don't know why.
    Logging the GLuint handle gives a value such as: "201770208" which seems to be appropriate.
    Any other ideas? Does a render texture get cleared at some point during the update process?
    Should such a handle be valid across rendering contexts?

    I have also tried adding the component to the rendering camera and doing:
    Code (CSharp):
    1. void OnPostRender()
    2. {
    3.      update(castedTexture.width, castedTexture.height, castedTexture.GetNativeTexturePtr());
    4. }
    Here is my current code:
    Code (C++):
    1. void unityCaster::update(int w, int h, void * texData)
    2. {
    3.     if (!instance)
    4.         return;
    5.  
    6.     if (texData == 0)
    7.     {
    8.         glClear(GL_COLOR_BUFFER_BIT);
    9.     }
    10.     else
    11.     {
    12.  
    13.         glEnable(GL_TEXTURE_2D);
    14.         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    15.                  
    16.         //logging this handle appears to give a valid pointer/handle
    17.         //it is consistent and changes appropriately with changes in Unity.
    18.         GLuint handle = (GLuint)texData;
    19.         glBindTexture(GL_TEXTURE_2D, handle);
    20.  
    21.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //alternatively GL_NEAREST or GL_LINEAR
    22.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    23.  
    24.         // Draw a textured quad to the whole screen
    25.         glBegin(GL_QUADS);
    26.         glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
    27.         glTexCoord2f(0, 1); glVertex3f(0, instance->height, 0);
    28.         glTexCoord2f(1, 1); glVertex3f(instance->width, instance->height, 0);
    29.         glTexCoord2f(1, 0); glVertex3f(instance->width, 0, 0);
    30.         glEnd();
    31.  
    32.         glFlush();
    33.         glDisable(GL_TEXTURE_2D);
    34.     }
    35.  
    36.     SDL_GL_SwapWindow(instance->window);
    37.     glFinish(); //draw to screen
    38. }
     
  4. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,919
    You're probably right about your context not being able to get resources from Unity's OpenGL context. But you can probably use wglGetCurrentContext (to acquire Unity's OpenGL context) and wglShareLists to share this data between Unity's context and your context.
     
  5. lacucaracha

    lacucaracha

    Joined:
    May 11, 2016
    Posts:
    8
  6. bugmagnet

    bugmagnet

    Joined:
    Apr 16, 2013
    Posts:
    48
    We were able to get as far as getting the Unity context, but we were not able to use it to take a texture and use it in our plugin (we could not switch context back to our own window, Unity would take it back). We WERE able to modify textures and windows that Unity itself is using (which is what the Unity plugin example does).

    >>lacucaracha
    it looks like you were not able to solve the issue in directx either? Is that correct?
     
  7. lacucaracha

    lacucaracha

    Joined:
    May 11, 2016
    Posts:
    8
    Hello again,

    The problem i have is to translate my opengl code into DirectX code, and i have 0 knowledge how to do that.
    I looked here : http://docs.unity3d.com/Manual/NativePluginInterface.html

    There is a demo script in the Example section at the end of the page.
    This is the only script i got to use directx / opengl in a native plugin for Unity.

    However the code below is never called by my Unity script, so i cannot get the context nor the information about opengl or DirectX form unity.

    This function is never called for me at the loading :

    Code (CSharp):
    1.  
    2.  
    3. void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
    4.     {
    5.         s_UnityInterfaces = unityInterfaces;
    6.  
    7.         s_Graphics = s_UnityInterfaces->Get<IUnityGraphics>();
    8.         s_Graphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
    9.  
    10.         // Run OnGraphicsDeviceEvent(initialize) manually on plugin load
    11.         // to not miss the event in case the graphics device is already initialized
    12.         OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);  
    13.     }
    14.  

    But this post is about your problem, i wont ask anything more about mine here, dont hesitate to go at mine if you can help me ^^

    http://forum.unity3d.com/threads/texture-pointer-with-directx.407853/

    Thx !
     
  8. Oninova

    Oninova

    Joined:
    Aug 22, 2016
    Posts:
    6
    I was wondering if anyone knows how to get a handle to the unity render context. I need to create a texture in a external application and render it in Unity using Texture2D.CreateExternalTexture(). When I create the texture in my external application I get a texture id of 1, which seems wrong. I'm guessing its because it has its own render context and does not share a texture pool with Unity. I could be totally wrong about this because I'm a bit of a OpenGL noob. If anyone could point me in the right direction it would be greatly appreciated.

    I tried this and only received a null value from wglGetCurrentContext, I'm guessing there is another way to get the context.

    How where you able to get the context?
     
    Last edited: Aug 23, 2016
  9. Oninova

    Oninova

    Joined:
    Aug 22, 2016
    Posts:
    6
    I was able to grab the handle to the opengl unity render context by using a low-level native plugin and fetching the handle with wglGetCurrentContext(). https://docs.unity3d.com/Manual/NativePluginInterface.html

    Using GetRenderEventFunc() allowed me to call GL.IssuePluginEvent() directly from the render thread, which in turn allowed me to grab the context handle. I am now able to render my textures externally and then display them in Unity using Texture2D.CreateExternalTexture().
     
  10. unity_qBIeG5jmaYv5QQ

    unity_qBIeG5jmaYv5QQ

    Joined:
    Aug 15, 2020
    Posts:
    1
    Hi, I need the same thing to draw the unity texture on the OpenGl window in C#.Can you please provide some more detail on what you have done to achieve render texture externally. i am doing my code in c# help is greatly appriciated
     
  11. Varaughe

    Varaughe

    Joined:
    Apr 22, 2013
    Posts:
    40
    There is a solution "Render to External Window" but it's Metal for Mac and DirectX for Windows.