Showing posts with label Drawing Essentials. Show all posts
Showing posts with label Drawing Essentials. Show all posts

Friday, July 15, 2022

Lesson05: Multi-Colored Cube


In this post we will try to draw a multi colored cube as shown above. The cube looks elongated because aspect ratio is not applied.

Implementation

MultiColorCube class is implemented as below to draw the single colored cube. 
 It's located in VOGLLib\Geometry\Cube\MultiColorCube.h.
       
//Implements multi colored cube 
class MultiColoredCube :public BaseGeometry
{
public:
	//OpenGL initialization
	void Init()
	{
		//initialize opengl context
		BaseGeometry::Init(new CubeMesh());
		//generate vbo data
		kount = mesh->GenerateVerticesData(false, VAOUtil::POS | VAOUtil::CLR, vaoutl);
		//setup vertices
		vaoutl.SetupVBO(0, VAOUtil::POS);
		vaoutl.SetupVBO(1, VAOUtil::CLR);
		vaoutl.unbindVAO();
	}

	//override
	virtual string vertexShaderSource()
	{
		return R"(
		#version 330 core
		layout (location = 0) in vec3 vVertex;
		layout (location = 1) in vec3 vColor;
		out vec3 fcolor;

		uniform mat4 transform;

		void main()
		{
		   gl_Position =  transform * vec4(vVertex, 1.0);
		   fcolor = vColor;
		};
		)";
	}

	//override
	virtual string fragmentShaderSource()
	{
		return R"(
		#version 330 core
		in vec3 fcolor;
		out vec4 FragColor;
		void main()
		{
		   FragColor = vec4(fcolor,1.0);
		};
		)";
	}
};
 
Lesson05 Project

This purpose of this Lesson05 project is to create a Window initialized with OpenGL context and draw a MultiColorCube with 6 colors one for each face.
As discussed in introduction create tutorial lesson project Lesson02 under Lessons folder.
Add a header file Scene.h as shown below. The class itself is self explanatory. 
The WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown. 
The Init method calls BaseScene's Init to create hosting window and OpenGL Context. It also attaches BaseCameraInputHandler to rotate the cube either by keyboard or mouse inputs.
The DrawScene method draws the cube with multiple colors and rotates as per keyboard or mouse inputs.
The Cleanup method release the resources.
The Scene class is implemented as below. It's located in Lessons\Lesson05\Scene.h file.
       
#include "Scene\BaseScene.h"
#include "Geometry\Cube\MultiColoredCube.h"

class Scene:public BaseScene
{
public:
	//message handler
	BEGIN_MSG_MAP(Scene0)
		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
		CHAIN_MSG_MAP(BaseScene)
	END_MSG_MAP()

	//override
	int Init(RECT rect, WCHAR *windowname)
	{
		//create host window and context
		BaseScene::Init(rect, windowname);
		//attach mouse keyboard input handler
		mskbd = new BaseCameraInputHandler(m_hWnd);

		//Create multicolor cube
		cube.Init();
		
		return 0;
	}

	//release resources
	void Cleanup()
	{
		cube.Cleanup();
		delete mskbd;
		
	}
	
	//draw the scene
	void DrawScene()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//get model view projection matrix. 
		//only model is modified
		//view and projection will be identity matrix
		mskbd->fetchCameraData(&cube.camera);
		cube.Draw();

	}

	//Close the window
	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = TRUE;
		DestroyWindow();
		PostQuitMessage(0);
		return 0;
	}


private:
	MultiColoredCube cube;
}; 
 
The main.cpp is located in Lessons\Lesson05\main.cpp file. It creates the scene object and calls its init method.
       
#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"Modern OpenGL-Tutorial - Lesson05");
	scene.ShowWindow(show);

	MSG msg;
	while (GetMessage(&msg, 0, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return 0;
}     
 
The output looks as shown in the top. The cube is rotated by 20 degrees pitch and 20 degrees yaw. The camera position at the origin looking down on -Z axis or the back face of the cube.
To rotate the cube X,Y and Z keys can be used. They rotate respectively pitch, yaw and roll the cube  by 10 degrees.
In the next post we shall learn essential 3d Math.

Thursday, July 14, 2022

Lesson04:Textured Cube

 A Texture is 2D image that can be wrapped around a 3D object like a gift wrapper. For example, resources\textures\bricks2.jpg is a 2D Texture file. 

Unlike cartesian coordinates, Texture follow UV  System as shown below.

TextureUtil Class is used for loading textures. It uses SOIL2 library discussed earlier to implement it.
It's located in VOGLLib\Geometry\TextureUtil.h.

Loading Texture
OpenGL supports up to 80 Textures and each with its own identifier. Loading textures is implemented in LoadTexture method in TextureUtil class.
SOIL_load_OGL_texture(filename.c_str(), SOIL_LOAD_AUTO, 0, SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y);
       
MIPMAP
Mipmapping is a technique where a high-resolution texture is downscaled and filtered so that each subsequent mip level is a quarter of the area of the previous level. This means that the texture and all of its generated mips requires no more than 1.5 times the original texture size. An example is shown below.


Inversion of Y Axis
After loading the image needs to be inverted since the V axis of the texture and Y axis of OpenGL  have opposite polarity.

Wrapping
As shown below, determines how the texture is wrapped.

Texture Filtering
Texture filtering is a method that is used to improve the texture quality in a scene. Without texture filtering, artifacts like aliasing generally look worse. Texture filtering makes textures look better and less blocky.

Applying Texture
As discussed previously, Texture coordinates are passed during rendering. The following mapping in relation to the UxV axes is used as texture coordinates for the vertices consecutively.
       
       //first triangle
		{  0.0,   1.0,   0.0 },
		{ -1.0,   0.0,   0.0 },
		{  0.0,  -1.0,   0.0 },
       //second triangle
		{  1.0,   0.0,   0.0 },
		{  0.0,   0.0,  -1.0 },
		{  0.0,   0.0,   1.0 },
        
Implementation

TexturedCube class is implemented as below to draw the textured cube. 
 It's located in VOGLLib\Geometry\Cube\TexturedCube .h.
       
#pragma once
#include "..\BaseGeometry.h"
#include "..\TextureUtil.h"
#include "CubeMesh.h"
//implements texturedcube
class TexturedCube:public BaseGeometry
{
public:
	//initialize
	void Init(GLushort	texunit, const string& filename)
	{
		//create mesh and the window
		BaseGeometry::Init(new CubeMesh());
		//generate VBOs for position and Texture coordinates
		kount = mesh->GenerateVerticesData(false, VAOUtil::POS | VAOUtil::TEX, vaoutl);
		//setup vertices
		vaoutl.SetupVBO(0, VAOUtil::POS);
		vaoutl.SetupVBO(1, VAOUtil::TEX);
		vaoutl.unbindVAO();
		this->filename = filename;

		//Load Texture from the file
		texutl.Init(texunit);
		texutl.LoadTexture(filename);
	}

	//update uniforms
	void UpdateUniforms()
	{
		//pass builtin texture to the fragment shader 
		texutl.MakeActive(shader.GetUniformLocation("tex"));
		BaseGeometry::UpdateUniforms();
	}

	//release resources
	void Cleanup()
	{
		BaseGeometry::Cleanup();
		texutl.Cleanup();
	}

	//override
	virtual string vertexShaderSource()
	{
		return R"(
		#version 330 core
		layout (location = 0) in vec3 vVertex;
		layout (location = 1) in vec2 vTexCrd;
		uniform mat4 transform;
		out vec2 FragTexCrd; 
		void main()
		{
			gl_Position = transform * vec4(vVertex, 1.0);
			FragTexCrd=vTexCrd;
		};
		)";
	}

	//override
	virtual string fragmentShaderSource()
	{
		return R"(
		#version 330 core
		in vec2 FragTexCrd;
		out vec4 FragColor;
		uniform sampler2D tex;
		void main()
		{
		   FragColor = texture(tex, FragTexCrd);
		};
		)";
	}

private:
	TextureUtil  texutl;
	std::string filename;
};   
 

This purpose of this Lesson04 project is to create a Window initialized with OpenGL context and draw a TexturedCube wrapped with texture.
As discussed in introduction create tutorial lesson project Lesson04 under Lessons folder.
Add a header file Scene.h as shown below. The class itself is self explanatory. 
The WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown. 
The Init method calls BaseScene's Init to create hosting window and OpenGL Context. It also attaches BaseCameraInputHandler to rotate the cube either by keyboard or mouse inputs. It initializes the IndexedCube with texture.
The DrawScene method draws the cube with loaded texture  and rotates as per keyboard or mouse inputs.
The Cleanup method release the resources.
The Scene class is implemented as below. It's located in Lessons\Lesson03\Scene.h file.
       
#include "Scene\BaseScene.h"
#include "Geometry\Cube\TexturedCube.h"

class Scene:public BaseScene
{
public:
	//message handler
	BEGIN_MSG_MAP(Scene0)
		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
		CHAIN_MSG_MAP(BaseScene)
	END_MSG_MAP()

	//override
	int Init(RECT rect, WCHAR *windowname)
	{
		//create host window and context
		BaseScene::Init(rect, windowname);
		//attach mouse keyboard input handler
		mskbd = new BaseCameraInputHandler(m_hWnd);

		//Create cube an set texture filename
		cube.Init(0, R"(..\..\resources\textures\bricks2.jpg)");
		
		return 0;
	}

	//release resources
	void Cleanup()
	{
		cube.Cleanup();
		delete mskbd;
		
	}
	
	//draw the scene
	void DrawScene()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//get model view projection matrix. 
		//only model is modified
		//view and projection will be identity matrix
		mskbd->fetchCameraData(&cube.camera);
		cube.Draw();

	}

	//Close the window
	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = TRUE;
		DestroyWindow();
		PostQuitMessage(0);
		return 0;
	}


private:
	TexturedCube cube;
};   
 
The main.cpp is located in Lessons\Lesson03\main.cpp file. It creates the scene object and calls its init method.
       
#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"Modern OpenGL-Tutorial - Lesson04");
	scene.ShowWindow(show);

	MSG msg;
	while (GetMessage(&msg, 0, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return 0;
}
 
The output looks as shown in the top. The cube is rotated by 20 degrees pitch and 20 degrees yaw. The camera position at the origin looking down on -Z axis or the back face of the cube.
To rotate the cube X,Y and Z keys can be used. They rotate respectively pitch, yaw and roll the cube  by 10 degrees,
In the next post we shall create a colored cube with transformation.

Lesson03:Indexed Cube with interpolated colors

 


In Lesson02 we saw that VBO were used to draw the single colored cube. In this post we  will discuss how to draw an Indexed cube using EBO with interpolated colors. It looks as shown above.

Drawing a cube with VBO requires binding VBO of 12 vertices containing position and optionally  normal vector, color, Texture coordinate data. Whereas an  Indexed draw requires binding VBO of 8 vertices containing position, normal vector, color, Texture coordinate data and bind an EBO containing 12 indices. 
To draw a VBO cube glDrawArrays API is used where as for EBO or Indexed, glDrawElements is used. Here the EBO contains indices data.
Color interpolation means a triangle's fragment's color is computed by interpolating colors of the vertices. 

Implementation
IndexedCube class is implemented as below. It's located in VOGLLib\Geometry\Cube\IndexedCube .h.
       
#pragma once
#include "..\BaseGeometry.h"
#include "CubeMesh.h"

//implements EBO Indexed cube
class IndexedCube :public BaseGeometry
{
public:

	void Init()
	{
		//initialize
		BaseGeometry::Init(new CubeMesh());
		//update VBO data of the vertices
		mesh->GenerateVerticesData(true, VAOUtil::POS | VAOUtil::CLR, vaoutl);
		//setup vertex for Position
		vaoutl.SetupVBO(0, VAOUtil::POS);
		//setup vertex for Color
		vaoutl.SetupVBO(1, VAOUtil::CLR);
		//populate EBO indices
		kount = mesh->GenerateIndicesData(vaoutl);
		//bind VBO
		vaoutl.SetupEBO();

		vaoutl.unbindVAO();
	}

	//draw
	void Draw()
	{
		BaseGeometry::Draw(true);
	}

	//override
	virtual string vertexShaderSource()
	{
		return R"(
		#version 330 core
		layout (location = 0) in vec3 vVertex;
		layout (location = 1) in vec3 vColor;
		out vec3 fcolor;

		uniform mat4 transform;

		void main()
		{
		   gl_Position =  transform * vec4(vVertex, 1.0);
		   fcolor = vColor;
		};
		)";
	}

	//override
	virtual string fragmentShaderSource()
	{
		return R"(
		#version 330 core
		in vec3 fcolor;
		out vec4 FragColor;
		void main()
		{
		   FragColor = vec4(fcolor,1.0);
		};
		)";
	}

};     
 
Lesson03 Project

This purpose of this Lesson03 project is to create a Window initialized with OpenGL context and draw a IndexedCube with interpolated colors.
As discussed in introduction create tutorial lesson project Lesson03 under Lessons folder.
Add a header file Scene.h as shown below. The class itself is self explanatory. 
The WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown. 
The Init method calls BaseScene's Init to create hosting window and OpenGL Context. It also attaches BaseCameraInputHandler to rotate the cube either by keyboard or mouse inputs.
The DrawScene method draws the cube with interpolated colors and rotates as per keyboard or mouse inputs.
The Cleanup method release the resources.

The Scene class is implemented as below. It's located in Lessons\Lesson03\Scene.h file.
       
#include "Scene\BaseScene.h"
#include "Geometry\Cube\IndexedCube.h"

class Scene:public BaseScene
{
public:
	//message handler
	BEGIN_MSG_MAP(Scene0)
		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
		CHAIN_MSG_MAP(BaseScene)
	END_MSG_MAP()

	//override
	int Init(RECT rect, WCHAR *windowname)
	{
		//create host window and context
		BaseScene::Init(rect, windowname);
		//attach mouse keyboard input handler
		mskbd = new BaseCameraInputHandler(m_hWnd);

		//Create cube an set color
		cube.Init();
		
		return 0;
	}

	//release resources
	void Cleanup()
	{
		cube.Cleanup();
		delete mskbd;
		
	}
	
	//draw the scene
	void DrawScene()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//get model view projection matrix. 
		//only model is modified
		//view and projection will be identity matrix
		mskbd->fetchCameraData(&cube.camera);
		cube.Draw();

	}

	//Close the window
	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = TRUE;
		DestroyWindow();
		PostQuitMessage(0);
		return 0;
	}


private:
	IndexedCube cube;
};      
 
The main.cpp is located in Lessons\Lesson03\main.cpp file. It creates the scene object and calls its init method.
       
#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"Modern OpenGL-Tutorial - Lesson03");
	scene.ShowWindow(show);

	MSG msg;
	while (GetMessage(&msg, 0, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return 0;
}     
 
The output looks as shown in the top. The cube is rotated by 20 degrees pitch and 20 degrees yaw. The camera position at the origin looking down on -Z axis or the back face of the cube.
To rotate the cube X,Y and Z keys can be used. They rotate respectively pitch, yaw and roll the cube  by 10 degrees,
In the next post we shall create a textured cube.

Tuesday, July 12, 2022

Lesson02: Single Colored cube


In the previous post the graphics pipeline and vertex processing were explained. 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.

Consider a cube with 8 vertices (0,1,2,3,4,5,6,7) and 6 faces (top, bottom, right, left, back, front) with it's center at the origin (0,0,0).

vertices 0,1,5,4 will have -X whereas vertices 3,2,6,7 will have +X.
vertices 0,4,7,3 will have -Y whereas vertices 1,5,6,2 will have +Y.
vertices 0,1,2,3 will have -Z whereas vertices 4,5,6,7 will have +Z.

top face is made of two triangles  made from vertices 0,4,7 and  7,3,0.
left face is made of two triangles  made from vertices 0,1,5 and 5,4,0.
bottom face is made of two triangles  made from vertices 1,5,6 and  6,2,1.
right  face is made of two triangles  made from vertices 6,7,3 and 3,2,6.
back face is made of two triangles  made from vertices 0,1,2 and 2,3,0.
front face is made of two triangles  made from vertices 4,5,6 and 6,7,4.

Implementation

CubeMesh class is implemented to generate vertices for the 6 faces as discussed above. It's located in VOGLLib\Geometry\Cube\CubeMesh.h
SingleColoredCube class is implemented as below to draw the single colored cube. 
 It's located in VOGLLib\Geometry\Cube\SingleColoredCube.h.
       
//Implements singled colored cube 
class SingleColoredCube:public BaseGeometry
{
public:

	//Initialize
	void Init(glm::vec3 color)
	{
		//setup with a cube and Compile and link shaders
		BaseGeometry::Init(new CubeMesh());
		//assign the color
		this->color = color;
		//Generate VBO data
		kount = mesh->GenerateVerticesData(FALSE,VAOUtil::POS, vaoutl);
		//Enable single vertex
		vaoutl.SetupVBO(0, VAOUtil::POS);
		vaoutl.unbindVAO();
	}
	//Override to supply color of the cube
	void UpdateUniforms()
	{
		BaseGeometry::UpdateUniforms();
		glUniform3fv(shader.GetUniformLocation("color"), 1, glm::value_ptr(color));
	}

private:
	//override
	string vertexShaderSource()
	{
		return R"(
		#version 330 core
		layout (location = 0) in vec3 vVertex;

		uniform mat4 transform;
		uniform vec3 color;
		out vec3 cubecolor;

		void main()
		{
			gl_Position =  transform * vec4(vVertex, 1.0);
			cubecolor = color;
		};
		)";
	}
	//override
	string fragmentShaderSource()
	{
		return R"(
		#version 330 core
		in vec3 cubecolor;
		out vec4 FragColor;
		void main()
		{
		   FragColor = vec4(cubecolor,1);
		};
		)";
	}

	glm::vec3 color;
};  
 
Lesson02 Project
This purpose of this Lesson02 project is to create a Window initialized with OpenGL context and draw a SingleColoredCube with fuchsia color.
As discussed in introduction create tutorial lesson project Lesson02 under Lessons folder.
Add a header file Scene.h as shown below. The class itself is self explanatory. 
The WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown. 
The Init method calls BaseScene's Init to create hosting window and OpenGL Context. It also attaches BaseCameraInputHandler to rotate the cube either by keyboard or mouse inputs.
The DrawScene method draws the cube with fuchsia color and rotates as per keyboard or mouse inputs.
The Cleanup method release the resources.
The Scene class is implemented as below. It's located in Lessons\Lesson02\Scene.h file.
       
#include "Scene\BaseScene.h"
#include "Geometry\Cube\SingleColoredCube.h"

class Scene:public BaseScene
{
public:
	//message handler
	BEGIN_MSG_MAP(Scene0)
		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
		CHAIN_MSG_MAP(BaseScene)
	END_MSG_MAP()

	//override
	int Init(RECT rect, WCHAR *windowname)
	{
		//create host window and context
		BaseScene::Init(rect, windowname);

		//attach keyboard/mouse input handler
		mskbd = new BaseCameraInputHandler(m_hWnd);

		//Create cube an set color
		cube.Init(glm::vec3(1.0f,0.0f,1.0f));
		return 0;
	}

	//release resources
	void Cleanup()
	{
		cube.Cleanup();
		delete mskbd;
	}
	
	//draw the scene
	void DrawScene()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//get model view projection matrix. 
		//only model is modified
		//view and projection will be identity matrix
		mskbd->fetchCameraData(&cube.camera);
		cube.Draw();

	}

	//Close the window
	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = TRUE;
		DestroyWindow();
		PostQuitMessage(0);
		return 0;
	}


private:
	SingleColoredCube cube;
};      
 
The main.cpp is located in Lessons\Lesson02\main.cpp file. It creates the scene object and calls its init method.
       
#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"Modern OpenGL-Tutorial - Lesson02");
	scene.ShowWindow(show);

	MSG msg;
	while (GetMessage(&msg, 0, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return 0;
}      
 
The output looks as shown in the top. The cube is rotated by 20 degrees pitch and 20 degrees yaw. The camera position at the origin looking down on -Z axis or the back face of the cube.
To rotate the cube X,Y and Z keys can be used. They rotate respectively pitch, yaw and roll the cube  by 10 degrees.
In the next post we shall create a indexed cube.

Sunday, July 10, 2022

Graphics Pipeline



Overview
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. The cube looks elongated horizontally because aspect ratio is not applied. 



Objects in a 3D scene are typically described using triangles, which in turn are defined by their vertices.
A vertex is the corner of the triangle where two edges meet, and thus every triangle is composed of three vertices. Modern OpenGL supports three types of primitives - Points(GL_POINTS), Triangle(GL_TRIANGLE)  and Line Strip (GL_LINE_STRIP).

In OpenGL, the Graphics pipeline is responsible for rendering 3D objects. The following gives a brief overview without over burdening with the complex  details. As you get familiar, this can be revisited to gain a deeper understanding.

FrameBuffer
The output of graphics pipeline ends up in in Framebuffer. Framebuffer is piece of memory within the graphics card that maps to the display. For simplicity, you can assume that it's like a bitmap covering entire viewport. Double framebuffers are used to avoid screen tearing while rendering the scene. For example, after the first frame of a scene is written by the pipeline into framebuffer1, it's drawn on the screen while the framebuffer2 is filled with the second frame of the scene. Then it's swapped with the first so that  the screen now displays the second frame while third frame is written in to the framebuffer1 etc.

OpenGL  Graphics pipeline
OpenGL provides a multi-stage graphics pipeline that is partially programmable using a language called GLSL (OpenGL Shading Language) as shown below. Each of these programmable units are called shaders.


To kick off this chain, the C++ application supplies vertex data consisting of  vertices. 
For a triangle primitive, each vertex maps following information:
  • X, Y, Z coordinate (mandatory)
  • Color (optional)
  • Normal vector (optional)
  • Texture coordinates (optional)
The vertex data is first fed to the vertex shader.

Vertex Shader
This stage is mandatory. A vertex shader is a graphics processing function used to add special effects to objects in a 3D environment by performing mathematical operations on the  vertex's data. Vertex Shaders don't actually change the type of data; they simply change the values of the data, so that a vertex emerges with a different color, different textures, or a different position in 3D space.

Tessellation Shader
This stage is optional and not available to OpenGL release 3.3.  After the vertex shader has processed each vertex’s associated data, the tessellation shader stage will continue processing those data, if it has been activated. Tessellation uses patchs to describe an object’s shape, and allows relatively simple collections of patch geometry to be tessellated to increase the number of geometric primitives providing better-looking models. The tessellation shading stage can potentially use two shaders to manipulate the patch data and generate the final shape.

Geometry Shader
This stage is optional. Allows additional processing of individual geometric primitives, including creating new ones, before rasterization. This shading stage is also optional, but very powerful.

Rasterization
The primitive assembly stage organizes the vertices into their associated geometric primitives in preparation for clipping and rasterization. Clipping removes all pixels outside of the viewport.
After clipping, the updated primitives are sent to the rasterizer for fragment generation. Consider a fragment a candidate pixel, in that pixels have a home in the framebuffer, while a fragment still can be rejected and never update its associated pixel location. Processing of fragments occurs in the next two stages, fragment shading and per-fragment operations.

Fragment Shader
This stage is necessary for the practical reasons. Fragment shader determines the fragment’s final color , and potentially its depth value. Fragment shaders are very powerful as they often employ texture mapping to augment the colors provided by the vertex processing stages. A fragment shader may also terminate processing a fragment if it determines the fragment shouldn’t be drawn; this process is called fragment discard.

Pixel Operations
A fragment’s visibility is determined using depth testing (also commonly known as Z-buffering) and stencil testing. If a fragment successfully makes it through all of the enabled tests, it may
be written directly to the framebuffer, updating the color (and possibly depth value) of its pixel, or if blending is enabled, the fragment’s color will be combined with the pixel’s current color to generate a new color that is written into the framebuffer.

Below we will discuss some of the  terminologies used commonly when vertex data of 3D objects is passed to the pipeline especially vertex shader to render them on screen. Don't get bogged down with its details. It will become clear in the next post. 

Vertex Buffer Object(VBO)
A vertex buffer object (VBO) is an OpenGL feature that provides methods for uploading vertex data (position, normal vector, color, Texture coordinate). A VBO can store one or more of these. 

Element Buffer Object(EBO)
To optimize data transfer, EBO stores indices of the vertices specifically, and OpenGL calls the indices  of these vertices to determine which vertices should be drawn. 

Vertex Array Object(VAO)
Vertex array objects store a set of buffer names - VBO and EBO to get vertex data from, as well as how the vertices are laid out in the vertex buffers. The vertex id associated with VAO maps to location defined in the vertex shader.

The following diagram depicts the relationships between VAO and VBO.



Shaders
A Shader is a user-defined program designed to run on some stage of a graphics processor. Shaders provide the code for certain programmable stages of the rendering pipeline. 
Shaders are written in the OpenGL Shading Language. The OpenGL rendering pipeline defines the following shader stages, with their enumerator name:
Vertex Shaders( GL_VERTEX_SHADER)
Geometry Shaders (GL_GEOMETRY_SHADER)
Fragment Shaders (GL_FRAGMENT_SHADER)
A program object can combine multiple shader stages (built from shader objects) into a single, linked whole. A program pipeline object can combine programs that contain individual shader stages into a whole pipeline.

Uniforms
Data such as vectors, matrices can be shared across the C++ application and shaders using a uniform.
A uniform is a global Shader variable declared with the "uniform" storage qualifier. These act as parameters that the user of a shader program can pass to that program. Their values are stored in a program object.
Uniforms are so named because they do not change from one shader invocation to the next within a particular rendering call thus their value is uniform among all invocations. This makes them unlike shader stage inputs and outputs, which are often different for each invocation of a shader stage.

The following diagram combines all the above and represents it,

Coordinate System
OpenGL uses Right hand side coordinate system where +Z is pointing towards you.
The following diagram shows how the positive rotation happens in Counter clockwise direction.
        Pitch(X Axis)                                    Yaw (Y Axis)                                  Roll (Z Axis)


In the next post we will discuss the nitty gritty of rendering a cube.

Saturday, July 9, 2022

Lesson01 : Creating OpenGL Context, Rendering


This post discusses  implementing the basic operations of creating OpenGL context, rendering and mouse/keyboard is discussed. Followed by an lesson that utilizes it.

Lesson01 Project
This purpose of this Lesson01 project is to create a Window initialized with OpenGL context and fill it with fuchsia color.
As discussed in introduction create tutorial lesson project Lesson01 under Lessons folder.
Add a header file Scene.h as shown below. The class itself is self explanatory. 
The WM_CLOSE  event is  handled in OnCloseWindow function and the window is destroyed and  application is shutdown. 
The Init method calls BaseScene's Init to create hosting window and OpenGL Context.
The DrawScene method clears the window and paints it with fuchsia color.
The Cleanup method does not do anything.
       
#include "Scene\BaseScene.h"

class Scene:public BaseScene
{
	BEGIN_MSG_MAP(Scene)
		MESSAGE_HANDLER(WM_CLOSE, OnCloseWindow)
		CHAIN_MSG_MAP(BaseScene)
	END_MSG_MAP()

	//shutdown the application when the wondow is closed
	LRESULT OnCloseWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = TRUE;
		DestroyWindow();
		PostQuitMessage(0);
		return 0;
	}
	
	//override to create application window and opengl context
	int Init(RECT rect, WCHAR *windowname)
	{
		BaseScene::Init(rect, windowname);
		return 0;
	}

	//no cleanup
	void Cleanup()
	{
	}
	
	//draw
	void DrawScene()
	{
		glClearColor(1, 0, 1, 1);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	}

};

Add a  main.cpp  as shown below. It 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)
{
	//create window and opengl context
	scene.Init(RECT{ 100, 100, 780, 500 }, L"Modern OpenGL-Tutorial");
	//draw
	scene.ShowWindow(show);

	//message pump
	MSG msg;
	while (GetMessage(&msg, 0, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return 0;
}


Output
The output looks as shown in the top.

The code and binaries for Lesson01 is found here

Thursday, July 7, 2022

Introduction



OpenGL is a graphics library  that can be used to render interactive 2D and 3D graphics applications.
OpenGL has wide range of applications. OpenGL library comes in two flavors. 
  • Deprecated fixed pipeline based API or legacy OpenGL  ( releases before 3.0)
  • Shader based API  or Modern OpenGL  (release 3.0 and onwards)
Modern OpenGL uses GPU instead CPU there by increasing user experience tremendously.  legacy OpenGL is still supported for backward compatibility as of latest release 4.6.
In this tutorial  we consider shader based approach.

Prerequisites
OpenGL programming is  different from normal programming.  Following is a check list:
  • 3D math such as vectors, planes, rays etc., 
  • Matrix arithmetic
  • Trigonometry
  • Understanding of motion related  physics
  • Intermediate C++ programming 
  • GLSL concepts and programming 
Tutorial Overview
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.
There are plenty of websites with rich documentation on different OpenGL related topics. For various topics, a short description will be provided along with a link for further details.

Tutorial Directory Structure
The folder structure will be as below. 

Lessons
contains source code and binaries of the applications using the different OpenGL topics and features discussed in the tutorial.

Externals
Contains 3rd party libraries used by the tutorial for loading OpenGL extensions, perform vector math, matrices calculations. loading texture, object model  etc.
OpenGL Extensions
The OpenGL header file included as a part of windows SDK supports OpenGL specification 1.1. The Modern OpenGL tutorial uses  OpenGL specification 3.3. The extensions will be loaded after the context is created. This will be discussed in detail in the next post.
As the OpenGL specification 3.3 has just too many APIs, the extensions will be supplied by a separate library, GLExtensions. The C++ prototypes of OpenGL specification 3.3 can be downloaded from Galogen web site. In addition, latest WGL extensions should also be included. This can be downloaded from this website. WGL Extensions are also included in GLExtensions. 
GLExtensions is located under externals folder and is configured as static library. The debug and release libraries are available as externals\GLExtensions\Lib\GLExtensions_static_x86_d.lib and externals\GLExtensions\Lib\GLExtensions_static_x86.lib.

OpenGL Context Creation and mouse/Keyboard Input
This tutorial is primarily targeted for windows OS, so Wiggle APIs are used for creating OpenGL context. For windowing, mouse and keyboard input etc., ATL and WIN32 APIs are used. 

GLM
Rendering 3D context involves lot of vector math, matrix math, trigonometry etc. GLM conveniently provides a math library including all these and more. In addition it also provides APIs for creation of Ortho and Perspective projection matrix, transformations such as translation, rotation and scaling, lookat matrix and much more. GLM can be downloaded from here.
GLM  files are located at externals\glm folder.

Simple OpenGL Image Library
SOIL2 is used for loading textures. displaying bitmaps etc. SOIL2 can be downloaded from here
SOIL2 is located under externals folder and is configured as static library. The debug and release libraries are available as externals\SOIL2\projects\VC15\libs\SOIL_static_x86_d.lib and externals\SOIL2\projects\VC15\libs\SOIL_static_x86.lib.

Resources
Contains bitmaps, texture bitmaps, wireframe models used in the tutorial.

Development Environment
VS2015 IDE is used for building externals and tutorials. 

Creating tutorial lesson projects
Following steps should be used to create tutorial lesson projects using VS IDE

Create an Win32 project in VS IDE wizard under Lessons folder. The name of the project should be Lessonnn. For example Lesson01.



Select Empty Project and no SDL check


Add include directory in VC directories


In the next post we will discuss creation of  Creating OpenGL Context, Rendering and Mouse/Keyboard input support.