creating multiple openGL windows of the same scene

I have an application where I need to draw as many as 4 openGL windows - Each of the views would represent one tile of a larger openGL scene.

Ideally, the whole scene would be updated once (not drawn) then each of the forms/windows would read and display one rectangular chunk of the scene.

Has anyone seen an example where this has been performed?

thanks for the help!

If you are using SDL or GLFW

If you are using SDL or GLFW to create the OpenGL Window, neither of these support multiple windows.

The OpenTK solution (www.opentk.com) says that it can, I haven't tried it yet, although I'm considering it, mainly due to other issues with SDL and OpenGL integration. GLFW had bad MacOSX support last I checked, so I didn't go with that, and SDL can't resize a window properly without killing the OpenGL context.

If you're just using Windows.Forms, aren't bothered about cross-platform issues, and want to have standard Windows controls on some of the forms too, I have a class I can post that does that.

Note the issue you will have is with texture/resource sharing. Each window will be an entirely separate OpenGL context. i.e. for each window, you will have to reload all textures, VBOs, DisplayLists etc.. all again.

Euan MacInnes

http://icarus.sourceforge.net
Windows, MacOSX, Linux C# project based off the Tao.Framework

Note the issue you will have

Note the issue you will have is with texture/resource sharing. Each window will be an entirely separate OpenGL context. i.e. for each window, you will have to reload all textures, VBOs, DisplayLists etc.. all again.
It is possible to share state between contexts (it's an option set at creation time). This should work for VBO's, Display Lists and textures and some other objects.

OpenTK does support multiple contexts with shared state through the GLContext class, but the higher level classes (GLControl which integrates with Windows.Forms or GameWindow) don't use this functionality yet. On the other hand, FreeGLUT can create multiple windows, and if it offers sharing as an option then this is the way to go at the moment.

------
OpenTK

I don't know if this helps,

I don't know if this helps, but it should be possible to just split a single window into 4 portions using Gl.glViewport...I just took an OpenGL class last year, and this is how we showed 4 different views of the same scene. Basically, we would call glViewport to set the viewport to the first quarter of the window, glLoadIdentity, call gluLookAt to set the eye position, then render the scene; repeat 3 times, once for each additional view.

We then reset the viewport to the whole window and drew 2 lines to show the division between the views.

I'm fairly new at OpenGL, so sorry if this is blindingly obvious or not what you were wanting! Wink

what a super forum! thanks

what a super forum! thanks for the replies.

I really like the idea of performing this in openGL - one form, one OpenGL context but multiple views inside the GLwindow. I'll pursue this.

But - I do have complete control over the software (windows, c#) and hardware that will be used to implement this - and can specify a monster quad core which might help by distributing the processes over several CPUs making the mult window/form approach more plausible.

I am still open to any other ideas (even if they seem blindingly obvious - I am pretty new to this too!)

thanks again for the help!

Frame buffer

Frame buffer object?

ideally, it would be nice to draw an entire scene into a frame buffer object, then pick areas from this to display on the screen.

I have seen this done with scene materials (a 3d scene is drawn, then used as a texture map) - is it out of the question to use this approach? Instead of using it as a texture map, I would just define a patch of data from the buffer which would be drawn to the screen (and repeat this 4 times with different patches to get my 4 screens)

thoughts?

I using two separate

I using two separate OpenGLControls on a form. I'll post enough of my code below to get a good idea of how I am doing it. Everything was working great and then I got a new laptop at work and since an image of my old drive was copied over to my laptop, it doesn't seem to be working correctly. Both of the OpenGLContorls respond to arrow key presses as you can see. Does anyone know if I am using the MakeCurrent() and SwapBuffers() methods correctly. I've been changed those around this morning and I am getting some different outputs. I hope this might help and if you guys have any answers for me, please let me know.

Here's my Code:
namespace AV_StatusV2_Tao
{
public partial class Form1 : Form
{
public float heading = 0.0f;
public float roll = 0.0f;
public float pitch = 0.0f;
private int[] textures = new int[3];
private int alt = 0;
private float x = 0.0f;
private float y = 0.0f;

public Form1()
{
InitializeComponent();
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(112)))), ((int)(((byte)(128)))), ((int)(((byte)(144)))));
headingOpenGLControl.Paint += new PaintEventHandler(headingOpenGLControl_Paint);
headingOpenGLControl.Resize += new EventHandler(headingOPenGLControl_Resize);
attitudeOpenGlControl.Paint += new PaintEventHandler(attitudeOpenGlControl_Paint);
attitudeOpenGlControl.Resize += new EventHandler(attitudeOpenGlControl_Resize);
}

private void Form1_Load(object sender, EventArgs e)
{
headingOpenGLControl.InitializeContexts();
headingOPenGLControl_Resize(this, null);
attitudeOpenGlControl.InitializeContexts();
attitudeOpenGlControl_Resize(this, null);
}

private void InitGL() // All Setup For OpenGL Goes Here
{
LoadTextures();
Gl.glEnable(Gl.GL_TEXTURE_2D);
Gl.glShadeModel(Gl.GL_SMOOTH); // Enable Smooth Shading
Gl.glClearColor(0.4375f, 0.5f, 0.5625f, 0.5f); // Black Background
Gl.glClearDepth(1.0f); // Depth Buffer Setup
Gl.glEnable(Gl.GL_DEPTH_TEST); // Enables Depth Testing
Gl.glDepthFunc(Gl.GL_LEQUAL); // The Type Of Depth Testing To Do
Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST); // Really Nice Perspective Calculations
}

private void LoadTextures()
{
// Load Heading Back Ground
Bitmap image1 = new Bitmap("HeadingBackGround.bmp");
image1.RotateFlip(RotateFlipType.RotateNoneFlipY);
System.Drawing.Imaging.BitmapData bitmapdata1;
Rectangle rect1 = new Rectangle(0, 0, image1.Width, image1.Height);
bitmapdata1 = image1.LockBits(rect1, System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);

// Load Airplane Indicator
Bitmap image2 = new Bitmap("Plane.bmp");
image2.RotateFlip(RotateFlipType.RotateNoneFlipY);
System.Drawing.Imaging.BitmapData bitmapdata2;
Rectangle rect2 = new Rectangle(0, 0, image2.Width, image2.Height);
bitmapdata2 = image2.LockBits(rect2, System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);

// Load Pitch Roll Texture
Bitmap image3 = new Bitmap("AttitudeLimeV2.bmp");
image3.RotateFlip(RotateFlipType.RotateNoneFlipY);
System.Drawing.Imaging.BitmapData bitmapdata3;
Rectangle rect3 = new Rectangle(0, 0, image3.Width, image3.Height);
bitmapdata3 = image3.LockBits(rect3, System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);

Gl.glEnable(Gl.GL_TEXTURE_2D);
// Create Texture Names
Gl.glGenTextures(3, textures);
// assign a texture name to texture data
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[0]);
// specifies a 2D texture image

Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, (int)Gl.GL_RGB8, image1.Width, image1.Height,
0, Gl.GL_BGR_EXT, Gl.GL_UNSIGNED_BYTE, bitmapdata1.Scan0);

Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); // Linear Filtering
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); // Linear Filtering

image1.UnlockBits(bitmapdata1);
// Free up Ram used to store data
image1.Dispose();

Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[1]);
// specifies a 2D texture image
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, (int)Gl.GL_RGB8, image2.Width, image2.Height,
0, Gl.GL_BGR_EXT, Gl.GL_UNSIGNED_BYTE, bitmapdata2.Scan0);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); // Linear Filtering
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); // Linear Filtering

image2.UnlockBits(bitmapdata2);
// Free up Ram used to store data
image2.Dispose();

Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[2]);
// specifies a 2D texture image
Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, (int)Gl.GL_RGB8, image3.Width, image3.Height,
0, Gl.GL_BGR_EXT, Gl.GL_UNSIGNED_BYTE, bitmapdata3.Scan0);
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); // Linear Filtering
Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); // Linear Filtering

image3.UnlockBits(bitmapdata3);
// Free up Ram used to store data
image3.Dispose();
}

private void headingOPenGLControl_Resize(object sender, EventArgs e)
{
int width = headingOpenGLControl.Width;
int height = headingOpenGLControl.Height;

Gl.glViewport(0, 0, width, height);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45.0f, (double)width / (double)height, 0.1f, 100.0f);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
InitGL();
}

private void headingOpenGLControl_Paint(object sender, PaintEventArgs e)
{
headingOpenGLControl.MakeCurrent();

Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

//Heading Indicator
Gl.glLoadIdentity(); // Reset The Current Matrix
Gl.glTranslatef(0.0f, 0.0f, -1.1f);
Gl.glRotatef(-heading, 0.0f, 0.0f, 1.0f);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[0]); // Select Our Texture
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex3f(-0.5f, -0.5f, 0.0f); // Bottom Left Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex3f(0.5f, -0.5f, 0.0f); // Bottom Right Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex3f(0.5f, 0.5f, 0.0f); // Top Right Of The Texture and Quad
Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex3f(-0.5f, 0.5f, 0.0f); // Top Left Of The Texture and Quad
Gl.glEnd();
headingOpenGLControl.SwapBuffers();

//Airplane
Gl.glLoadIdentity(); // Reset The Current Matrix
Gl.glTranslatef(0.0f, 0.0f, -1.1f);
//Gl.glRotatef(-heading, 0.0f, 0.0f, 1.0f); // Rotate On The Z Axis
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[1]); // Select Our Texture
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex3f(-0.125f, -0.125f, 0.0f); // Bottom Left Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex3f(0.125f, -0.125f, 0.0f); // Bottom Right Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex3f(0.125f, 0.125f, 0.0f); // Top Right Of The Texture and Quad
Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex3f(-0.125f, 0.125f, 0.0f); // Top Left Of The Texture and Quad
Gl.glEnd();

//headingOpenGLControl.SwapBuffers();

}

private void attitudeOpenGlControl_Resize(object sender, EventArgs e)
{
int width = attitudeOpenGlControl.Width;
int height = attitudeOpenGlControl.Height;

Gl.glViewport(0, 0, width, height);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45.0f, (double)width / (double)height, 0.1f, 100.0f);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
InitGL();
}

private void attitudeOpenGlControl_Paint(object sender, PaintEventArgs e)
{
attitudeOpenGlControl.MakeCurrent();

Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

//Wing Indicator
Gl.glLoadIdentity(); // Reset The Current Matrix
Gl.glTranslatef(0.0f, 0.0f, -0.29f);
Gl.glBegin(Gl.GL_TRIANGLES);
Gl.glColor4d(0,0,0,1);
Gl.glVertex3f(0.0f, 0.0f, 0.0f);
Gl.glVertex3f(-0.1f, -0.03f, 0.0f);
Gl.glVertex3f(-0.05f, -0.03f, 0.0f);
Gl.glVertex3f(0.0f, 0.0f, 0.0f);
Gl.glVertex3f(0.1f, -0.03f, 0.0f);
Gl.glVertex3f(0.05f, -0.03f, 0.0f);
Gl.glColor3f(1f, 1f, 1f);
Gl.glEnd();

// y=0.01066666/deg - for up, + for down
//Attitude Indicator
Gl.glLoadIdentity(); // Reset The Current Matrix
Gl.glTranslatef(0.0f, 0.0f, -0.3f);
Gl.glRotatef(roll, 0.0f, 0.0f, 1.0f); // Rotate On The Z Axis
Gl.glTranslatef(0.0f, y, -0.3f);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[2]); // Select Our Texture
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex3f(-1f, -1f, 0.0f); // Bottom Left Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex3f(1f, -1f, 0.0f); // Bottom Right Of The Texture and Quad
Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex3f(1f, 1f, 0.0f); // Top Right Of The Texture and Quad
Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex3f(-1f, 1f, 0.0f); // Top Left Of The Texture and Quad
Gl.glEnd();

//attitudeOpenGlControl.SwapBuffers();
}

private void button1_Click(object sender, EventArgs e)
{
heading = heading + 5.0f;
this.Refresh();
}

private void btnAltitudeTest_Click(object sender, EventArgs e)
{
if (alt < 24000)
{
alt = alt + 1000;
}
altitudeMeterBar.Value = alt;
lblAltitudeValue.Text = alt.ToString();
}

private void attitudeRoll(bool direction)
{
//roll right for true, left for flase
if (direction)
{ roll = roll + 2; }
else { roll = roll - 2; }
}

private void headingChange(bool direction)
{
//positive degree change if true, false for negative
if (direction)
{ heading = heading + 2; }
else { heading = heading - 2; }
}

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;

if (((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN)) && ((attitudeOpenGlControl.Focused) || (headingOpenGLControl.Focused)))
{
switch (keyData)
{
case Keys.Down:
y = y - 0.01f;
//attitudeRoll(true);
attitudeOpenGlControl.Refresh();
break;

case Keys.Up:
y = y + 0.01f;
//attitudeRoll(false);
attitudeOpenGlControl.Refresh();
break;

case Keys.Left:
roll = roll - 2.0f;
headingChange(false);
headingOpenGLControl.Refresh();
attitudeOpenGlControl.Refresh();
break;

case Keys.Right:
roll = roll + 2.0f;
headingChange(true);
headingOpenGLControl.Refresh();
attitudeOpenGlControl.Refresh();
break;

}
}
return base.ProcessCmdKey(ref msg, keyData);
}

private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}

This wasn't quite the

This wasn't quite the suggested approach here.

Ideally, you should create one OpenGL context and one openGL control on a form, but then use Viewports to cut that OpenGL context into many "rectangles" and then render these at the same time.

i.e. if you have an OpenGL scene that is 800*600, then to split this into two vertically:

glViewport(0,0,400,600);

// Now render the left one

then

glViewport(400,0,400,600);

// Now render the right one

//finally

glSwapBuffers

You'll get no real performance benefit from having multiple OpenGL contexts, it's nothing to do with dualcore/quadcore performance there, it will all be bottlenecks to the GFX cards and uploading geometry.

The way to scale for multi-core CPUs is by offloading other non OpenGL work into other threads, i.e. networking, file IO (including texture loading), physics, animation etc..

Euan MacInnes

http://icarus.sourceforge.net
Windows, MacOSX, Linux C# project based off the Tao.Framework

Yes - This is really where I

Yes - This is really where I am going with this!
one form, one GL context -

what I hope to implement has one small difference from the above outline. Mine would go something like this:

// create an openGL scene (say 800x600)

// CREATE 3D SCENE
// DRAW THE WHOLE SCENE TO BUFFER OR MEMORY OR SOMETHING...

// now draw just the upper left

glViewport(0,0,400,300);

// COPY PORTION OF BITMAP DATA FROM ABOVE BUFFER TO SCREEN (0,0,400,300)

// RENDER THIS VIEWPORT1

// and now maybe just the lower right...

glViewport(400,0,400,300);

// COPY PORTION OF BITMAP DATA FROM ABOVE BUFFER TO SCREEN (400,300,400,300)

// notice that I grabbed the lower right patch, but am drawing it in the upper right area of the screen...

// RENDER THIS VIEWPORT2

//finally push both of these viewports to the display...
glSwapBuffers

as mentioned in my original (rather vague) post, I would actually do this 4 times to grab and draw 4 different small patches of the original scene to the display.

(To help further, I would use scissors to only draw the area of the original 3D scene that I intend to copy to the display... but in my above example, of course, the whole scene would need to be drawn)

you see where I am going with that?

thanks again for all the ideas and help!

-marnold

I wanted to follow through

I wanted to follow through with this thread and provide info for others who may be trying to do the same thing.

I was successful in using FBO's to store an entirely rendered 3d scene. After you get the kinks out, they work great.

The FBO was then applied to several simple rectangles on the screen as a texture map. The texture coordinates determine what part (and how much) of the original scene to be drawn. The is a very flexible approach, but is very hardware dependent - FBO's are only compatible with more recent hardware.

With this approach, you can also develop your animation and 3d scene separately, then as a final step simply draw the scene to an FBO rather than to the screen.

I am also confident that this plays nicely with adding antialiasing - the more complex aa routines work well with texture maps. since the FBO is applied as a texture map, you can roll the aa into this final render step.

You are absolutely right !!!

You are absolutely right !!!

Theme by La Boite a site | Powered by Drupal