I am trying to draw a simple cube for a homework assignment for a class but for some reason it isn't showing up.
I am using uniform blocks and modern OpenGL. I am sure I am not doing something correctly.
My complete code is below. The below example depends on GLEW + GLFW + GLM.
What I found interesting is that for my light and material uniform blocks I get the index but the uniform block for my MVP matrix I don't get the index.
Any ideas?
Here is the C++ code:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#ifndef OPENGL_INCLUDES_
#define OPENGL_INCLUDES_
#include "GL\glew.h"
#ifndef GLFW_INCLUDES_
#define GLFW_INCLUDES_
#if defined(_WIN32)
#include <Windows.h>
#define GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WGL
#elif defined(__linux__)
#include <X11/X.h>
#include <X11/extensions/Xrandr.h>
#define GLFW_EXPOSE_NATIVE_X11
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include "GLFW\glfw3.h"
#include "GLFW\glfw3native.h"
#endif
#endif
#ifndef GLM_INCLUDES_
#define GLM_INCLUDES_
#include <glm/glm.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#endif
GLFWwindow* MainWindow;
#ifdef _WIN32
HWND MainWindowWin32Handle;
#endif
GLint WindowWidth = 1024;
GLint WindowHeight = 768;
GLulong SizeDivizor = 1;
GLboolean RiftAvailable = false;
GLboolean UseApplicationWindowFrame = false;
GLuint MainOpenGLShaderProgramID;
GLuint MatricesUniformBlockID;
GLuint MatricesUniformBufferID;
GLuint LightsUniformBlockID;
GLuint LightsUniformBufferID;
GLuint MaterialsUniformBlockID;
GLuint MaterialsUniformBufferID;
glm::mat4 ViewMatrix;
glm::mat4 ViewModelMatrix;
glm::mat4 ProjectionMatrix;
glm::mat4 MVPMatrix;
glm::mat3 NormalMatrix;
class StandardCube;
std::vector<StandardCube> Cubes;
class StandardCube {
private:
GLfloat* Vertices;
GLfloat* Normals;
GLuint* Indices;
GLuint VAO;
glm::mat4 ModelMatrix;
public:
void LoadIntoOpenGL() {
Vertices = new GLfloat[72]
{
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f
};
Normals = new GLfloat[72] {
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f
};
Indices = new GLuint[36] {0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20
};
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
GLuint MeshBufferID;
glGenBuffers(1, &MeshBufferID);
glBindBuffer(GL_ARRAY_BUFFER, MeshBufferID);
GLuint TotalBufferData = (sizeof(GLfloat) * 72) + (sizeof(GLfloat) * 72);
glBufferData(GL_ARRAY_BUFFER, TotalBufferData, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, NULL, sizeof(GLfloat) * 72, Vertices);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 72, sizeof(GLfloat) * 72, Normals);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 72));
glEnableVertexAttribArray(1);
GLuint IndexBufferID;
glGenBuffers(1, &IndexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * 36, Indices, GL_STATIC_DRAW);
glBindVertexArray(NULL);
ModelMatrix = glm::mat4(1.0f);
}
void DrawMe() {
MVPMatrix = ProjectionMatrix * ViewMatrix * ModelMatrix;
ViewModelMatrix = ViewMatrix * ModelMatrix;
NormalMatrix = glm::transpose(glm::inverse(glm::mat3(MVPMatrix)));
glBindBuffer(GL_UNIFORM_BUFFER, MatricesUniformBufferID);
glBufferSubData(GL_UNIFORM_BUFFER, NULL, sizeof(glm::mat4), glm::value_ptr(MVPMatrix));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(ViewModelMatrix));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4) + sizeof(glm::mat4), sizeof(glm::mat3), glm::value_ptr(NormalMatrix));
glBindBuffer(GL_UNIFORM_BUFFER, NULL);
glBindVertexArray(VAO);
glDrawElementsInstanced(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL, 1);
glBindVertexArray(NULL);
}
};
static void GLFWKeyCallback(GLFWwindow* p_Window, GLint p_Key, GLint p_Scancode, GLint p_Action, GLint p_Mods) {
if (p_Key == GLFW_KEY_ESCAPE && p_Action == GLFW_PRESS) {
glfwSetWindowShouldClose(p_Window, GL_TRUE);
}
if (p_Key == GLFW_KEY_O && p_Action == GLFW_PRESS) {
glClearColor(0.2f, 0.1f, 0.3f, 1.0f);
}
if (p_Key == GLFW_KEY_I && p_Action == GLFW_PRESS) {
glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
}
}
static void GLFWWindowResizeCallBack(GLFWwindow* p_Window, GLint width, GLint height) {
//CurrentGLFWApplication->WindowResizeCallBack(p_Window, width, height);
}
static void GLFWMouseMovementCallBack(GLFWwindow* p_Window, GLdouble MouseX, GLdouble MouseY) {
//CurrentGLFWApplication->MouseMovementCallBack(p_Window, MouseX, MouseY);
}
static void GLFWFramebufferSizeCallback(GLFWwindow* window, GLint width, GLint height)
{
glViewport(0, 0, width, height);
}
int initializeGLFWGLEW() {
MainWindow = NULL;
if (!glfwInit())
{
fprintf(stderr, "GLFW failed to initialize.");
glfwTerminate();
return EXIT_FAILURE;
}
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
if (UseApplicationWindowFrame) {
MainWindow = glfwCreateWindow(WindowWidth, WindowHeight, "Basic Oculus Rift Example", NULL, NULL);
}
else {
if (!RiftAvailable) {
MainWindow = glfwCreateWindow(WindowWidth, WindowHeight, "Basic Oculus Rift Example", NULL, NULL);
}
else {
GLint MonitorCount;
GLFWmonitor** GLFW_Monitors = glfwGetMonitors(&MonitorCount);
GLFWmonitor* MonitorToUse;
switch (MonitorCount)
{
case 0:
printf("No monitors found, exiting.\n");
return EXIT_FAILURE;
break;
case 1:
printf("Two monitors expected, found only one, using primary...\n");
MonitorToUse = glfwGetPrimaryMonitor();
break;
case 2:
printf("Two monitors found, using second monitor\n");
MonitorToUse = GLFW_Monitors[1];
break;
default:
printf("More than two monitors found, using second monitor\n");
MonitorToUse = GLFW_Monitors[1];
}
MainWindow = glfwCreateWindow(WindowWidth, WindowHeight, "Basic Oculus Rift Example", MonitorToUse, NULL);
}
}
if (!MainWindow)
{
fprintf(stderr, "Could not determine OpenGL version; exiting.");
glfwTerminate();
return EXIT_FAILURE;
}
glfwMakeContextCurrent(MainWindow);
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
return EXIT_FAILURE;
}
glfwSetInputMode(MainWindow, GLFW_STICKY_KEYS, GL_TRUE);
glfwSetKeyCallback(MainWindow, GLFWKeyCallback);
glfwSetWindowSizeCallback(MainWindow, GLFWWindowResizeCallBack);
glfwSetCursorPosCallback(MainWindow, GLFWMouseMovementCallBack);
glfwSetFramebufferSizeCallback(MainWindow, GLFWFramebufferSizeCallback);
glfwSwapBuffers(MainWindow);
glfwPollEvents();
return EXIT_SUCCESS;
}
int prepareOpenGL() {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_MULTISAMPLE);
return EXIT_SUCCESS;
}
int loadShaders() {
// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Compile Vertex Shader
printf("Compiling Vertext Shader.\n\n");
char const * VertexSource = "#version 330 \n\n\
layout(std140) uniform MatrixInformation {\n\
mat4 m_pvm;\n\
mat4 m_viewModel;\n\
mat3 m_normal;\n\
};\n\
layout(std140) uniform Lights {\n\
vec3 l_dir; \n\
};\n\
in vec4 position;\n\
in vec3 normal;\n\
\n\
\n\
out Data{\n\
vec3 normal;\n\
vec4 eye;\n\
} DataOut;\n\
\n\
void main() {\n\
\n\
DataOut.normal = normalize(m_normal * normal);\n\
DataOut.eye = -(m_viewModel * position);\n\
\n\
gl_Position = m_pvm * position;\n\
}\n\
\n";
glShaderSource(VertexShaderID, 1, &VertexSource, NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
GLint Result = GL_FALSE;
int InfoLogLength;
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0){
std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
std::string ErrorMessage = std::string(&VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
printf("Compiling Fragment Shader.\n\n");
char const * FragmentSource = "#version 330\n\
layout(std140) uniform Materials {\n\
vec4 diffuse;\n\
vec4 ambient;\n\
vec4 specular;\n\
vec4 emissive;\n\
float shininess;\n\
int texCount;\n\
};\
\n\
layout(std140) uniform Lights {\n\
vec3 l_dir; \n\
};\
\n\
in Data{\n\
vec3 normal;\n\
vec4 eye;\n\
} DataIn;\n\
\n\
out vec4 colorOut;\
\n\
void main() {\n\
\n\
vec4 spec = vec4(0.0);\n\
\n\
vec3 n = normalize(DataIn.normal);\n\
vec3 e = normalize(vec3(DataIn.eye));\n\
\n\
float intensity = max(dot(n, l_dir), 0.0);\n\
\n\
if (intensity > 0.0) {\n\
vec3 h = normalize(l_dir + e);\n\
\n\
float intSpec = max(dot(h, n), 0.0);\n\
spec = specular * pow(intSpec, shininess);\n\
}\n\
\n\
colorOut = max(intensity * diffuse + spec, ambient);\n\
}";
glShaderSource(FragmentShaderID, 1, &FragmentSource, NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0){
std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
std::string ErrorMessage = std::string(&FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
// Link the program
printf("Linking shader program.\n\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0){
std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
std::string ErrorMessage = std::string(&ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
MainOpenGLShaderProgramID = ProgramID;
return EXIT_SUCCESS;
}
int prepareShaderUniforms() {
glUseProgram(MainOpenGLShaderProgramID);
LightsUniformBlockID = glGetUniformBlockIndex(MainOpenGLShaderProgramID, "Lights");
glUniformBlockBinding(MainOpenGLShaderProgramID, LightsUniformBlockID, 2);
glGenBuffers(1, &LightsUniformBufferID);
glBindBuffer(GL_UNIFORM_BUFFER, LightsUniformBufferID);
glBindBufferBase(GL_UNIFORM_BUFFER, 2, LightsUniformBufferID);
GLfloat LightDirection[3] = { 1.0f, 1.0f, 0.0f };
glBufferData(GL_UNIFORM_BUFFER, sizeof(LightDirection), &LightDirection, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, NULL);
MatricesUniformBlockID = glGetUniformBlockIndex(MainOpenGLShaderProgramID, "MatrixInformation");
glUniformBlockBinding(MainOpenGLShaderProgramID, MatricesUniformBlockID, 1);
glGenBuffers(1, &MatricesUniformBufferID);
glBindBuffer(GL_UNIFORM_BUFFER, MatricesUniformBufferID);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, MatricesUniformBufferID);
GLsizeiptr TotalBufferSize = sizeof(glm::mat4) + sizeof(glm::mat4);
TotalBufferSize += sizeof(glm::mat3);
glBufferData(GL_ARRAY_BUFFER, TotalBufferSize, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, NULL);
MaterialsUniformBlockID = glGetUniformBlockIndex(MainOpenGLShaderProgramID, "Materials");
glUniformBlockBinding(MainOpenGLShaderProgramID, MaterialsUniformBlockID, 3);
glGenBuffers(1, &MaterialsUniformBufferID);
glBindBuffer(GL_UNIFORM_BUFFER, MaterialsUniformBufferID);
glBindBufferBase(GL_UNIFORM_BUFFER, 3, MaterialsUniformBufferID);
GLfloat Material[18];
//Diffuse
Material[0] = 0.5f;
Material[1] = 0.0f;
Material[2] = 0.0f;
Material[3] = 1.0f;
//Ambient
Material[4] = 0.2f;
Material[5] = 0.2f;
Material[6] = 0.2f;
Material[7] = 1.0f;
//Specular
Material[8] = 0.0f;
Material[9] = 0.0f;
Material[10] = 0.0f;
Material[11] = 1.0f;
//Emissive
Material[12] = 0.0f;
Material[13] = 0.0f;
Material[14] = 0.0f;
Material[15] = 1.0f;
//Shininess
Material[16] = 2.0f;
//Texture Count
Material[17] = 0.0f;
glBufferData(GL_UNIFORM_BUFFER, sizeof(Material), &Material, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, NULL);
return EXIT_SUCCESS;
}
int loadCubes() {
StandardCube NewCube;
NewCube.LoadIntoOpenGL();
Cubes.push_back(NewCube);
return EXIT_SUCCESS;
}
int prepareMatricies() {
GLfloat AspectRatio = (GLfloat)(WindowWidth) / (GLfloat)(WindowHeight);
ProjectionMatrix = glm::perspective(45.0f, AspectRatio, 1.0f, 1000.0f);
ViewMatrix = glm::lookAt(
glm::vec3(4.0f, 3.0f, 3.0f), // camera is at (4,3,3), in world space - Where the camera is inside world.
glm::vec3(0.0f, 0.0f, 0.0f), // and looks at the origin - What point the camera is looking at inside world.
glm::vec3(0.0f, 1.0f, 0.0f)// head is up(set to 0,1,0) - the direction of up for camera.
);
glViewport(0, 0, WindowWidth, WindowHeight);
return EXIT_SUCCESS;
}
int main(int argc, char** argv) {
if (initializeGLFWGLEW() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
if (prepareOpenGL() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
if (loadShaders() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
if (prepareShaderUniforms() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
if (loadCubes() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
if (prepareMatricies() == EXIT_FAILURE) {
exit(EXIT_FAILURE);
}
while (!glfwWindowShouldClose(MainWindow))
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (auto & C : Cubes) {
C.DrawMe();
}
glfwSwapBuffers(MainWindow);
glfwPollEvents();
}
exit(EXIT_SUCCESS);
}
Here is my vertex shader:
#version 330
layout(std140) uniform MatrixInformation {
mat4 m_pvm;
mat4 m_viewModel;
mat3 m_normal;
};
layout(std140) uniform Lights {
vec3 l_dir;
};
in vec4 position;
in vec3 normal;
out Data{
vec3 normal;
vec4 eye;
} DataOut;
void main() {
DataOut.normal = normalize(m_normal * normal);
DataOut.eye = -(m_viewModel * position);
gl_Position = m_pvm * position;
}
And here is my fragment shader:
#version 330
layout(std140) uniform Materials {
vec4 diffuse;
vec4 ambient;
vec4 specular;
vec4 emissive;
float shininess;
int texCount;
};
layout(std140) uniform Lights {
vec3 l_dir;
};
in Data{
vec3 normal;
vec4 eye;
} DataIn;
out vec4 colorOut;
void main() {
vec4 spec = vec4(0.0);
vec3 n = normalize(DataIn.normal);
vec3 e = normalize(vec3(DataIn.eye));
float intensity = max(dot(n, l_dir), 0.0);
if (intensity > 0.0) {
vec3 h = normalize(l_dir + e);
float intSpec = max(dot(h, n), 0.0);
spec = specular * pow(intSpec, shininess);
}
colorOut = max(intensity * diffuse + spec, ambient);
}
So the issue is this part:
//Binding uniform buffer
glBindBuffer(GL_UNIFORM_BUFFER, MatricesUniformBufferID);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, MatricesUniformBufferID);
GLsizeiptr TotalBufferSize = sizeof(glm::mat4) + sizeof(glm::mat4);
TotalBufferSize += sizeof(glm::mat3);
//uploading to the unbound array buffer
glBufferData(GL_ARRAY_BUFFER, TotalBufferSize, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, NULL);
So you need to change
glBufferData(GL_ARRAY_BUFFER, TotalBufferSize, NULL, GL_DYNAMIC_DRAW);
to
glBufferData(GL_UNIFORM_BUFFER, TotalBufferSize, NULL, GL_DYNAMIC_DRAW);
Additionally uploading NULL doesn't work on my OpenGL driver (although it should), so I had to
std::array<unsigned char,sizeof(glm::mat4)*2+sizeof(glm::mat3)> buff;
glBufferData(GL_UNIFORM_BUFFER, TotalBufferSize, buff.data(), GL_DYNAMIC_DRAW);
As for how you would find that out: the old-school method is to litter your code with glGetError calls that only go off in debug mode. The more modern method is to create a debug context and print break on the callback to find out whereabouts the error is (then you can make glGetError calls in that area to get the exact function call if you don't know it by then, since modern OpenGL drivers are threaded and the callback may be triggered one or two function calls later).
Related
I am writing a program in Windows with C++, OpenGL and Freeglut, to learn how to use it..
The code I have written is working as follows:
#define GLEW_STATIC
#include <iostream>
#include <GL/glew.h>;
#include <freeglut.h>
#include <Windows.h>
#include <stdio.h>
#include <string>
#include <vector>
#include "fstream"
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>;
#include <chrono>
GLint partColorUniformLocation;
GLint bufferColorUniformLocation;
GLint vpMatrixUniformLocation;
GLint shows;
float radi = 5.0f;
float angx = 0.30;
float angy = 4.60;
float camX = -cos(angx) * sin(angy) * radi;
float camY = -cos(angx) * cos(angy) * radi;
float camZ = sin(angx) * radi;
float zoom = 5.0f;
float ww0;
float ww1;
float wh0;
float wh1;
float menubar;
float menur = 400;
using namespace std;
using glm::vec3;
using glm::vec4;
using glm::mat3;
using glm::mat4;
mat4 PV;
vector <mat3> Model;
mat3 World = mat3(1.0f);
vec3 UP(0.0f, 0.0f, 1.0f);
HWND hWnd;
HINSTANCE hInst;
float ww;
float wh;
int ln0 = 1280;
int shaderProgram;
unsigned int VBO;
GLuint boxID;
unsigned int VCOx;
unsigned int VCOy;
unsigned int VCOz;
GLuint coordxID;
GLuint coordyID;
GLuint coordzID;
unsigned int VTO;
GLuint ptexID;
GLuint texID;
unsigned int VTO2;
GLuint rptexID;
GLuint rtexID;
vector <char> b;
vector <unsigned char> bu;
GLuint texture0;
GLint mW;
void GetDesktopResolution(int& ww, int& wh);
void getDesktop();
void box();
void coord();
void reshape(int ww, int wh);
void display(void);
void texturemenu();
void bmpsread();
void maketexture0(int tw, int th);
void mouseButton(int button, int state, int x, int y);
void mouseMove(int x, int y);
void installShader();
const char* vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 inNormal;\n"
"layout (location = 2) in vec3 inColor;\n"
"layout (location = 3) in vec2 tex;\n"
"uniform mat4 VP;\n"
"out vec3 outColor;\n"
"out vec3 Normal;\n"
"out vec2 outTex;\n"
"void main()\n"
"{\n"
" vec4 v = vec4(position, 1.0);\n"
" gl_Position = VP * v;\n"
" outColor = inColor;\n"
" Normal = inNormal;\n"
" outTex = tex;\n"
"}\0";
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 color;\n"
"in vec3 outColor;\n"
"in vec3 Normal;\n"
"in vec2 outTex;\n"
"uniform int shows;\n"
"uniform sampler2D texture0;\n"
"uniform vec3 partColor;"
"uniform vec3 bufferColor;"
"void main()\n"
"{\n"
" if (shows == 0) \n"
" {\n"
" color = vec4(bufferColor/255.0, 1.0);\n"
" }\n"
" else if (shows == 1)\n"
" {\n"
" color = vec4(1.0, 1.0, 1.0, 1.0);\n"
" }\n"
" else if (shows == 2)\n"
" {\n"
" color = vec4(partColor, 1.0);\n"
" }\n"
" else if (shows == 3)\n"
" {\n"
" color = texture(texture0, outTex);\n"
" }\n"
"}\n\0";
void GetDesktopResolution(float& ww, float& wh) {
RECT desktop;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktop);
ww = desktop.right;
wh = desktop.bottom;
}
void getDesktop() {
wh = glutGet(GLUT_WINDOW_HEIGHT);
ww = glutGet(GLUT_WINDOW_WIDTH);
menubar = 35;
ww0 = -(ww - menur) / 10.0f;
ww1 = (ww - menur) / 10.0f;
wh0 = -(wh - menubar) / 10.0f + 30;
wh1 = (wh - menubar) / 10.0f + 30;
zoom = 0.2f;
}
void box() {
vector <float> box;
box.resize(72);
box = {
-10.0f, -10.0f, 20.0f,
10.0f, -10.0f, 20.0f,
10.0f, -10.0f, 20.0f,
10.0f, 10.0f, 20.0f,
10.0f, 10.0f, 20.0f,
-10.0f, 10.0f, 20.0f,
-10.0f, 10.0f, 20.0f,
-10.0f, -10.0f, 20.0f,
-10.0f, -10.0f, 0.0f,
10.0f, -10.0f, 0.0f,
10.0f, -10.0f, 0.0f,
10.0f, 10.0f, 0.0f,
10.0f, 10.0f, 0.0f,
-10.0f, 10.0f, 0.0f,
-10.0f, 10.0f, 0.0f,
-10.0f, -10.0f, 0.0f,
-10.0f, -10.0f, 20.0f,
-10.0f, -10.0f, 0.0f,
10.0f, -10.0f, 20.0f,
10.0f, -10.0f, 0.0f,
10.0f, 10.0f, 20.0f,
10.0f, 10.0f, 0.0f,
-10.0f, 10.0f, 20.0f,
-10.0f, 10.0f, 0.0f
};
glGenBuffers(1, &boxID);
glBindBuffer(GL_ARRAY_BUFFER, boxID);
glBufferData(GL_ARRAY_BUFFER, box.size() * sizeof(float), box.data(), GL_STATIC_DRAW);
box.clear();
glGenVertexArrays(1, &VBO);
glBindVertexArray(VBO);
glBindBuffer(GL_ARRAY_BUFFER, boxID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindVertexArray(0);
}
void coord() {
vector <float> coordx(6);
coordx = {
0.0f, 0.0f, 0.0f,
10.0f, 0.0f, 0.0f
};
vector <float> coordy(6);
coordy = {
0.0f, 0.0f, 0.0f,
0.0f, 10.0f, 0.0f
};
vector <float> coordz(6);
coordz = {
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 10.0f
};
glGenBuffers(1, &coordxID);
glBindBuffer(GL_ARRAY_BUFFER, coordxID);
glBufferData(GL_ARRAY_BUFFER, coordx.size() * sizeof(float), coordx.data(), GL_STATIC_DRAW);
coordx.clear();
glGenBuffers(1, &coordyID);
glBindBuffer(GL_ARRAY_BUFFER, coordyID);
glBufferData(GL_ARRAY_BUFFER, coordy.size() * sizeof(float), coordy.data(), GL_STATIC_DRAW);
coordy.clear();
glGenBuffers(1, &coordzID);
glBindBuffer(GL_ARRAY_BUFFER, coordzID);
glBufferData(GL_ARRAY_BUFFER, coordz.size() * sizeof(float), coordz.data(), GL_STATIC_DRAW);
coordz.clear();
glGenVertexArrays(1, &VCOx);
glBindVertexArray(VCOx);
glBindBuffer(GL_ARRAY_BUFFER, coordxID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glGenVertexArrays(1, &VCOy);
glBindVertexArray(VCOy);
glBindBuffer(GL_ARRAY_BUFFER, coordyID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glGenVertexArrays(1, &VCOz);
glBindVertexArray(VCOz);
glBindBuffer(GL_ARRAY_BUFFER, coordzID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindVertexArray(0);
}
void reshape(int ww, int wh) {
getDesktop();
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
partColorUniformLocation = glGetUniformLocation(shaderProgram, "partColor");
bufferColorUniformLocation = glGetUniformLocation(shaderProgram, "bufferColor");
vpMatrixUniformLocation = glGetUniformLocation(shaderProgram, "VP");
shows = glGetUniformLocation(shaderProgram, "shows");
}
void display(void) {
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, ww - menur, wh - menubar);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glFrontFace(GL_CW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
mat4 Projection = glm::ortho(ww0, ww1, wh0, wh1, -1000.0f, 1000.0f); //left, right, bottom, top, zNear, zFar
mat4 View = glm::lookAt(glm::vec3(camX, camY, camZ), glm::vec3(0.0f, 0.0f, 0.0f), UP);
mat4 VP = Projection * View;
vec3 partColor;
glUniformMatrix4fv(vpMatrixUniformLocation, 1, GL_FALSE, &VP[0][0]);
glUniform1i(shows, 2);
glLineWidth(2);
glBindVertexArray(VCOx); // Coordx
partColor = vec3(1.0f, 0.0f, 0.0f);
glUniform3fv(partColorUniformLocation, 1, &partColor[0]);
glDrawArrays(GL_LINES, 0, 2);
glBindVertexArray(VCOy); // Coordy
partColor = vec3(0.0f, 1.0f, 0.0f);
glUniform3fv(partColorUniformLocation, 1, &partColor[0]);
glDrawArrays(GL_LINES, 0, 2);
glBindVertexArray(VCOz); // Coordz
partColor = vec3(0.0f, 0.0f, 1.0f);
glUniform3fv(partColorUniformLocation, 1, &partColor[0]);
glDrawArrays(GL_LINES, 0, 2);
glBindVertexArray(VBO); // Box
partColor = vec3(1.0f, 1.0f, 1.0f);
glUniform3fv(partColorUniformLocation, 1, &partColor[0]);
glDrawArrays(GL_LINES, 0, 24);
glUniform1i(shows, 1);
glDisable(GL_DEPTH_TEST);
glViewport(0, wh - 35, ww, wh);
VP = glm::ortho(0.0f, ww, 0.0f, 35.0f) * glm::mat4(1.0f); //left, right, bottom, top
glUniformMatrix4fv(vpMatrixUniformLocation, 1, GL_FALSE, &VP[0][0]);
glBindVertexArray(VTO2);
glDrawArrays(GL_TRIANGLES, 0, 6);
glEnable(GL_TEXTURE0);
glUniform1i(shows, 3);
glViewport(0, wh - menubar, ln0, 35);
VP = glm::ortho(0.0f, float(ln0), 0.0f, 35.0f) * glm::mat4(1.0f); //left, right, bottom, top
glUniformMatrix4fv(vpMatrixUniformLocation, 1, GL_FALSE, &VP[0][0]);
glBindVertexArray(VTO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisable(GL_TEXTURE0);
glutSwapBuffers();
}
void texturemenu() {
vector <float> ptex(18);
float top = 35;
float bottom = 0;
float left = 0;
float right = 1280;
ptex = {
right, top, 0.0f,
left, top, 0.0f,
left, bottom, 0.0f,
left, bottom, 0.0f,
right, bottom, 0.0f,
right, top, 0.0f
};
vector <float> tex(12);
tex = {
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
glGenBuffers(1, &ptexID);
glBindBuffer(GL_ARRAY_BUFFER, ptexID);
glBufferData(GL_ARRAY_BUFFER, ptex.size() * sizeof(float), ptex.data(), GL_STATIC_DRAW);
ptex.clear();
glGenTextures(1, &texID);
glBindBuffer(GL_ARRAY_BUFFER, texID);
glBufferData(GL_ARRAY_BUFFER, tex.size() * sizeof(float), tex.data(), GL_STATIC_DRAW);
tex.clear();
glGenVertexArrays(1, &VTO);
glBindVertexArray(VTO);
glBindBuffer(GL_ARRAY_BUFFER, ptexID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, texID);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindVertexArray(0);
}
void bmpsread() {
bu.clear();
bu.resize(3 * 35 * ln0, 255);
int bmpn = 0;
string sbmp;
sbmp = "C:/C++/icons/dll/new.bmp";
if (sbmp.size() > 0) {
ifstream fo;
unsigned int fn = 0;
fo.open(sbmp, std::ios::binary | std::ios::in);
if (fo.is_open()) {
std::ifstream in_file(sbmp, std::ios::binary | std::ios::ate);
fo.seekg(0, std::ios::beg);
int file_size = in_file.tellg();
fn = file_size;
b.resize(fn);
fo.seekg(0, std::ios::beg);
fo.read(b.data(), fn);
}
fo.close();
fo.clear();
int dataPos = *(int*)&(b[0x0A]);
int www = *(int*)&(b[0x12]);
int ka = 0;
int kx = ceil((((float)www * 3) + 1) / 4) * 4 - www * 3;
if (kx == 4) { kx = 0; }
for (int i = 0; i < 35; i++) {
for (int j = 0; j < www; j++) {
bu[i * ln0 * 3 + (bmpn + j) * 3] = (unsigned char)b[i * www * 3 + j * 3 + ka + dataPos];
bu[i * ln0 * 3 + (bmpn + j) * 3 + 1] = (unsigned char)b[i * www * 3 + j * 3 + ka + 1 + dataPos];
bu[i * ln0 * 3 + (bmpn + j) * 3 + 2] = (unsigned char)b[i * www * 3 + j * 3 + ka + 2 + dataPos];
}
ka += kx;
}
b.clear();
}
bmpn += 37;
}
void maketexture0(int tw, int th) {
glGenTextures(1, &texture0);
auto textureUniformLocation = glGetUniformLocation(shaderProgram, "texture0");
glUniform1i(texture0, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glEnable(GL_TEXTURE0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_BGR, GL_UNSIGNED_BYTE, bu.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
bu.clear();
}
void installShader() {
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDetachShader(shaderProgram, vertexShader);
glDetachShader(shaderProgram, fragmentShader);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitWindowSize(800, 600);
mW = glutCreateWindow(" Texture Test");
glewInit();
HWND win_handle = FindWindow(0, L" Texture Test");
WNDCLASS wc = { 0 };
if (!win_handle) {
return -1;
}
SetWindowLong(win_handle, GWL_STYLE, (GetWindowLong(win_handle, GWL_STYLE) | WS_MAXIMIZE));
ShowWindowAsync(win_handle, SW_SHOWDEFAULT);
ShowWindowAsync(win_handle, SW_SHOWMAXIMIZED);
box();
coord();
installShader();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
Without Texture:
But after I add texture to create a menu bar (in this sample a "new.bmp" picture) with adding following code after "coord();" in "int main(int argc, char** argv)":
bmpsread();
texturemenu();
maketexture0(ln0, 35);
Then my box disappears and my coordinates are wrong.
With Texture:
What am I doing wrong?
I have this OpenGL code the draws a cube and pyramid. However, this program rotates the pyramid and cube together. I am tasked with only making the cube itself move not both objects at the same time. I know for this to happen I have to implement shaders for both. I'm not sure how to go about implementing both of the shaders at once. Any tips?
/*
This program demonstrates simple lighting.
A pyramid is lighted by a point light and can be rotated by mouse.
Ying Zhu
Georgia State University
October 2016
*/
// GLEW header
#include <GL/glew.h> // This must appear before freeglut.h
// Freeglut header
#include <GL/freeglut.h>
// GLM header files
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
// #include <glm/gtx/transform2.hpp>
#include <glm/gtc/matrix_access.hpp>
// #include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/type_ptr.hpp>
// C++ header files
#include <iostream>
using namespace std;
using namespace glm;
#define BUFFER_OFFSET(offset) ((GLvoid *) offset)
// VBO buffer IDs
GLuint vertexArrayBufferID = 0;
GLuint normalArrayBufferID = 0;
GLuint cubePosition = 0;
GLuint cubeElements = 0;
GLuint program; // shader program ID
// Shader variable IDs
GLint vPos; // vertex attribute: position
GLint normalID; // vertex attribute: normal
GLint mvpMatrixID; // uniform variable: model, view, projection matrix
GLint modelMatrixID; // uniform variable: model, view matrix
GLint normalMatrixID; // uniform variable: normal matrix for transforming normals
GLint lightSourcePositionID; // uniform variable: for lighting calculation
GLint diffuseLightProductID; // uniform variable: for lighting calculation
GLint ambientID;
GLint attenuationAID;
GLint attenuationBID;
GLint attenuationCID;
// Transformation matrices
mat4 projMatrix;
mat4 mvpMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
mat3 normalMatrix; // Normal matrix for transforming normals
// Light parameters
vec4 lightSourcePosition = vec4(0.0f, 4.0f, 0.0f, 1.0f);
vec4 diffuseMaterial = vec4(0.5f, 0.5f, 0.0f, 1.0f);
vec4 diffuseLightIntensity = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f);
float attenuationA = 1.0f;
float attenuationB = 0.2f;
float attenuationC = 0.0f;
vec4 diffuseLightProduct;
// Camera parameters
vec3 eyePosition = vec3(0.0f, 0.0f, 4.0f);
vec3 lookAtCenter = vec3(0.0f, 0.0f, 0.0f);
vec3 upVector = vec3(0.0f, 1.0f, 0.0f);
float fieldOfView = 30.0f;
float nearPlane = 0.1f;
float farPlane = 1000.0f;
// Mouse controlled rotation angles
float rotateX = 0;
float rotateY = 0;
struct VertexData {
GLfloat vertex[3];
VertexData(GLfloat x, GLfloat y, GLfloat z) {
vertex[0] = x; vertex[1] = y; vertex[2] = z;
}
};
//---------------------------------------------------------------
// Initialize vertex arrays and VBOs
void prepareVBOs() {
// Define a 3D pyramid.
GLfloat vertices[][4] = {
{1.0f, -1.0f, 1.0f, 1.0f}, // face 1
{-1.0f, -1.0f, -1.0f, 1.0f},
{1.0f, -1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f, 1.0f }, // face 2
{0.0f, 1.0f, 0.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 3
{ 0.0f, 1.0f, 0.0f, 1.0f },
{-1.0f, -1.0f, 1.0f, 1.0f},
{ -1.0f, -1.0f, 1.0f, 1.0f }, // face 4
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f, 1.0f }, // face 5
{ 1.0f, -1.0f, -1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 6
{ -1.0f, -1.0f, 1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f }
};
GLfloat normals[][4] = {
{0.0f, -1.0f, 0.0f, 1.0f}, // normal 1
{0.0f, -1.0f, 0.0f, 1.0f },
{0.0f, -1.0f, 0.0f, 1.0f },
{0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 2
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{-0.0f, 0.4472f, 0.8944f, 1.0f}, // normal 3
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{-0.8944f, 0.4472f, 0.0f, 1.0f}, // normal 4
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{0.0f, 0.4472f, -0.8944f, 1.0f}, // normal 5
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }, // normal 6
{ 0.0f, -1.0f, 0.0f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }
};
// Cube positioins
VertexData vertexData[] = {
VertexData(0.0, 0.0, 0.0), /* Index 0 */
VertexData(0.0, 0.0, 1.0), /* Index 1 */
VertexData(0.0, 1.0, 0.0), /* Index 2 */
VertexData(0.0, 1.0, 1.0), /* Index 3 */
VertexData(1.0, 0.0, 0.0), /* Index 4 */
VertexData(1.0, 0.0, 1.0), /* Index 5 */
VertexData(1.0, 1.0, 0.0), /* Index 6 */
VertexData(1.0, 1.0, 1.0), /* Index 7 */
};
// Cube elements
GLubyte indices[] = {
4, 5, 7, // +X face
4, 7, 6,
0, 2, 3, // ‐X face
0, 3, 1,
2, 6, 7, // +Y face
2, 7, 3,
0, 1, 5, // ‐Y face
0, 5, 4,
0, 4, 6, // +Z face
0, 6, 2,
1, 3, 7, // ‐Z face
1, 7, 5
};
// Get an unused buffer object name. Required after OpenGL 3.1.
glGenBuffers(1, &vertexArrayBufferID);
// If it's the first time the buffer object name is used, create that buffer.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Allocate memory for the active buffer object.
// 1. Allocate memory on the graphics card for the amount specified by the 2nd parameter.
// 2. Copy the data referenced by the third parameter (a pointer) from the main memory to the
// memory on the graphics card.
// 3. If you want to dynamically load the data, then set the third parameter to be NULL.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &normalArrayBufferID);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);
glGenBuffers(1, &cubePosition);
glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData),
vertexData, GL_STATIC_DRAW);
glGenBuffers(1, &cubeElements);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),
indices, GL_STATIC_DRAW);
}
//---------------------------------------------------------------
// Print out the output of the shader compiler
void printLog(GLuint obj)
{
int infologLength = 0;
char infoLog[1024];
if (glIsShader(obj)) {
glGetShaderInfoLog(obj, 1024, &infologLength, infoLog);
}
else {
glGetProgramInfoLog(obj, 1024, &infologLength, infoLog);
}
if (infologLength > 0) {
cout << infoLog;
}
}
//-------------------------------------------------------------------
void prepareShaders() {
// Vertex shader source code
// A point light source is implemented.
// For simplicity, only the ambient and diffuse components are implemented.
// The lighting is calculated in world space, not in camera space.
const char* vSource = {
"#version 330\n"
"in vec4 vPos;"
"in vec4 normal;"
"uniform mat4x4 mvpMatrix;"
"uniform mat4x4 modelMatrix;"
"uniform mat3x3 normalMatrix;"
"uniform vec4 lightSourcePosition;"
"uniform vec4 diffuseLightProduct;"
"uniform vec4 ambient;"
"uniform float attenuationA;"
"uniform float attenuationB;"
"uniform float attenuationC;"
"out vec4 color;"
"void main() {"
" gl_Position = mvpMatrix * vPos;"
// Transform the vertex position to the world space.
" vec4 transformedVertex = modelMatrix * vPos;"
// Transform the normal vector to the world space.
" vec3 transformedNormal = normalize(normalMatrix * normal.xyz);"
// Light direction
" vec3 lightVector = normalize(transformedVertex.xyz - lightSourcePosition.xyz);"
// Distance between the light source and vertex
" float dist = distance(lightSourcePosition.xyz, transformedVertex.xyz);"
// Attenuation factor
" float attenuation = 1.0f / (attenuationA + (attenuationB * dist) + (attenuationC * dist * dist));"
// Calculate the diffuse component of the lighting equation.
" vec4 diffuse = attenuation * (max(dot(transformedNormal, lightVector), 0.0) * diffuseLightProduct);"
// Combine the ambient component and diffuse component.
" color = ambient + diffuse;"
"}"
};
// Fragment shader source code
const char* fSource = {
"#version 330\n"
"in vec4 color;"
"out vec4 fragColor;"
"void main() {"
" fragColor = color;"
"}"
};
// Declare shader IDs
GLuint vShader, fShader;
// Create empty shader objects
vShader = glCreateShader(GL_VERTEX_SHADER);
fShader = glCreateShader(GL_FRAGMENT_SHADER);
// Attach shader source code the shader objects
glShaderSource(vShader, 1, &vSource, NULL);
glShaderSource(fShader, 1, &fSource, NULL);
// Compile shader objects
glCompileShader(vShader);
printLog(vShader);
glCompileShader(fShader);
printLog(fShader);
// Create an empty shader program object
program = glCreateProgram();
// Attach vertex and fragment shaders to the shader program
glAttachShader(program, vShader);
glAttachShader(program, fShader);
// Link the shader program
glLinkProgram(program);
printLog(program);
}
//---------------------------------------------------------------
// Retrieve the IDs of the shader variables. Later we will
// use these IDs to pass data to the shaders.
void getShaderVariableLocations(GLuint shaderProgram) {
// Retrieve the ID of a vertex attribute, i.e. position
vPos = glGetAttribLocation(shaderProgram, "vPos");
normalID = glGetAttribLocation(shaderProgram, "normal");
mvpMatrixID = glGetUniformLocation(shaderProgram, "mvpMatrix");
modelMatrixID = glGetUniformLocation(shaderProgram, "modelMatrix");
normalMatrixID = glGetUniformLocation(shaderProgram, "normalMatrix");
lightSourcePositionID = glGetUniformLocation(shaderProgram, "lightSourcePosition");
diffuseLightProductID = glGetUniformLocation(shaderProgram, "diffuseLightProduct");
ambientID = glGetUniformLocation(shaderProgram, "ambient");
attenuationAID = glGetUniformLocation(shaderProgram, "attenuationA");
attenuationBID = glGetUniformLocation(shaderProgram, "attenuationB");
attenuationCID = glGetUniformLocation(shaderProgram, "attenuationC");
}
//---------------------------------------------------------------
void setShaderVariables() {
// value_ptr is a glm function
glUniformMatrix4fv(mvpMatrixID, 1, GL_FALSE, value_ptr(mvpMatrix));
glUniformMatrix4fv(modelMatrixID, 1, GL_FALSE, value_ptr(modelMatrix));
glUniformMatrix3fv(normalMatrixID, 1, GL_FALSE, value_ptr(normalMatrix));
glUniform4fv(lightSourcePositionID, 1, value_ptr(lightSourcePosition));
glUniform4fv(diffuseLightProductID, 1, value_ptr(diffuseLightProduct));
glUniform4fv(ambientID, 1, value_ptr(ambient));
glUniform1f(attenuationAID, attenuationA);
glUniform1f(attenuationBID, attenuationB);
glUniform1f(attenuationCID, attenuationC);
}
//---------------------------------------------------------------
// Set lighting related parameters
void setLightingParam() {
diffuseLightProduct = diffuseMaterial * diffuseLightIntensity;
}
//---------------------------------------------------------------
// Build the model matrix. This matrix will transform the 3D object to the proper place.
mat4 buildModelMatrix() {
mat4 rotationXMatrix = rotate(mat4(1.0f), radians(rotateX), vec3(1.0f, 0.0f, 0.0f));
mat4 rotationYMatrix = rotate(mat4(1.0f), radians(rotateY), vec3(0.0f, 1.0f, 0.0f));
mat4 matrix = rotationYMatrix * rotationXMatrix;
return matrix;
}
//---------------------------------------------------------------
void buildMatrices() {
modelMatrix = buildModelMatrix();
mvpMatrix = projMatrix * viewMatrix * modelMatrix;
normalMatrix = column(normalMatrix, 0, vec3(modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2]));
normalMatrix = column(normalMatrix, 1, vec3(modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2]));
normalMatrix = column(normalMatrix, 2, vec3(modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2]));
// Use glm::inverseTranspose() to create a normal matrix, which is used to transform normal vectors.
normalMatrix = inverseTranspose(normalMatrix);
}
//---------------------------------------------------------------
// Handles the display event
void display()
{
// Clear the window with the background color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
buildMatrices();
setShaderVariables();
// Activate the shader program
glUseProgram(program);
// If the buffer object already exists, make that buffer the current active one.
// If the buffer object name is 0, disable buffer objects.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Associate the vertex array in the buffer object with the vertex attribute: "position"
glVertexAttribPointer(vPos, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// Enable the vertex attribute: "position"
glEnableVertexAttribArray(vPos);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glVertexAttribPointer(normalID, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(normalID);
// Start the shader program. Draw the object. The third parameter is the number of triangles.
glDrawArrays(GL_TRIANGLES, 0, 18);
glBindBuffer(GL_ARRAY_BUFFER, cubePosition);
glVertexAttribPointer(vPos, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vPos);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeElements);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
// Refresh the window
glutSwapBuffers();
}
//---------------------------------------------------------------
// Handles the reshape event
void reshape(int width, int height)
{
// Specify the width and height of the picture within the window
glViewport(0, 0, width, height);
projMatrix = perspective(fieldOfView, (float)width / (float)height, nearPlane, farPlane);
viewMatrix = lookAt(eyePosition, lookAtCenter, upVector);
}
//---------------------------------------------------------------
// Read mouse motion data and convert them to rotation angles.
void passiveMotion(int x, int y) {
rotateY = (float)x * -0.8f;
rotateX = (float)y * -0.8f;
// Generate a dislay event to force refreshing the window.
glutPostRedisplay();
}
//-----------------------------------------------------------------
void init() {
prepareVBOs();
prepareShaders();
getShaderVariableLocations(program);
setLightingParam();
// Specify the background color
glClearColor(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
//---------------------------------------------------------------
void main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("Lighting Demo");
glutReshapeWindow(800, 800);
glewInit();
init();
// Register the display callback function
glutDisplayFunc(display);
// Register the reshape callback function
glutReshapeFunc(reshape);
// Register the passive mouse motion call back function
// This function is called when the mouse moves within the window
// while no mouse buttons are pressed.
glutPassiveMotionFunc(passiveMotion);
// Start the event loop
glutMainLoop();
}
Well, the most obvious culprit here would be setting a single ModelMatrix for both - I can't see any logic in your code to set them independently for each object you're rendering.
Since each object has a different rotation (and presumably, unless you're planning to draw one on top of the other, a different translation), you would want to be generating / loading a different model matrix for each draw call.
You dont need to use different shaders, you just need to use different model matricies. Say you have two objects in you scene something like this:
while (!myWindow(shouldClose))
{
myShader.use();
glBindVertexArray(myVao1);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid
glBindVertaxArray(myVao2);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}
Say you want only the second model to rotate on the y axis, you could do something like this:
float rotationDegree = 0;
while (!myWindow(shouldClose))
{
myShader.use();
myShader.setMat4(glm::mat4(1.0f)) // Make sure to set it to normal matrix for the pyrmamid
glBindVertexArray(myVao1);
glDrawArrays(GL_TRIANGLES, 0, x); // Draw pyramid
glBindVertaxArray(myVao2);
glm::mat4 model = glm::mat4(1.0f);
glm::rotate(model, glm::radians(rotationDegree), glm::vec3(0.0f, 1.0f, 0.0f));
rotateionDegree += 0.01;
myShader.setMat4("model", model); // Set you model matrix in your shader.
glDrawArrays(GL_TRIANGLES, 0, x); // Draw cube
}
I have been using this tutorial to create a skybox but sampling the texture returns black. If I use my texture coordinates as colour then I get sensible looking coloured skybox so I presume the problem is with the texture sampling! Could it be a problem with my graphics card or openGL version?
Here is my code:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "utils.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#define CUSTOM_PI 3.1415926535897931
/*
*
* Include files for Windows, Linux and OSX
* __APPLE is defined if OSX, otherwise Windows and Linux.
*
*/
#ifdef __APPLE__
#define GLFW_INCLUDE_GLCOREARB 1
#include <GLFW/glfw3.h>
#else
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#endif
struct Vertex {
GLfloat position[3];
};
float aspectRatio;
// Position of camera in world space
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 200.0f);
// Camera Orientation in view space
glm::vec3 cameraOrientation = glm::vec3(0.0f, 1.0f, 0.0f);
/* yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction
* vector pointing to the right so we initially rotate a bit to the left. */
float yaw = -90.0f;
float pitch = 0.0f;
float yawPitchStep = 2.0f;
float cameraSpeed = 0.0f;
float cameraAccelerationStep = 0.025f;
float cameraDecelerationStep = 0.05f;
bool left = false;
bool right = false;
bool page_up = false;
bool page_down = false;
bool up = false;
bool down = false;
GLuint shaderProgram;
unsigned int skyboxVAO, skyboxVBO;
unsigned int cubemapTexture;
/* Whenever the window size changed (by OS or user resize) this callback
* function executes */
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
/* Make sure the viewport matches the new window dimensions; note that width
* and height will be significantly larger than specified on retina
* displays. */
if (!(width == 0 || height == 0)) {
aspectRatio = (float)width / (float)height;
glViewport(0, 0, width, height);
}
}
static void key_callback(GLFWwindow *window, int key, int scancode, int action,
int mods) {
if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
}
if (key == GLFW_KEY_LEFT) {
if (action == GLFW_PRESS)
left = true;
if (action == GLFW_RELEASE)
left = false;
}
if (key == GLFW_KEY_RIGHT) {
if (action == GLFW_PRESS)
right = true;
if (action == GLFW_RELEASE)
right = false;
}
if (key == GLFW_KEY_PAGE_UP) {
if (action == GLFW_PRESS)
page_up = true;
if (action == GLFW_RELEASE)
page_up = false;
}
if (key == GLFW_KEY_PAGE_DOWN) {
if (action == GLFW_PRESS)
page_down = true;
if (action == GLFW_RELEASE)
page_down = false;
}
if (key == GLFW_KEY_UP) {
if (action == GLFW_PRESS)
up = true;
if (action == GLFW_RELEASE)
up = false;
}
if (key == GLFW_KEY_DOWN) {
if (action == GLFW_PRESS)
down = true;
if (action == GLFW_RELEASE)
down = false;
}
}
// loads a cubemap texture from 6 individual texture faces
// order:
// +X (right)
// -X (left)
// +Y (top)
// -Y (bottom)
// +Z (front)
// -Z (back)
// -------------------------------------------------------
unsigned int loadCubemap(std::vector<std::string> faces) {
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++) {
unsigned char *data =
stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width,
height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
} else {
std::cout << "Cubemap texture failed to load at path: " << faces[i]
<< std::endl;
stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return textureID;
}
void setup() {
// These pointers will receive the contents of our shader source code files
GLchar *vertexSource, *fragmentSource;
// These are handles used to reference the shaders
GLuint vertexShader, fragmentShader;
/* Read our shaders into the appropriate buffers */
vertexSource = fileToBuf("./skyboxShader.vert");
fragmentSource = fileToBuf("./skyboxShader.frag");
/* Assign our handles a "name" to new shader objects */
vertexShader = glCreateShader(GL_VERTEX_SHADER);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
/* Associate the source code buffers with each handle */
glShaderSource(vertexShader, 1, (const GLchar **)&vertexSource, 0);
glShaderSource(fragmentShader, 1, (const GLchar **)&fragmentSource, 0);
/* Compile our shader objects */
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
/* Assign our program handle a "name" */
shaderProgram = glCreateProgram();
// Attach our shaders to our program
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindAttribLocation(shaderProgram, 0, "in_Position");
// Link our program, and set it as being actively used
glLinkProgram(shaderProgram);
checkShader(shaderProgram, "Basic Shader");
glUseProgram(shaderProgram);
GLuint cubemapTexture;
glGenTextures(1, &cubemapTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
float skyboxVertices[] = {
// positions
-1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f};
// skybox VAO
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float),
(void *)0);
// load textures
// -------------
std::vector<std::string> faces{
"./textures/skybox/right.jpg", "./textures/skybox/left.jpg",
"./textures/skybox/top.jpg", "./textures/skybox/bottom.jpg",
"./textures/skybox/front.jpg", "./textures/skybox/back.jpg"};
cubemapTexture = loadCubemap(faces);
// shader configuration
// --------------------
glUniform1i(glGetUniformLocation(shaderProgram, "skybox"), 0);
}
void render(float time, glm::mat4 projection, glm::mat4 view) {
// draw skybox as last
glDepthFunc(GL_LEQUAL); // change depth function so depth test passes when
// values are equal to depth buffer's content
glUseProgram(shaderProgram);
view =
glm::mat4(glm::mat3(view)); // remove translation from the view matrix
glm::mat4 VP = projection * view;
// Bind Model, View, Perspective transformation matrix to be a uniform
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "vpmatrix"), 1,
GL_FALSE, glm::value_ptr(VP));
// skybox cube
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // set depth function back to default
}
int main(void) {
int k = 0;
float time = 0;
GLFWwindow *window;
if (!glfwInit()) {
printf("Failed to start GLFW\n");
exit(EXIT_FAILURE);
}
#ifdef __APPLE__
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
window = glfwCreateWindow(800, 800, "Graphics Test", NULL, NULL);
aspectRatio = 1.0f;
if (!window) {
glfwTerminate();
printf("GLFW Failed to start\n");
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
#ifndef __APPLE__
glewExperimental = GL_TRUE;
int err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
}
#endif
glfwSetKeyCallback(window, key_callback);
fprintf(stderr, "GL INFO %s\n", glGetString(GL_VERSION));
glEnable(GL_DEPTH_TEST);
setup();
printf("Ready to render\n");
while (!glfwWindowShouldClose(window)) { // Main loop
time = glfwGetTime();
// Make our blue to ensure skybox is working.
glClearColor(0.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* Defines the projection:
* With 45 degree field of view
* With A given aspect ratio
* Cliping everything closer than 0.1 to the camera
* Cliping everything further than 1000 from the camera */
glm::mat4 Projection =
glm::perspective(45.0f, aspectRatio, 0.1f, 1000.0f);
if (left) {
yaw -= yawPitchStep;
}
if (right) {
yaw += yawPitchStep;
}
if (page_up) {
pitch += yawPitchStep;
}
if (page_down) {
pitch -= yawPitchStep;
}
if (up) {
cameraSpeed += cameraAccelerationStep;
printf("Camera Speed = %f\n", cameraSpeed);
}
if (down) {
cameraSpeed -= cameraDecelerationStep;
if (cameraSpeed < 0) {
cameraSpeed = 0;
}
printf("Camera Speed = %f\n", cameraSpeed);
}
glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
glm::vec3 cameraDirection = glm::normalize(front);
cameraPos += cameraSpeed * cameraDirection;
glm::mat4 View = glm::lookAt(cameraPos, cameraPos + cameraDirection,
cameraOrientation);
render(time, Projection, View);
k++;
glfwSwapBuffers(window); // Swap front and back rendering buffers
glfwPollEvents(); // Poll for events.
}
glfwTerminate(); // Close window and terminate GLFW
exit(EXIT_SUCCESS); // Exit program
}
And my vertex shader:
#version 400
precision highp float;
// Position of vertex
in vec3 in_Position;
// The model, view, and projection matrices which needs to be applied to every vertex
uniform mat4 vpmatrix;
// Texture coordinates passed on to fragment shader
out vec3 TexCoords;
void main(void) {
TexCoords = in_Position;
vec4 pos = vpmatrix * vec4(in_Position, 1.0);
gl_Position = pos.xyww;
}
And my fragment shader:
#version 400
precision highp float;
in vec3 TexCoords;
out vec4 FragColor;
uniform samplerCube skybox;
void main()
{
FragColor = texture(skybox, TexCoords);
// FragColor = vec4(TexCoords, 1.0f);
}
The global variable unsigned int cubemapTexture;, which is used in the function render is never set, because there is a 2nd (local) variable named cubemapTexture in the function setup.
Remove the local variable cubemapTexture from the function setup to solve the issue:
unsigned int cubemapTexture;
void setup() {
.....
GLuint cubemapTexture; // delete this part of the
glGenTextures(1, &cubemapTexture); //
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture); //
.....
cubemapTexture = loadCubemap(faces);
.....
}
void render(float time, glm::mat4 projection, glm::mat4 view) {
.....
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
.....
}
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps. I want to be able to move the camera around the screen as well.
I have modified the code on this page of the tutorial I am studying to try to instance many spheres. However, I find that at as little as 64 spheres my fps falls below 60, and at 900 spheres my fps is a measly 4. My understanding of instancing is naive, but I believe that I should be getting more frames-per-second than this. 60 fps should be attainable with only 64 spheres. I believe that I am, in some way, causing the CPU and GPU to communicate more often than they should have to. So my question is: How do I instance so many objects (ideally millions) without causing the fps to fall low (ideally 60 fps)?
I am calculating fps by calculating (10 / time_elapsed) every 10 frames, where time_elapsed is the time that has elapsed since the last fps call. I am printing this out using printf on line 118 of my code.
I have been learning OpenGL through this tutorial and so I use 32-bit GLEW and 32-bit GLFW in Visual Studio 2013. I have 8 GB of RAM on a 64-bit operating system (Windows 7) with a 2.30 GHz CPU.
I have tried coding my own example based on the tutorial above. Source code:
(set line #2 to be the number of spheres to be instanced. Make sure line#2 has a whole-number square root. Set line 4 to be the detail of the sphere, the lowest it can go is 0. Higher number = more detailed.)
// Make sure NUM_INS is a square number
#define NUM_INS 1
// Detail up to 4 is probably good enough
#define SPHERE_DETAIL 4
#include <vector>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
std::vector<GLfloat> create_sphere(int recursion);
// The MAIN function, from here we start our application and run the Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Comment to remove wireframe mode
// Setup OpenGL options
glEnable(GL_DEPTH_TEST);
// Setup and compile our shader(s)
Shader shader("core.vs", "core.frag");
// Generate a list of 100 quad locations/translation-vectors
std::vector<glm::vec2> translations(NUM_INS);
//glm::vec2 translations[NUM_INS];
int index = 0;
GLfloat offset = 1.0f / (float)sqrt(NUM_INS);
for (GLint y = -(float)sqrt(NUM_INS); y < (float)sqrt(NUM_INS); y += 2)
{
for (GLint x = -(float)sqrt(NUM_INS); x < (float)sqrt(NUM_INS); x += 2)
{
glm::vec2 translation;
translation.x = (GLfloat)x / (float)sqrt(NUM_INS) + offset;
translation.y = (GLfloat)y / (float)sqrt(NUM_INS) + offset;
translations[index++] = translation;
}
}
// Store instance data in an array buffer
GLuint instanceVBO;
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * NUM_INS, &translations[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// create 12 vertices of a icosahedron
std::vector<GLfloat> vv = create_sphere(SPHERE_DETAIL);
GLuint quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, vv.size() * sizeof(GLfloat), &vv[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
// Also set instance data
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute.
glBindVertexArray(0);
// For printing frames-per-second
float counter = 0;
double get_time = 0;
double new_time;
// Game loop
while (!glfwWindowShouldClose(window))
{
// Print fps by printing (number_of_frames / time_elapsed)
counter += 1;
if (counter > 10) {
counter -= 10;
new_time = glfwGetTime();
printf("fps: %.2f ", (10/(new_time - get_time)));
get_time = new_time;
}
// Check and call events
glfwPollEvents();
// Clear buffers
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw 100 instanced quads
shader.Use();
glm::mat4 model;
model = glm::rotate(model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f));
// Camera/View transformation
glm::mat4 view;
GLfloat radius = 10.0f;
GLfloat camX = sin(glfwGetTime()) * radius;
GLfloat camZ = cos(glfwGetTime()) * radius;
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// Projection
glm::mat4 projection;
projection = glm::perspective(45.0f, (GLfloat)screenWidth / (GLfloat)screenHeight, 0.1f, 100.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(shader.Program, "model");
GLint viewLoc = glGetUniformLocation(shader.Program, "view");
GLint projLoc = glGetUniformLocation(shader.Program, "projection");
// Pass the matrices to the shader
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(quadVAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, vv.size() / 3, NUM_INS); // 100 triangles of 6 vertices each
glBindVertexArray(0);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
std::vector<GLfloat> add_color(std::vector<GLfloat> sphere) {
// Add color
std::vector<GLfloat> colored_sphere;
for (GLint i = 0; i < sphere.size(); i+=9) {
colored_sphere.push_back(sphere[i]);
colored_sphere.push_back(sphere[i+1]);
colored_sphere.push_back(sphere[i+2]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+3]);
colored_sphere.push_back(sphere[i+4]);
colored_sphere.push_back(sphere[i+5]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(sphere[i+6]);
colored_sphere.push_back(sphere[i+7]);
colored_sphere.push_back(sphere[i+8]);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
colored_sphere.push_back(0.0f);
}
return colored_sphere;
}
std::vector<GLfloat> tesselate(std::vector<GLfloat> shape, int recursion) {
if (recursion > 0) {
std::vector<GLfloat> new_sphere = {};
for (GLint i = 0; i < shape.size(); i += 9) {
// 1.902113 approximately
GLfloat radius = sqrt(1.0f + pow((1.0f + sqrt(5.0f)) / 2.0f, 2));
// Every 9 points is a triangle. Take 1 triangle and turn it into 4 triangles.
GLfloat p_one[] = {shape[i], shape[i + 1], shape[i + 2]};
GLfloat p_two[] = {shape[i + 3], shape[i + 4], shape[i + 5]};
GLfloat p_thr[] = {shape[i + 6], shape[i + 7], shape[i + 8]};
GLfloat p_one_two[] = { (p_one[0] + p_two[0]) / 2.0f, (p_one[1] + p_two[1]) / 2.0f, (p_one[2] + p_two[2]) / 2.0f };
GLfloat p_one_thr[] = { (p_one[0] + p_thr[0]) / 2.0f, (p_one[1] + p_thr[1]) / 2.0f, (p_one[2] + p_thr[2]) / 2.0f };
GLfloat p_two_thr[] = { (p_two[0] + p_thr[0]) / 2.0f, (p_two[1] + p_thr[1]) / 2.0f, (p_two[2] + p_thr[2]) / 2.0f };
GLfloat r_one_two = sqrt((p_one_two[0]*p_one_two[0]) + (p_one_two[1]*p_one_two[1]) + (p_one_two[2]*p_one_two[2]));
GLfloat r_one_thr = sqrt((p_one_thr[0]*p_one_thr[0]) + (p_one_thr[1]*p_one_thr[1]) + (p_one_thr[2]*p_one_thr[2]));
GLfloat r_two_thr = sqrt((p_two_thr[0]*p_two_thr[0]) + (p_two_thr[1]*p_two_thr[1]) + (p_two_thr[2]*p_two_thr[2]));
GLfloat t_one_two[] = { radius * p_one_two[0] / r_one_two, radius * p_one_two[1] / r_one_two, radius * p_one_two[2] / r_one_two };
GLfloat t_one_thr[] = { radius * p_one_thr[0] / r_one_thr, radius * p_one_thr[1] / r_one_thr, radius * p_one_thr[2] / r_one_thr };
GLfloat t_two_thr[] = { radius * p_two_thr[0] / r_two_thr, radius * p_two_thr[1] / r_two_thr, radius * p_two_thr[2] / r_two_thr };
// Triangle 1:
new_sphere.push_back(p_one[0]);
new_sphere.push_back(p_one[1]);
new_sphere.push_back(p_one[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
// Triangle 2:
new_sphere.push_back(p_two[0]);
new_sphere.push_back(p_two[1]);
new_sphere.push_back(p_two[2]);
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Triangle 3:
new_sphere.push_back(p_thr[0]);
new_sphere.push_back(p_thr[1]);
new_sphere.push_back(p_thr[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
// Center Triangle:
new_sphere.push_back(t_one_two[0]);
new_sphere.push_back(t_one_two[1]);
new_sphere.push_back(t_one_two[2]);
new_sphere.push_back(t_one_thr[0]);
new_sphere.push_back(t_one_thr[1]);
new_sphere.push_back(t_one_thr[2]);
new_sphere.push_back(t_two_thr[0]);
new_sphere.push_back(t_two_thr[1]);
new_sphere.push_back(t_two_thr[2]);
}
return tesselate(new_sphere, recursion - 1);
}
printf("number of vertices to be rendered: %d || ", shape.size());
return shape;
}
std::vector<GLfloat> create_sphere(int recursion) {
// Define the starting icosahedron
GLfloat t_ = (1.0f + sqrt(5.0f)) / 2.0f;
std::vector<GLfloat> icosahedron = {
-1.0f, t_, 0.0f, -t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, t_, 1.0f, t_, 0.0f,
-1.0f, t_, 0.0f, 1.0f, t_, 0.0f, 0.0f, 1.0f, -t_,
-1.0f, t_, 0.0f, 0.0f, 1.0f, -t_, -t_, 0.0f, -1.0f,
-1.0f, t_, 0.0f, -t_, 0.0f, -1.0f, -t_, 0.0f, 1.0f,
1.0f, t_, 0.0f, 0.0f, 1.0f, t_, t_, 0.0f, 1.0f,
0.0f, 1.0f, t_, -t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
-t_, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -1.0f, -t_, 0.0f,
-t_, 0.0f, -1.0f, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -t_,
0.0f, 1.0f, -t_, 1.0f, t_, 0.0f, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, 1.0f, 0.0f, -1.0f, t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-1.0f, -t_, 0.0f,
1.0f, -t_, 0.0f,-1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_,
1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_, t_, 0.0f, -1.0f,
1.0f, -t_, 0.0f, t_, 0.0f, -1.0f, t_, 0.0f, 1.0f,
0.0f, -1.0f, t_, t_, 0.0f, 1.0f, 0.0f, 1.0f, t_,
-1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-t_, 0.0f, 1.0f,
0.0f, -1.0f, -t_,-1.0f, -t_, 0.0f,-t_, 0.0f, -1.0f,
t_, 0.0f, -1.0f, 0.0f, -1.0f, -t_, 0.0f, 1.0f, -t_,
t_, 0.0f, 1.0f, t_, 0.0f, -1.0f, 1.0f, t_, 0.0f,
};
// Tesselate the icososphere the number of times recursion
std::vector<GLfloat> colorless_sphere = tesselate(icosahedron, recursion);
// Add color and return
return add_color(colorless_sphere);
}
Vertex Shader: (named core.vs)
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 offset;
out vec3 fColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position.x + offset.x, position.y + offset.y, position.z, 1.0f);
fColor = color;
}
Fragment Shader: (named core.frag)
#version 330 core
in vec3 fColor;
out vec4 color;
void main()
{
color = vec4(fColor, 1.0f);
}
Shader class: (named Shader.h)
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <GL/glew.h>
class Shader
{
public:
GLuint Program;
// Constructor generates the shader on the fly
Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensures ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::badbit);
try
{
// Open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar * fShaderCode = fragmentCode.c_str();
// 2. Compile shaders
GLuint vertex, fragment;
GLint success;
GLchar infoLog[512];
// Vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// Print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// Print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// Shader Program
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// Print linking errors if any
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// Delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// Uses the current shader
void Use()
{
glUseProgram(this->Program);
}
};
#endif
My ultimate goal is to render 1 million spheres of different sizes and colors at 60 fps.
This is an unreasonable expectation.
Let's say that each sphere consists of 50 triangles. Kinda small for a good sphere shape, but lets assume they're that small.
1 million spheres at 50 tris per sphere is 50 million triangles per frame. At 60 FPS, that's 3 billion triangles per second.
No commercially available GPU is good enough to do that. And that's just a 50 triangle sphere; your 4x tessellated icosahedron will be over 5,000 triangles.
Now yes, drawing 60 such spheres is only ~300,000 triangles per frame. But even that at 60 FPS is ~18 million triangles per second. Hardware does exist that can handle that many triangles, but it's very clearly a lot. And you're definitely not going to get 1 million of them.
This is not a matter of GPU/CPU communication or overhead. You're simply throwing more work at your GPU than it could handle. You might be able to improve a couple of things here and there, but nothing that's going to get you even one tenth of what you want.
At least, not with this overall approach.
For your particular case of wanting to draw millions of spheres, I would use raytraced impostors rather than actual geometry of spheres. That is, you draw quads, who's positions are generated by the vertex (or geometry) shader. You generate a quad per sphere, such that the quad circumscribes the sphere. Then the fragment shader does a simple ray-sphere intersection test to see if the fragment in question (from the direction of the camera view) hits the sphere or not. If the ray doesn't hit the sphere, you discard the fragment.
You would also need to modify gl_FragDepth to give the impostor the proper depth value, so that intersecting spheres can work.
I asked a question the other day, about rendering TTF fonts using SDL, and was pointed towards SDL_TTFL I've tried using the SDL_TTF library, but All I'm getting is garbage on screen
I have included my shaders, which are very simple for this program, and also the snipped I'm using to load the text into surface, and to bind it to the texture. I'm not trying to do anything crazy here at all. Is there anything I'm doing wrong you can see? I'm not really too sure how to debug shaders etc.
Fragment Shader (frag.glsl):
#version 330
in vec2 texCoord;
in vec4 fragColor;
out vec3 finalColor;
uniform sampler2D myTextureSampler;
void main() {
finalColor = texture( myTextureSampler, texCoord ).rgb;
}
Vertex Shader (vert.glsl)
#version 330
in vec3 vert;
in vec4 color;
in vec2 texcoord;
out vec4 fragColor;
out vec2 texCoord;
void main() {
fragColor = color;
gl_Position = vec4(vert, 1);
texCoord = texcoord;
}
Font Loading (loadFont.cpp)
//Initialise TTF
if( TTF_Init() == -1 )
throw std::runtime_error("SDL_TTF failed to initialise.");
//Load the texture
font = TTF_OpenFont( filePath.c_str(), 12 );
if(!font)
throw std::runtime_error("Couldn't load: "+ filePath);
TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
surface = TTF_RenderUTF8_Blended(font, "Hello", this->textColor);
Uint8 colors = surface->format->BytesPerPixel;
int texture_format;
if (colors == 4) { // alpha
if (surface->format->Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
} else { // no alpha
if (surface->format->Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
}
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, colors, surface->w, surface->h, 0,
texture_format, GL_UNSIGNED_BYTE, surface->pixels);
SDL_FreeSurface(surface);
Vertex Attribute Setup
GLfloat vertices[] = {
//X Y Z R G B A U V
-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 1.f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 1.f,
-1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 0.f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 1.f,
1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 0.f,
-1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 0.f
};
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(program->attrib("vert"));
glVertexAttribPointer(program->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), NULL);
glEnableVertexAttribArray(program->attrib("color"));
glVertexAttribPointer(program->attrib("color"), 4, GL_FLOAT, GL_TRUE, 9*sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(program->attrib("texcoord"));
glVertexAttribPointer(program->attrib("texcoord"), 2, GL_FLOAT, GL_TRUE, 9*sizeof(GLfloat), (const GLvoid*)(7 * sizeof(GLfloat)));
I've attached the code I'm using for the vertex attributes as per the comment below.
EDIT:
In a reply that has been deleted since, It was asked whether SDL_TTF was returning 3 or 4 channels. It's returning a BGRA image. I've tried changing my fragment shader to
Fragment shader
#version 330
in vec2 texCoord;
in vec4 fragColor;
out vec4 finalColor;
uniform sampler2D myTextureSampler;
void main() {
finalColor = texture( myTextureSampler, texCoord ).rgba;
}
Note the vec4, and using rgba rather than rgb. This just leads to a black rectangle.
I also tried generating a surface using SDL_LoadBMP(), which gives the exact same results.
Your call to
glTexImage2D(GL_TEXTURE_2D, 0, colors, surface->w, surface->h, 0,
texture_format, GL_UNSIGNED_BYTE, surface->pixels);
Is a problem.
The third paramter is wrong:
http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml
internalFormat
Specifies the number of color components in the texture.
Must be one of base internal formats given in Table 1,
one of the sized internal formats given in Table 2, or one
of the compressed internal formats given in Table 3, below.
I suspect you want yours to be GL_RGBA (or what format you want opengl to store your texture in)
EDIT:
I just saw it now, but you are using only 3 channels in your fragment shader. The Blended function requires that you use 4 channels otherwise the alpha channel is going to be messed up.
I think your "main" problem lies somewhere else though as that should just make the colour constant over the entire surface. (Not the "garbage" you are seeing)
I quickly wrote this program that mostly does what your doing. I think it will help you more than my repository as it's straight to the point.
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_ttf.h>
#include <string>
#include <iostream>
using namespace std;
SDL_Window *window = NULL;
SDL_GLContext context = NULL;
TTF_Font* font = NULL;
SDL_Surface* surface = NULL;
//OpenGL Objects
GLuint vao;
GLuint vbo;
GLuint texture;
//Shader Objects
GLuint program;
GLuint vs;
GLuint fs;
//Sampler Object
GLuint uniformSampler;
//Callback Function
APIENTRY GLvoid debugMessageCallbackFunction( GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message, GLvoid* userParam)
{
cerr << endl << "\t" << message << endl;
}
//The shaders are identical to yours
const string fragmentShaderString =
"#version 130\n" // My laptop can't do OpenGL 3.3 so 3.0 will have to do
"in vec2 texCoord;\n"
"in vec4 fragColor;\n"
"\n"
"out vec4 finalColor;\n"
"\n"
"uniform sampler2D myTextureSampler;\n"
"void main() {\n"
" finalColor = texture( myTextureSampler, texCoord ) * fragColor;\n"
"}";
const string vertexShaderString =
"#version 130\n"
"\n"
"in vec3 vert;\n"
"in vec4 color;\n"
"in vec2 texcoord;\n"
"\n"
"out vec4 fragColor;\n"
"out vec2 texCoord;\n"
"void main() {\n"
" fragColor = color;\n"
" gl_Position = vec4(vert, 1);\n"
" texCoord = texcoord;\n"
"}\n";
//Your vertices, but I changed alpha to 1.0f
const GLfloat vertices[] =
{
//X Y Z R G B A U V
-1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 1.f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 1.f,
-1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 0.f,
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 1.f,
1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 0.f,
-1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 0.f
};
int main(int argc, char* args[])
{
//Create Window and Context
window = SDL_CreateWindow("SDL Text with OpenGL", 0, 0, 640, 480, SDL_WINDOW_OPENGL);
//Set Core Context
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
context = SDL_GL_CreateContext(window);
//Simple OpenGL State Settings
glViewport( 0.f, 0.f, 640.f, 480.f);
glClearColor( 0.f, 0.f, 0.f, 1.f);
//Init Glew
//Set glewExperimental for Core Context
glewExperimental=true;
glewInit();
//Set Blending
//Required so that the alpha channels show up from the surface
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//Simple callback function for GL errors
glDebugMessageCallbackARB(debugMessageCallbackFunction, NULL);
//Create Shaders
vs = glCreateShader(GL_VERTEX_SHADER);
fs = glCreateShader(GL_FRAGMENT_SHADER);
//Source Pointers
const GLchar* vsSource= &vertexShaderString[0];
const GLchar* fsSource = &fragmentShaderString[0];
//Set Source
glShaderSource(vs, 1, &vsSource, NULL);
glShaderSource(fs, 1, &fsSource, NULL);
//Compile Shaders
glCompileShader(fs);
glCompileShader(vs);
//Create Shader Program
program = glCreateProgram();
//Attach Shaders to Program
glAttachShader(program, vs);
glAttachShader(program, fs);
//No need for shaders anymore
glDeleteShader(vs);
glDeleteShader(fs);
//Set Attribute Locations
glBindAttribLocation(program, 0, "vert");
glBindAttribLocation(program, 1, "color");
glBindAttribLocation(program, 2, "texcoord");
//Link Program
glLinkProgram(program);
//Setup VAO and VBO
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * 6, vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), NULL);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat),(GLvoid*)(3*sizeof(GLfloat)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat),(GLvoid*)(7*sizeof(GLfloat)));
//Init TTF
TTF_Init();
//Open Font
font = TTF_OpenFont("DroidSansFallbackFull.ttf", 30);
SDL_Color color = {255, 255, 255, 255};
//Create Surface
surface = TTF_RenderUTF8_Blended(font, "This is TEXT!", color);
//Your format checker
GLenum format = (surface->format->BytesPerPixel==3)?GL_RGB:GL_RGBA;
//Create OpenGL Texture
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D( GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0,
format, GL_UNSIGNED_BYTE, surface->pixels);
//Set Some basic parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Set up Sampler
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
uniformSampler = glGetUniformLocation(program, "myTextureSampler");
//It defaults to using GL_TEXTURE0, so it's not necessary to set it
//in this program it's generally a good idea.
//--------------------------------------------------------------------------------------
// DRAW STAGE
//--------------------------------------------------------------------------------------
glUseProgram(program);
//glBindVertexArray(vao); - still in use
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
SDL_GL_SwapWindow(window);
//Sleep for 2s before closing
SDL_Delay(2000);
}
I didn't do any error checking or close any of the resources since it's just meant to be a reference and not meant to be used.
Usually I don't use glew, but writing code to manually get the functions for such a small program seemed pointless.
It compiles with
g++ source.cpp -g -lSDL2 -lSDL2_ttf -lGL -GLEW -o demo
on linux. You might need to make some adjustments for Windows (Headers files might change slightly and libraries will change as wel) and I think it will work without change on Mac.
EDIT 2:
To compile it on windows with mingw you need to add APIENTRY to callback function and the main should have arguments. Changed code to reflect this.
Tested it and it works on both windows and linux. (Provided that your implementation have access to the GL_ARB_debug_callback extension, if not just comment that out)
Does work nicely, only got to edit the const GLfloat vertices[] array to be able to change the text color consistently. For a solid color text, have all RGB components in the array equal to 1.0f and render the texture in color. For a multicolored text, first render the texture in white with SDL_Color color = { 255, 255, 255, 255 };, then edit the array as shown here below.
float width = (float)surface->w;
float height = (float)surface->h;
// alpha to 1.0f
const GLfloat vertices[] = {
// X Y Z R G B A U V
-1.0, -height / width, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, height / width, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, -height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, height / width, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
-1.0f, height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f
};