By the title you might think this is the same issue as the previous post. I read that one. Looks like we’re doing things significantly different.
So, I’m slowly trying to add features to my basic OGL program. And I went to add a texture to the quad rather than coloring it with vertex colors. I left in place the vertex color uniform, even though it is no longer using it. If I use the VertexColor uniform to color the fragment, it works as expected. But when I try and use the sampler, it always comes out black.
The quad moves around the screen. It’s clearly drawing the quad. It’s just black instead of using the texture. In fact, if you change the fragment shader to use the vertex color uniform, the program draws a blue quad as expected.
I was trying to work off a tutorial that used STBI to load images. But for whatever reason, STBI did not work on my machine (probably because of the class being involved in inheritance - the tutorial I was working from was not really object oriented and certainly was not using inheritance). So, I started using FreeImage. As far as I can tell, the jpeg is loading. FreeImage correctly determines its height and width anyway. And it doesn’t crash.
I’ve spent about 2 whole days going over this code with a fine tooth comb and googling this problem and have come up with nothing that gives me any hint as to why textures are not working for me.
It’s difficult to figure out because there are no errors or crashing, just no texture. I may have made a really elementary mistake here. This is my first time trying to texture something in OGL.
I might also mention that this project is to rewrite an existing/working DX11 project in OGL 4.5, so a lot of comments were copied and pasted directly from the DX code. Don’t let that throw you off.
Sorry to post all the code, but at this point, the problem could be anywhere. I could be declaring my vertex array wrong, have a bad shader, not actually loading the image, not binding the texture correctly, etc.
Can anyone tell me why I’m getting a black quad instead of a textured quad?
Let me start with my shader:
Simple.vrt
#version 450 core
layout (location = 0) in vec3 Pos;
layout (location = 1) in vec2 UV;
uniform vec2 PositionOffset;
smooth out vec2 TextureCoordinates;
void main()
{
gl_Position = vec4(Pos.x + PositionOffset.x, Pos.y + PositionOffset.y, Pos.z, 1.0f);
TextureCoordinates = UV;
};
Simple.frg
#version 450 core
smooth in vec2 TextureCoordinates;
layout (location =0) out vec4 OutputColor;
uniform vec4 VertexColor;
uniform sampler2D Image;
void main()
{
OutputColor = texture(Image, TextureCoordinates);
};
Main.cpp
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <WinBase.h>
#include "..\System\OSClass.h"
#include "..\Game.h"
using namespace OGLGameNameSpace;
//=====================================================================================================================
// wWinMain()
//
// Purpose:
// This is where the code begins.
//
// Notes:
// All Windows programs must start with WinMain as the application entry point. Technically this is our application
// here, but we want to follow object oriented design principles and so we encapsulate and obfuscate everything in various
// classes that do all the work. That makes this function the conductor of this symphony. It calls each piece and maintains
// the event loop that keeps everything running.
//
// It may not be readily apparent, but the Game1.InitializeOGL() is actually calling the Initialize method of the OGLGame
// class to initialize OGL and then run Game1's initialize method.
//
// In practice, this code should not be altered at all unless you just want to tweak something. All code for the application
// should be in or called from the Game object Game1.
//
// There is quite a bit of setup for OGL. The following paths are the Additional Include paths for 64 bit. This presumes
// that you have GLFW and GLEW installed in a "Libraries" folder shared by all OGL programs.
// C:\VirtuallyProgramming\OGL\Libraries\GLFW\glfw-3.1.2.bin.WIN64\include\GLFW
// C:\VirtuallyProgramming\OGL\Libraries\GLEW\glew - 1.13.0\include\GL
//
// Additionally, these Additional Library paths have to be set as well.
// C:\VirtuallyProgramming\OGL\Libraries\GLFW\glfw-3.1.2.bin.WIN64\lib-vc2015
// C:\VirtuallyProgramming\OGL\Libraries\GLEW\glew - 1.13.0\lib\Release\x64
//
// The following Additional Dependencies must be defined:
// opengl32.lib
// glew32s.lib
// glew32.lib
// glfw3.lib
//
// And if you intend to use the lib files for static linking rather than a .DLL you have to set "GLEW_STATIC" as a
// pre-processor definition in Properties==>C++==>Preprocessor. Not doing this will result in a "glew32.dll is missing" error.
//
//
//=====================================================================================================================
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
OSClass OS;
Game Game1;
int ReturnCode = -1;
if (OS.Initialize())
{
if (OS.MainWindowPointer())
{
if (Game1.InitializeOGL())
{
while (!OS.ShouldClose()) //Loop until GLFW told to close.
{
OS.PollEvents();
Game1.Update();
Game1.Draw();
glfwSwapBuffers(OS.MainWindowPointer());
}
ReturnCode = 0;
}
else
{
//std::cerr << "GLEW failed to initialize.";
}
}
else
{
//std::cerr << "GLFW failed to create a window for the program.";
}
}
else
{
//std::cerr << "GLFW failed to initialize.";
}
return ReturnCode;
}
OSClass.h
#pragma once
#include <glew.h>
#include <glfw3.h>
namespace OGLGameNameSpace
{
class OSClass
{
public:
OSClass();
~OSClass();
bool Initialize(void);
bool ShouldClose(void);
GLFWwindow* MainWindowPointer(void);
void PollEvents(void);
private:
const char* APP_TITLE = "Most Basic OGL Program";
GLFWwindow* MainWindow = nullptr;
};
void glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode);
}
OSClass.cpp
#include "OSClass.h"
using namespace OGLGameNameSpace;
OSClass::OSClass()
{
}
OSClass::~OSClass()
{
glfwTerminate();
}
bool OSClass::Initialize(void)
{
bool InitializedOK = false;
if (glfwInit())
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
MainWindow = glfwCreateWindow(1360, 768, APP_TITLE, NULL, NULL);
if (MainWindow)
{
glfwMakeContextCurrent(MainWindow);
glfwSetKeyCallback(MainWindow, glfw_OnKey);
InitializedOK = true;
}
}
return InitializedOK;
}
bool OSClass::ShouldClose(void)
{
return (bool)glfwWindowShouldClose(MainWindow);
}
GLFWwindow* OSClass::MainWindowPointer(void)
{
return MainWindow;
}
void OSClass::PollEvents()
{
glfwPollEvents(); //Needed for callback functions.
}
void OGLGameNameSpace::glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode)
{
if (Key == GLFW_KEY_ESCAPE && Action == GLFW_PRESS) glfwSetWindowShouldClose(Window, GL_TRUE);
}
OGLGameClass.h
#pragma once
//#include "GameTimer.h"
//#include "KeyboardClass.h"
#include <glew.h>
#include <glfw3.h>
#include <glm.hpp>
#include "Texture2DClass.h"
namespace OGLGameNameSpace
{
//Predefined colors made to match XNA that you can use or not use.
//const float RGBABlue[4] = { 0.0f,0.0f,1.0f,1.0f };
//const float RGBABlack[4] = { 0.0f,0.0f,0.0f,1.0f };
//const float RGBAWhite[4] = { 1.0f,1.0f,1.0f,1.0f };
//const float RGBAGreen[4] = { 0.0f,0.5019607843137255f,0.0f,1.0f };
//const float RGBARed[4] = { 1.0f,0.0f,0.0f,1.0f };
//const float RGBAYellow[4] = { 1.0f,1.0f,0.0f,1.0f };
//const float RGBACornFlowerBlue[4] = { 0.392156862745098f,0.5843137254901961f,0.9294117647058824f,1.0f };
//=====================================================================================================================
// OGLGame
//
// Purpose:
// To encapsulate GLEW and any OGL initialization.
//
// Notes:
// In order to write a "game" the programmer is expected to create a game object that inherits from this class which will
// hook it in to this whole system. The main code will call this object to initialize OGL and this code will call the game
// object that contains all the code for the game.
//
// This is very basic bare bones code not designed to handle every feature of every graphics card. You may want to expand
// on this code a little bit to do things like checking whether the graphics card supports anti-aliasing or not before turning
// anti-aliasing on. However, by making a few minor adjustments such as matching the screen resolution to your own, this should
// give you the code needed to start every DX project you might want to do and run it on your computer.
//
//=====================================================================================================================
class OGLGameClass
{
public:
OGLGameClass();
~OGLGameClass();
//bool ShouldClose(void);
bool InitializeOGL(void);
bool InitializeGameClass(void);
virtual bool Initialize();
virtual bool LoadContent();
virtual void UnloadContent();
virtual void Update();
virtual void Draw();
virtual void Shutdown();
GLdouble GetTime(void);
// GameTimer Timer;
//protected:
//
// KeyboardClass Keyboard;
//
};
//=====================================================================================================================
}
OGLGameClass.cpp
#include "OGLGameClass.h"
using namespace OGLGameNameSpace;
//=====================================================================================================================
// Constructor
//
// Purpose:
// Initialize basic values for the OGLGame object.
//
// Notes:
//
//
//=====================================================================================================================
OGLGameClass::OGLGameClass()
{
glewExperimental = GL_TRUE;
}
//=====================================================================================================================
//=====================================================================================================================
// Destructor
//
// Purpose:
// Cleanup of any class specific code.
//
// Notes:
// Not used, but you are welcome to use it if you like.
//
//=====================================================================================================================
OGLGameClass::~OGLGameClass()
{
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::InitializeOGL()
//
// Purpose:
//
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an initialization method.
//
//=====================================================================================================================
bool OGLGameClass::InitializeOGL(void)
{
bool Initialized = false; //Return value - must be true if properly initialized.
if (glewInit() == GLEW_OK)
{
Initialized = true;
Initialized = InitializeGameClass();
}
return Initialized;
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::InitializeGameClass()
//
// Purpose:
// To run the programmer's custom initialization code at startup.
//
// Input:
// None.
//
// Output:
// bool - true if no errors occured.
//
// Notes:
// This method calls into the Game class that inheriets from this class which allows the programmer to include their own
// code to initialize and load art assets. The timer object is also initialized to start the clock so that we can know
// how much time has passed between each frame.
//
// All of the method that this method calls out to return false if there was a failure or error. So, if everything returns
// true, this method will return true also to indicate that everything initialized properly.
//
//=====================================================================================================================
bool OGLGameClass::InitializeGameClass()
{
bool Initialized = false; //Return value - must be true if properly initialized.
if (Initialize()) //Call Game class's Initialize method to do programmer defined initialization.
{
if (LoadContent()) //Call Game class's LoadContent method to load programmer defined game assets.
{
Initialized = true; //Initialize the Game Timer and we're good.
}
}
return Initialized;
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::Initialize()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Initialize() method of its own.
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an initialization method.
//
////=====================================================================================================================
bool OGLGameClass::Initialize(void)
{
bool Initialized = false; //Return value - must be true if properly initialized.
Initialized = InitializeGameClass();
return Initialized;
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::LoadContent()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have a LoadContent() method of its own.
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement a load content method.
//
//=====================================================================================================================
bool OGLGameClass::LoadContent()
{
return true;
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::UnloadContent()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an UnloadContent() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//=====================================================================================================================
void OGLGameClass::UnloadContent()
{
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::Update()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Update() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//=====================================================================================================================
void OGLGameClass::Update()
{
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::Draw()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Draw() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//=====================================================================================================================
void OGLGameClass::Draw()
{
}
//=====================================================================================================================
//=====================================================================================================================
// OGLGame::Shutdown()
//
// Purpose:
// To cleanly shut down DirectX and our application.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// The first thing this method does is call the Game object that inherits from this class's UnloadContent() method to
// free up any memory used for art assets by that object. Then it basically just shuts down DX and releases any memory
// associated with DX.
//
//=====================================================================================================================
void OGLGameClass::Shutdown()
{
UnloadContent();
//Keyboard.Shutdown(); //Release DirectInput and the keyboard device.
}
//=====================================================================================================================
GLdouble OGLGameClass::GetTime(void)
{
return glfwGetTime();
}
Game.h
#pragma once
#include "System\OGLGameClass.h"
//#include "System\Texture2DClass.h"
#include "System\ShaderClass.h"
//#include "SceneObjectClass.h" //New class to allow us to put objects in the scene.
//#include "TextureClass.h"
//#include "SkyBoxClass.h"
//#include "ModelCompiler.h"
namespace OGLGameNameSpace
{
//=====================================================================================================================
// Game : public
//
// Purpose:
// This is where this frame work is modified to create a game.
//
// Notes:
// I am changing the comments on this class from the previous example. You modify this class in order to create your
// game. So, you will quickly notice the code in this class has been modified to create a scene with objects in it that
// you can move around in using either the keyboard or an XBox 360 controller if you have one hooked up to your PC (which
// I HIGHLY recommend although for programming it can be more convienient not to have to leave the keyboard). For actual
// games I would prefer to see the XBox 360 controller be the primary, if not sole input device.
//
// Notice that this class inherits from OGLGame. Because of that, the methods of this class are automatically
// called at the appropriate time. You just have to fill these methods with instructions to create a game.
//
//=====================================================================================================================
class Game : public OGLGameNameSpace::OGLGameClass
{
public:
Game(void);
virtual ~Game(void);
bool Initialize(void);
bool LoadContent(void);
void UnloadContent(void);
void Update();
void Draw();
private:
GLuint vbo, vao, ibo;
ShaderClass Shader;
Texture2DClass Texture;
//SceneObjectClass Triangle;
//SceneObjectClass Ground;
//SceneObjectClass YellowCube;
//TextureClass GrassTexture;
//SkyBoxClass SkyBox;
//XMMATRIX View;
//XMMATRIX Projection;
//XMVECTOR CameraFacingNormal;
//XINPUT_STATE Controller1State;
//XINPUT_STATE PreviousController1State;
//float CameraHeight;
//XMVECTOR CameraPosition;
//float CameraTilt;
//bool InFullScreenMode;
//XMFLOAT3 DiffuseLightDirection;
//XMFLOAT4 AmbientLightColor;
//XMFLOAT4 DiffuseLightColor;
//ModelCompiler ModelBuilder;
//RigidModel Model;
#define VERTICESINCUBE 36 //Constant to make it more clear what this number is.
};
//=====================================================================================================================
}
Game.cpp
#include "Game.h"
using namespace OGLGameNameSpace;
Game::Game(void)
{
}
Game::~Game(void)
{
}
//=====================================================================================================================
// Game::Initialize()
//
// Purpose:
// To allow any code you want to run once at startup not associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
//
//
//=====================================================================================================================
bool Game::Initialize()
{
bool GameObjectInitializedProperly = false; //Must be set to true to keep the program from closing.
//Projection = XMMatrixPerspectiveFovRH(XM_PIDIV4, DXViewPort.Width / DXViewPort.Height, 0.1f, 5000.0f); //Create a RH Perspective Projection matrix.
GameObjectInitializedProperly = true; // Shader.Initialize(GraphicsDevice, GraphicsDeviceContext); //Do any initialization required for the Shader class and fail if it fails.
return GameObjectInitializedProperly;
}
//=====================================================================================================================
//=====================================================================================================================
// Game::LoadContent()
//
// Purpose:
// To allow any code you want to run once at startup associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
// Here we are creating all the objects in the scene. The switch statement makes it easy to manage this and add more
// objects of your own. You merely define all the objects vertices it will need to be drawn and then list the index order
// of every vertex to specify what order it will be drawn in. These examples merely count the number of vertices for the
// index which is largely pointless. However, the design means you "could" reuse vertices or specify them out of order and
// use the index buffer to specify what order to draw them in.
//
// You'll quickly notice that the number of vertices you have to specify for even the most simple of objects is nearly
// unacceptable. There are better ways, but we're learning the most straight forward way here which we will build on later.
//
// Textures were created with Paint.Net by saving as .DDS files with mip maps.
//
//=====================================================================================================================
bool Game::LoadContent()
{
bool NoCatastrophicFailuresOccured = false; //Close the program if even one mesh fails to initialize correctly.
vbo = 0;
vao = 0;
ibo = 0;
glClearColor(0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f);
GLfloat VertexBuffer[] = {
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f
};
GLuint Indices[] = {
0,1,2,
0,2,3
};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), VertexBuffer, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLvoid*)(sizeof(GLfloat) * 3));
//glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex, (GLvoid*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(1);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
Texture.Load("Textures/Dog.jpg", true);
NoCatastrophicFailuresOccured = Shader.LoadShader("Shaders/Simple.vrt", "Shaders/Simple.frg");
return NoCatastrophicFailuresOccured;
}
//=====================================================================================================================
//=====================================================================================================================
// Game::UnloadContent()
//
// Purpose:
// To allow any code you want to run as the program closes in order to cleanup.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// All the game objects you create need their Shutdown() methods called to release the associate vertex and index buffers.
// Since Shaders are owned by this class, it is also responsible for calling the Shutdown() method of any shaders created.
//
//=====================================================================================================================
void Game::UnloadContent()
{
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
}
//=====================================================================================================================
//=====================================================================================================================
// Game::Update()
//
// Purpose:
// To do everything that needs to be done in one frame except draw stuff to the screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// This is where most of your game code will be. It gets called every frame to change anything that needs to be changed
// or done during that frame. It runs in a loop that needs to be called at least 30 times per second but there's nothing
// to control how often it gets called. Things would move at unpredictable rates if we did not use TimeDelta to take in to
// account the amount of time that has passed since the last frame.
//
// We start out by processing the keyboard and game controller input to change the camera's position and direction. You
// can also toggle full screen on and off.
//
// The camera movement this frame is stored as a 3D vector that we treat more like a 2D vector. The facing normal should
// point in the direction we want the camera to face. And as a normal should have a length of 1. Any movement during the
// frame is cumulative from the various controls. When you move it uses either the CameraFacingNormal or a normal rotated 90
// degrees away from the camera facing. It's basic vector addition to add the movement to the camera position.
//
// XMMatrixLookAtRH is used to create a view matrix to simulate the camera every frame. Generally, I would say it is a
// good idea to not continuously recreate the view matrix but it's easier then maintaining a view matrix between frames and
// this is really early in this tutorial series.
//
// Finally some very simple rigid animation is thrown in to show you not only how to do it, but that it can be done and how
// easy it is to do. Experiment by turning the rotations off and on and changing their directions and speed.
//
// The scene is lit with a simple Blinn-Phong shader that has "directional" lighting as opposed to point lights or
// spot lights. Directional lighting is nothing more than a direction that the light shines in. It is a normalized vector
// describing a direction and it has a color. That's all it is. Look at the shader for more detail. By rotating that direction
// the light source seems to orbit the scene similar to a day and night cycle except the light shines through solid objects.
//
//=====================================================================================================================
void Game::Update()
{
//if (!Keyboard.Update()) PostQuitMessage(0); //If DirectInput has crashed, go ahead and shutdown the app. Otherwise, get the current keyboard state.
//if (Keyboard.KeyPressed(DIK_LSHIFT)) //If these keys are pressed while the left shift key is pressed...
//{
// if (Keyboard.KeyPressed(DIK_D)) CameraMovement += XMVector4Transform(CameraFacingNormal, XMMatrixRotationZ(XM_PIDIV2)); //Move right. Pi over 2 is 90 degrees.
// if (Keyboard.KeyPressed(DIK_A)) CameraMovement += XMVector4Transform(CameraFacingNormal, XMMatrixRotationZ(-XM_PIDIV2)); //Move left.
//}
}
//=====================================================================================================================
//=====================================================================================================================
// Game::Draw()
//
// Purpose:
// To do draw a single frame on the computer screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// Since all the drawing code is tucked away neatly elsewhere, this method ends up being very short and sweet. There's
// basically nothing to it. The first two lines clear the backbuffer in corn flower blue which is a constant. And then
// it clears the depth stencil. You can clear the screen in any color you like.
//
// Then each game object's Draw() method needs to be called to tell it to draw it self to the back buffer. The parameters
// for the shader have to be sent here.
//
// Finaly, the swapchain is told to make the backbuffer the frontbuffer and draw it to the screen by presenting it and
// the image appears on the computer monitor.
//
//=====================================================================================================================
void Game::Draw()
{
glClear(GL_COLOR_BUFFER_BIT);
GLfloat Time = GetTime();
GLfloat BlueColor = (sin(Time) / 2) + 0.5f;
glm::vec2 Position;
Position.x = (sin(Time) / 2);
Position.y = (cos(Time) / 2);
Shader.Use();
Shader.SetUniform("VertexColor", glm::vec4(0.0f, 0.0f, BlueColor, 1.0f));
Shader.SetUniform("PositionOffset", glm::vec2(Position.x, Position.y));
//Shader.SetUniform("Texture", Texture.GetTextureID());
//Shader.SetUniform("Texture", Texture.GetHandle());
//Shader.SetUniform("Texture", 0);
Texture.Bind(0);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); //Release it.
}
//=====================================================================================================================
ShaderClass.h
#pragma once
#include <glew.h>
#include <fstream>
#include <sstream>
#include <glm.hpp>
#include <type_ptr.hpp>
#include <map>
namespace OGLGameNameSpace
{
class ShaderClass
{
public:
ShaderClass();
~ShaderClass();
bool LoadShader(std::string VertexShaderFileName, std::string FragmentShaderFileName);
void Use();
void SetUniform(std::string Name, GLint IntegerValue);
void SetUniform(std::string Name, glm::vec2& Tuple);
void SetUniform(std::string Name, glm::vec3& Tuple);
void SetUniform(std::string Name, glm::vec4& Tuple);
void SetUniform(std::string Name, glm::mat4& Matrix);
//void SetUniform(std::string Name, )
private:
GLuint Shader;
enum ShaderType { Vertex, Fragment, Program };
std::string LoadSourceCode(std::string SourceCodeFile);
GLuint CompileShader(std::string SourceCodeFile, ShaderType Type);
bool NoCompileErrors(GLuint Shader, ShaderType Type);
GLint GetUniformLocation(std::string Name);
std::map<std::string, GLint> UniformLocations;
};
}
ShaderClass.cpp
#include "ShaderClass.h"
using namespace OGLGameNameSpace;
ShaderClass::ShaderClass()
{
Shader = 0;
}
ShaderClass::~ShaderClass()
{
glDeleteProgram(Shader);
}
bool ShaderClass::LoadShader(std::string VertexShaderFileName, std::string FragmentShaderFileName)
{
bool NoCatastrophicFailuresOccured = false;
GLuint VertexShader;
GLuint FragmentShader;
GLuint ShaderProgram;
GLint Result;
VertexShader = CompileShader(VertexShaderFileName, ShaderType::Vertex);
if (NoCompileErrors(VertexShader, ShaderType::Vertex))
{
FragmentShader = CompileShader(FragmentShaderFileName, ShaderType::Fragment);
if (NoCompileErrors(FragmentShader, ShaderType::Fragment))
{
ShaderProgram = glCreateProgram();
glAttachShader(ShaderProgram, VertexShader);
glAttachShader(ShaderProgram, FragmentShader);
glLinkProgram(ShaderProgram);
if (NoCompileErrors(ShaderProgram, ShaderType::Program))
{
Shader = ShaderProgram;
NoCatastrophicFailuresOccured = true;
}
}
glDeleteShader(FragmentShader);
}
glDeleteShader(VertexShader);
UniformLocations.clear();
return NoCatastrophicFailuresOccured;
}
void ShaderClass::Use()
{
if (Shader > 0)
{
glUseProgram(Shader);
}
}
std::string ShaderClass::LoadSourceCode(std::string SourceCodeFile)
{
std::ifstream ShaderFile;
std::stringstream CodeText;
try
{
ShaderFile.open(SourceCodeFile, std::ios::in);
if (!ShaderFile.fail())
{
CodeText << ShaderFile.rdbuf(); //Takes all the source code and puts it in a single stringstream.
}
ShaderFile.close();
}
catch (std::exception Exception)
{
}
return CodeText.str(); //Convert the stringstream of the source code into a std::string.
}
GLuint ShaderClass::CompileShader(std::string SourceCodeFile, ShaderType Type)
{
GLuint ShaderHandle = 0;
std::string ShaderSourceCode;
const GLchar* SourceCodePointer;
ShaderSourceCode = LoadSourceCode(SourceCodeFile);
SourceCodePointer = ShaderSourceCode.c_str();
switch (Type)
{
case ShaderClass::Vertex:
ShaderHandle = glCreateShader(GL_VERTEX_SHADER);
break;
case ShaderClass::Fragment:
ShaderHandle = glCreateShader(GL_FRAGMENT_SHADER);
break;
default:
break;
}
glShaderSource(ShaderHandle, 1, &SourceCodePointer, nullptr);
glCompileShader(ShaderHandle);
return ShaderHandle;
}
bool ShaderClass::NoCompileErrors(GLuint Shader, ShaderType Type)
{
GLint Status = 0;
GLint Length = 0;
GLchar InfoLog[512];
bool NoErrorsFound = false;
if (Type == ShaderType::Program)
{
glGetProgramiv(Shader, GL_LINK_STATUS, &Status);
if (Status == GL_FALSE)
{
glGetProgramiv(Shader, GL_INFO_LOG_LENGTH, &Status);
glGetProgramInfoLog(Shader, sizeof(InfoLog), nullptr, InfoLog);
}
else
{
NoErrorsFound = true;
}
}
else
{
glGetShaderiv(Shader, GL_COMPILE_STATUS, &Status);
if (Status == GL_FALSE)
{
glGetShaderiv(Shader, GL_INFO_LOG_LENGTH, &Status);
std::string ErrorLog(Status, ' ');
glGetProgramInfoLog(Shader, Status, &Status, &ErrorLog[0]);
}
else
{
NoErrorsFound = true;
}
}
return NoErrorsFound;
}
GLint ShaderClass::GetUniformLocation(std::string Name)
{
std::map<std::string, GLint>::iterator UniformsIterator = UniformLocations.find(Name);
if (UniformsIterator == UniformLocations.end())
{
UniformLocations[Name] = glGetUniformLocation(Shader, Name.c_str());
}
return UniformLocations[Name];
}
void ShaderClass::SetUniform(std::string Name, GLint IntegerValue)
{
GLint Location;
Location = GetUniformLocation(Name);
glUniform1i(Location, IntegerValue);
}
void ShaderClass::SetUniform(std::string Name, glm::vec2& Tuple)
{
GLint Location;
Location = GetUniformLocation(Name);
glUniform2f(Location, Tuple.x, Tuple.y);
}
void ShaderClass::SetUniform(std::string Name, glm::vec3& Tuple)
{
GLint Location;
Location = GetUniformLocation(Name);
glUniform3f(Location, Tuple.x, Tuple.y, Tuple.z);
}
void ShaderClass::SetUniform(std::string Name, glm::vec4& Tuple)
{
GLint Location;
Location = GetUniformLocation(Name);
glUniform4f(Location, Tuple.x, Tuple.y, Tuple.z, Tuple.w);
}
void ShaderClass::SetUniform(std::string Name, glm::mat4& Matrix)
{
GLint Location = GetUniformLocation(Name);
glUniformMatrix4fv(Location, 1, GL_FALSE, glm::value_ptr(Matrix));
}
Texture2DClass.h
#pragma once
#include <string>
#include <glew.h>
#include <FreeImage.h>
namespace OGLGameNameSpace
{
class Texture2DClass
{
public:
Texture2DClass();
~Texture2DClass();
bool Load(std::string File, bool GenerateMipMaps = true);
void Bind(GLuint TexUnit = 0);
GLuint GetHandle();
GLuint GetTextureID();
private:
GLuint TextureHandle;
GLuint TextureID;
};
}
Texture2DClass.cpp
#include "Texture2DClass.h"
//#ifndef STBI_INCLUDE_STB_IMAGE_H
//#define STBI_INCLUDE_STB_IMAGE_H
//#define STB_IMAGE_IMPLEMENTATION //Required by STB.
//#define STBI_FAILURE_USERMSG
//#include <stb_image.h>
//#endif STBI_INCLUDE_STB_IMAGE_H
using namespace OGLGameNameSpace;
Texture2DClass::Texture2DClass() : TextureHandle(0)
{
}
Texture2DClass::~Texture2DClass()
{
}
bool Texture2DClass::Load(std::string File, bool GenerateMipMaps)
{
int Width;
int Height;
int Components;
bool TextureLoaded = false;
FREE_IMAGE_FORMAT ImageFormat = FreeImage_GetFileType(File.c_str(), 0);
FIBITMAP* ImageBitMap = FreeImage_Load(ImageFormat, File.c_str());
FIBITMAP* TempPointer = ImageBitMap;
ImageBitMap = FreeImage_ConvertTo32Bits(ImageBitMap);
FreeImage_Unload(TempPointer);
Width = FreeImage_GetWidth(ImageBitMap);
Height = FreeImage_GetHeight(ImageBitMap);
GLubyte* Texture = new GLubyte[4 * Width*Height];
char* Pixels = (char*)FreeImage_GetBits(ImageBitMap);
for (int x = 0; x < (Width*Height); x++)
{
Texture[x * 4 + 0] = Pixels[x * 4 + 2];
Texture[x * 4 + 1] = Pixels[x * 4 + 1];
Texture[x * 4 + 2] = Pixels[x * 4 + 0];
Texture[x * 4 + 3] = Pixels[x * 4 + 3];
}
TextureID = 0;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)Texture);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, Pixels);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glBindTexture(GL_TEXTURE_2D, 0);
if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);
TextureLoaded = true;
//unsigned char* ImageData = stbi_load(File.c_str(), &Width, &Height, &Components, STBI_rgb_alpha);
//if (ImageData != NULL)
//{
// glGenTextures(1, &TextureHandle);
// glBindTexture(GL_TEXTURE_2D, TextureHandle);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ImageData);
// if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(ImageData);
// glBindTexture(GL_TEXTURE_2D, 0);
// TextureLoaded = true;
//}
return TextureLoaded;
}
void Texture2DClass::Bind(GLuint TexUnit)
{
glActiveTexture(GL_TEXTURE0 + TexUnit);
glBindTexture(GL_TEXTURE_2D, TextureHandle);
}
GLuint Texture2DClass::GetHandle()
{
return TextureHandle;
}
GLuint Texture2DClass::GetTextureID()
{
return TextureID;
}