How to Render an Opengl Frame in C++ Builder

How to render an openGL frame in C++ builder?

easy, just use TForm::Handle as window handle ...

Here some ancient example of mine in BCB5 ported to BDS2006:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <gl/gl.h>
#include <gl/glu.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
int TForm1::ogl_init()
{
if (ogl_inicialized) return 1;
hdc = GetDC(Form1->Handle); // get device context
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
hrc = wglCreateContext(hdc); // create current rendering context
if(hrc == NULL)
{
ShowMessage("Could not initialize OpenGL Rendering context !!!");
ogl_inicialized=0;
return 0;
}
if(wglMakeCurrent(hdc, hrc) == false)
{
ShowMessage("Could not make current OpenGL Rendering context !!!");
wglDeleteContext(hrc); // destroy rendering context
ogl_inicialized=0;
return 0;
}
ogl_resize();
glEnable(GL_DEPTH_TEST); // Zbuf
glDisable(GL_CULL_FACE); // vynechavaj odvratene steny
glDisable(GL_TEXTURE_2D); // pouzivaj textury, farbu pouzivaj z textury
glDisable(GL_BLEND); // priehladnost
glShadeModel(GL_SMOOTH); // gourard shading
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color
ogl_inicialized=1;
return 1;
}
//---------------------------------------------------------------------------
void TForm1::ogl_exit()
{
if (!ogl_inicialized) return;
wglMakeCurrent(NULL, NULL); // release current rendering context
wglDeleteContext(hrc); // destroy rendering context
ogl_inicialized=0;
}
//---------------------------------------------------------------------------
void TForm1::ogl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

float x=0.5,y=0.5,z=20.0;
glBegin(GL_QUADS);

glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-x,-y,-z);
glVertex3f(-x,+y,-z);
glVertex3f(+x,+y,-z);
glVertex3f(+x,-y,-z);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-x,-y,+z);
glVertex3f(-x,+y,+z);
glVertex3f(+x,+y,+z);
glVertex3f(+x,-y,+z);

glEnd();



glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
void TForm1::ogl_resize()
{
xs=ClientWidth;
ys=ClientHeight;
if (xs<=0) xs = 1; // Prevent a divide by zero
if (ys<=0) ys = 1;
if (!ogl_inicialized) return;
glViewport(0,0,xs,ys); // Set Viewport to window dimensions
glMatrixMode(GL_PROJECTION); // operacie s projekcnou maticou
glLoadIdentity(); // jednotkova matica projekcie
gluPerspective(30,float(xs)/float(ys),0.1,100.0); // matica=perspektiva,120 stupnov premieta z viewsize do 0.1
glMatrixMode(GL_TEXTURE); // operacie s texturovou maticou
glLoadIdentity(); // jednotkova matica textury
glMatrixMode(GL_MODELVIEW); // operacie s modelovou maticou
glLoadIdentity(); // jednotkova matica modelu (objektu)
ogl_draw();
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ogl_inicialized=0;
hdc=NULL;
hrc=NULL;
ogl_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
ogl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
ogl_resize();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
ogl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
ogl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender, TShiftState Shift,
TPoint &MousePos, bool &Handled)
{
glMatrixMode(GL_PROJECTION);
glTranslatef(0,0,+2.0);
ogl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelUp(TObject *Sender, TShiftState Shift,
TPoint &MousePos, bool &Handled)
{
glMatrixMode(GL_PROJECTION);
glTranslatef(0,0,-2.0);
ogl_draw();
}
//---------------------------------------------------------------------------
  1. create empty 1-Form project

  2. add this to form class header as its user defined members

        int     xs,ys;
    HDC hdc; // device context
    HGLRC hrc; // rendering context
    int ogl_inicialized;
    int ogl_init();
    void ogl_exit();
    void ogl_draw();
    void ogl_resize();
  3. add timer ~ 20-40 ms

  4. create events and copy bodies for resize,repaint,ontimer,... to match above source code
  5. compile and run

Notes

  • it is not required that all OpenGL stuff is member of form class
  • timer can have any interval
  • OpenGL can be also just a part of a window not only the whole thing
  • can combine with VCL components (use panels for buttons etc and resize OpenGL to area outside)
  • If you cannot get it to work comment me, but i do not see anything difficult to do ...
  • Do not forget to include gl.h !!!
  • if all work then you should see green quad in the center of form
  • mouse wheel moves camera forward/backward ('zoom')

When you're ready to go beond OpenGL 1.0 take a look at:

  • complete GL+GLSL+VAO/VBO C++ example

Have fun ...

OpenGL get Device Context

You did not state your OS but I assume Windows from the function names. The problem is exactly as Reto Koradi stated in the comment. To set up OpenGL you need to do this:

  1. Obtain OS handle to object with valid device context

    It can be OS window or OS bitmap. If you have just console app then you need to create a valid OS window first and use its handle (to my knowledge console does not have Canvas).

    you can use GLUT for the window creation or If your compiler IDE has an window App you can use that. You can also combine OpenGL and Window components. VCL is also not a problem (I am using it for years with OpenGL)

    In windows you can use CreateWindowEx so google an example for it...

    Anyway you should have your handle in a variable like:

    HWND hwin=NULL;

    If you have no experience with windows applications then use GLUT for this. Otherwise you would need to learn a lot of stuff just to cover window creation, message handling of events and user/app interaction which can be really overwhelming for a rookie without guide.

  2. Get Device context for that handle

    HDC hdc = GetDC(hwin);
  3. Set pixel format you need of device context

    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;
    SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
  4. Create OpenGL rendering context for device context

    HGLRC hrc = wglCreateContext(hdc);
  5. Set it as default OpenGL context

    wglMakeCurrent(hdc, hrc);

This is absolute minimum without any error checking , additional buffers etc. For more info and actual code see related QA's:

  • How to render an openGL frame in C++ builder? for oldstyle GL
  • simple complete GL+VAO/VBO+GLSL+shaders example in C++ with the new stuff

You can use GLUT for all of this. This is first hit I found by quick search:

  • How can I set an OpenGL display (Window created by OpenGL) to maximized?

Or follow OpenGL tutorials there are tons of them out there ...

Rendering OpenGL mesh to SFML RenderTexture

I found the answer! All thanks to "Groogy" in this thread: https://en.sfml-dev.org/forums/index.php?topic=7446.0

I had skimmed that thread earlier, which prompted me to add texture.setView(texture.getDefaultView()); when creating the RenderTexture. However that was not enough, I instead had to call glViewport with the texture bound. (I assumed that is what sf::View would do under the covers, but apparently that is not the case.)

So my RenderTexture creation now looks like this:

sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
if (!texture.create(800, 600, true)) {
std::cerr << "Failed to create RenderTexture" << std::endl;
}
if (!texture.setActive(true)) {
std::cerr << "Failed to activate texture" << std::endl;
}
sprite.setTexture(texture.getTexture());

glViewport(0, 0, 800, 600); // <-- Required
glCheckError();

if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate texture" << std::endl;
}
}

And now things are working.

Creating an OpenGL View without Interface Builder

They way I have gotten this to work is I don't implement an init method in my view. Then in my controller or app delegate I have.

@implementation AppDelegate

@synthesize window = _window;
@synthesize view = _view;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; // I'm going to make a full screen view.

NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Needed if using opengl 3.2 you can comment this line out to use the old version.
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
0
};

NSOpenGLPixelFormat *pix = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
self.view = [[OpenGLViewCoreProfile alloc] initWithFrame:mainDisplayRect pixelFormat:pix];

// Below shows how to make the view fullscreen. But you could just add to the contact view of any window.
self.window = [[NSWindow alloc] initWithContentRect:mainDisplayRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:YES];

self.window.opaque = YES;
self.window.hidesOnDeactivate = YES;
self.window.level = NSMainMenuWindowLevel + 1; // Show window above main menu.
self.window.contentView = self.view;
[self.window makeKeyAndOrderFront:self]; // Display window.
}

@end

Can can make call -makeCurrentContext in your -prepareOpenGl method. All that I have written below is not necessary but nice for performance reasons. I have started using the CVDisplayLink to sync frame drawing with the screen refresh rate so my openGLview looks like

// This is the callback function for the display link.
static CVReturn OpenGLViewCoreProfileCallBack(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext) {
@autoreleasepool {
OpenGLViewCoreProfile *view = (__bridge OpenGLViewCoreProfile*)displayLinkContext;
[view.openGLContext makeCurrentContext];
CGLLockContext(view.openGLContext.CGLContextObj); // This is needed because this isn't running on the main thread.
[view drawRect:view.bounds]; // Draw the scene. This doesn't need to be in the drawRect method.
CGLUnlockContext(view.openGLContext.CGLContextObj);
CGLFlushDrawable(view.openGLContext.CGLContextObj); // This does glFlush() for you.

return kCVReturnSuccess;
}
}

- (void)reshape {
[super reshape];
CGLLockContext(self.openGLContext.CGLContextObj);

... // standard opengl reshape stuff goes here.

CGLUnlockContext(self.openGLContext.CGLContextObj);
}

- (void)prepareOpenGL {
[super prepareOpenGL];

[self.openGLContext makeCurrentContext];
GLint swapInt = 1;
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];

CGLLockContext(self.openGLContext.CGLContextObj);

... // all opengl prep goes here

CGLUnlockContext(self.openGLContext.CGLContextObj);

// Below creates the display link and tell it what function to call when it needs to draw a frame.
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
CVDisplayLinkSetOutputCallback(self.displayLink, &OpenGLViewCoreProfileCallBack, (__bridge void *)self);
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self.displayLink,
self.openGLContext.CGLContextObj,
self.pixelFormat.CGLPixelFormatObj);
CVDisplayLinkStart(self.displayLink);
}

Run OpenGL shader to modify existing texture / frame buffer

At first:
It is technically possible and safe to read and write in the same pass using this extension - however i wouldn't recommend this, especially for learners, since the extension is tightly limited and might not be supported on every hardware.

That being said:

So it's not possible to use the same texture as a sampler and frame buffer?

You can use the same texture as a framebuffer texture attachment and render to it and as a texture sampler to look up values in the shader, but not in the same pass. That means, if you have two textures you could read from A and write to B and afterwards switch textures and read from B and write to A. But never A->A or B->B (without the extension mentioned).

As a technical detail, a texture currently being used as a target can also be bound to a sampler shader variable at the same time, but you must not use it.

So let's say I want to blur just a small part of a texture. I have to run it through a shader to a second texture and then copy that texture back to the first texture / frame buffer?

Second texture yes. But for efficiency reasons do not copy the texture data back. Just delete the source texture and use the target texture you have rendered to in the future. If you have to do this often, keep the source texture as a render target for later use to increase performance. If you have to do it every frame just swap the textures every frame. The overhead is minimal.



Related Topics



Leave a reply



Submit