Thursday, April 16, 2026

Synopsis


The following discusses different topics in Modern OpenGL Tutorials.
______________________________________________________________________________________________________________________________________________________________________
Introduction
The following discusses different topics in Modern OpenGL Tutorials.OpenGL is a graphics library that can be used to render interactive 2D and 3D graphics applications.OpenGL has wide range of applications.This tutorials attempts to teach basic to advanced concepts one at a time. The goal is to create an advanced , multifunctional library from scratch. The tutorials will be a set of C++ header (.h) files.This tutorial is primarily targeted for windows OS. Visual Studio development environment is used for writing, compiling and debugging the code.
______________________________________________________________________________________________________________________________________________________________________

Implementation - Initializing OpenGL Context
OpenGL is a drawing library that requires a context to draw upon. 
Creating OpenGL context in windows OS is not a trivial task. It's mainly because opengl32.lib supplied by windows OS supports OpenGL specification 1.1. 
The display card providers such as NVidia or Intel or AMD actually implement the latest and greatest OpenGL specification, including additional functionality and supply it as OpenGL Installable Client Driver or ICD.
______________________________________________________________________________________________________________________________________________________________________
Lesson01 : Initializing OpenGL Context
This lesson discusses  implementing the basic operations of creating a hosting window, Initializing it with OpenGL context, rendering it and handle mouse/keyboard inputs.
______________________________________________________________________________________________________________________________________________________________________
Primer: Vector
Vectors are widely used  in 3D calculations. A 3D vector is represented as [Vx, Vy,  Vz] where  Vx, Vy and Vz represent numbers in 3D cartesian space.
______________________________________________________________________________________________________________________________________________________________________
Primer: Matrices and Affine transformations
OpenGL uses matrix operations for a lot of purposes. For example, translations, scaling, rotations. Also computing of projection matrices, view matrices etc. The following discusses it in detail.
______________________________________________________________________________________________________________________________________________________________________
Implementation - BaseCamera and Camera Data
Camera is used for projections and animations. It holds camera data which is used for storing mouse and keyboard input information. Further, it stores computed transformation information such as pitch, yaw and roll angles, translation, scaleby as well as Model, View and Projection matrix information of the 3D object. 
______________________________________________________________________________________________________________________________________________________________________
Primer: Graphics Pipeline
A scene consists of  a set of  3D objects. Transformations such as translation, scaling and rotation as a result of Camera movement, mouse and keyboard input brings them into life. 
For example  the following diagram shows a multi color cube with  50 degrees pitch and 20 degrees yaw.____________________________________________________________________________________________________________________________________________________________________
Implementation:Vertex Buffer
In this post we shall deep dive and understand mechanics behind sending vertex data to the GPU. Vertex data is obtained from mesh objects such as cubemesh which will be discussed later. The vertex data consists of Position, Color, Normal, and Texture Coordinate of each vertex. They are basically stored into VBO, EBO buffers. Later they are packaged into VAO objects.
______________________________________________________________________________________________________________________________________________________________________
In this post we shall deep dive and understand implementing a mesh.
______________________________________________________________________________________________________________________________________________________________________
The following provide an overview of shader programs and their implementation in this tutorial.
______________________________________________________________________________________________________________________________________________________________________
Implementation: Pipeline
In this post we shall deep dive and understand mechanics behind rendering 3D objects on the screen.
So far we discussed  FrameBuffers,Vertex Array Object containing VBO and EBO buffers and  Program Object containing Vertex Shader and Fragment Shaders.$$
______________________________________________________________________________________________________________________________________________________________________
In this post we shall deep dive and understand implementing a mesh and geometric object for cube shape. We will try to draw a single colored cube as shown above and rotate it along the three axes. The cube looks elongated because aspect ratio is not applied. 
______________________________________________________________________________________________________________________________________________________________________
In the previous posts the graphics pipeline and vertex processing were explained. Also, the implementation of the CubeMesh and SingleColoredCube were covered. 
In this post we will try to draw a single colored cube as shown above. The cube looks elongated because aspect ratio is not applied.
______________________________________________________________________________________________________________________________________________________________________
 In this post we  will discuss how to draw an IndexedCube using VBOs and EBO with interpolated colors. It looks as shown above. It looks elongated because aspect ratio is not applied.
______________________________________________________________________________________________________________________________________________________________________
In the previous posts the graphics pipeline and vertex processing were explained. Also, the implementation of the CubeMesh and IndexedCube were covered. 
In this post we will try to draw a cube with interpolated colors as shown above. The cube looks elongated because aspect ratio is not applied.
______________________________________________________________________________________________________________________________________________________________________















Wednesday, August 3, 2022

Lesson08: Start Camera and Roll

 In this post understand cameras space and Perspective and Orthographic  projections interactively.


The cube can be rotated using x, y and z keys. The The camera space can be changed by checking LookAt checkbox and supplying  varying input for Position, Target and Up vectors. 
Similarly, Perspective Projection can be changed by checking Perspective checkbox and supplying  varying input for FOV, Near  Plane and Far Plane.  The FOV can be changed by typing page up and down keys or mouse wheel.
Orthographic Projection can be changed by checking Orthographic checkbox and supplying  varying input for X minmax, y minmax and Z minmax values. 

Monday, July 25, 2022

Primer: Understanding View space, Perspective and Orthographic Projections

So far our camera was in a fixed position and transformations happened in object space.  In this post we will  look at the larger picture.

As shown in the diagram below, a 3D object in a scene starts off from object space. Model transformation places it in the world space.  Based on the camera position the entire world is transformed into Camera space or view space. This is called view transform. This is done using viewing matrix. It is later moved to screen space using Projection matrix.


So far in our examples, camera is located at the origin and view and projection matrixes are not computed; instead identity matrix is used.  Next we will try to move around the camera and understand different type of projections.

Model Matrix
Model Matrix is responsible for moving 3D objects  from object space to world space. The affine transformation discussed in Lesson 06 are applied.

View Matrix
When the hypothetical camera position is changed, the whole world is transformed around it using view matrix. The view matrix is computed as below from glm as below.
mat4 lookAt(vec3 const& eye, vec3 const& center, vec3 const& up);
The internals is as described below (courtesy : learnopengl.com).
 

1. Camera position
The camera position or eye is a vector in world space that points to the camera's position. 

2. Camera direction vector
The  Camera direction vector is the direction at which the camera is pointing at. This is computed from the scene's origin or center in this case it's  (0,0,0). Subtracting the camera position vector from the scene's origin vector thus results in the direction vector. This must always point to +Z axis by RHS convention.

3. World up vector
The up vector (0,1,0) points to worlds Y axis. This may not be always the case.

4. Right axis vector
The right axis vector represents the positive x-axis of the camera space. This is obtained by doing a cross product on the up vector and the camera direction vector from step 2. 

5. Up axis vector
The Up axis vector points to camera's positive y-axis. It's computed by the cross product of the right and direction vector.

Output:

As mentioned earlier,  the World up vector need not be always (0,1,0).
For example,  if  the camera is moved on top of the cube say (0, 3, 0), we will be looking at the  top of the cube.  The eye is (0,3,0), center is (0,0.0) and up is (1, 0, 0) not (0,1,0).

Projections
The final matrix is projection matrix. There are two types of projections
Orthographic and Perspective. The traits are as below.


The following shows another view with the view frustum. A view frustum is a rectangular box that confines the image rendered to be shown on the screen. 


The volume or the frustum is defined by the left, right, top, bottom, near and far plane values. 

The perspective projection is computed in glm as below
perspective(float fovy, float aspect, float zNear, float zFar);
In Perspective projection, the frustum appears as a truncated pyramid. 
fovy is the  Field of View (FOV) or zoom factor 
aspect is the aspect ratio of the viewport. i.e., width/height.
ZNear is the distance from the camera to the near plane
ZFar is the distance from the camera to the far plane
Example:
perspective(45.0, 1.84, 1.0, 100.0)
The screenshot below shows the perspective projection of the colored cube rotated 20 degree pitch and 20 degree yaw.

The ortho projection is computed in glm as below
ortho(xmin, xmax, ymin, ymax, zmin, zmax);
xmin, xmax are left and right of the view frustum
ymin, ymax are top and bottom of the view frustum
ZMin is the distance from the camera to the near plane
ZMax is the distance from the camera to the far plane
In orthogonal view it appears as a cube. 
Example: ortho(-1.84, 1.84, -1.0, 1.0, 1.0, 100.0); The 1.84 is the aspect ratio.
The screenshot below shows the orthographic  projection of the colored cube rotated 20 degree pitch and 20 degree yaw.

Unproject
Sometimes it's useful to x,y,z coordinates in the object space based on the mouse cursor position. UnProject() accomplishes this.  
The X,Y coordinates from the mouse cursor position and Z value from the depth buffer is used to compute the window coordinates. Later this is passed to glm unProject() along with Model, View and Projection Matrix, to get the co ordinates in world space.
For example, 

In the above, the red dot in the cube represents mouse cursor position  x = 353 y = 168
from depth buffer Z = 0.79
Using  Model, View, and Perspective Projection matrices, the world space coordinate is calculated as
0.22, 0.12, 0.43.


Sunday, July 24, 2022

Lesson09: Understanding Affine Transformation interactively.

Overview 
In this post we shall understand affine transformations interactively the three kinds of affine 
transformations: Scale, Translate and Rotation on X, Y and  Z axes as discussed in the previous post.
The rotation has been already demonstrated in the earlier examples. In this post we will deep dive into Translation and Scaling.

Details
The GUI has two windows - Console Window with OpenGL context. This renders a Cube with a texture having each of the 6 faces labeled as below.


Input Dialog
Provides an user interface to experiment interactively - scaling, translation and rotation.  First input is provided in the input text boxes. It's submitted for rendering the cube when Apply button is clicked. The cumulative value is updated in the caption. Reset button resets values. Both + and -ve values can be submitted. Note:- The values are applied when the associated checkboxes are checked.
The cube also be rotated using X,Y, Z keys and using Mouse inputs.


Camera 
As we are still passing Identity matrix for view and projection matrices, the camera is sitting at the origin (0,0,0) looking down on -Z axis. In this scenario, the back face is visible with x=0, y=0, z=-0.5.

Scale
First rotate cube by 30 degree pitch and 30 degree yaw. Enter different values for scaling in x, y and z axes.

Translate
First rotate cube by 30 degree pitch and 30 degree yaw. Enter different values for translation in x, y and z axes.

Rotate
As noted earlier, the rotation happens in counter clockwise direction along the axis. The diagram below maps faces to numbers.
Rotation around X axis
Rotate the cube by 90 degrees increments. The sequences of faces that should display are as below.


Rotation around Y axis
Rotate the cube by 90 degrees increments. The sequences of faces that should display are as below.

Rotation around Z axis
Rotate the cube by 90 degrees increments. The sequences of faces that should display are as below.
Order of Transformation
Following order is used:
  1. Scale
  2. Rotate by Z,X,Y
  3. Translate
Note in code, the matrix would be computed in reverse order as below.
                M = mat4(1);
		M = translate(M, translateby);
		M = rotate(M, radians((float)(yaw)), vec3(0.0f, 1.0f, 0.0f));
		M = rotate(M, radians((float)(pitch)), vec3(1.0f, 0.0f, 0.0f));
		M = rotate(M, radians((float)(roll)), vec3(0.0f, 0.0f, 1.0f));
		M = scale(M, scaleby);



Friday, July 22, 2022

Lesson07: Drawing Text and Images interactively.

Overview 
As discussed in the  previous article, we saw how Text can be drawn in multiple fonts, sizes and colors and images can be placed side by side.
Also, the implementation of the TextMesh , TextureUtil  and TextImageSketcher were covered. 
In this post we will try to draw  text and image interactively.

Details
The purpose of  Lesson07 is to draw text and image and work with them interactively by changing fonts, colors, resize height and width.

System  class diagram
The scene class does use camera. It has an pointer to TextImageSketcher called ptextutl.

Implementation

Scene
The functionality is implemented in the Scene class derived from BaseScene class.



The three methods Init, DrawScene and Cleanup are overridden as below:
The Init method calls 
  1. BaseScene::Init to create hosting window and OpenGL Context. 
  2. It creates an input dialog to work with text and images interactively.
  3. It calls init function with texture id GL_TEXTURE0 + 4, height and width of the bitmap. on the TextImageSketcher object to populate VBO/EBO buffers and bind them. Also create texture and bitmap. 
  4. It also compiles and links shader programs.
The DrawScene method draws a string and image. 

The Cleanup method releases the resources related to VBO/EBO, Texture related resources and shader programs.

The message handler for IDOK is called when Apply button is pressed after updates are made in the input dialog. It refreshes the display.

The message handler for IDCANCEL is called when in Cancel button is pressed the input dialog. It calls 
OnCloseWindow to destroy window.

Finally the WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown when the window is closed or Escape key is pressed. 

Application
Scene class is hosted main.cpp. which creates the scene object and displays it. Message pump is added to process windows messages.

Input dialog
The input dialog as shown below can be used to change settings and understand drawing text and images.

Output
The output looks as shown in the top. 



    Thursday, July 21, 2022

    Implementation: Drawing Text and Images

    Overview 
    Modern OpenGL does not support drawing text or Images so it needs to be handled independently. There many popular libraries available such as freetype to render text in a scene.

    Details
    In this post we shall discuss drawing text using GDI+ APIs. The plan is to  draw text and Images onto a memory based bitmap which is later loaded as a texture.
    A new mesh class  TextMesh is defined for loading bitmap as texture. It's used by TextImageSketcher object for loading vertex data and texture data to the vertex and fragment shaders.
    TextImageSketcher uses GDIPlus to create ARGB in memory bitmap containing text to be drawn. The size of the bitmap should be multiples of 128. i.e.,  128, 256, 512 etc.
    First bitmap is filled with black color and then Text is drawn using the font and color. Note the text color needs to be non black. Font can be changed by passing LOGFONT structure, Also text color can be changed by passing COLLOREF of the color.
    Fragment shader is modified for blending fragments with the background.
    The client needs to first Startup() GDIPlus in the beginning of the application and Shutdown()  at the end.
    Instantiate TextImageSketcher object by Init() method supply unique texture id and size.
    DrawText() can be called to draw text and supply font details and text color. 
    DrawImage() can be called to render images.
    The TextImageSketcher object can be translated, rotated etc.  just like any 3D object.

    System  class diagram
    TextImageSketcher derives from BaseGeometry class. It overrides mesh with an instance of TextMesh to generate 2D geometry. It also uses TextureUtil to render text and images into the texture.


    TextMesh
    TextMesh derives from IGeometryMesh class. It provides 2D geometry for drawing text and images. It supplies vertices and texture coordinates.





    Members
    NameDescription
    texturemapProvides texture map.
    verticesProvides 2D vertices.

    Methods
    NameDescription
    InitThis method generates vertex data for the GPU to render the geometrical shape. This method is called if the data is sent in  non indexed mode.
    It generates position and vertex data.

    TextureUtil
    TextureUtil implements handling textures from image files and bitmaps. It's used by multiple purposes such as rendering 3D objects or text.
    Members
    NameDescription
    textureIDUnique texture handle returned after creating the texture.
    texunitUnique texture unit. It's value should be one of the 80 values supported by the system.

    Methods
    NameDescription
    CleanupReleases resources.
    InitAssigns unique texture unit to texunit.
    LoadTextTextureUsed to render text. It generates texture from GDI bitmap.
    LoadTextTextureImageUsed to render text. It wraps 2D image from GDI bitmap.
    LoadTextureLoads texture from an image file.
    MakeActiveMakes the texture active.

    TextImageSketcher
    TextImageSketcher derives from BaseGeometry class. It renders text and images. The utility class TextureUtil is used for loading text and images onto the texture.



    Members
    NameDescription
    wd
    ht
    Specifies the dimensions of the bitmap. 
    texutlAn instance of TextureUtil. It helps with loading text and images onto the texture.

    Methods
    NameDescription
    InitThis method overrides the base class Init method to load texture.  Generates the vertices data consisting of surface normals in non indexed mode and later sets them up in VBO buffer.  It also prepares the bitmap for rendering text and images on it. It also initializes the texutl object with texture id for loading textures.
    UpdateUniformsThis method overrides the base class UpdateUniforms method. First it updates  "cameraposition" uniform. It also updates "tex" uniform to pass texture object.
    vertexShaderSourceThis method overrides the base class vertexShaderSource method. It returns the vertex shader code to render the texture object.
    fragmentShaderSourceThis method overrides the base class fragmentShaderSource method. It returns the fragment shader code to render the  texture object.
    CleanupThis method overrides the base class Cleanup method. It releases the resources used by the host object.
    Init()
    Shutdown()
    These methods are called by the client during start up and shutdown to initialize GDI Plus environment.
    ClearCanvasThis method clears the bitmap for fresh renderings of text and images.
    DrawtextThis method renders input text string based on the input such as font, color and string formats.
    DrawimageThis method reenders the image in the file name and resizesto fir based on the clipping width and height inputs
    DrawCanvasThis method loads the bitmap onto the texture.

    Output







    Saturday, July 16, 2022

    Lesson06: Lighting a Textured Cube interactively

    Overview 
    In the previous posts drawing a textured cube were explained. Also, the implementation of the LightingUtil and LightedTexCube were covered. 
    In this post we will try to draw a textured cube with lighting interactively. The cube looks as shown above. The cube looks elongated because aspect ratio is not applied.

    Details
    The purpose of  Lesson06 is to create a lighted textured cube and work with it interactively with different types of light to understand nuances of lighting.

    System  class diagram
    The scene class overrides camera with an instance of ThreeDCamera. It has an instance of LightedTexCube called cube derived from TexturedCube class.

    Implementation

    Scene
    The functionality is implemented in the Scene class derived from BaseScene class.

    The three methods InitDrawScene and Cleanup are overridden as below:
    The Init method calls 
    1. BaseScene::Init to create hosting window and OpenGL Context. 
    2. It also attaches camera to a ThreeDCamera class object. This camera  processes keyboard and mouse inputs as explained earlier.
    3. It creates an input dialog to work with lighting settings interactively.
    4. It calls init function on the LightedTexCube object to populate VBO/EBO buffers and bind them. 
    5. Load textures from the image file
    6. It also compiles and links shader programs.
    The DrawScene method draws the cube with texture wrapped and rotates as per keyboard or mouse inputs. The lighting settings are taken from the light instance of LightingUtil of the cube object.

    The Cleanup method releases the resources related to VBO/EBO, Texture related resources and shader programs.

    The message handler for IDOK is called when Apply button is pressed after updates are made in the input dialog. It refreshes the display.

    The message handler for IDCANCEL is called when in Cancel button is pressed the input dialog. It calls 
    OnCloseWindow to destroy window.

    Finally the WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown when the window is closed or Escape key is pressed. 

    #include "Scene\BaseScene.h"
    #include "Scene\Camera\ThreeDCamera.h"
    #include "Geometry\LightingUtil.h"
    #include "Geometry\Cube\LightedTexCube.h"
    #include "InputDlg.h"
    
    DWORD WINAPI ThreadFunction(LPVOID lpParam);
    
    class Scene:public BaseScene
    {
    public:
    	//message handler
    	BEGIN_MSG_MAP(Scene0)
    		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
    		COMMAND_ID_HANDLER(IDOK, OnDoRefresh)
    		COMMAND_ID_HANDLER(IDCANCEL, OnClose)
    		CHAIN_MSG_MAP(BaseScene)
    	END_MSG_MAP()
    
    
    	//override
    	int Init(RECT rect, WCHAR *windowname)
    	{
    		//create host window and context
    		BaseScene::Init(rect, windowname);
    
    		CreateThread(NULL, 0, ThreadFunction, this, 0, NULL);
    
    		//attach mouse keyboard input handler
    		camera = new ThreeDCamera(m_hWnd);
    		::Sleep(500);
    		updatecube();
    
    		return 0;
    	}
    
    	void updatecube()
    	{
    		if (pcube != nullptr)
    		{
    			auto src = pcube->light.lightsrc.src;
    			pdlg->update(&pcube->light);
    			if (src != pcube->light.lightsrc.src)
    			{
    				pcube->Cleanup();
    				delete pcube;
    				pcube = nullptr;
    			}
    		}
    
    		if (pcube == nullptr)
    		{
    			pcube = new LightedTexCube();
    			pdlg->update(&pcube->light);
    			pcube->Init(0, R"(..\resources\textures\rocks2.bmp)");
    		}
    	}
    
    	//release resources
    	void Cleanup()
    	{
    		pcube->Cleanup();
    		delete camera;
    	}
    
    
    	LRESULT OnDoRefresh(WORD wParam, WORD wParam2, HWND lParam, BOOL& bHandled)
    	{
    		bHandled = TRUE;
    		updatecube();
    		Invalidate();
    		return 0;
    	}
    
    	LRESULT OnClose(WORD wParam, WORD wParam2, HWND lParam, BOOL& bHandled)
    	{
    		return OnCloseWindow(0, 0, (LPARAM)nullptr, bHandled);
    	}
    
    
    	//draw the scene
    	void DrawScene()
    	{
    		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    		SceneCamera()->augumentModelMatrix(*pcube);
    		wstring wcap = L"Lighting Settings - " + pcube->getangless();
    		SetWindowTextW(wcap.c_str());
    		pcube->Draw();
    		SceneCamera()->MM.Reset();
    	}
    
    	//Close the window
    	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    	{
    		bHandled = TRUE;
    		DestroyWindow();
    		PostQuitMessage(0);
    		return 0;
    	}
    
    	inline ThreeDCamera*  SceneCamera()
    	{
    		static auto ret = dynamic_cast<ThreeDCamera*>(camera);
    		return ret;
    	}
    
    	void CreateInputDlg()
    	{
    		pdlg = new InputDlg();
    		pdlg->Create(m_hWnd);
    		pdlg->ShowWindow(SW_SHOW);
    		Invalidate();
    	}
    
    private:
    	LightedTexCube *pcube = nullptr;
    	int IDM_INPUTDLG = 1001;
    	InputDlg* pdlg;
    
    };
    
    DWORD WINAPI ThreadFunction(LPVOID lpParam)
    {
    	Scene* pscene = (Scene*)lpParam;
    	pscene->CreateInputDlg();
    
    	MSG msg;
    	while (GetMessage(&msg, 0, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessageA(&msg);
    	}
    
    	return 0;
    }

    Application
    Scene class is hosted main.cpp. which creates the scene object and displays it. Message pump is added to process windows messages.

    #include "Scene.h"
    
    Scene scene;
    
    int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd_line, int show)
    {
    	scene.Init(RECT{ 100, 100, 780, 500 }, L"Lesson06: Lighting");
    	scene.ShowWindow(show);
    
    	MSG msg;
    	while (GetMessage(&msg, 0, 0, 0)) 
    	{
    		TranslateMessage(&msg);
    		DispatchMessageA(&msg);
    	}
    
    	return 0;
    }

    Input dialog
    The input dialog as shown below can be used to change settings and understand lighting.

    Output
    The output looks as shown in the top.