Sep 10
30
This article is going to give a brief comparison of OSX and Windows 7 in relation to OpenGL programming, and programming in general. I am in no way wanting to start a flame war, but feel free to fight amongst yourselves in the comments section. I will admit it sounds like I’m bashing Windows and praising OSX, but in relation to OpenGL, this is the truth.
If any of you have taken a real good look at the tutorials on the site, you may notice that some of them have screenshots from Windows and others have screenshots from OSX, the newer tutorials being from OSX. This is because I first started programming on Windows many years ago, and whilst I still program on Windows when I have to, a majority of my work is now done on OSX. Don’t get yourselves thinking that I automatically have some money behind me, just because I own a Mac, doesn’t automatically translate to “He’s got a Mac, how the heck did he afford that?! …Must be some rich guy.” I’m a uni student, who spent half a year saving for it, and these days it’s my most valuable asset.
Anyway, I started my OpenGL programming back on Windows XP and as Windows Vista and Windows 7 were released, I migrated from one to the other. The downside to being an OpenGL programmer in a Windows environment, is that while you can get everything setup easily, you still have to go through that setting up stage every time you format, reinstall or upgrade your operating system, as Windows is primarily meant for DirectX, and while you still need the DirectX SDK to develop on your operating system, you can generally run your previous programs no worries.
Well after quite a few years, I finally grew up and was able to come to University, where I was given a good rundown of Ubuntu and I started doing some research in one of the labs which also used Ubuntu. From then on, I spent about a year and a half developing with OpenGL on Ubuntu and it was all fairly straight forward after you had the correct packages installed, although it was a little confusing as to which OpenGL packages I had to install, and I still am unsure about what they all are.
Finally, I met a guy at the University who suggested I borrow one of the Macbooks and start porting my stuff onto it in an attempt to get me to the WWDC on a scholarship, as one person had recently left the lab I was in, and his old Macbook was floating around with no one to use it. This was a 2004 model Macbook, but it was running OSX 10.5 at the time.
I ported some of my better demos to OSX and to my excitement, I was accepted to go over to the WWDC in 2009. While at the WWDC, it was a little funny to see such hardcore Apple fans, I think I was the only person with a PC laptop and a Nokia phone, while everyone else had an iPhone and a Macbook. But as the conference went on, I was intrigued as to some of the upcoming features in OSX 10.6. More importantly OpenCL, which still isn’t really supported on PC’s unless you get the drivers from GPU manufacturers, and the fact that OSX is powered by OpenGL and they have so much support for it.
One thing to learn when developing with OpenGL on OSX, is that OSX will forever love you and be nice to you. Once you have XCode installed, which is the preferred IDE for developing on OSX and the iPhone, you will find that all you have to do is create a new command line project, and import the OpenGL and GLUT frameworks into your project. You don’t have to worry about dll files in the correct directory, or having to download the library and header files and put them in the right location. You just add the frameworks and go. This is because OSX uses OpenGL for it’s entire GUI, everything you see on the screen. This is awesome to wrap your head around as an OpenGL developer, because it shows you a new side to Computer Graphics. It isn’t just 3D games and visualizations which was what I was familiar with, it has now extended to entire User Interfaces.
Apple have also done some amazing things in terms of OpenGL debugging. Yes, you can in fact debug an OpenGL application on OSX, there is a tool for that! Or if you have an iPhone and are doing iPhone development, I’ll use the pun “There’s an app for that!”. The OpenGL debugger shows you how much time you are spending on different calls, just like a regular debugger, but my favourite part, is that you can pause your application and view all your current buffers, your depth buffers and your colour buffers, at any time. So you can make sure your application is running as expected, which saves a lot of time compared to not knowing what is going on in the GPU.
If you are on Windows and you have tried my GLSL tutorials, and you haven’t read the comments section, you may run into an error when you try executing any of the tutorials. This is because I have not included a line to glewInit() that is essential on Windows. It’s just one of those things you have to do, to make sure you have access to your extensions when using the GLEW library. Well on OSX there is no GLEW library, OSX has full support for all OpenGL extensions right off the bat and you don’t have to worry about declaring what extensions are available. It’s little things like this all over the place that make developing for OpenGL on OSX so nice.
Starting off with OpenGL, I have come to love it extensively, but there is no denying it is not the be all and end all of computer graphics. Microsoft DirectX has it’s Direct3D API which can do everything that OpenGL can do, just in it’s own way (well eventually, often OpenGL is just that tiny step ahead). While OSX has a GUI powered completely by OpenGL, the GUI for Windows now uses DirectX to offload everything on the GPU and speed it up. I remember watching videos of codename Longhorn, now known as Windows Vista, and it was thought of as quite an achievement to have a GUI that runs on the GPU. I even remember stories at the time, that suggested Microsoft didn’t want to support OpenGL at all in Windows, and that if they did, it was going to be a bunch of Direct3D calls underneath the OpenGL calls which would slow down your program dramatically. Luckily they never went along with this and OpenGL is natively supported still on Windows. Could you imagine the backlash they would have suffered, when for example, games by iD software, including the Quake and Doom series, rely on OpenGL!
But while you can get the DirectX SDK and various SDK’s from GPU manufacturers, there is no “it just works” approach to computer graphics programming in Windows like there is on OSX. The only advantage to Windows is that it runs both DirectX and OpenGL whilst OSX only runs OpenGL (which is not their fault). You have to install your editor, just as you do on OSX, but then you also have to install the SDK’s, correct drivers and there are no Direct3D debuggers that I am aware of. This isn’t too much hassle if you know what you are doing but for beginners, can cause some initial headaches.
When it comes to debugging Direct3D, you can enable debugging information, but I am yet to see anything that offers the functionality that the OpenGL debuggers provide.
Sep 10
29
I apologize for the delay, especially since this tutorial is not massive. Currently I am on Uni holidays, but I have been working and doing assignments.
However I have found time to finish off the third OpenGL 4 tutorial. This one is on using matrices and the GLM library.
At the moment, there is a problem with the video, my exporter isn’t exporting the AVI sequence for the screen recording. I will be fixing this shortly.
Cheers,
Swifltess
Sep 10
29
OpenGL 3.x and OpenGl 4.x have since deprecated and started removing all matrix operations as part of the OpenGL library, which I believe is a really good thing for the computer graphics community, but for beginners can make things a little difficult to get up and running at first. What this means if you are coming from the previous OpenGL tutorials, is that you can no longer use calls such as glTranslate or glRotate. Even glLoadIdentity and setting the gluPerspective is now redundant.
When it comes to computer graphics in 3D, you typically have a number of matrices that make up your final scene. They are the projection matrix, model matrix and view matrix. You can also go into the texture matrix and normal matrix, etc, but for simply positioning shapes in the world I will stick with the more basic matrices. OpenGL 2.x and prior combined the model and view matrices into the modelview matrix and used a stack to let us add transformations matrices to the current modelview matrix. We could then pop off the top of the stack to a previous matrix if needed.
Personally I prefer to use the separate matrices so that you don’t have to constantly monitor the one matrix, whilst also making it easier to use scene graphs and other scene management techniques. How you do this is up to you, but I am going to use multiple matrices.
The projection matrix is a 4×4 matrix that defines the 2D to 3D projection for a scene. This is often calculated by a pre-defined function, such as gluPerspective in previous versions of OpenGL. The library we are going to be looking at, provides a similar method.
The view matrix, which can also be known as the world matrix determines the position of the ‘camera’ in space. This can be thought of as a global matrix that affects everything and places everything relative to the location of the ‘camera’.
The model matrix is a local matrix that affects models on a more discrete basis. For example, if you want to translate, rotate or scale a specific object you will use that objects local matrix or model matrix.
Luckily in the wake of OpenGL dropping support for its own matrices, a mathematics library has been developed to replace it, called GLM. GLM is short for OpenGL Mathematics and has been designed to replicate the matrix functions found in GLSL along with giving us the scale, rotate and translate methods we are used to from OpenGL 2.x and lower. GLM can be found here: http://glm.g-truc.net/
Installation is simple; once you unzip the file you download, find the folder name “glm” and copy it into your IDE’s header directory.
Once you have GLM installed and ready to use, move on to the coding section of this tutorial
The very first thing we need to do to get GLM working in our OpenGL application is to include the relevant header files. So go and open up main.h and add the following lines at the end of the file:
// GLM include files #include <glm/glm.hpp> #include <glm/gtc/matrix_projection.hpp> #include <glm/gtc/matrix_transform.hpp>
Now we should be able to use glm functions, stored in the glm namespace. To access them will be done similar to glm::function_name unless you add the line:
using namespace glm;
From which you can then just call function_name.
Let’s set up the variables that will hold our matrices now. Open up opengl_3.h and in the private section of our class, created three variables, one for each of our matrices.
glm::mat4 projectionMatrix; // Store the projection matrix glm::mat4 viewMatrix; // Store the view matrix glm::mat4 modelMatrix; // Store the model matrix
Out of these three matrices, the projectionMatrix is the only matrix we will keep consistent throughout the entire application (of course, you can change this during your application if you are after a specific effect). So in our setupScene method, inside of opengl_3.cpp, we are going to create our projectionMatrix. To do so, we call a glm::perspective method, which works virtually identically to gluPerspective. It requires us to specify a fov (field of view) in degrees, followed by the aspect ratio for our window and finally the near and far planes for our scene. To do so, add the following line:
projectionMatrix = glm::perspective(60.0f, (float)windowWidth / (float)windowHeight, 0.1f, 100.f); // Create our perspective projection matrix
Now that isn’t so scary so far. And the following is hopefully just as nice to us. Everything else we will be changing in our C++ code will take place in the renderScene method, followed by modifying our vertex shader. Let’s continue with our opengl_3.cpp file for now. The first thing we are going to do is after we clear our color, depth and stencil buffers, we are going to set our view and model matrices. I am going to set the view matrix, to translate back 5 units into the scene. This is the equivalent to our old glTranslate(0, 0, -5). The view matrix is going to be just for kicks at the moment, as we have no geometry, but it will scale geometry by a half.
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.f)); // Create our view matrix which will translate us back 5 units modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f)); // Create our model matrix which will halve the size of our model
After we have set these matrices, we then have to use them. To do so, we need to send them through to the shader that will be using them. In our code, we first bind our shader, just like we would in the GLSL tutorials. We then need to link to the location of the variables we will be using inside of the shader. This is the locations for the perspectiveMatrix, viewMatrix and modelMatrix.
int projectionMatrixLocation = glGetUniformLocation(shader->id(), "projectionMatrix"); // Get the location of our projection matrix in the shader int viewMatrixLocation = glGetUniformLocation(shader->id(), "viewMatrix"); // Get the location of our view matrix in the shader int modelMatrixLocation = glGetUniformLocation(shader->id(), "modelMatrix"); // Get the location of our model matrix in the shader
Once we have these locations stored, we can go ahead and send through our matrices using glUniformMatrix4fv, just like we would in previous versions of OpenGL if we wanted to play with matrices. GLM allows us to access our matrices to send through to OpenGL easily. So add the following code after our locations.
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]); // Send our projection matrix to the shader glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]); // Send our view matrix to the shader glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]); // Send our model matrix to the shader
Now we will move onto our vertex shader so that we can put these variables to good use. Inside of our vertex shader, we need the three matrix variables. So to create those, we are creating 3 mat4 variables.
uniform mat4 projectionMatrix; uniform mat4 viewMatrix; uniform mat4 modelMatrix;
To use these matrices, you might remember a line similar to:
gl_Position = glModelViewProjectionMatrix * gl_Vertex;
Well in our new code, our line will be similar:
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);
Where in_Position is the equivalent to gl_Vertex.
To wrap up, that is all that is required to switch from the OpenGL 2.x and lower style of matrix use, to the new developer-managed OpenGL 3.x and higher style of matrix use. I will point out that this tutorial still won’t show anything more than a window on the screen. In the next tutorial we will finally be getting a square rendering on the screen. Well done so far!
Visual Studio 2010 Project
PDF version
If you have any questions you can comment below or email me at swiftless@gmail.com
Aug 10
23
This post is just to let everyone know that I have not forgotten about the site. I have been extremely busy with university and while I have got the and done for the next three OpenGL 4 tutorials, as well as the shadow mapping code, I haven’t had the time to write up the tutorials to go along with them.
If you would like to stay more up to date, join the SwiftlessTutorials facebook group, link on the right.
I’m aiming to have the next tutorial up shortly.
Cheers,
Swiftless
Aug 10
8
The second of the new OpenGL 4 tutorials is now up and available for reading and downloading. This one introduces the GLSL shaders from the GLSL section of the site in to our new OpenGL 4 framework.
Cheers,
Swiftless
Aug 10
8
Because OpenGL 3.x and up is extremely shader oriented, I am going to show you how to integrate our shader code from the GLSL section of the website. I am not going to go into GLSL details here, as I have done that, and will continue to do that in the GLSL portion of the site. This tutorial is a quick guide on how to get shaders working in our current framework. So why don’t we get to it, so we can move on to using matrices in the new OpenGL pipeline. Keep in mind that I am only working with Vertex and Fragment shaders still, I will introduce Geometry shaders in a later tutorial.
The code for this tutorial is going to be fairly minimal, and I may have updates to the shader.cpp and shader.h files that are in the GLSL portion of the site. If you have any queries, feel free to ask.
The first thing we are going to do, is copy our shader.h and shader.cpp files from the GLSL section of the site, and add them into our new Visual Studio project. Then go ahead and open opengl_3.h.
Inside of opengl_3.h where we have our opengl context class structure, jump up the top and make sure you include the shader.h file into your source code.
#include "shader.h"
Once we have done this, go down into our private variables for our context class, and add an instance of a shader variable. If you are working with several shaders, you can make this into an array, or create separate shader variables. Personally I would create an array of shaders.
class OpenGLContext {
...
private:
int windowWidth; // Store the width of our window
int windowHeight; // Store the height of our window
Shader *shader; // Our GLSL shader
...
};
Once we have a shader variable declared that we can play with, open up opengl_3.cpp and jump down to the setupScene method. Inside of here we are going to set our shader to a new shader object, based on shader.vert and shader.frag. Both of which are included in the project.
void OpenGLContext::setupScene(void) {
glClearColor(0.4f, 0.6f, 0.9f, 0.0f); // Set the clear color based on Microsofts CornflowerBlue (default in XNA)
shader = new Shader("shader.vert", "shader.frag"); // Create our shader by loading our vertex and fragment shader
}
And then all we have left to do is jump to our renderScene method and bind and unbind our shader as required. You will notice that we don’t have any geometry to draw yet, so you won’t see anything yet, but don’t worry, we are getting there
void OpenGLContext::renderScene(void) {
glViewport(0, 0, windowWidth, windowHeight); // Set the viewport size to fill the window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Clear required buffers
shader->bind(); // Bind our shader
shader->unbind(); // Unbind our shader
SwapBuffers(hdc); // Swap buffers so we can see our rendering
}
I mentioned at the start that these are pretty much the same shader.cpp and shader.h files that are in the GLSL section of the website. Here I am going to list one major change that I have made. I am binding two variables to our shader. These variables are constants and are called in_Position and in_Color and will be used to send positions and colors to our shader without binding and unbinding them every frame. These lines are added in our init method for our shader:
void Shader::init(const char *vsFile, const char *fsFile) {
...
glBindAttribLocation(shader_id, 0, "in_Position"); // Bind a constant attribute location for positions of vertices
glBindAttribLocation(shader_id, 1, "in_Color"); // Bind another constant attribute location, this time for color
...
}
Because this tutorial has been so short, I am going to give a quick talk about the changes done to vertex and fragment shaders in GLSL 1.50. To declare which version of GLSL you are using, you can use a line similar to the following, which sets our GLSL version to 1.50:
#version 150 core
This can be changed for any valid version of GLSL. In GLSL 1.50, we no longer have gl_ModelViewMatrix, gl_ProjectionMatrix, gl_Vertex, etc, etc. Because we now have to manage and send all of this information to the shaders ourselves. In the next tutorial I will introduce self manipulated model, view and projection matrices, but for now I will stick with the basics. Because I created the two variables in_Position and in_Color to bind to our shader, we have to create these. They will both be vec3 variables, and because we are using GLSL 1.5, we also need to declare if the variable is going “in” or “out” of the current shader. There are no more uniform or varying variables to deal with. I am also going to create one “out” variable called pass_Color which is also a vec3 variable. This will send any outgoing color from the vertex shader to the fragment shader. Luckily setting the final position requires some similar code, which is setting the gl_Position variable.
#version 150 core
in vec3 in_Position;
in vec3 in_Color;
out vec3 pass_Color;
void main(void)
{
gl_Position = vec4(in_Position, 1.0);
pass_Color = in_Color;
}
#version 150 core
in vec3 pass_Color;
out vec4 out_Color;
void main(void)
{
out_Color = vec4(pass_Color, 1.0);
}
If all went well, you should now be set up to use shaders in your application and your application should run without any errors. Stick with me for the next two tutorials, and by the end of the fourth tutorial, we will now be rendering things to the screen. OpenGL 3.x and 4.x takes a bit more preparation, but it does make a difference in the end, especially when it comes to speed as without the immediate mode, our rendering should be extremely more efficient.
Visual Studio 2010 Project
PDF version
If you have any questions you can comment below or email me at swiftless@gmail.com
Aug 10
3
I have just finished up the first of the OpenGL 4 tutorials. These will come with a downloadable PDF version as well as the Visual Studio 2010 project files.
When I get on my PC next, I will also try to get a quick YouTube video out of the final application running so you can see what to expect.
Cheers,
Swiftless
Aug 10
3
OpenGL has undergone some major changes recently from version 3 and onwards. OpenGL 3 introduced a new way of programming in OpenGL that slightly raises the learning curve, but also does away with the entire Immediate Mode by deprecating a lot of the functions. Now I know I have called this part of the site “OpenGL 4 Tutorials”, and all of a sudden I am talking about OpenGL 3, this is because OpenGL 3 and OpenGL 4 are extremely similar, with OpenGL 4 bringing in some new extensions, and because my graphics card only supports OpenGL 3.x. If you would like to see some OpenGL 4 extensions in use, you can always donate and help me get a new graphics card. In this tutorial, I am going to be simply creating a window with an OpenGL 3.2 context attached, nothing more, nothing less. These tutorials are going to be for Windows only, but if you are keen to get them working on a different OS (My Macbook doesn’t support OpenGL 3.x), please send me the ports and I will post them up.
An OpenGL 3+ and 4 context requires you to create a standard OpenGL context (version 1.x or 2.x) and then get your application to opt-in to using an OpenGL 4 context.
Without the Immediate Mode, OpenGL has gone for an almost entirely shader driven architecture, which gives us control over everything from the word go, and also greatly improves performance.
When it comes to rendering shapes, instead of setting the state and then calling the vertices one by one, OpenGL 3 and upwards wants us to use Vertex Buffer Objects and Vertex Buffer Arrays straight out. Vertex Buffer Objects were available in OpenGL 1.5 and up as an extension, but they are now a part of the core of OpenGL.
Matrices are one of the major things affected by OpenGL 3. To put it simply, OpenGL does no matrix operations for us such as setting the Projection Matrix, setting the Model Matrix and setting the View Matrix. This means calls such as glRotate and glTranslate are now deprecated and not recommended. OpenGL now likes us to handle our own matrices. I will talk about this in a later tutorial, but there is a library called GLM, designed for use with OpenGL and GLSL, which will help us with our matrix operations.
*note* You can jump straight to the bottom of this page to grab the PDF version of this tutorial, or the project files. *note*
Let’s finally get into this tutorial, because it is going to be a long one. I’m also going to be aiming for a more Object Oriented approach to programming in these newer tutorials.
This tutorial is going to be very similar in some ways to most Win32 OpenGL tutorials available, the only difference being that it has an OpenGL 3.2 context instead of a standard context. So open up Visual Studio and create a new Windows Console Application in C++. Make sure it is empty to begin with, and then create 4 files. I am calling them: “main.cpp, main.h, opengl_3.cpp, opengl_3.h”. I am going to go through these files one by one, starting with the header files, so that we don’t get mixed up shuffling between them.
Main.h is going to contain all of our header files for the time being. So open it up and paste the following code. I won’t explain it, as it is pretty straight forward; we are including some header files and linking some libraries, the only tricky part may be “#pragma once”, which isn’t standard but is widely supported, and means to only include the file once during compilation.
#pragma once // Include Windows functions #include // Include input and output operations #include // OpenGL and GLEW Header Files and Libraries #include #include #pragma comment(lib, "glew32.lib") #pragma comment(lib, "opengl32.lib")
As you can see from the above code, we are once again using GLEW. GLEW is up to date with the latest OpenGL 3.x and possibly 4.0 extensions. I highly recommend the use of it as it makes coding in OpenGL so much easier without having to worry about declaring extensions by yourself.
The first thing we want to include here is our main.h file. So add the line:
#include "main.h"
Once we have included our main header file which contains access to the GLEW and OpenGL commands, we are going to want to create a new class. I am going to call this class OpenGLContext and it is going to be responsible for creating a context, handling setting up of our scene, resizing of our scene, rendering of our scene, and finally, cleaning up of our scene. Start off with an empty class like so:
/**
OpenGLContext is a class designed to store all of your OpenGL functions and keep them
out of the way of your application logic. Here we have the ability to create an OpenGL
context on a given window and then render to that window.
*/
class OpenGLContext {
public:
private:
protected:
};
Next up, we are going to come up with some protected variables that will keep track of our window. When working with the Win32 API, each Window you see has its own HWND which is the identifier for that window. We also have two different contexts we need to work with, the first is the HDC, which is the device context, and the second is the HGLRC which is an OpenGL rendering context. If we create a variable to handle each of these, we get:
class OpenGLContext {
...
protected:
HGLRC hrc; // Rendering context
HDC hdc; // Device context
HWND hwnd; // Window identifier
};
Once we have the information we need for a window and a context, we need to keep track of the width and the height of our window. If we don’t keep track of these, then whenever we resize the window, we will find that parts of it are not rendered to, or we render more than we have to, resulting in some of our image being cut off. We are going to add two private variables to keep track of this for us:
class OpenGLContext {
...
private:
int windowWidth; // Store the width of our window
int windowHeight; // Store the height of our window
...
};
Finally we need to come up with some method stubs for controlling our OpenGLContext object. I am going to work with two different constructors and a destructor for some standard methods. Followed by four other methods for working with our context which are going to be called “create30Context(HWND), setupScene(void), reshapeWindow(int, int), renderScene(void)”.
class OpenGLContext {
public:
OpenGLContext(void); // Default constructor
OpenGLContext(HWND hwnd); // Constructor for creating our context given a hwnd
~OpenGLContext(void); // Destructor for cleaning up our application
bool create30Context(HWND hwnd); // Creation of our OpenGL 3.x context
void setupScene(void); // All scene information can be setup here
void reshapeWindow(int w, int h); // Method to get our window width and height on resize
void renderScene(void); // Render scene (display method from previous OpenGL tutorials)
...
};
This is all we are going to need for our opengl_3.h header file for now. So let’s go and start on opengl_3.cpp.
To give use access to our class methods, you are going to want to include opengl_3.h of course, so that will be line number one for this file.
#include "opengl_3.h"
Now let’s get to building us some methods for our class. I am going to start off with the two constructors and the destructor. Our first constructor, the default one, at the moment does nothing, so you can just create an empty constructor. But the second one will make a call to our create30Context method.
/**
Default constructor for the OpenGLContext class. At this stage it does nothing
but you can put anything you want here.
*/
OpenGLContext::OpenGLContext(void) {
}
/**
Constructor for the OpenGLContext class which will create a context given a windows HWND.
*/
OpenGLContext::OpenGLContext(HWND hwnd) {
create30Context(hwnd); // Create a context given a HWND
}
When we get to our destructor, it will handle the removal of our rendering context and our device context. For this, we make use of wglMakeCurrent, wglDeleteContext and ReleaseDC.
/**
Destructor for our OpenGLContext class which will clean up our rendering context
and release the device context from the current window.
*/
OpenGLContext::~OpenGLContext(void) {
wglMakeCurrent(hdc, 0); // Remove the rendering context from our device context
wglDeleteContext(hrc); // Delete our rendering context
ReleaseDC(hwnd, hdc); // Release the device context from our window
}
Let’s get onto our create30Context method now. This method will take in a HWND value to identify a window, and will create a valid rendering and device context on top of it. Create an empty method for this, and then we will set the HWND for our object, and get a valid HDC. I’ve made this method a Boolean value, so we can return a false value if we are unable to successfully create a context of any kind. However by default, we will fall back to and OpenGL 2.1 context if unable to create an OpenGL 3.x context.
/**
create30Context creates an OpenGL context and attaches it to the window provided by
the HWND. This method currently creates an OpenGL 3.2 context by default, but will default
to an OpenGL 2.1 capable context if the OpenGL 3.2 context cannot be created.
*/
bool OpenGLContext::create30Context(HWND hwnd) {
this->hwnd = hwnd; // Set the HWND for our window
hdc = GetDC(hwnd); // Get the device context for our window
return true; // We have successfully created a context, return true
}
The next thing we need to do, is create a variable called a PIXELFORMATDESCRIPTOR which determines how many bits of colour and depth we get, along with giving us double buffering and support for OpenGL. To do this, we first need to create a new variable, I am going to call it pfd and then we need to allocate some empty memory for it. Here, 32 bits of colour, gives us R, G, B and A channels, while 24 bits gives us only R, G and B (each channel takes 8 bits). After we have set aside some memory for this variable, we need to set the size of it, which will always be sizeof(PIXELFORMATDESCRIPTOR). Next, we will set the flags which allow double buffering, opengl support and drawing to a window. Next up, we set the iPixelType to allow RGBA pixels, before we set how many bits we want in our color and depth buffers. Finally, we need to set the iLayerType. We are going to set this to the Main_Plane for our window.
bool OpenGLContext::create30Context(HWND hwnd) {
this->hwnd = hwnd; // Set the HWND for our window
hdc = GetDC(hwnd); // Get the device context for our window
PIXELFORMATDESCRIPTOR pfd; // Create a new PIXELFORMATDESCRIPTOR (PFD)
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); // Clear our PFD
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Set the size of the PFD to the size of the class
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; // Enable double buffering, opengl support and drawing to a window
pfd.iPixelType = PFD_TYPE_RGBA; // Set our application to use RGBA pixels
pfd.cColorBits = 32; // Give us 32 bits of color information (the higher, the more colors)
pfd.cDepthBits = 32; // Give us 32 bits of depth information (the higher, the more depth levels)
pfd.iLayerType = PFD_MAIN_PLANE; // Set the layer of the PFD
return true; // We have successfully created a context, return true
}
Next up, we need to choose a pixel format for our device context, based on our pixel format descriptor. To do this, we make a call to ChoosePixelFormat and pass through our hdc and pfd variables, in return we get an integer value that represents our pixel format, which we can then use to set our pixel format for our device context using SetPixelFormat as seen below.
bool OpenGLContext::create30Context(HWND hwnd) {
this->hwnd = hwnd; // Set the HWND for our window
hdc = GetDC(hwnd); // Get the device context for our window
...
int nPixelFormat = ChoosePixelFormat(hdc, &pfd); // Check if our PFD is valid and get a pixel format back
if (nPixelFormat == 0) // If it fails
return false;
bool bResult = SetPixelFormat(hdc, nPixelFormat, &pfd); // Try and set the pixel format based on our PFD
if (!bResult) // If it fails
return false;
return true; // We have successfully created a context, return true
}
Once we have our pixel format all set up. We need to go ahead and create our OpenGL context. To do this, we need to first create an OpenGL 2.1 compatible context, and then call a new OpenGL 3.x extension called wglCreateContextAttribsARB to create an OpenGL 3.x compatible context. So let’s create our OpenGL 2.1 context as per a standard Win32 OpenGL application.
HGLRC tempOpenGLContext = wglCreateContext(hdc); // Create an OpenGL 2.1 context for our device context wglMakeCurrent(hdc, tempOpenGLContext); // Make the OpenGL 2.1 context current and active And now we will go ahead and enable GLEW so that we can use our new OpenGL 3.x context creation extension.
GLenum error = glewInit(); // Enable GLEW if (error != GLEW_OK) // If GLEW fails return false;
To create our context correctly, we need to set some attributes. These include the major and minor version of the OpenGL version we wish to use, followed by a flag to set this as a forward compatible context. When I say major and minor version, OpenGL versions come in this naming convention “major.minor”, so in OpenGL 3.2, the major version is 3 and the minor version is 2. This can be change to give OpenGL 3.2, 3.1, 3.0 or even 4.0 contexts.
int attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, // Set the MAJOR version of OpenGL to 3
WGL_CONTEXT_MINOR_VERSION_ARB, 2, // Set the MINOR version of OpenGL to 2
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, // Set our OpenGL context to be forward compatible
0
};
Now that our attributes are all sorted out, we are going to do a check to see if our OpenGL 3.x context creation extension is available. If it is, we are going to create our OpenGL 3.2 context, if it isn’t, then we are going to stick with the OpenGL 2.1 context we created above. To switch to the new context after it has been created, we need to make sure that no context is current on our window, and then delete the old context, before finally making our new OpenGl 3.2 context current.
if (wglewIsSupported("WGL_ARB_create_context") == 1) { // If the OpenGL 3.x context creation extension is available
hrc = wglCreateContextAttribsARB(hdc, NULL, attributes); // Create and OpenGL 3.x context based on the given attributes
wglMakeCurrent(NULL, NULL); // Remove the temporary context from being active
wglDeleteContext(tempOpenGLContext); // Delete the temporary OpenGL 2.1 context
wglMakeCurrent(hdc, hrc); // Make our OpenGL 3.0 context current
}
else {
hrc = tempOpenGLContext; // If we didn't have support for OpenGL 3.x and up, use the OpenGL 2.1 context
}
Finally, to check that we have created our context correctly, we are just going to pull out the MAJOR and MINOR version numbers for OpenGL and output them to a console.
int glVersion[2] = {-1, -1}; // Set some default values for the version
glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]); // Get back the OpenGL MAJOR version we are using
glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]); // Get back the OpenGL MAJOR version we are using
std::cout << "Using OpenGL: " << glVersion[0] << "." << glVersion[1] << std::endl; // Output which version of OpenGL we are using On Windows, you won’t get a console for a Win32 Application, but a nifty trick to get console output, is to open up Command Prompt, navigate to your directory with your executable file, and use something like: “program.exe > temp.txt”
This will run your application, and save all output to the file temp.txt. Linux programmers may already be familiar with calls like this.
Now we still have three more methods we need to create here before we are done, but luckily for us, they are extremely short. The first I will look at is setupScene, which takes no parameters, and will only set up our OpenGL Clear Color value. I am going to set it to the nice CornFlowerBlue you get when working with XNA. I grew fond of this colour, and it looks nicer than a standard black or white.
/**
setupScene will contain anything we need to setup before we render
*/
void OpenGLContext::setupScene(void) {
glClearColor(0.4f, 0.6f, 0.9f, 0.0f); // Set the clear color based on Microsofts CornflowerBlue (default in XNA)
}
Moving on, we now have our reshapeWindow method. This method will take in two integers, one for the width of our window and the other for the height of our window. It will then set our OpenGLContext objects width and height to these values.
/**
reshapeWindow is called every time our window is resized, and it sets our windowWidth and windowHeight
so that we can set our viewport size.
*/
void OpenGLContext::reshapeWindow(int w, int h) {
windowWidth = w; // Set the window width
windowHeight = h; // Set the window height
}
And the last method for this file, is our renderScene method, which once again takes no arguments. The first thing we need to do, is set the size of our viewport to the size of our window, which is done with a call to glViewport. Next we will clear our color, depth and stencil buffers, before finally swapping our front and back buffers to draw to the screen.
/**
renderScene will contain all our rendering code.
The first thing we are going to do is set our viewport size to fill the entire window.
Next up we are going to clear our COLOR, DEPTH and STENCIL buffers to avoid overlapping
of renders.
Any of your other rendering code will go here.
Finally we are going to swap buffers.
*/
void OpenGLContext::renderScene(void) {
glViewport(0, 0, windowWidth, windowHeight); // Set the viewport size to fill the window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Clear required buffers
SwapBuffers(hdc); // Swap buffers so we can see our rendering
}
Now we have everything in place for our basic OpenGL 3.2 context, so let’s go and finish off this tutorial with the main.cpp file, which will handle all of our Window creation.
For the purpose of these tutorials, I am going to assume that we are working with single window applications and am not going to go for a more OO approach to window creation. So let’s get on to creating our window.
The first thing we need to do here is to include our common header files, main.h and opengl_3.h. Followed by declaring some variables. We are going to need an instance of an OpenGLContext object, a boolean value to say as to whether or not we are still running our application and a standard HINSTANCE variable for Win32. We are also going to give a method declaration for WndProc which is also required for a Windows application.
#include "main.h" #include "opengl_3.h" OpenGLContext openglContext; // Our OpenGL Context class bool running = true; // Whether or not the application is currently running HINSTANCE hInstance; // The HINSTANCE of this application LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Standard window callback
The rest of this tutorial, is made up of three methods. The first one here, is required in any Windows application and is responsible for our windows callbacks. We are going to look for calls to resize the window, and for calls to destroy the window. When we find a call to resize the window, we are going to call our openglContext.reshapeWindow method. Lets take a look at the definition for this method.
/**
WndProc is a standard method used in Win32 programming for handling Window messages. Here we
handle our window resizing and tell our OpenGLContext the new window size.
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
return DefWindowProc(hWnd, message, wParam, lParam);
}
In order to look for different window calls, we turn to the message parameter in the method definition. This is a UINT variable, which can be tied to any of the WM_* variables. We are going to need WM_SIZE for window resizing, and WM_DESTROY for calls to destroy the window. I am going to use a simple switch statement to check for these like so:
switch (message) {
case WM_SIZE: // If our window is resizing
{
}
case WM_DESTROY:
{
}
}
When it comes to our window resizing, we are going to need to update our OpenGL context so that it knows the new size of the window. To do this, we can pull out the width and height of the window using calls to LOWORD(lParam) and HIWORD(lParam), which makes things fairly simple for us. Our method now becomes:
/**
WndProc is a standard method used in Win32 programming for handling Window messages. Here we
handle our window resizing and tell our OpenGLContext the new window size.
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_SIZE: // If our window is resizing
{
openglContext.reshapeWindow(LOWORD(lParam), HIWORD(lParam)); // Send the new window size to our OpenGLContext
break;
}
case WM_DESTROY:
{
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
The only thing left to check for now, is when a window is destroyed. Then we need to send a call to PostQuitMessage so that we can exit our application.
/**
WndProc is a standard method used in Win32 programming for handling Window messages. Here we
handle our window resizing and tell our OpenGLContext the new window size.
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_SIZE: // If our window is resizing
{
openglContext.reshapeWindow(LOWORD(lParam), HIWORD(lParam)); // Send the new window size to our OpenGLContext
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
While we now have a method which will handle all of the call backs for our window, we need to actually go ahead and create a window. So I am going to create a new method called createWindow, which will return a boolean value which determines whether or not the window was created successfully. It’s parameters are an LPCWSTR for the title of the window, and two integers, one for the width and one for the height of the window.
bool createWindow(LPCWSTR title, int width, int height) {
return true;
}
Once we have our method skeleton in place, we need to setup some variables for our window. We will need a WNDCLASS, HWND and a DWORD. The WNDCLASS, which I will call windowClass sets all the basic information for a window, such as the icon, cursor, type of redraw, the instance of the owner application, etc. The HWND variable which I will just call hwnd, will hold the unique identifier for the window which we create, and finally the DWORD variable, which I am going to call dwExStyle and that just sets the type of window and that it has an edge.
I’m not going to go into detail about all the settings for this, as it’s not an OpenGL topic, its a Win32 topic, so here’s just the code. I have included a quick error check to make sure the windowClass variable can be registered:
bool createWindow(LPCWSTR title, int width, int height) {
WNDCLASS windowClass;
HWND hWnd;
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
hInstance = GetModuleHandle(NULL);
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
windowClass.lpfnWndProc = (WNDPROC) WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;
windowClass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = title;
if (!RegisterClass(&windowClass)) {
return false;
}
return true;
}
Next up, we are going to create our window using the above window class, and then we are going to create our OpenGL context on the window before showing the window and making a quick call to update it.
bool createWindow(LPCWSTR title, int width, int height) {
...
hWnd = CreateWindowEx(dwExStyle, title, title, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);
openglContext.create30Context(hWnd); // Create our OpenGL context on the given window we just created
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
return true;
}
This should now give us a window, with an OpenGL 3 context, and it should also be visible. The last thing we need to do is create a WinMain method which will be used to launch our application. Lets create an empty WinMain method, and then we will go ahead and fill this in. Note that I have left in the MSG variable called msg, just because we use it for our return value.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
MSG msg;
return (int) msg.wParam;
}
Before we fill this in, I am going to give a brief rundown of what to expect. The first few lines are going to be used to convert the title of the window from a char array to an LPCWSTR, they are just an annoying necessity because the CreateWindowEx method doesn’t support char arrays. The next two lines after this will create our window, and setup our OpenGL context, and we will end with a loop that will continue running for as long as the application does, and will be responsible for rendering and checking for window messages. Lets take a look at the first few lines, which convert our char array.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
MSG msg;
/**
The following 6 lines of code do conversion between char arrays and LPCWSTR variables
which are used in the Windows API.
*/
char *orig = "OpenGL 3 Project"; // Our windows title
size_t origsize = strlen(orig) + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t wcstring[newsize];
mbstowcs_s(&convertedChars, wcstring, origsize, orig, _TRUNCATE);
return (int) msg.wParam;
}
Now let’s make the calls to create our window and setup our OpenGL scene.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
...
createWindow(wcstring, 500, 500); // Create our OpenGL window
openglContext.setupScene(); // Setup our OpenGL scene
return (int) msg.wParam;
}
And finally we can set up our infinite loop. We will use the Win32 call to PeekMessage to check for calls to our window. If we get a WM_QUIT message we are going to set our running variable to false. Otherwise we are going to translate and dispatch the message, which is done in our callback. And if we don’t have any messages to process, we are going to call our openglContext.renderScene method before returning.
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
...
/**
This is our main loop, it continues for as long as running is true
*/
while (running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // If we have a message to process, process it
if (msg.message == WM_QUIT) {
running = false; // Set running to false if we have a message to quit
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else { // If we don't have a message to process
openglContext.renderScene(); // Render our scene (which also handles swapping of buffers)
}
}
return (int) msg.wParam;
}
And wow, if you made it this far, give yourself a well deserved pat on the back! It took me a few days to write this tutorial, which is an initial setup on top of which all our other tutorials will build, so once you get it down you are ready to move onto the next. I am also providing Visual Studio 2010 projects for these tutorials, and PDF versions for download. For those of you who have been waiting, I’m sorry it has taken so long, but this is now a 13 page, 4,500+ word introduction to setting up an OpenGL compatible context on a window.
Visual Studio 2010 Project
PDF version
If you have any questions you can comment below or email me at swiftless@gmail.com
Jun 10
24
It’s been a little while, but I have finally pumped out another tutorial, this time on bump mapping using GLSL.
Go take a look and let me know what you think.
Cheers,
Swiftless
Jun 10
24
Bump mapping is essential in todays computer games, and computer graphics in general. Would you like to know the best thing about it? It is extremely simple to implement. Bump mapping works by taking an image which stores surface normals on a per-pixel basis. This means that we can apply standard lighting techniques that require a normal value on a per-pixel basis instead of a per-vertex or per-surface basis. This gives our applications a greatly added sense of realism.
How does bump mapping work in OpenGL with GLSL though? GLSL texturing works on a per-texel basis, where a texel can be thought of as a scaled pixel, either larger or smaller, and we get access to a texel at any fragment on an objects surface when we are texturing in GLSL. We already covered texturing in GLSL in a previous tutorial, so I won’t go over it again.
When you look at a normal map, it is generally a blue and purple color. To get your head around normal mapping, all you need to understand is that each pixel in this image file, can be mapped to a normal value. Once you can understand that, normal mapping is extremely straight forward.
Don’t get this tutorial on bump mapping confused with tangent-space bump mapping though. There aren’t many tutorials around that explain the different, but regular bump mapping only cares about the normal maps relation to lights. Tangent space bump mapping takes it a step further and takes into account the objects surfaces in relation-to the light as well as the normal maps relation.
Enough theory, how can we get it into OpenGL and GLSL. The first thing we will want to do is load in our normal map, and apply it to our object in OpenGL. This requires multi-texturing in OpenGL, which I don’t believe I have covered in GLSL, so I will include that in this tutorial. This code is going to be based on the GLSL texturing tutorial, but you may notice some additions to make it cross-platform between OSX and Windows correctly.
We are only going to need one new variable for the main.cpp file which will be used to store our normal map texture. This is stored and used just like a normal texture. I am going to call this normal_texture, and define it with our texture variable at the top of our file.
GLuint normal_texture; // Our normal map texture
Then go down to your init method, and remove the current line that creates a texture, and replace it with the following two lines, which will create both of our new
texture = LoadTexture("colour_map.raw", 256, 256);
normal_texture = LoadTexture("normal_map.raw", 256, 256);
Inside of our init method you may also notice the following code, this is for cross-platform compatibility and simply checks if we are on OSX or not. If not, we initialize glew.
#if ( (defined(__MACH__)) && (defined(__APPLE__)) ) #else glewInit(); #endif
The rest of our code for the main source code file, goes into our cube method, which I have now modified to draw a square instead.
You should already have code to set the first texture, and we are going to adjust this slightly by adding the following line after we set our active texture:
glEnable(GL_TEXTURE_2D);
Our code to set up our first texture should now look like:
glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); int texture_location = glGetUniformLocation(shader.id(), "color_texture"); glUniform1i(texture_location, 0); glBindTexture(GL_TEXTURE_2D, texture);
Now we need to set up our second texture. This is done by activating the next texture, GL_TEXTURE1 and modifying the other calls to point to our normal map texture. When we do this, we need to make sure that our glUniform1i for our normal map location in the shader, has the value 1. When multi-texturing in GLSL, each texture used will have an incremented value in the shader. Our final code will look like:
glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); int normal_location = glGetUniformLocation(shader.id(), "normal_texture"); glUniform1i(normal_location, 1); glBindTexture(GL_TEXTURE_2D, normal_texture);
I won’t explain the code for drawing the square, as I have done this in previous tutorials, so I will jump to after our square. We will need to do some cleaning up, as we don’t want our texture states to mess up when we render other objects later on. All I am doing to clean up, is setting the active texture, binding it to nothing, and then disabling texturing on this texture. Note that I am doing this in reverse to how I have set the textures. You can do this in any order, but I like this as I find it easier to follow (count up on initialization, and then count down on de-initialization).
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D);
Our vertex shader requires absolutely no extra calls, so just use the one provided in the previous texturing tutorial.
Shader.frag is going to contain all of our bump mapping calculations. To get this going, we need to add a new uniform variable, which will load in our normal mapping texture:
uniform sampler2D normal_texture;
Now that we have access to our normal map, lets load it in. For a normal, we only require a 3 value vector, or vec3 in GLSL, and we are going to normalize this to make sure the final normal vector is of unit length. In other words, it has a length of 1.0. But in this line take note of the (* 2.0 – 1.0). When we read in a texture value, we get a number between 0.0 and 1.0 as color cannot have a negative value. This then multiplies the value by 2.0, giving us values between 0.0 and 2.0, and then subtracts 1.0 so we get our final values in the range of -1.0 to 1.0. We have to do this manipulation as a normal vector is 3D and can be pointing in any direction. It’s a nifty little trick which allows us to convert a normal map texture file to a normal map.
// Extract the normal from the normal map vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);
Since we now have the normal value for the current pixel, I am going to set up a quick light, located at the top right of the scene, and also a little towards us. Usually you would base this on actual lights in your scene, and to do so, take a look at my GLSL lighting tutorial.
// Determine where the light is positioned (this can be set however you like) vec3 light_pos = normalize(vec3(1.0, 1.0, 1.5));
Finally we can get to some familiar code. We are going to calculate the diffuse value for the current pixel, based on our normal extracted from the normal map. Then we are going to multiply the diffuse value against our color texture, and assign the output to our gl_FragColor variable.
// Calculate the lighting diffuse value float diffuse = max(dot(normal, light_pos), 0.0); vec3 color = diffuse * texture2D(color_texture, gl_TexCoord[0].st).rgb; // Set the output color of our current pixel gl_FragColor = vec4(color, 1.0);
And now you should have a perfectly working bump mapping shader. If you have any questions, feel free to email me at swiftless@gmail.com or leave a comment below.
Colour Map Texture
Normal Map Texture
#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
#include <stdlib.h>
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <OpenGL/glext.h>
#else
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <GL/glext.h>
#endif
#include "shader.h"
Shader shader;
GLuint texture;
GLuint normal_texture; // Our normal map texture
GLuint LoadTexture( const char * filename, int width, int height )
{
GLuint texture;
unsigned char * data;
FILE * file;
//The following code will read in our RAW file
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
data = (unsigned char *)malloc( width * height * 3 );
fread( data, width * height * 3, 1, file );
fclose( file );
glGenTextures( 1, &texture ); //generate the texture with the loaded data
glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture to it’s array
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); //set texture environment parameters
//And if you go and use extensions, you can use Anisotropic filtering textures which are of an
//even better quality, but this will do for now.
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//Here we are setting the parameter to repeat the texture instead of clamping the texture
//to the edge of our shape.
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
//Generate the texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
free( data ); //free the texture
return texture; //return whether it was successfull
}
void FreeTexture( GLuint texture )
{
glDeleteTextures( 1, &texture );
}
void init(void) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
#else
glewInit();
#endif
shader.init("shader.vert", "shader.frag");
texture = LoadTexture("colour_map.raw", 256, 256);
normal_texture = LoadTexture("normal_map.raw", 256, 256);
}
void cube (void) {
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
int texture_location = glGetUniformLocation(shader.id(), "color_texture");
glUniform1i(texture_location, 0);
glBindTexture(GL_TEXTURE_2D, texture);
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
int normal_location = glGetUniformLocation(shader.id(), "normal_texture");
glUniform1i(normal_location, 1);
glBindTexture(GL_TEXTURE_2D, normal_texture);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
void display (void) {
glClearColor (0.0,0.0,0.0,1.0);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt (0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
shader.bind();
cube();
shader.unbind();
glutSwapBuffers();
}
void reshape (int w, int h) {
glViewport (0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
glMatrixMode (GL_MODELVIEW);
}
int main (int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); //set up the double buffering
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("A basic OpenGL Window");
init();
glutDisplayFunc(display);
glutIdleFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
// Set the position of the current vertex
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
uniform sampler2D color_texture;
uniform sampler2D normal_texture;
void main() {
// Extract the normal from the normal map
vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);
// Determine where the light is positioned (this can be set however you like)
vec3 light_pos = normalize(vec3(1.0, 1.0, 1.5));
// Calculate the lighting diffuse value
float diffuse = max(dot(normal, light_pos), 0.0);
vec3 color = diffuse * texture2D(color_texture, gl_TexCoord[0].st).rgb;
// Set the output color of our current pixel
gl_FragColor = vec4(color, 1.0);
}