
OpenTK and WPF interoperation
Posted Wednesday, 9 September, 2009 - 15:28 by the Fiddler| Project: | The Open Toolkit library |
| Version: | 0.9.9-2b |
| Component: | Code |
| Category: | support request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | in progress (review) |
Right now, it is possible to create an OpenTK.GLControl and host it in a WPF using a WindowsFormsHost.
The question is: can we create an OpenGL context directly on a WPF window and do away with the host control?
The attached solution creates a WPF solution, obtains its handle and creates a GraphicsContext. This works without errors, however OpenGL rendering results in artifacts. The msdn entry for the OnRender method, notes that:
The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for later asynchronous use by layout and drawing.
This explains why the rendering artifacts appear: OpenGL tries to render to the window directly and does not participate into WPF composition.
One *could* use offscreen OpenGL rendering, perform a readback and present the results into the WPF window, but this is inefficient. It would be nice to find a better solution or confirm that it cannot be done.
Any ideas?
| Attachment | Size |
|---|---|
| OpenTKWPF.zip | 1.81 MB |


Comments
#1
A related discussion on the msdn forums indicates that:
I don't know why, but overriding OnRender does not work when the class is Window or derives from Window. That's the only reason I can think of that this method wouldn't work.
Most commonly, only classes the derive from FrameworkElement override OnRender. Most applications use pre-existing classes that derive from FrameworkElement rather than making their own.
Maybe overriding from FrameworkElement would yield better results?
Edit: same question on stackoverflow.
#2
This post outlines a potential solution that is trivial to adopt to OpenTK: Managed DirectX Interop with WPF.
They still use a WindowsFormsHost to host the DirectX control but render a transparent overlay on top it. This way, they can receive input events and host WPF controls on top of the WindowsFormsHost without introducing airspace issues. Quite nice!
#3
Hi,
Here is a solution to have a 100% WPF control drawing OpenGL.
I'm still working on ...
It is currently based on Tao, but currently we only have to got a solution... after we can convert it to OpenTk.
Performance
I think that there are several ways to improve it , it sounds that not creating a WGL context, but draw into a FBO (http://www.opentk.com/doc/graphics/frame-buffer-objects)
I have no experience with FBO and do not know to have a "MakeCurrent" method based on this... I hope you will find some time to help me.
If you can help me to avoid to create a WindowsForm control and draw only in memory ?
Issues
When I use the SimpleOpenGLControl (Tao) or the OpenTk GLControl.... in the WindowsHostForm I got some refreshing problems.
Here I got the same problem...
#4
#5
Hi,
I have a new version...
The difference is that we create a BitmapSource at startup and then we use glReadPixel to put the image directly in the image.
Like this we avoid one copy.
But I have a question, it sounds that glReadPixel do not always read the "right" buffer or that maybe it read a previous image.
Is it possible ?
Thanks
#6
if you are using framebuffer for offline rendering (render to texture then glReadPixels to memory bitmap); it is most likely to utilize pixel buffer objects(PBO) with double buffering. for detailed info: PBO
#7
Yes, it is possible. Make sure you call
SwapBuffersorGL.Finish()just before you callGL.ReadPixels.#8
Thanks,
But why calling SwapBuffer ? It sounds that we have 2 buffers... it will be better if we can always play with one buffer...
Is there a way to force glReadPixel to read on the "drawing" buffer ?
The second buffer is the WPF image... so we also avoid the flickering effect.
#9
You can use
GL.ReadBuffer()to instructGL.ReadPixels()to read from the backbuffer.You can also request a single-buffered context (use a
GraphicsContextconstructor that takes a "buffers" argument), but note that this is not supported very well on modern operating systems (Aero might turn off, for example).#10
Thanks
I use glReadBuffer and glReadPixels but doesn't see any performance improvement .
When I use GL_AUX0 it doesn't work !! I got no image !
#11
Yes, modern systems SwapBuffers is pretty fast on modern hardware (microseconds) and the ReadPixels call overshadows it completely.
Auxiliary buffers are a relic of the past, there's absolutely no reason to use them nowadays.
#12
Ok, so I will remove their use...
But what will be the best solution... I have also try to use FBO but I can't !
When I call :
Gl.glGenRenderbuffersEXT(1, out DepthRenderbuffer);
It crash !!
Also, how do I specify to OpenGL to draw on the FBO
Here is my code to create the FBO :
Can you help me to debug it ?
Thanks
#13
Do you get a crash or a NullReferenceException? The former indicates a driver bug, while the latter means that your video card does not support FBOs.
Have you checked for FBO support? Search the extension string for "EXT_framebuffer_object":
Gl.glGetString(Gl.GL_EXTENSIONS).Contains("EXT_framebuffer_object"). Many Intel cards don't support this (try updating your drivers).#14
Your're right, I have update my drivers and now all is fine...
Except that when I try to create the BitmapSource... the application is blocked !!!
Here is the code of the class, can you take a closer look... it sounds that the FBO is locked or something like this !
#15
Try moving the line
just after the ReadPixels call (before you create the BitmapSource).
#16
Thanks,
I have already try... but it is the same situation.
Even, without FBO it works like this... I have just added a call to "CreateFrameBuffer" !
#17
I am not familiar with WPF so I can't tell what's wrong here. Wild guess: BitmapSource expects to be created on the main thread.
A quick google search uncovered an interesting hack to modify an existing BitmapSource directly (instead of creating a System.Drawing.Bitmap). This should be much faster and could avoid the threading issues (you create the BitmapSource once in the main thread and modify its data as needed).
#18
I already use it in another version... it change nothing because we already the buffer pointer here.
#19
I have also try this (base on another sample about FBO) , but :
1) I can only create 2 PBOs
2) It display nothing ! and I don't know why !
To generate the PBO :
To generate the image :
#20
Another problem is that when I use glReadPixel the image is flipped vertically !
The OpenGl image is draw from the bottom to the top... and the XP image is from the top to the bottom.
Is there a way to change this ?
#21
There is a function named PixelZoom which does not affect ReadPixels. And it appears that you should flip image line by line
#22
Alternatively, try modifying your perspective projection to invert the y-axis (glFrustum or custom math, depending on whether you target OpenGL 2.1 or 3.0).
Edit: better link.
#23
Thanks for your ideas....
1) It sounds that PixelZoom does not affect glReadPixel !
2) The second solution is not acceptable, I want to create a control for the general usage and avoid them to have some "side effects", because using glScale can be easily overiden by normal drawing.
Can someone help me for the PBO problem ? I'm unable to draw on it and get an image using glReadPixel :-(
#24
I wrote an WPF OpenTK control for my own needs that may useful. It still wraps the wiforms control but it also has some WPF specific features for bindable shaders.
It also has an FBO class.
Here are a couple blog posts about it:
http://steveharoz.com/blog/?p=82
http://steveharoz.com/blog/?p=116
And here's the project site
Some aspects of it really need to be updated (especially the texture classes), but it's a spare time project.
-Steve
#25
Thanks steve,
But your control is not 100% WPF, so by example we cannout put other controls over this control !
The control I'm building is a 100% WPF control that follows the WPF rule and behavior.
Regards
-------------------------------------------------------------------------------------------------------------------------------------
So, I have find a working version based on FBO, the problem that remain is that the image sounds "Transparent" !!
Maybe the problem come from :
- Gl.glRenderbufferStorageEXT(Gl.GL_RENDERBUFFER_EXT, Gl.GL_RGBA8, width, height);
- Gl.glReadPixels(0, 0, _form.ClientSize.Width, _form.ClientSize.Height, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, _bitmapData.Scan0);
Can someone help me with this problem ?
The first is RGBA8 and the second is BGRA
Here I create the FBO :
Here I create the BitmapSource :
#26
Screenshot of the issue?
If the formats do not match, ReadPixels will convert its data to the format you specified. You'll take a (significant) speed hit but the results will be consistent (BGRA in this case).
Check your alpha values - maybe the result really *is* transparent? OpenGL doesn't take the alpha channel into account, unless you enable blending.
#27
Here, there is an image of an "opaque" cube :
http://picasaweb.google.com/viewon01/OpenGL#5382408228029550290
What is strange is that the color are correct, I have try Red cube, Green cube, Blue cube ... all are corrects... but it remain a transparency problem !
#28
Seems like your lighting equation is calculating alpha values. Quick sanity check: disable alpha writes and see if it works.
Edit: if this works, simply remove the alpha value from your light colors.
#29
Thanks...
But it doesn't work :-(
I use this :
glRenderbufferStorageEXT(Gl.GL_RENDERBUFFER_EXT, Gl.GL_RGBA8 ...
A lot of sample use "GL_DEPTH_COMPONENT32"... but I got a "Black" (empty) image with this !
And even , when I use GL_BGRA (like for glReadPixel...) I also got a black image :-(
#30
Drivers tend to be picky about DEPTH_COMPONENT formats. You should check whether the FBO is complete and try a different format if it is not.
It's also a good idea to check for OpenGL errors when running in debug mode. This can save you a lot of time in the long run.
Judging from the screenshot, I am pretty sure that this is an issue with the alpha values. Maybe WPF is expecting a RGBA image instead of BGRA? (Try changing the order in glReadPixels).
Another idea is to mask all color channels one by one and see if the rendered image looks correct on each step.