Sign in to follow this  

My "Hello World" OpenGL application doesn't display anything

This topic is 1103 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to write a simple OpenGL program that draws a simple line on the screen.

 

All OpenGL functions are dwelling inside a class named Engine.

class Engine
{
	public:
		~Engine();
		static Engine * GI();

		void Run();
		static void WmPaint();

	private:
		Engine();
		static Engine * m_Instance;

		HANDLE		m_hTimerQueue;
		ULONG_PTR	m_GdiPlusToken;

		static VOID CALLBACK WaitOrTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired);
		static void InitializeOpenGL();
		static void DestroyOpenGL();
};

I used my OpenGL text book to write this program. I don't understand what I wrote with OpenGL functions. I just copied them from my textbook. The OpenGL related class methods are:

 

Engine::WmPaint()          : Called every time the window receives WM_PAINT message.

Engine::InitializeOpenGL() : Does initial operations.

Engine::DestroyOpenGL()    : Does some necessary final operations before closing.

 

The class method definitions are as seen below.

#include "Engine.h"

#include <windows.h>
//#include <gdiplus.h>

#include <GL/GL.h>
#include <GL/GLU.h>
#pragma comment (lib, "opengl32.lib")

#include "Settings.h"
#include "Status.h"

Engine * Engine::m_Instance = 0;

Engine::Engine()
{
	WaitForSingleObject(Status::hEvent_GuiInitialized, INFINITE);
	HANDLE hTimer;
	m_hTimerQueue = CreateTimerQueue();
	BOOL bOk = CreateTimerQueueTimer(	/*_Out_     (PHANDLE)*/			&hTimer,
						/*_In_opt_  (HANDLE)*/			m_hTimerQueue,
						/*_In_      (WAITORTIMERCALLBACK)*/	WaitOrTimerCallback,
						/*_In_opt_  (PVOID)*/			NULL,
						/*_In_      (DWORD)*/			0,
						/*_In_      (DWORD)*/			Settings::RefreshPeriod_ms,
						/*_In_      (ULONG)*/			WT_EXECUTEDEFAULT);
	//Gdiplus::GdiplusStartupInput GdiPlusStartupInput;
	//Gdiplus::GdiplusStartup(&m_GdiPlusToken, &GdiPlusStartupInput, NULL);
	InitializeOpenGL();
}


Engine::~Engine()
{
	DeleteTimerQueue(m_hTimerQueue);
	//Gdiplus::GdiplusShutdown(m_GdiPlusToken);
	DestroyOpenGL();
}

Engine * Engine::GI()
{
	if (m_Instance == 0)
	{
		m_Instance = new Engine();
	}
	return m_Instance;
}

void Engine::Run()
{
	
}

void Engine::WmPaint()
{
	//glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glColor3ub(255, 128, 80);
	glBegin(GL_POINTS);
	//for (int i=1; i<200; i++)
	//glVertex3i(i, i, 0);
	for (float i=-1.0; i<+1.0; i+=0.001)
	glVertex3f(i, i, 0);
	glEnd();
}

/*void Engine::WmPaint()
{
	static PAINTSTRUCT Ps;
	static HBRUSH hBrush1 = CreateSolidBrush(RGB(50,100,255));
	static HBRUSH hBrush2 = CreateSolidBrush(RGB(255,100,50));
	static Gdiplus::Image img(L"images\\myimage.png");
	static Gdiplus::SolidBrush brBlack(Gdiplus::Color::Black);
	HDC hDc = 0;
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(Status::hWndGraph, &ps);
		Gdiplus::Graphics g(hdc);
		//g.FillRectangle(&brBlack, 0, 0, Settings::GRAPHAREA_WIDTH, Settings::GRAPHAREA_HEIGHT);
		g.Clear(Gdiplus::Color(0, 0, 0));
		g.DrawImage(&img, Gdiplus::Rect(5, 5, 5 + img.GetWidth(), 5 + img.GetHeight()));
	EndPaint(Status::hWndGraph, &ps);
}*/

VOID CALLBACK Engine::WaitOrTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired)
{
	//RedrawWindow(Gui::GI()->MainWnd.GetGraphHandle(), NULL, NULL, RDW_UPDATENOW);
	//InvalidateRect(Status::hWndGraph, NULL, FALSE);
	//SendMessageW(Status::hWndGraph, WM_PRINT, 0, PRF_CLIENT);
	//UpdateWindow(Status::hWndGraph);
	//MessageBoxW(NULL, L"Timer", L"Timer", NULL);
}

void Engine::InitializeOpenGL()
{
	Status::hDC = GetDC(Status::hWndMain);
	if (Status::hDC == NULL) throw(1);

	PIXELFORMATDESCRIPTOR Pfd;
	ZeroMemory(&Pfd, sizeof(Pfd));
	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 = 16;
	Pfd.iLayerType = PFD_MAIN_PLANE;

	int iFormat = ChoosePixelFormat(Status::hDC, &Pfd);
	if (iFormat == 0) throw(2);

	bool bOK = SetPixelFormat(Status::hDC, iFormat, &Pfd);
	if (bOK == FALSE) throw(3);
	
	Status::hGLRC = wglCreateContext(Status::hDC);
	if (Status::hGLRC == NULL) throw(4);

	bool bReturn = wglMakeCurrent(Status::hDC, Status::hGLRC);
	if (bReturn == FALSE) throw(5);

        //std::string version = (char*)glGetString(GL_VERSION); 
        // Outputs: 2.1.0

	///////////////////

	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glViewport(0, 0, Settings::GRAPHAREA_WIDTH, Settings::GRAPHAREA_HEIGHT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, 1.0f, 1000.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void Engine::DestroyOpenGL()
{
	bool bReturn = wglMakeCurrent(Status::hDC, NULL);
	if (bReturn == FALSE) throw(1);
	bReturn = wglDeleteContext(Status::hGLRC);
	if (bReturn == FALSE) throw(2);
}

I am handling the WM_PAINT message like this:

case WM_PAINT:
	if (hWnd == Status::hWndGraph)		// The graphic area
	{
		Engine::GI()->WmPaint();
		return TRUE;
		//lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
		//return lResult;
	} 
	else if (hWnd == Status::hWndMain)	// The main window
	{
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	else
	{
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	break;

The graphic area is a second window which is embedded (child) inside the main window.

 

sicEzrX.png

 

 

Main Window

Class Styles     : CS_HREDRAW | CS_VREDRAW

Window Styles    : WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_TABSTOP | WS_GROUP | WS_VISIBLE

Window Ex-Styles : WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT | WS_EX_LEFT | WS_EX_LTRREADING

 

Graphic Area

Class Styles     : CS_HREDRAW | CS_VREDRAW | CS_OWNDC

Window Styles    : WS_CHILD | WS_VISIBLE

Window Ex-Styles : 0

 

Yesterday, I was able to run GDI+ code in the program structure. The GDI+ code is still there, commented out. Today, I'm trying to run OpenGL code. Whatever I did, I wasn't successful. Can you please find what I'm doing wrong here?

Edited by hkBattousai

Share this post


Link to post
Share on other sites
Is Engine::InitializeOpenGL called at all?

Also why is the error handling so horrible? Why do you throw ints instead of std::runtime_errors with an actually readable string?

The first thing I would try is getting a non-black color during glClear (via an appropriate call to glClearColor). The rendering code I see won't be any use. Even if the setup of the projection/model matrices and the other states actually worked like that, you are rendering degenerate triangles which will be either culled completely or likely invisible.

Well, you are rendering points, not triangles. But you are rendering them at z=0 and that's outside of your view frustum unless I missed something else. Since I seem to suffer from a caffeine undersupply this morning that looks decidedly possible. I'd start with trying to get a non-black clear color and then see where things are going. Edited by BitMaster

Share this post


Link to post
Share on other sites

Is Engine::InitializeOpenGL called at all?

Also why is the error handling so horrible? Why do you throw ints instead of std::runtime_errors with an actually readable string?

 

Engine::InitializeOpenGL() is called in the constructor.

 

Some parts may look horrible. This is just a start up project to learn how to use OpenGL.

Share this post


Link to post
Share on other sites

Your perspective matrix has a znear of 1. Your modelview is identity. Your points are at z=0. They are outside of your view area. Try placing your points at z>=1. z <= -1. (Thanks Brother Bob!)

 

On another note you should probably invalidate your window somewhere. Windows are only redrawn if necessary. Look up InvalidateRect. To check if this is your culprit, try resizing your window with the mouse and see if something is displayed afterwards.

 

This checks if your general OpenGL setup is correct:

[quote name='BitMaster' timestamp='1417683122' post='5196204']
The first thing I would try is getting a non-black color during glClear (via an appropriate call to glClearColor).
[/quote

Edited by duckflock

Share this post


Link to post
Share on other sites

Your perspective matrix has a znear of 1. Your modelview is identity. Your points are at z=0. They are outside of your view area. Try placing your points at z>=1.

The symptom is correct, but it has to be z <= -1, because the Z-axis is pointing out from the screen towards you. It may be confusing, but the clip plane values are the distances along the negative Z-axis, which mean that a near clip plane value of 1 places the near clip plane value at Z = -1.

Share this post


Link to post
Share on other sites

 

Your perspective matrix has a znear of 1. Your modelview is identity. Your points are at z=0. They are outside of your view area. Try placing your points at z>=1.

The symptom is correct, but it has to be z <= -1, because the Z-axis is pointing out from the screen towards you. It may be confusing, but the clip plane values are the distances along the negative Z-axis, which mean that a near clip plane value of 1 places the near clip plane value at Z = -1.

 

Doh, that's rightrolleyes.gif . Fixed.

Share this post


Link to post
Share on other sites


On another note you should probably invalidate your window somewhere. Windows are only redrawn if necessary. Look up InvalidateRect. To check if this is your culprit, try resizing your window with the mouse and see if something is displayed afterwards.

 

I enabled that line in the timer function. Now it is called periodically. (I had it disabled, because GDI+ was running alright without it.)

VOID CALLBACK Engine::WaitOrTimerCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired)
{
	//RedrawWindow(Gui::GI()->MainWnd.GetGraphHandle(), NULL, NULL, RDW_UPDATENOW);
	InvalidateRect(Status::hWndGraph, NULL, FALSE);
	//SendMessageW(Status::hWndGraph, WM_PRINT, 0, PRF_CLIENT);
	//UpdateWindow(Status::hWndGraph);
	//MessageBoxW(NULL, L"Timer", L"Timer", NULL);
}


Your perspective matrix has a znear of 1. Your modelview is identity. Your points are at z=0. They are outside of your view area. Try placing your points at z <= -1.

 

I hope, I did it correctly:

// Previous:
gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, 1.0f, 1000.0f);

// Now:
gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, -2.0f, 5.0f);


I'd start with trying to get a non-black clear color and then see where things are going.

 

I changed the arguments of glClearColor() in the initialization method.

// Previous:
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

// Now:
glClearColor(0.0f, 0.5f, 0.5f, 0.0f);

Result:

 

Nothing changed. Nothing.

Share this post


Link to post
Share on other sites

 

 


Your perspective matrix has a znear of 1. Your modelview is identity. Your points are at z=0. They are outside of your view area. Try placing your points at z <= -1.

 

I hope, I did it correctly:

// Previous:
gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, 1.0f, 1000.0f);

// Now:
gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, -2.0f, 5.0f);

You did not. The clip plane distances have to be positive. You have to change the coordinates of your vertices, either by setting the Z-coordinates directly or indirectly by translating the model view matrix.

Share this post


Link to post
Share on other sites


You did not. The clip plane distances have to be positive. You have to change the coordinates of your vertices, either by setting the Z-coordinates directly or indirectly by translating the model view matrix.

 

I'm sorry, I don't understand what you are saying. I'm a total newbie and this is my hello world project in OpenGL. Can you please tell me what do I have to do exactly by adding some sample code?

Share this post


Link to post
Share on other sites

The call to gluPerspective in your first post was fine. What you need to change is the actual coordinates you pass to glVertex. The last parameter is the Z-coordinarte, which you set to zero in your first post. Set the Z-coordinate to something else, for example:

glVertex3f(i, i, -2);

instead of

glVertex3f(i, i, 0);

But even if this is a problem, your object probably won't show up anyway. You said that changing the clear color didn't work either, so there are other more fundamental problems as well.

Edited by Brother Bob

Share this post


Link to post
Share on other sites


You did not. The clip plane distances have to be positive. You have to change the coordinates of your vertices, either by setting the Z-coordinates directly or indirectly by translating the model view matrix.

 

I have been investigating the issue. I learned that glPerspective() has an alternative called glOrtho(), and it is more suitable for 2D projection. I think I also learned how to use it. Please correct me if I'm wrong in any part. We first change current matrix by glMatrixMode(GL_PROJECTION). Now we can load a projection matrix to take a projection of the 3D world on the the 2D screen. There are infinitely many projection matrix alternatives, but (as far as I know) two of them are standard alternatives. One is "perspective" projection, the other is "orthogonal". We take the perspective projection by calling the glPerspective() function. While we take orthogonal projection by calling the glOrtho() function. When we load a new projection matrix, it is multiplied by the current one. So we reset it first by loading identity matrix to it by calling glLoadIdentity().

 

A proper projection matrix initialization may be like this:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(/*GLdouble left*/	-2.0,
        /*GLdouble right*/	+2.0,
        /*GLdouble bottom*/	-2.0,
        /*GLdouble top*/	+2.0,
        /*GLdouble zNear*/	-2.0,
        /*GLdouble zFar*/	+2.0);
//gluPerspective(52.0f, (GLfloat) Settings::GRAPHAREA_WIDTH / (GLfloat) Settings::GRAPHAREA_HEIGHT, -2.0f, 5.0f);

With an initialization like this, any object withing the cube [(-2,+2),(-2,+2),(-2,+2)] will be orthogonally projected to the x-y plane at z=-2.

 

Am I correct? I don't think so. Because I still don't see anything on my screen. I'm waiting for someone to counter-prove me by correcting my mistakes in my explanation.

 

I changed the z-dimension component of my vertexes as seen below.

void Engine::WmPaint()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glColor3ub(255, 128, 80);
	glBegin(GL_POINTS);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, +i, -0.5f);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, -i, +0.5f);
	glEnd();
}

Share this post


Link to post
Share on other sites

But even if this is a problem, your object probably won't show up anyway. You said that changing the clear color didn't work either, so there are other more fundamental problems as well.


As I already said, and Brother Bob just reinforced again, talking about vertex positions is useless if you cannot even get a different clear color working.

Share this post


Link to post
Share on other sites

I think glClearColor is doing nothing because you only call it in InitializeOpenGL() (you probably intended to clear the background every redraw correct?).

 

You are also forgetting to flush the buffer in your Engine::WmPaint()

SwapBuffers() is the OpenGL function that takes the backbuffer and draws it to the screen. OpenGL is double buffered so if you never swap with the backbuffer then what you draw remains hidden.

Edited by Gl2eenDl2agon

Share this post


Link to post
Share on other sites

I think glClearColor is doing nothing because you only call it in InitializeOpenGL() (you probably intended to clear the background every redraw correct?).


glClearColor sets the color to which the color buffer is cleared during a call to glClear. So calling it once during initialization would be enough.

Share this post


Link to post
Share on other sites

You are also forgetting to flush the buffer in your Engine::WmPaint()

SwapBuffers() is the OpenGL function that takes the backbuffer and draws it to the screen. OpenGL is double buffered so if you never swap with the backbuffer then what you draw remains hidden.

 

OF COURSE!! That damn SwapBuffers()! I knew something was missing.

 

5k6dMFK.png

void Engine::WmPaint()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glColor3ub(255, 128, 80);
	glBegin(GL_POINTS);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, -i, -0.5f);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, +i, +0.5f);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, -0.5 * i, -2.0f);
	for (float i=-1.0; i<+1.0; i+=0.001) glVertex3f(i, +0.5 * i, +2.0f);
	glEnd();
	SwapBuffers(Status::hDC);
}

Now it works alright. Thank you everybody for sharing your time with me.

Share this post


Link to post
Share on other sites

Is there any reason why you put your opengl rendering code in your WM_PAINT handling code?

This is perfectly legit for a window that is only redrawn rarely (like when its resized or stuff) and not applicable for any sort or real time rendering.

 

For games you'd put a call to your rendering code inside of the infinite loop that has your win32 message pump code.

Most games try to pump out as many frames as possible but you can reduce CPU usage by doing something like Sleep( 3 ).

 

Its perfectly legal in win32-land to render to your window outside of a WM_PAINT event (in fact, other processes can even render to your window! ).

Edited by Gl2eenDl2agon

Share this post


Link to post
Share on other sites

I'm going to write a 2D platform game which won't have a lot of animation. I'm going to refresh the screen with a Win32 API timer. I'm not going to throttle the system resources like most other games do; I personally hate it.

Share this post


Link to post
Share on other sites

I'm going to write a 2D platform game which won't have a lot of animation. I'm going to refresh the screen with a Win32 API timer. I'm not going to throttle the system resources like most other games do; I personally hate it.


"Animation" here also means "moving objects." A platformer game most certainly needs smoothly moving objects. Think of when the player jumps and then falls; you want that to be displayed without the user having to hit a key to get each next frame.

In general, though, I'd steer you well away from even using the Win32 API in the first place. SDL2 (and similar libraries like GLFW or SFML) do everything you need, do it easier and simpler and with fewer error cases, and are portable. Even bigger game engines have in many cases migrated to SDL2 or its ilk. You should never have to write any raw Win32 window management or WGL code unless you're doing some very special and intrinsically Windows-specific. You should rarely have to write any Win32 anything outside of some rare high-performance IO/networking/threading code (and even then, there's high-quality, simpler, easier, portable libraries like asio, tbb, and so on to prefer over the Win32 versions).

Share this post


Link to post
Share on other sites

In general, though, I'd steer you well away from even using the Win32 API in the first place. SDL2 (and similar libraries like GLFW or SFML) do everything you need, do it easier and simpler and with fewer error cases, and are portable. Even bigger game engines have in many cases migrated to SDL2 or its ilk. You should never have to write any raw Win32 window management or WGL code unless you're doing some very special and intrinsically Windows-specific. You should rarely have to write any Win32 anything outside of some rare high-performance IO/networking/threading code (and even then, there's high-quality, simpler, easier, portable libraries like asio, tbb, and so on to prefer over the Win32 versions).

 

Well, pretty much only hobbyist engines and a few commercial engines like Source internally use SDL2 for window management AFAIK (and it's not uncommon for a few to only use SDL2 for it's Mac OSX and Linux ports). You shouldn't tell someone to shy away from Win32 just because you don't like it. It's fine to use, and if anything you'll get experience out of the endeavor.

Share this post


Link to post
Share on other sites

This topic is 1103 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this