I am trying to build a code that uses OpenGL and shaders that adds texture to a square pyramid. After I added the functions to add the texture, ran the code but instead of rendering, I get a console window giving me the following error:
INFO: OpenGL Version: 4.4.0 NVIDIA 512.15
ERROR::SHADER::VERTEX::COMPILATION_FAILED
0(2) : error C1503: undefined variable "cameraPosition"
0(2) : error C1503: undefined variable "cameraPosition"
0(2) : error C1503: undefined variable "cameraPosition"
0(2) : error C1035: assignment of incompatible types
I have tried copying and pasting the cameraPosition variable from the bottom of my code to one of my main functions that has the gl_Position variable. But that did not solve the problem. Do I need to change the vec3 to vec4 somewhere or do I need to copy and paste the cameraPostion variable and definition somewhere else? Here is my code:
#include <iostream> // cout, cerr
#include <cstdlib> // EXIT_FAILURE
#include <GL/glew.h> // GLEW library
#include <GLFW/glfw3.h> // GLFW library
// GLM Math Header inclusions
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SOIL2.h> // SOIL2 library
using namespace std; // Standard namespace
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Input Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
// Declare View Matrix
glm::mat4 viewMatrix;
// Initialize FOV
GLfloat fov = 45.f;
// Define Camera Attributes
glm::vec3 cameraPosition = glm::vec3(0.f, 0.f, 3.f);
glm::vec3 target = glm::vec3(0.f, 0.f, 0.f);
glm::vec3 cameraDirection = glm::normalize(cameraPosition - target);
glm::vec3 worldUp = glm::vec3(0.f, 1.f, 0.f);
glm::vec3 cameraRight = glm::normalize(glm::cross(worldUp, cameraDirection));
glm::vec3 cameraUp = glm::normalize(glm::cross(cameraDirection, cameraRight));
glm::vec3 cameraFront = glm::normalize(glm::vec3(0.f, 0.f, -1.f));
// Declare target prototype
glm::vec3 getTarget();
// Camera transformation prototype
void TransformCamera();
// Boolean for keys and mouse buttons
bool keys[1024], mouseButtons[3];
// Boolean to check camera transformations
bool isPanning = false, isOrbiting = false;
// Radius, Pitch, and Yaw
GLfloat radius = 3.f, rawYaw = 0.f, rawPitch = 0.f, degYaw, degPitch;
GLfloat deltaTime = 0.f, lastFrame = 0.f;
GLfloat lastX = 400, lastY = 300, xChange, yChange;
bool firstMouseMove = true; // Detect inititial mouse movement
void initCamera();
// Unnamed namespace
namespace
{
const char* const WINDOW_TITLE = "Basic Camera Movement"; // Macro for window title
// Variables for window width and height
int WINDOW_WIDTH = 800;
int WINDOW_HEIGHT = 600;
// Stores the GL data relative to a given mesh
struct GLMesh
{
GLuint vao; // Handle for the vertex array object
GLuint vbos[2]; // Handles for the vertex buffer objects
GLuint nIndices; // Number of indices of the mesh
};
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Triangle mesh data
GLMesh gMesh;
// Shader program
GLuint gProgramId;
}
/* User-defined Function prototypes to:
* initialize the program, set the window size,
* redraw graphics on the window when resized,
* and render graphics on the screen
*/
bool UInitialize(int, char* [], GLFWwindow** window);
void UResizeWindow(GLFWwindow* window, int width, int height);
void UProcessInput(GLFWwindow* window);
void UCreateMesh(GLMesh& mesh);
void UDestroyMesh(GLMesh& mesh);
void URender();
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void UDestroyShaderProgram(GLuint programId);
/* Vertex Shader Source Code*/
const GLchar* vertexShaderSource = GLSL(440,
layout(location = 0) in vec3 position; // Vertex data from Vertex Attrib Pointer 0
layout(location = 1) in vec3 color; // Color data from Vertex Attrib Pointer 1
layout(location = 2) in vec2 texture; // Texture data from Vertex Attrib Pointer 2
out vec3 vertexColor; // variable to transfer color data to the fragment shader
out vec3 vertexTexture;
//Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform sampler2D myTexture; // Sampler
void main()
{
gl_Position = projection * view * model * vec4(cameraPosition.x, cameraPosition.y, cameraPosition.z, 1.0f); // transforms vertices to clip coordinates
vertexColor = color; // references incoming color data
vertexTexture = texture;
}
);
/* Fragment Shader Source Code*/
const GLchar* fragmentShaderSource = GLSL(440,
in vec3 vertexColor; // Variable to hold incoming color data from vertex shader
out vec3 fragmentColor;
out vec3 fragmentTexture;
void main()
{
fragmentColor = texture(myTexture, textTexture);
fragmentTexture = vec2(vertexTexture);
}
);
int main(int argc, char* argv[])
{
if (!UInitialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh
UCreateMesh(gMesh); // Calls the function to create the Vertex Buffer Object
// Create the shader program
if (!UCreateShaderProgram(vertexShaderSource, fragmentShaderSource, gProgramId))
return EXIT_FAILURE;
// Sets the background color of the window to black (it will be implicitely used by glClear)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Set input call back functions
glfwSetKeyCallback(gWindow, key_callback);
glfwSetCursorPosCallback(gWindow, cursor_position_callback);
glfwSetMouseButtonCallback(gWindow, mouse_button_callback);
glfwSetScrollCallback(gWindow, scroll_callback);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
{
// Set delta time
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// Resize window and graphics simultaneously
glfwGetFramebufferSize(gWindow, &WINDOW_WIDTH, &WINDOW_HEIGHT);
// input
// -----
UProcessInput(gWindow);
// Render this frame
URender();
glfwPollEvents();
// Poll camera transformations
TransformCamera();
}
// Release mesh data
UDestroyMesh(gMesh);
// Release shader program
UDestroyShaderProgram(gProgramId);
exit(EXIT_SUCCESS); // Terminates the program successfully
}
// Initialize GLFW, GLEW, and create a window
bool UInitialize(int argc, char* argv[], GLFWwindow** window)
{
// GLFW: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// GLFW: window creation
// ---------------------
* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL);
if (*window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(*window);
glfwSetFramebufferSizeCallback(*window, UResizeWindow);
// GLEW: initialize
// ----------------
// Note: if using GLEW version 1.13 or earlier
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
std::cerr << glewGetErrorString(GlewInitResult) << std::endl;
return false;
}
// Displays GPU OpenGL version
cout << "INFO: OpenGL Version: " << glGetString(GL_VERSION) << endl;
return true;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void UProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float cameraSpeed = 2.5 * deltaTime;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPosition += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPosition -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPosition -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPosition += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
cameraPosition -= cameraSpeed * cameraUp;
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
cameraPosition += cameraSpeed * cameraUp;
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void UResizeWindow(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// Functioned called to render a frame
void URender()
{
// Enable z-depth
glEnable(GL_DEPTH_TEST);
// Wireframe mode
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Clear the frame and z buffers
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 1. Scales the object by 2
glm::mat4 scale = glm::scale(glm::vec3(1.0f, 1.0f, 1.0f));
// 2. Rotates shape by 15 degrees in the x axis
glm::mat4 rotation = glm::rotate(45.0f, glm::vec3(1.0, 1.0f, 1.0f));
// 3. Place object at the origin
glm::mat4 translation = glm::translate(glm::vec3(0.0f, 0.0f, 0.0f));
// Model matrix: transformations are applied right-to-left order
glm::mat4 model = translation * rotation * scale;
// Transforms the camera: move the camera back (z axis)
glm::mat4 view = glm::lookAt(cameraPosition, getTarget(), worldUp);
// Creates a perspective projection
glm::mat4 projection = glm::perspective(fov, (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, 0.1f, 100.0f);
// Set the shader to be used
glUseProgram(gProgramId);
// Retrieves and passes transform matrices to the Shader program
GLint modelLoc = glGetUniformLocation(gProgramId, "model");
GLint viewLoc = glGetUniformLocation(gProgramId, "view");
GLint projLoc = glGetUniformLocation(gProgramId, "projection");
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));
GLuint crateTexture = {1};
glBindTexture(GL_TEXTURE_2D, crateTexture);
// Activate the VBOs contained within the mesh's VAO
glBindVertexArray(gMesh.vao);
// Draws the triangles
glDrawElements(GL_TRIANGLES, gMesh.nIndices, GL_UNSIGNED_SHORT, NULL); // Draws the triangle
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
glfwSwapBuffers(gWindow); // Flips the the back buffer with the front buffer every frame.
}
// Implements the UCreateMesh function
void UCreateMesh(GLMesh& mesh)
{
// Position and Color data
GLfloat verts[] = {
0.0f, 1.0f, 0.0f, // Top Center Vertex 0
1.0f, 0.0f, 0.0f, 1.0f, // Red
0.5f, 1.0f, // UV
-1.0f, -1.0f, 1.0f, // Bottom Left Vertex 1
0.0f, 1.0f, 0.0f, 1.0f, // Green
0.0f, 0.0f, // UV
1.0f, -1.0f, 1.0f, // Bottom Right Vertex 2
0.0f, 0.0f, 1.0f, 1.0f, // Blue
1.0f, 0.0f, // UV
1.0f, -1.0f, -1.0f, // Bottom Back Right Vertex 3
1.0f, 0.0f, 1.0f, 1.0f, // Magenta
0.0f, 0.0f, // UV
-1.0f, -1.0f, -1.0f, // Bottom Back Left Vertex 4
1.0f, 1.0f, 0.0f, 1.0f, // Yellow
1.0f, 0.0f // UV
};
// Index data to share position data
GLushort indices[] = {
// Sides
0, 1, 2, // Triangle 1
0 ,2, 3, // Triangle 2
0, 3, 1, // Triangle 3
0, 3, 4, // Triangle 4
// Base
1, 2, 3, // Triangle 5
1, 4, 3 // Triangle 6
};
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
const GLuint floatsPerTexture = 2;
glGenVertexArrays(1, &mesh.vao); // we can also generate multiple VAOs or buffers at the same time
glBindVertexArray(mesh.vao);
// Create 2 buffers: first one for the vertex data; second one for the indices
glGenBuffers(2, mesh.vbos);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vbos[0]); // Activates the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); // Sends vertex or coordinate data to the GPU
mesh.nIndices = sizeof(indices) / sizeof(indices[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vbos[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Strides between vertex coordinates is 6 (x, y, z, r, g, b, a). A tightly packed stride is 0.
GLint stride = sizeof(float) * (floatsPerVertex + floatsPerColor);// The number of floats before each
// Create Vertex Attribute Pointers
glVertexAttribPointer(0, floatsPerVertex, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, floatsPerColor, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerVertex));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, floatsPerTexture, GL_FLOAT, GL_FALSE, stride, (char*)(sizeof(float) * floatsPerTexture));
glEnableVertexAttribArray(2);
// Load textures
int crateTexWidth, crateTexHeight, gridTexWidth, gridTexHeight;
unsigned char* crateImage = SOIL_load_image("crate.png", &crateTexWidth, &crateTexHeight, 0, SOIL_LOAD_RGB);
unsigned char* gridImage = SOIL_load_image("crate.png", &gridTexWidth, &gridTexHeight, 0, SOIL_LOAD_RGB);
// Generate Textures
GLuint crateTexture = {1};
glGenTextures(1, &crateTexture);
glBindTexture(GL_TEXTURE_2D, crateTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, crateTexWidth, crateTexHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, crateImage);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(crateImage);
glBindTexture(GL_TEXTURE_2D, 0);
// Generate Textures
GLuint gridTexture;
glGenTextures(1, &gridTexture);
glBindTexture(GL_TEXTURE_2D, gridTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, gridTexWidth, gridTexHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, gridImage);
glGenerateMipmap(GL_TEXTURE_2D);
SOIL_free_image_data(gridImage);
glBindTexture(GL_TEXTURE_2D, 0);
}
void UDestroyMesh(GLMesh& mesh)
{
glDeleteVertexArrays(1, &mesh.vao);
glDeleteBuffers(2, mesh.vbos);
}
// Implements the UCreateShaders function
bool UCreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId)
{
// Compilation and linkage error reporting
int success = 0;
char infoLog[512];
// Create a Shader program object.
programId = glCreateProgram();
// Create the vertex and fragment shader objects
GLuint vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
// Retrive the shader source
glShaderSource(vertexShaderId, 1, &vtxShaderSource, NULL);
glShaderSource(fragmentShaderId, 1, &fragShaderSource, NULL);
// Compile the vertex shader, and print compilation errors (if any)
glCompileShader(vertexShaderId); // compile the vertex shader
// check for shader compile errors
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShaderId, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
glCompileShader(fragmentShaderId); // compile the fragment shader
// check for shader compile errors
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShaderId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
return false;
}
// Attached compiled shaders to the shader program
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId); // links the shader program
// check for linking errors
glGetProgramiv(programId, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programId, sizeof(infoLog), NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
}
glUseProgram(programId); // Uses the shader program
return true;
}
void UDestroyShaderProgram(GLuint programId)
{
glDeleteProgram(programId);
}
// Define Input Callback functions
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
// Display ASCII Keycode
//cout << "ASCII: " << key << endl;
if (action == GLFW_PRESS)
keys[key] = true;
else if (action == GLFW_RELEASE)
keys[key] = false;
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
/*
// Display scroll offset
if (yoffset > 0)
cout << "Scroll Up: ";
if (yoffset < 0)
cout << "Scroll Down: ";
cout << yoffset << endl;
*/
// Clamp FOV
if (fov >= 1.f && fov <= 45.f)
fov -= yoffset * 0.01f;
//Default FOV
if (fov < 1.f)
fov = 1.f;
if (fov > 45.f)
fov = 45.f;
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) {
// Display mouse x and y coordinates
// cout << "Mouse X: " << xpos << endl;
// cout << "Mouse Y: " << ypos << endl;
if (firstMouseMove) {
lastX = xpos;
lastY = ypos;
firstMouseMove = false;
}
// Calculate cursor offset
xChange = xpos - lastX;
yChange = lastY - ypos;
lastX = xpos;
lastY = ypos;
// Pan camera
if (isPanning) {
if (cameraPosition.z < 0.f)
cameraFront.z = 1.f;
else
cameraFront.z = -1.f;
GLfloat cameraSpeed = xChange * deltaTime;
cameraPosition += cameraSpeed * cameraRight;
cameraSpeed = yChange * deltaTime;
cameraPosition += cameraSpeed * cameraUp;
}
// Orbit camera
if (isOrbiting) {
rawYaw += xChange;
rawPitch += yChange;
// Convert Yaw and Pitch to degrees
degYaw = glm::radians(rawYaw);
// degPitch = glm::radians(rawPitch)
degPitch = glm::clamp(glm::radians(rawPitch), -glm::pi<float>() / 2.f + .1f, glm::pi<float>() / 2.f - .1f);
// Azimuth Altitude formula
cameraPosition.x = target.x + radius * cosf(degPitch) * sin(degYaw);
cameraPosition.y = target.y + radius * sinf(degPitch);
cameraPosition.z = target.z + radius * cosf(degPitch) * cosf(degYaw);
}
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
/*
// Detect mouse button clicks
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
cout << "LMB clicked!" << endl;
if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS)
cout << "MMB clicked!" << endl;
if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS)
cout << "RMB clicked!" << endl;
*/
if (action == GLFW_PRESS)
mouseButtons[button] = true;
else if (action == GLFW_RELEASE)
mouseButtons[button] = false;
}
// Define getTarget function
glm::vec3 getTarget() {
if (isPanning)
target = cameraPosition + cameraFront;
return target;
}
// Define TransformCamera function
void TransformCamera() {
// Pan camera
if (keys[GLFW_KEY_LEFT_ALT] && mouseButtons[GLFW_MOUSE_BUTTON_MIDDLE])
isPanning = true;
else
isPanning = false;
// Orbit camera
if ((mouseButtons[GLFW_MOUSE_BUTTON_LEFT]))
isOrbiting = true;
else
isOrbiting = false;
// Reset camera
if (keys[GLFW_KEY_F])
initCamera();
}
void initCamera() {
cameraPosition = glm::vec3(0.f, 0.f, 3.f);
target = glm::vec3(0.f, 0.f, 0.f);
cameraDirection = glm::normalize(cameraPosition - target);
worldUp = glm::vec3(0.f, 1.f, 0.f);
cameraRight = glm::normalize(glm::cross(worldUp, cameraDirection));
cameraUp = glm::normalize(glm::cross(cameraDirection, cameraRight));
cameraFront = glm::normalize(glm::vec3(0.f, 0.f, -1.f));
}
You never actually bother to declare/define a cameraPosition (presumably) uniform in your vertex shader or set its value via an appropriate glUniform*() call.
...not that you'd really want to since then every vertex would end up being set to the same position and you'd end up with a dot somewhere (maybe) on screen.
Rather,
gl_Position = projection * view * model * vec4(cameraPosition.x, cameraPosition.y, cameraPosition.z, 1.0f);
should probably be:
gl_Position = projection * view * model * vec4(position, 1.0);
...so all your fancy geometry in verts actually has some chance of being sensibly displayed.
Related
I am trying to load gltf 2.0 models in openGl. For 1st model it is works fine. But for 2nd model, Model is loading well but have same texture as previous one. I don't know how to apply different textures on different models and changing position of one model changes position of all models same way. I searched much online but very few examples of loading gltf in openGL. They also not show how to load 2nd or 3rd gltf model in same project (screen).
My code is using libraries to load gltf and to compile shaders. That are few line of code libraries have simple openGl code for loading gltf and compiling shaders.
Main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
#define GL_SILENCE_DEPRECATION
#include <glad/glad.h> // Extend OpenGL Specfication to version 4.5 for WIN64 and versions 4.1 for Apple (note: two different files).
#include <imgui/imgui.h> // Load GUI library - Dear ImGui - https://github.com/ocornut/imgui
#include <imgui/imgui_impl_glfw.h> // Platform ImGui using GLFW
#include <imgui/imgui_impl_opengl3.h> // Platform new OpenGL - aka better than 3.3 core version.
#include <GLFW/glfw3.h> // Add GLFW - library to launch a window and callback mouse and keyboard - https://www.glfw.org
//#include <tinygltf/tiny_gltf.h> // Model loading library - tiny gltf - https://github.com/syoyo/tinygltf
//#include "src/stb_image.hpp" // Add library to load images for textures
#define GLM_ENABLE_EXPERIMENTAL // Enable all functions include non LTS
#include <glm/glm.hpp> // Add helper maths library - GLM 0.9.9.9 - https://github.com/g-truc/glm - for example variables vec3, mat and operators.
#include <glm/gtx/transform.hpp> // Help us with transforms
using namespace glm;
//#include "src/Mesh.hpp" // Simplest mesh holder and OBJ loader - can update more - from https://github.com/BennyQBD/ModernOpenGLTutorial
#include "src/Pipeline.hpp" // Setup pipeline and load shaders.
#include "src/Content.hpp" // Setup content loader and drawing functions - https://github.com/KhronosGroup/glTF - https://github.com/syoyo/tinygltf
#include "src/Debugger.hpp" // Setup debugger functions.
// Main fuctions
void startup();
void update();
void render();
void ui();
void endProgram();
// HELPER FUNCTIONS OPENGL
void hintsGLFW();
//string readShader(string name);
//void checkErrorShader(GLuint shader);
inline void errorCallbackGLFW(int error, const char *description){cout << "Error GLFW: " << description << "\n";};
void debugGL();
void APIENTRY openGLDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const GLvoid *userParam);
GLenum glCheckError_(const char *file, int line);
#define glCheckError() glCheckError_(__FILE__, __LINE__)
// Setup all the message loop callbacks - do this before Dear ImGui
// Callback functions for the window and interface devices
void onResizeCallback(GLFWwindow *window, int w, int h);
void onKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
void onMouseButtonCallback(GLFWwindow *window, int button, int action, int mods);
void onMouseMoveCallback(GLFWwindow *window, double x, double y);
void onMouseWheelCallback(GLFWwindow *window, double xoffset, double yoffset);
// VARIABLES
GLFWwindow *window; // Keep track of the window
auto windowWidth = 800; // Window width
auto windowHeight =800; // Window height
auto running(true); // Are we still running our main loop
mat4 projMatrix; // Our Projection Matrix
vec3 cameraPosition = vec3(0.0f, 0.0f, 5.0f); // Where is our camera
vec3 cameraFront = vec3(0.0f, 0.0f, -0.5f); // Camera front vector
vec3 cameraUp = vec3(0.0f, 1.0f, 0.0f); // Camera up vector
auto aspect = (float)windowWidth / (float)windowHeight; // Window aspect ration
auto fovy = 45.0f; // Field of view (y axis)
bool keyStatus[1024]; // Track key strokes
auto currentTime = 0.0f; // Framerate
auto deltaTime = 0.0f; // time passed
auto lastTime = 0.0f; // Used to calculate Frame rate
Pipeline pipeline; // Add one pipeline plus some shaders.
Pipeline pipelinelegs; // Add one pipeline plus some shaders.
Content content; // Add one content loader (+drawing).
Content contentlegs;
GLuint texture, texture2;
//Pipeline pipelineClegs; // Add one pipeline plus some shaders.
//Content contentClegs;
Debugger debugger; // Add one debugger to use for callbacks ( Win64 - openGLDebugCallback() ) or manual calls ( Apple - glCheckError() )
vec3 modelPosition; // Model position
vec3 modelRotation; // Model rotation
int main()
{
cout << endl << "===" << endl << "3D Graphics and Animation - Running..." << endl;
if (!glfwInit()) // Check if GLFW is working, if not then stop all
{
cout << "Could not initialise GLFW...";
return -1;
}
glfwSetErrorCallback(errorCallbackGLFW); // Setup a function callback to catch and display all GLFW errors.
hintsGLFW(); // Setup glfw with various hints.
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); // Grab reference to monitor
// windowWidth = mode->width; windowHeight = mode->height; //fullscreen
// window = glfwCreateWindow(windowWidth, windowHeight, title.c_str(), glfwGetPrimaryMonitor(), NULL); // fullscreen
// Create our Window
const auto windowTitle = "My 3D Graphics and Animation OpenGL Application"s;
window = glfwCreateWindow(windowWidth, windowHeight, windowTitle.c_str(), NULL, NULL);
if (!window) // Test if Window or OpenGL context creation failed
{
cout << "Could not initialise GLFW...";
glfwTerminate();
return -1;
}
glfwSetWindowPos(window, 10, 10); // Place it in top corner for easy debugging.
glfwMakeContextCurrent(window); // making the OpenGL context current
// GLAD: Load OpenGL function pointers - aka update specs to newest versions - plus test for errors.
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD...";
glfwMakeContextCurrent(NULL);
glfwTerminate();
return -1;
}
glfwSetWindowSizeCallback(window, onResizeCallback); // Set callback for resize
glfwSetKeyCallback(window, onKeyCallback); // Set Callback for keys
glfwSetMouseButtonCallback(window, onMouseButtonCallback); // Set callback for mouse click
glfwSetCursorPosCallback(window, onMouseMoveCallback); // Set callback for mouse move
glfwSetScrollCallback(window, onMouseWheelCallback); // Set callback for mouse wheel.
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // Set mouse cursor Fullscreen
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set mouse cursor FPS fullscreen.
// Setup Dear ImGui and add context - https://blog.conan.io/2019/06/26/An-introduction-to-the-Dear-ImGui-library.html
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO(); //(void)io;
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGui::StyleColorsLight(); // ImGui::StyleColorsDark(); // Setup Dear ImGui style
// Setup Platform/Renderer ImGui backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
const auto glsl_version = "#version 410";
ImGui_ImplOpenGL3_Init(glsl_version);
#if defined(__WIN64__)
debugGL(); // Setup callback to catch openGL errors. V4.2 or newer
#elif(__APPLE__)
glCheckError(); // Old way of checking for errors. Newest not implemented by Apple. Manually call function anywhere in code to check for errors.
#endif
glfwSwapInterval(1); // Ony render when synced (V SYNC) - https://www.tomsguide.com/features/what-is-vsync-and-should-you-turn-it-on-or-off
glfwWindowHint(GLFW_SAMPLES, 2); // Multisampling - covered in lectures - https://www.khronos.org/opengl/wiki/Multisampling
startup(); // Setup all necessary information for startup (aka. load texture, shaders, models, etc).
cout << endl << "Starting main loop and rendering..." << endl;
do{ // run until the window is closed
auto currentTime = (float)glfwGetTime(); // retrieve timelapse
deltaTime = currentTime - lastTime; // Calculate delta time
lastTime = currentTime; // Save for next frame calculations.
glfwPollEvents(); // poll callbacks
update(); // update (physics, animation, structures, etc)
render(); // call render function.
ui(); // call function to render ui.
#if defined(__APPLE__)
glCheckError(); // Manually checking for errors for MacOS, Windows has a callback.
#endif
glfwSwapBuffers(window); // swap buffers (avoid flickering and tearing)
running &= (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_RELEASE); // exit if escape key pressed
running &= (glfwWindowShouldClose(window) != GL_TRUE);
} while (running);
endProgram(); // Close and clean everything up...
// cout << "\nPress any key to continue...\n";
// cin.ignore(); cin.get(); // delay closing console to read debugging errors.
return 0;
}
void hintsGLFW(){
auto majorVersion = 3; auto minorVersion = 3; // define OpenGL version - at least 3.3 for bare basic NEW OpenGL
#if defined(__WIN64__)
majorVersion = 4; minorVersion = 5; // Recommended for Windows 4.5, but latest is 4.6 (not all drivers support 4.6 version).
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // Create context in debug mode - for debug message callback
#elif(__APPLE__)
majorVersion = 4; minorVersion = 1; // Max support for OpenGL in MacOS
#endif
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // https://www.glfw.org/docs/3.3/window_guide.html
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, majorVersion);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minorVersion);
}
void endProgram()
{
// Clean ImGui
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwMakeContextCurrent(NULL); // destroys window handler
glfwTerminate(); // destroys all windows and releases resources.
}
void startup()
{
// string name = "assets/Fabric01_4K_BaseColor.png";
// int iWidth, iHeight, iChannels;
// unsigned char *iData = stbi_load(name.c_str(), &iWidth, &iHeight, &iChannels, 0);
// glGenTextures(1, &texture);
// //glActiveTexture(GL_TEXTURE0);
// glBindTexture(GL_TEXTURE_2D, texture);
// // glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, iWidth, iHeight);
// // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, iWidth, iHeight, GL_RGB, GL_UNSIGNED_BYTE, iData);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// if (iData)
// {
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
// glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iWidth, iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, iData);
// }else
// {
// std::cout << "Failed to load texture" << std::endl;
// }
// glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(iData);
// //glBindTexture(GL_TEXTURE_2D, 0);
// string namea = "assets/metal.png";
// int Width, Height, Channels;
// unsigned char *Data = stbi_load(namea.c_str(), &Width, &Height, &Channels, 0);
// glGenTextures(1, &texture2);
// glBindTexture(GL_TEXTURE_2D, texture2);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// if (Data)
// {
// glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
// glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data);
// }else
// {
// std::cout << "Failed to load texture" << std::endl;
// }
// glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(Data);
//glBindTexture(GL_TEXTURE_2D, 0);
cout << "VENDOR: " << (char *)glGetString(GL_VENDOR) << endl;
cout << "VERSION: " << (char *)glGetString(GL_VERSION) << endl;
cout << "RENDERER: " << (char *)glGetString(GL_RENDERER) << endl;
cout << endl << "Loading content..." << endl;
content.LoadGLTF("assets/Model.gltf");
content2.LoadGLTF("assets/Model2.gltf");
pipeline.CreatePipeline();
pipeline.LoadShaders("shaders/vs_model.glsl", "shaders/fs_model.glsl");
modelPosition = glm::vec3(0.0f, 0.0f, 0.0f);
modelRotation = glm::vec3(0.0f, 0.0f, 0.0f);
// A few optimizations.
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Get the correct size in pixels - E.g. if retina display or monitor scaling
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
// Calculate proj_matrix for the first time.
aspect = (float)windowWidth / (float)windowHeight;
projMatrix = glm::perspective(glm::radians(fovy), aspect, 0.1f, 1000.0f);
}
void update()
{
// modelRotation.y += 0.05f;
if (keyStatus[GLFW_KEY_LEFT]) modelRotation.y += 0.05f;
if (keyStatus[GLFW_KEY_RIGHT]) modelRotation.y -= 0.05f;
if (keyStatus[GLFW_KEY_UP]) modelRotation.x += 0.05f;
if (keyStatus[GLFW_KEY_DOWN]) modelRotation.x -= 0.05f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_UP]) cameraPosition.y += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_DOWN]) cameraPosition.y -= 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_RIGHT]) cameraPosition.x += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_LEFT]) cameraPosition.x -= 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_I]) cameraPosition.z += 0.10f;
if (keyStatus[GLFW_KEY_C] && keyStatus[GLFW_KEY_O]) cameraPosition.z -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_UP]) cameraUp.y += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_DOWN]) cameraUp.y -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_RIGHT]) cameraUp.x += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_LEFT]) cameraUp.x -= 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_I]) cameraUp.z += 0.10f;
if (keyStatus[GLFW_KEY_U] && keyStatus[GLFW_KEY_O]) cameraUp.z -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_UP]) cameraFront.y += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_DOWN]) cameraFront.y -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_RIGHT]) cameraFront.x += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_LEFT]) cameraFront.x -= 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_I]) cameraFront.z += 0.10f;
if (keyStatus[GLFW_KEY_F] && keyStatus[GLFW_KEY_O]) cameraFront.z -= 0.10f;
if (keyStatus[GLFW_KEY_R]) pipeline.ReloadShaders();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void render()
{
glViewport(0, 0, windowWidth, windowHeight);
// Clear colour buffer
glm::vec4 inchyraBlue = glm::vec4(0.345f, 0.404f, 0.408f, 1.0f);
glm::vec4 backgroundColor = inchyraBlue;
glClearBufferfv(GL_COLOR, 0, &backgroundColor[0]);
// Clear deep buffer
static const GLfloat one = 1.0f;
glClearBufferfv(GL_DEPTH, 0, &one);
// Enable blend
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Use our shader programs
glUseProgram(pipeline.pipe.program);
//glUniform1i(glGetUniformLocation(pipeline.pipe.program, "texture1"), 0);
// Setup camera
glm::mat4 viewMatrix = glm::lookAt(cameraPosition, // eye
cameraPosition + cameraFront, // centre
cameraUp); // up
// Do some translations, rotations and scaling
// glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(modelPosition.x+rX, modelPosition.y+rY, modelPosition.z+rZ));
glm::mat4 modelMatrix = glm::translate(glm::mat4(5.0f), glm::vec3(0.0f, 0.4f, 0.0f));
modelMatrix = glm::rotate(modelMatrix, modelRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
modelMatrix = glm::rotate(modelMatrix, modelRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMatrix = glm::scale(modelMatrix, glm::vec3(0.5f, 0.5f, 0.5f));
glm::mat4 modelMatrix2 = glm::translate(glm::mat4(5.0f), glm::vec3(0.0f, -0.0f, 0.0f));
modelMatrix2 = glm::rotate(modelMatrix2, modelRotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
modelMatrix2 = glm::rotate(modelMatrix2, modelRotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMatrix2 = glm::scale(modelMatrix2, glm::vec3(0.5f, 0.5f, 0.5f));
glm::mat4 mv_matrix = viewMatrix * modelMatrix;
//glm::mat4 mv_matrixlegs = viewMatrix * modelMatrixlegs;
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"), 1, GL_FALSE, &modelMatrix[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "modelMatrix2"), 1, GL_FALSE, &modelMatrix2[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "view_matrix"), 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "proj_matrix"), 1, GL_FALSE, &projMatrix[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "modelMatrixlegs"), 1, GL_FALSE, &modelMatrixlegs[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "view_matrix"), 1, GL_FALSE, &viewMatrix[0][0]);
// glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "proj_matrix"), 1, GL_FALSE, &projMatrix[0][0]);
content.DrawModel(content.vaoAndEbos, content.model);
content2.DrawModel(content2.vaoAndEbos, content2.model);
#if defined(__APPLE__)
glCheckError();
#endif
}
void ui()
{
ImGuiIO &io = ImGui::GetIO();
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration;
window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
window_flags |= ImGuiWindowFlags_NoSavedSettings;
window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
window_flags |= ImGuiWindowFlags_NoNav;
const auto PAD = 10.0f;
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
ImVec2 work_size = viewport->WorkSize;
ImVec2 window_pos, window_pos_pivot;
window_pos.x = work_pos.x + work_size.x - PAD;
window_pos.y = work_pos.y + work_size.y - PAD;
window_pos_pivot.x = 1.0f;
window_pos_pivot.y = 1.0f;
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
window_flags |= ImGuiWindowFlags_NoMove;
ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
bool *p_open = NULL;
if (ImGui::Begin("Info", nullptr, window_flags)) {
ImGui::Text("About: 3D Graphics and Animation 2022"); // ImGui::Separator();
ImGui::Text("Performance: %.3fms/Frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("Pipeline: %s", pipeline.pipe.error?"ERROR":"OK");
}
ImGui::End();
// Rendering imgui
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void onResizeCallback(GLFWwindow *window, int w, int h)
{
windowWidth = w;
windowHeight = h;
// Get the correct size in pixels
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
if (windowWidth > 0 && windowHeight > 0)
{ // Avoid issues when minimising window - it gives size of 0 which fails division.
aspect = (float)w / (float)h;
projMatrix = glm::perspective(glm::radians(fovy), aspect, 0.1f, 1000.0f);
}
}
void onKeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS)
keyStatus[key] = true;
else if (action == GLFW_RELEASE)
keyStatus[key] = false;
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
void onMouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
}
void onMouseMoveCallback(GLFWwindow *window, double x, double y)
{
int mouseX = static_cast<int>(x);
int mouseY = static_cast<int>(y);
}
void onMouseWheelCallback(GLFWwindow *window, double xoffset, double yoffset)
{
int yoffsetInt = static_cast<int>(yoffset);
}
void APIENTRY openGLDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const GLvoid *userParam) // Debugger callback for Win64 - OpenGL versions 4.2 or better.
{
debugger.OpenGLDebugCallback(source, type, id, severity, length, message, userParam);
}
void debugGL() // Debugger function for Win64 - OpenGL versions 4.2 or better.
{
// Enable Opengl Debug
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback((GLDEBUGPROC)openGLDebugCallback, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
}
GLenum glCheckError_(const char *file, int line) // Debugger manual function for Apple - OpenGL versions 3.3 to 4.1.
{
GLenum errorCode;
while ((errorCode = glGetError()) != GL_NO_ERROR) debugger.GlGetError(errorCode, file, line);
return errorCode;
}
FragmentShader.glsl
#version 430 core
out vec4 color;
in VS_OUT
{
vec3 vertex;
vec3 normals;
vec2 tc;
} fs_in;
uniform sampler2D tex;
uniform sampler2D model_matrix;
uniform sampler2D modelMatrix2;
void main(void){
color = texture(tex, fs_in.tc);
}
VertexShader.glsl
#version 410 core
layout (location = 0) in vec3 in_vertex;
layout (location = 1) in vec3 in_normals;
layout (location = 2) in vec2 in_tc;
out VS_OUT
{
vec3 vertex;
vec3 normals;
vec2 tc;
} vs_out;
uniform mat4 model_matrix;
uniform mat4 modelMatrixlegs;
uniform mat4 view_matrix;
uniform mat4 proj_matrix;
void main(void)
{
gl_Position = proj_matrix * view_matrix * model_matrix * vec4(in_vertex, 1.0);
vs_out.vertex = in_vertex;
vs_out.normals = in_normals;
vs_out.tc = in_tc;
}
Models don't have positions or rotations or scaling values.
When you call glDrawArrays (or glDrawElements or glDrawElementsIndirect or whatever), then OpenGL renders the current model with the current texture and the current position and rotation and scaling value. Until that point they are separate. You have to realize this - you can draw the same model with different position/rotation/scaling, or the same model with different textures, or different models with the same texture, or so on. There's no need to link them together until you actually draw them and you shouldn't think they are linked together.
I don't see where DrawModel is defined, but I assume it calls glDrawArrays or glDrawElements. So it uses the texture and position and rotation and scaling value (a.k.a. the matrix) that are current when you call it.
So all you have to do is make model 1's matrix current before drawing model 1, and make model 2's matrix current before drawing model 2. Same for the textures.
i.e. probably something like this
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"),
1, GL_FALSE, &modelMatrix[0][0]);
// ^^^^^^^^^^^ model 1 matrix
glBindTexture(GL_TEXTURE_2D, texture);
// ^^^^^^^ model 1 texture
content.DrawModel(content.vaoAndEbos, content.model);
//^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ model 1
glUniformMatrix4fv(glGetUniformLocation(pipeline.pipe.program, "model_matrix"),
1, GL_FALSE, &modelMatrix2[0][0]);
// ^^^^^^^^^^^^ model 2 matrix
glBindTexture(GL_TEXTURE_2D, texture2);
// ^^^^^^^^ model 2 texture
content2.DrawModel(content2.vaoAndEbos, content2.model);
//^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^ model 2
You can delete the modelMatrixlegs variable from the shader because it's not actually doing anything.
I'm trying to make the colours on a shape darken, and then brighten periodically. The way I thought of to do it was to use a uniform variable that changes periodically from 0 to 1 and back and multiply it by the inputted colours to form the output colour. I have a uniform variable ublackness which does that.
A check of whether it can be located always returns -1, however the previous variables and the next variable work as intended. I know the name is correct, and I know it is being used because with the multiplication there, the screen is black (presumably because the colours are getting multiplied by 0 or something) but without it it displays fine. Here is the relevant code in the cpp file
GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
GLuint colorIndex = glGetAttribLocation(g_shaderProgramID, "aColor");
shadeIndex = glGetUniformLocation(g_shaderProgramID, "ublackness");
if (shadeIndex = -1)
{
cout << "it didn't work" << endl;
}
g_MVP_Index = glGetUniformLocation(g_shaderProgramID, "uModelViewProjectionMatrix");
And here is the relevant fragment shader file.
#version 330 core
// interpolated values from the vertex shaders
in vec3 vColor;
//thing
uniform float ublackness;
// output data
out vec3 fColor;
void main()
{
// set output color
fColor = vColor * ublackness;
}
And in case anyone tells me to use the vertex shader instead, I've tried that with the same results. I have also tried sending a vec3, and creating a temp vec3 and setting its colors to vcolor * blackness then outputting that.
I've also heard that nvidia graphics cards affect how functions like glUniform1f work, and I use that function, but that didn't seem relevant because it doesn't even get to that part of the code with a valid index. In case it is relevant, however, I have a NVIDIA Geforce GTX 1080. I've also heard that you need to use glUseProgram to get glGetUniformLocation to work, however the other functions work without it, and ublackness still doesn't work with it, so I didn't bother leaving it in. It is still used when rendering, however.
I highlighted the code I thought was relevant, but in case more is needed, here is all the main cpp code:
#include <cstdio> // for C++ i/o
#include <iostream>
#include <string>
#include <time.h>
using namespace std; // to avoid having to use std::
#define GLEW_STATIC // include GLEW as a static library
#include <GLEW/glew.h> // include GLEW
#include <GLFW/glfw3.h> // include GLFW (which includes the OpenGL header)
#include <glm/glm.hpp> // include GLM (ideally should only use the GLM headers that are actually used)
#include <glm/gtx/transform.hpp>
using namespace glm; // to avoid having to use glm::
#include "shader.h"
// struct for vertex attributes
struct Vertex
{
GLfloat position[3];
GLfloat color[3];
};
Vertex g_vertices[] = {
// vertex 1
-0.5f, 0.5f, 0.5f, // position
1.0f, 0.0f, 1.0f, // colour
// vertex 2
-0.5f, -0.5f, 0.5f, // position
1.0f, 0.0f, 0.0f, // colour
// vertex 3
0.5f, 0.5f, 0.5f, // position
1.0f, 1.0f, 1.0f, // colour
// vertex 4
0.5f, -0.5f, 0.5f, // position
1.0f, 1.0f, 0.0f, // colour
// vertex 5
-0.5f, 0.5f, -0.5f, // position
0.0f, 0.0f, 1.0f, // colour
// vertex 6
-0.5f, -0.5f, -0.5f,// position
0.0f, 0.0f, 0.0f, // colour
// vertex 7
0.5f, 0.5f, -0.5f, // position
0.0f, 1.0f, 1.0f, // colour
// vertex 8
0.5f, -0.5f, -0.5f, // position
0.0f, 1.0f, 0.0f, // colour
};
GLuint g_indices[] = {
0, 1, 2, // triangle 1
2, 1, 3, // triangle 2
4, 5, 0, // triangle 3
0, 5, 1, // ...
2, 3, 6,
6, 3, 7,
4, 0, 6,
6, 0, 2,
1, 5, 3,
3, 5, 7,
5, 4, 7,
7, 4, 6, // triangle 12
};
GLuint g_IBO = 0; // index buffer object identifier
GLuint g_VBO = 0; // vertex buffer object identifier
GLuint g_VAO = 0; // vertex array object identifier
GLuint g_shaderProgramID = 0; // shader program identifier
GLuint g_MVP_Index = 0; // location in shader
GLuint shadeIndex = 0;
glm::mat4 g_modelMatrix[6]; // object's model matrix
glm::mat4 g_viewMatrix; // view matrix
glm::mat4 g_projectionMatrix; // projection matrix
float translation[6];
float scaling[6];
float rotation[6];
float totalRotation[6];
float orbit[6];
float totalOrbit[6];
float totalShade = 1;
int shadeChange = 1;
// function used to render the scene
static void render_scene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear colour buffer and depth buffer
glUseProgram(g_shaderProgramID); // use the shaders associated with the shader program
glBindVertexArray(g_VAO); // make VAO active
for (int i = 0; i < 5; i++)
{
g_modelMatrix[i] = glm::rotate(totalOrbit[i], glm::vec3(0.0f, 1.0f, 0.0f))
* glm::translate(glm::vec3(translation[i], 0.0f, 0.0f))
* glm::rotate(totalRotation[i], glm::vec3(0.0f, 1.0f, 0.0f))
* glm::scale(glm::vec3(scaling[i], scaling[i], scaling[i]));
glm::mat4 MVP = g_projectionMatrix * g_viewMatrix * g_modelMatrix[i];
// set uniform model transformation matrix
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); // display the vertices based on their indices and primitive type
}
g_modelMatrix[5] = glm::rotate(totalOrbit[5], glm::vec3(0.0f, 1.0f, 0.0f))
* glm::translate(glm::vec3(translation[5], 0.0f, 0.0f))
* glm::rotate(totalOrbit[5], glm::vec3(0.0f, 1.0f, 0.0f))
* glm::translate(glm::vec3(0.4, 0.0f, 0.0f))
* glm::rotate(totalRotation[5], glm::vec3(0.0f, 1.0f, 0.0f))
* glm::scale(glm::vec3(scaling[5], scaling[5], scaling[5]));
glm::mat4 MVP = g_projectionMatrix * g_viewMatrix * g_modelMatrix[5];
// set uniform model transformation matrix
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); // display the vertices based on their indices and primitive type
//glm::mat4 MVP = g_projectionMatrix * g_viewMatrix * g_modelMatrix[0];
// set uniform model transformation matrix
//glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glFlush(); // flush the pipeline
}
// error callback function
static void error_callback(int error, const char* description)
{
cerr << description << endl; // output error description
}
// key press or release callback function
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
// quit if the ESCAPE key was press
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
// set flag to close the window
glfwSetWindowShouldClose(window, GL_TRUE);
return;
}
}
static void init(GLFWwindow* window)
{
glClearColor(0.0, 0.0, 0.0, 1.0); // set clear background colour
glEnable(GL_DEPTH_TEST); // enable depth buffer test
// create and compile our GLSL program from the shader files
g_shaderProgramID = loadShaders("VertexShader.vert", "FragShader.frag");
// find the location of shader variables
GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
GLuint colorIndex = glGetAttribLocation(g_shaderProgramID, "aColor");
shadeIndex = glGetUniformLocation(g_shaderProgramID, "ublackness");
if (shadeIndex = -1)
{
cout << "it didn't work" << endl;
}
g_MVP_Index = glGetUniformLocation(g_shaderProgramID, "uModelViewProjectionMatrix");
srand(time(NULL));
// initialise model matrix to the identity matrix
g_modelMatrix[0] = glm::mat4(1.0f); //sun
scaling[0] = 0.5f * (100 + ((rand() % 100)))/100;
translation[0] = 0.0f;
rotation[0] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[0] = 0.0f;
g_modelMatrix[1] = glm::mat4(1.0f); //tilted planet
scaling[1] = 0.1f * (100 + ((rand() % 100))) / 100;
translation[1] = 0.7f * (100 + ((rand() % 15))) / 100;
rotation[1] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[1] = 0.3f * (100 + ((rand() % 100))) / 100;
g_modelMatrix[2] = glm::mat4(1.0f); //ringed planet
scaling[2] = 0.1f * (100 + ((rand() % 100))) / 100;
translation[2] = 1.5f * (100 + ((rand() % 15))) / 100;
rotation[2] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[2] = 0.3f * (100 + ((rand() % 100))) / 100;
g_modelMatrix[3] = glm::mat4(1.0f); //moon planet
scaling[3] = 0.1f * (100 + ((rand() % 100))) / 100;
translation[3] = 2.8f * (100 + ((rand() % 15))) / 100;
rotation[3] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[3] = 0.3f * (100 + ((rand() % 100))) / 100;
g_modelMatrix[4] = glm::mat4(1.0f); //fading planet
scaling[4] = 0.1f * (100 + ((rand() % 100))) / 100;
translation[4] = 4.0f * (100 + ((rand() % 15))) / 100;
rotation[4] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[4] = 0.3f * (100 + ((rand() % 100))) / 100;
g_modelMatrix[5] = glm::mat4(1.0f); //moon
scaling[5] = 0.02f * (100 + ((rand() % 100))) / 100;
translation[5] = translation[3];
rotation[5] = 1.0f * (100 + ((rand() % 100))) / 100;
orbit[5] = orbit[3];
g_viewMatrix = glm::lookAt(glm::vec3(0, 6, 1), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
int width, height;
glfwGetFramebufferSize(window, &width, &height);
float aspectRatio = static_cast<float>(width) / height;
g_projectionMatrix = glm::perspective(45.0f, aspectRatio, 0.1f, 100.0f);
// generate identifier for VBO and copy data to GPU
glGenBuffers(1, &g_VBO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices), g_vertices, GL_STATIC_DRAW);
// generate identifier for IBO and copy data to GPU
glGenBuffers(1, &g_IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_indices), g_indices, GL_STATIC_DRAW);
// generate identifiers for VAO
glGenVertexArrays(1, &g_VAO);
// create VAO and specify VBO data
glBindVertexArray(g_VAO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO);
// interleaved attributes
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
glVertexAttribPointer(colorIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, color)));
glEnableVertexAttribArray(positionIndex); // enable vertex attributes
glEnableVertexAttribArray(colorIndex);
}
static void update_scene(GLFWwindow* window, float frameTime)
{
// declare variables to transform the object
for (int i = 0; i < 6; i++)
{
totalOrbit[i] += orbit[i] * frameTime;
totalRotation[i] += rotation[i] * frameTime;
}
if (totalShade = 1)
{
shadeChange = -0.05f;
}
else if (totalShade = 0)
{
shadeChange = 0.05f;
}
totalShade += shadeChange;
glUniform1f(shadeIndex, totalShade);
}
int main(void)
{
GLFWwindow* window = NULL; // pointer to a GLFW window handle
glfwSetErrorCallback(error_callback); // set error callback function
// initialise GLFW
if (!glfwInit())
{
// if failed to initialise GLFW
exit(EXIT_FAILURE);
}
// minimum OpenGL version 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// create a window and its OpenGL context
window = glfwCreateWindow(800, 600, "Creating a Window", NULL, NULL);
// if failed to create window
if (window == NULL)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window); // set window context as the current context
glfwSwapInterval(1); // swap buffer interval
// initialise GLEW
if (glewInit() != GLEW_OK)
{
// if failed to initialise GLEW
cerr << "GLEW initialisation failed" << endl;
exit(EXIT_FAILURE);
}
// set key callback function
glfwSetKeyCallback(window, key_callback);
/*
// if not using key or mouse callback functions
// use sticky mode to avoid missing state changes from polling
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, GL_TRUE);
*/
// initialise rendering states
init(window);
double lastUpdateTime = glfwGetTime(); // last update time
double elapsedTime = lastUpdateTime; // time elapsed since last update
float frameTime = 0.0f; // frame time
int frameCount = 0;
// the rendering loop
while (!glfwWindowShouldClose(window))
{
update_scene(window, frameTime); // update the scene
render_scene(); // render the scene
glfwSwapBuffers(window); // swap buffers
glfwPollEvents(); // poll for events
frameCount++;
elapsedTime = glfwGetTime() - lastUpdateTime; // current time - last update time
if (elapsedTime >= 1.0f) // if time since last update >= to 1 second
{
frameTime = static_cast<float>(1.0f / frameCount); // calculate frame time
string str = "FPS = " + to_string(frameCount) + "; FT = " + to_string(frameTime);
glfwSetWindowTitle(window, str.c_str()); // update window title
frameCount = 0; // reset frame count
lastUpdateTime += elapsedTime; // update last update time
}
}
// clean up
glDeleteProgram(g_shaderProgramID);
glDeleteBuffers(1, &g_IBO);
glDeleteBuffers(1, &g_VBO);
glDeleteVertexArrays(1, &g_VAO);
// close the window and terminate GLFW
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
here is all the vertex shader code:
#version 330 core
// input data (different for all executions of this shader)
in vec3 aPosition;
in vec3 aColor;
// ModelViewProjection matrix
uniform mat4 uModelViewProjectionMatrix;
// output data (will be interpolated for each fragment)
out vec3 vColor;
void main()
{
// set vertex position
gl_Position = uModelViewProjectionMatrix * vec4(aPosition, 1.0);
// the color of each vertex will be interpolated
// to produce the color of each fragment
vColor = aColor;
}
And here is all the shader cpp code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
#define GLEW_STATIC // include GLEW as a static library
#include <GLEW/glew.h> // include GLEW
#include "shader.h"
// function to load shaders
GLuint loadShaders(const string vertexShaderFile, const string fragmentShaderFile)
{
GLint status; // for checking compile and linking status
// load vertex shader code from file
string vertexShaderCode; // to store shader code
ifstream vertexShaderStream(vertexShaderFile, ios::in); // open file stream
// check whether file stream was successfully opened
if(vertexShaderStream.is_open())
{
// read from stream line by line and append it to shader code
string line = "";
while(getline(vertexShaderStream, line))
vertexShaderCode += line + "\n";
vertexShaderStream.close(); // no longer need file stream
}
else
{
// output error message and exit
cout << "Failed to open vertex shader file - " << vertexShaderFile << endl;
exit(EXIT_FAILURE);
}
// load fragment shader code from file
string fragmentShaderCode; // to store shader code
ifstream fragmentShaderStream(fragmentShaderFile, ios::in); // open file stream
// check whether file stream was successfully opened
if(fragmentShaderStream.is_open())
{
// read from stream line by line and append it to shader code
string line = "";
while(getline(fragmentShaderStream, line))
fragmentShaderCode += line + "\n";
fragmentShaderStream.close(); // no longer need file stream
}
else
{
// output error message and exit
cout << "Failed to open fragment shader file - " << fragmentShaderFile << endl;
exit(EXIT_FAILURE);
}
// create shader objects
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// provide source code for shaders
const GLchar* vShaderCode = vertexShaderCode.c_str();
const GLchar* fShaderCode = fragmentShaderCode.c_str();
glShaderSource(vertexShaderID, 1, &vShaderCode, NULL);
glShaderSource(fragmentShaderID, 1, &fShaderCode, NULL);
// compile vertex shader
glCompileShader(vertexShaderID);
// check compile status
status = GL_FALSE;
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE)
{
// output error message
cout << "Failed to compile vertex shader - " << vertexShaderFile << endl;
// output error information
int infoLogLength;
glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
char* errorMessage = new char[infoLogLength + 1];
glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, errorMessage);
cout << errorMessage << endl;
delete[] errorMessage;
exit(EXIT_FAILURE);
}
// compile fragment shader
glCompileShader(fragmentShaderID);
// check compile status
status = GL_FALSE;
glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE)
{
// output error message
cout << "Failed to compile fragment shader - " << fragmentShaderFile << endl;
// output error information
int infoLogLength;
glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
char* errorMessage = new char[infoLogLength + 1];
glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, errorMessage);
cout << errorMessage << endl;
delete[] errorMessage;
exit(EXIT_FAILURE);
}
// create program
GLuint programID = glCreateProgram();
// attach shaders to the program object
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
// flag shaders for deletion (will not be deleted until detached from program)
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
// link program object
glLinkProgram(programID);
// check link status
status = GL_FALSE;
glGetProgramiv(programID, GL_LINK_STATUS, &status);
if(status == GL_FALSE)
{
// output error message
cout << "Failed to link program object." << endl;
// output error information
int infoLogLength;
glGetShaderiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength);
char* errorMessage = new char[infoLogLength + 1];
glGetShaderInfoLog(programID, infoLogLength, NULL, errorMessage);
cout << errorMessage << endl;
delete[] errorMessage;
exit(EXIT_FAILURE);
}
return programID;
}
In case you are wondering what the code does, it creates a cube, then transforms it to make a solar system. I was trying to make 1 cube go dark then bright but I wanted to make sure it worked first by using it on all of them.
I believe:
if (shadeIndex = -1)
should be:
if (shadeIndex == -1)
And then you will need to add a call to glUniform1f in your code, in order to actually set the uniform value, of course.
I'm currently working on an assignment on creating a solar system using openGL. I've managed to create the planets, orbit of planet around the sun, and rotation of each planet axis.
i'm stuck on how do i dynamically draw a line for the orbit path for the planets. so that even if the orbit path changes the line would change.(those thin lines that shows the orbit path in most solar system images)
Hope to get a direction to work towards to instead of the answer.
Thanks ahead for the replies (if any)
P.S : Using GLFW not GLUT library. Below is what i have so far
static void init(GLFWwindow* window)
{
glEnable(GL_DEPTH_TEST);
// Create the shader object
g_shaderProgramID = loadShaders("ModelSpaceVS.vert", "ColorFS.frag");
// Getting Locations of shader's variables
GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
GLuint colorIndex = glGetAttribLocation(g_shaderProgramID, "aColor");
g_modelMatrixIndex = glGetUniformLocation(g_shaderProgramID, "uModelMatrix");
// Creating camera object
// Set camera's view matrix
g_camera.setViewMatrix(vec3(0.0f, 15.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0, 1, 0));
// Window Aspect Ratio
int width, height;
glfwGetFramebufferSize(window, &width, &height);
float aspectRatio = static_cast<float>(width) / height;
// Set camera's projection matrix
g_camera.setProjectionMatrix(perspective(45.0f, aspectRatio, 0.1f, 100.0f));
// Initialise the model matrix to identity matrix
// Set to 1.0f so that during the transformation process where
// the matrix is multiplied, it would be 1*<value> = <value>
// The transformation && scale && rotate are placed in the update_scene Function
// as the update scene needs to update the orbitting value as well
g_modelMatrix[0] = mat4(1.0f);
g_modelMatrix[1] = mat4(1.0f);
g_modelMatrix[2] = mat4(1.0f);
g_modelMatrix[3] = mat4(1.0f);
g_modelMatrix[4] = mat4(1.0f);
// Generate identifier for VBO and copy data to GPU
glGenBuffers(4, g_VBO);
// Binding the vertices for the planet (Only 1 vertices for sun + 4 planets)
// Transformation is not done here
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(planetOrigin), planetOrigin, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_VBO[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_indices), g_indices, GL_STATIC_DRAW);
// Line Vertex
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[2]); // Binding the VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(linePos), linePos, GL_STATIC_DRAW); // Copy data to buffer
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[3]); // Binding the VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(lineColor), lineColor, GL_STATIC_DRAW); // Copy data to buffer
// generate identifiers for VAOs
glGenVertexArrays(2, g_VAO);
// create VAO and specify VBO data
glBindVertexArray(g_VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_VBO[1]);
// interleaved attributes
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertices), reinterpret_cast<void*>(offsetof(Vertices, position)));
glVertexAttribPointer(colorIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertices), reinterpret_cast<void*>(offsetof(Vertices, color)));
glEnableVertexAttribArray(positionIndex); // enable vertex attributes
glEnableVertexAttribArray(colorIndex);
// Binding the VBO to a VAO
// Lines
glBindVertexArray(g_VAO[1]); // Create the VAO object
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[2]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[3]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}
// Keyboard Input Actions
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
// Exit the program on ESC
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
// Closes the window and end the program
glfwSetWindowShouldClose(window, GL_TRUE);
return;
}
else if (key == GLFW_KEY_P && action == GLFW_PRESS)
{
// Animating the planets Orbit around the sun
// and also their own individual rotation about their own axis
if (animate == false)
animate = true;
else if (animate == true)
animate = false;
}
else if (key == GLFW_KEY_R && action == GLFW_PRESS)
{
// Randomize the Size / Orbit Speed Around the Sun / Rotation Speed about their own axis
// of all the planet and the sun
//
//
// Rand + 100 to prevent the orbit or rotationSpeed from being 0
// Or being too low that it seems to stop
//
// Randomizing the orbitSpeed of planet
// Capping the orbit Speed at 300, minimum 100
orbitSpeed = (rand() % 200) + 100;
// Randomizing the rotationSpeed of planet + sun
// upon its own axis
// Capping the rotation Speed at 300, minimum 100
rotationSpeed = (rand() % 200) + 100;
// Randomizing the planet/sun size
// Values of the rand() is to ensure the planet will never be bigger than the sun
// rand() % 3 / 10 + 0.7 = min : 0.7, Max : 1.0
// rand() % 5 / 10 + 0.2 = min : 0.2, Max : 0.7
sunSize = (rand() % 2) + 3.0;
planetSize1 = (rand() % 2) + 0.9;
planetSize2 = (rand() % 2) + 0.9;
planetSize3 = (rand() % 2) + 0.9;
planetSize4 = (rand() % 2) + 0.9;
}
else if (key == GLFW_KEY_1 && action == GLFW_PRESS)
{
// Set the camera view to a far-away top down view of the
// solar system
// Looks like a RTS camera style
// Set camera's view matrix
g_camera.setViewMatrix(vec3(0.0f, 10.0f, 20.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0, 1, 0));
}
else if (key == GLFW_KEY_2 && action == GLFW_PRESS)
{
// Set the camera view to a top down view
// directly looking at it from above
// Set camera's view matrix
g_camera.setViewMatrix(vec3(0.0f, 20.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), vec3(0, 1.0, 0));
}
}
static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
// variables to store mouse cursor coordinates
static double previous_xpos = xpos;
static double previous_ypos = ypos;
double delta_x = xpos - previous_xpos;
double delta_y = ypos - previous_ypos;
// pass mouse movement to camera class
g_camera.updateYaw(delta_x);
g_camera.updatePitch(delta_y);
// update previous mouse coordinates
previous_xpos = xpos;
previous_ypos = ypos;
}
// Frame Buffer Action
static void framebuffer_Size_callback(GLFWwindow* window, int width, int height)
{
string title = "Assignment 1 - " + to_string(width) + " x " + to_string(height);
glfwSetWindowTitle(window, title.c_str());
}
// error callback function
static void error_callback(int error, const char* description)
{
// output error description
cerr << "Error Occurred: " << description << endl;
}
// Update Scene Function
// Updates the "planet" solitary rotation about its own axis
// Updates the "planet" orbit around a point around the "sun"
static void update_scene(GLFWwindow* window, double frameTime)
{
static float rotateAngleY = 0.0f;
static float rotateAngleY1 = 0.0f;
static float rotateAngleY2 = 0.0f;
static float rotateAngleY3 = 0.0f;
static float sunRotateAngleY = 0.0f;
static float orbitAngle1 = 0.0f;
static float orbitAngle2 = 0.0f;
static float orbitAngle3 = 0.0f;
static float orbitAngle4 = 0.0f;
if (animate == true)
{
rotateAngleY += 1.5 * rotationSpeed * frameTime;
rotateAngleY1 += 1.0 * rotationSpeed * frameTime;
rotateAngleY2 -= 2.0 * rotationSpeed * frameTime;
rotateAngleY3 -= 2.5 * rotationSpeed * frameTime;
sunRotateAngleY += 0.1 * rotationSpeed * frameTime;
orbitAngle1 += 0.6 * orbitSpeed * frameTime;
orbitAngle2 += 0.5 * orbitSpeed * frameTime;
orbitAngle3 += 0.2 * orbitSpeed * frameTime;
orbitAngle4 += 0.1 * orbitSpeed * frameTime;
}
// Updating the Scene the rotate the individual planets
// Each of the planet needs their initial translate/scale value
// if the transform/scale here is vec(0.0,0.0,0.0) || vec3(1.0,1.0,1.0)
// They will be rendered on the origin, which is where the sun is
// at the same size
//
// rotate(radians(orbitAngle1), vec3(0.0f, 1.0f, 0.0f)) - Controls Orbit Rotation of Planet
// rotate(radians(rotateAngleY), vec3(1.0f, 1.0f, 0.0f)) - Controls Orbit About Own Axis
// rotate(radians(-60.0f), vec3(0.0f, 0.0f, 1.0f)) - Controls the rotation of a planet on display(without movement)
g_modelMatrix[0] = translate(vec3(0.0f, 0.0, 0.0f)) * rotate(radians(sunRotateAngleY), vec3(0.0, 1.0, 0.0f))
* scale(vec3(1.0f, 1.0f, 1.0f) * sunSize);
g_modelMatrix[1] = rotate(radians(orbitAngle1), vec3(0.0f, 1.0f, 0.0f)) * translate(vec3(2.0f, 0.0f, 0.0f) + (sunSize/10)) * rotate(radians(rotateAngleY), vec3(0.0f, 1.0f, 0.0f))
* rotate(radians(-60.0f), vec3(0.0f, 0.0f, 1.0f)) * scale(vec3(1.0f, 1.0f, 1.0f) * planetSize1);
g_modelMatrix[2] = rotate(radians(orbitAngle2), vec3(0.0f, 1.0f, 0.0f)) * translate(vec3(4.0f, 0.0f, 0.0f) + (sunSize/10)) * rotate(radians(rotateAngleY1), vec3(0.0f, 1.0f, 0.0f))
* rotate(radians(-45.0f), vec3(0.0f, 0.0f, 1.0f)) * scale(vec3(1.0f, 1.0f, 1.0f)* planetSize2);
g_modelMatrix[3] = rotate(radians(orbitAngle3), vec3(0.0f, 1.0f, 0.0f)) * translate(vec3(6.0f, 0.0f, 0.0f) + (sunSize/10)) * rotate(radians(rotateAngleY2), vec3(0.0f, 1.0f, 0.0f))
* rotate(radians(45.0f), vec3(0.0f, 0.0f, 1.0f)) * scale(vec3(1.0f, 1.0f, 1.0f) * planetSize3);
g_modelMatrix[4] = rotate(radians(orbitAngle4), vec3(0.0f, 1.0f, 0.0f)) * translate(vec3(8.0f, 0.0f, 0.0f) + (sunSize/10)) * rotate(radians(rotateAngleY), vec3(0.0f, 1.0f, 0.0f))
* rotate(radians(60.0f), vec3(0.0f, 0.0f, 1.0f)) * scale(vec3(1.0f, 1.0f, 1.0f) * planetSize4);
}
// Render Scene Function
static void render_scene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear colour buffer && clear depth buffer
glUseProgram(g_shaderProgramID);
// Make VAO active
glBindVertexArray(g_VAO[0]);
// Sun Object
// compute multiplication of model, view and projection matrices
mat4 MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[0];
glUniformMatrix4fv(g_modelMatrixIndex, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// Planet 1
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[1];
glUniformMatrix4fv(g_modelMatrixIndex, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// Planet 2
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[2];
glUniformMatrix4fv(g_modelMatrixIndex, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// Planet 3
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[3];
glUniformMatrix4fv(g_modelMatrixIndex, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
// Planet 4
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[4];
glUniformMatrix4fv(g_modelMatrixIndex, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
glFlush(); // Flushing the pipelines
}
// ------------------------------------------------------------------------------------
// Main Function
int main(void)
{
srand(time(NULL));
GLFWwindow* window = NULL; // Creating of window variable
glfwSetErrorCallback(error_callback);
// Initialise glfw
// if false = failed initialization
if (!glfwInit())
{
cout << "glfw Initialisation failed" << endl;
exit(EXIT_FAILURE);
}
// Declaring the version of OpenGL
// Version 3.3 in this case
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// Creating the window object
window = glfwCreateWindow(1080, 720, "Assignment 2 - 1080 x 720", NULL, NULL);
// Check if the Creating of window Object succeeded
if (window == NULL)
{
cout << "Creation of window failed" << endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
// Making Context Current
glfwMakeContextCurrent(window);
// Determine Swap Buffer Interval
glfwSwapInterval(1);
// Set a background color for window
// Black - Solar System in space
glClearColor(0.0, 0.0, 0.0, 1.0);
// Initialise GLEW
if (glewInit() != GLEW_OK)
{
cout << "Initialisation of GLEW failed" << endl;
exit(EXIT_FAILURE);
}
// Keyboard/Mouse Input/Callbacks
glfwSetFramebufferSizeCallback(window, framebuffer_Size_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
// Init function
init(window);
// Frame Variables
double lastUpdateTime = glfwGetTime(); // last update time
double elapsedTime = lastUpdateTime; // time elapsed since last update
double frameTime = 0.0f; // frame time
int frameCount = 0; // number of frames since last update
// Rendering Loop
while (!glfwWindowShouldClose(window))
{
g_camera.update(window);
update_scene(window, frameTime);
render_scene();
// Swap Buffer
glfwSwapBuffers(window);
// Poll Events
glfwPollEvents();
frameCount++;
elapsedTime = glfwGetTime() - lastUpdateTime; // current time - last update time
if (elapsedTime >= 1.0f) // if time since last update >= to 1 second
{
frameTime = 1.0f / frameCount; // calculate frame time
string str = "FPS = " + to_string(frameCount) + "; FT = " + to_string(frameTime);
glfwSetWindowTitle(window, str.c_str()); // update window title
frameCount = 0; // reset frame count
lastUpdateTime += elapsedTime; // update last update time
}
}
// Cleaning Up Shader Program
glDeleteProgram(g_shaderProgramID);
// Cleaning Up Vertexes
glDeleteBuffers(14, g_VBO);
glDeleteVertexArrays(2, g_VAO);
// Destroying the window before terminating the program
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
I am following along with this tutorial series, but also trying to customise my solution as I go along (essentially I am trying to render a 3D point cloud -ie. a whole bunch of XYZ points).
I have been able to get the camera working and a 3D environment going. I am happy to work on the rest of that by myself, but the problem I am having is that the scroll wheel is not responding. I am hoping this is blindingly obvious to someone. It seems that the only user callback the program is getting is that of the mouse position - 100% of the time - and that's preventing the scroll_callback function form being heard. Can someone explain why my scroll wheel callback is not being received. Code below.
Let me know if any further info is required.
I am using Visual Studio 2017 Community.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <Shader.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;
// camera
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
bool firstMouse = true;
float yaw = -90.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 pitch = 0.0f;
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
float fov = 45.0f;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
Shader ourShader("VertexShader.vs", "FragShader.fs");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
struct Point
{
float x;
float y;
float z;
};
Point points[500];
for (int i = 0; i < 500; i++)
{
points[i].x = (float)((rand() % SCR_WIDTH) + 1);
points[i].y = (float)((rand() % SCR_WIDTH) + 1);
points[i].z = (float)((rand() % SCR_WIDTH) + 1);
// X Coords to Normalised Device coordinates
if (points[i].x > 400)
{
points[i].x = points[i].x * 0.00125f;
}
else if (points[i].x < 400)
{
points[i].x = points[i].x * -0.00125f;
}
else if (points[i].x == 400)
{
points[i].x = 0.0f;
}
// Y Coords to Normalised Device coordinates
if (points[i].y > 400)
{
points[i].y = points[i].y * 0.00125f;
}
else if (points[i].y < 400)
{
points[i].y = points[i].y * -0.00125f;
}
else if (points[i].y == 400)
{
points[i].y = 0.0f;
}
// Z Coords to Normalised Device coordinates
if (points[i].z > 400)
{
points[i].z = points[i].z * 0.00125f;
}
else if (points[i].z < 400)
{
points[i].z = points[i].z * -0.00125f;
}
else if (points[i].z == 400)
{
points[i].z = 0.0f;
}
//cout << points[i].x << ", " << points[i].y << ", " << points[i].z << endl;
}
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate shader
ourShader.use();
// create transformations
glm::mat4 projection;
projection = glm::perspective(glm::radians(95.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
ourShader.setMat4("projection", projection); // note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
// camera/view transformation
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
ourShader.setMat4("view", view);
glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
ourShader.setMat4("model", model);
// draw our points array
//glUseProgram(shaderProgram);
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glPointSize(3.0f);
glDrawArrays(GL_POINTS, 0, 500);
// glBindVertexArray(0); // no need to unbind it every time
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float cameraSpeed = 2.5 * deltaTime;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cameraPos += cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cameraPos -= cameraSpeed * cameraFront;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}
// glfw: 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.
glViewport(0, 0, width, height);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
float sensitivity = 0.1f; // change this value to your liking
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
// make sure that when pitch is out of bounds, screen doesn't get flipped
if (pitch > 89.0f)
pitch = 89.0f;
if (pitch < -89.0f)
pitch = -89.0f;
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));
cameraFront = glm::normalize(front);
}
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
if (fov >= 1.0f && fov <= 45.0f)
fov -= yoffset;
if (fov <= 1.0f)
fov = 1.0f;
if (fov >= 45.0f)
fov = 45.0f;
}
You're going to facepalm a little:
projection = glm::perspective(glm::radians(95.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
should be:
projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
I ran your code in a debugger and the scroll callback function is running every time but the fov value was never being used. You can use the same technique if you ever want to check if a function is being called by adding a break point anywhere in the function.
my OpenGL version is 4.0. I would like to draw a sphere through latitude and longitude. I use this method:
x=ρsinϕcosθ
y=ρsinϕsinθ
z=ρcosϕ
This is a part of my code:
glm::vec3 buffer[1000];
glm::vec3 outer;
buffercount = 1000;
float section = 10.0f;
GLfloat alpha, beta;
int index = 0;
for (alpha = 0.0 ; alpha <= PI; alpha += PI/section)
{
for (beta = 0.0 ; beta <= 2* PI; beta += PI/section)
{
outer.x = radius*cos(beta)*sin(alpha);
outer.y = radius*sin(beta)*sin(alpha);
outer.z = radius*cos(alpha);
buffer[index] = outer;
index = index +1;
}
}
GLuint sphereVBO, sphereVAO;
glGenVertexArrays(1, &sphereVAO);
glGenBuffers(1,&sphereVBO);
glBindVertexArray(sphereVAO);
glBindBuffer(GL_ARRAY_BUFFER,sphereVBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(glm::vec3) *buffercount ,&buffer[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
...
while (!glfwWindowShouldClose(window))
{
...
...
for (GLuint i = 0; i < buffercount; i++)
{
...
...
glm::mat4 model;
model = glm::translate(model, buffer[i]);
GLfloat angle = 10.0f * i;
model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
glUniformMatrix4fv(modelMat, 1, GL_FALSE, glm::value_ptr(model));
}
glDrawArrays(GL_TRIANGLE_FAN, 0, 900);
glfwSwapBuffers(window);
}
if section = 5, the performance is like this:
if section = 20. the performance is like this:
I think that I might have logic problem in my code. I am struggle in this problem...
-----update-----
I edited my code, It doesn't have any error, but I got a blank screen. I guess that something wrong in my vertex shader. I might pass wrong variables to vertex sheder. Please help me.
gluperspective is deprecated in my OpenGL 4.1
I switch to :
float aspect=float(4.0f)/float(3.0f);
glm::mat4 projection_matrix = glm::perspective(60.0f/aspect,aspect,0.1f,100.0f);
It shows that this error: constant expression evaluates to -1 which cannot be narrowed to type 'GLuint'(aka 'unsigned int')
GLuint sphere_vbo[4]={-1,-1,-1,-1};
GLuint sphere_vao[4]={-1,-1,-1,-1};
I'm not sure how to revise it...I switch to:
GLuint sphere_vbo[4]={1,1,1,1};
GLuint sphere_vao[4]={1,1,1,1};
I put Spektre's code in spherer.h file
This is a part of my main.cpp file:
...
...
Shader shader("basic.vert", "basic.frag");
sphere_init();
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
shader.Use();
GLuint MatrixID = glGetUniformLocation(shader.Program, "MVP");
GLfloat radius = 10.0f;
GLfloat camX = sin(glfwGetTime()) * radius;
GLfloat camZ = cos(glfwGetTime()) * radius;
// view matrix
glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
glm::mat4 view_matrix = view;
// projection matrix
float aspect=float(4.0f)/float(3.0f);
glm::mat4 projection_matrix = glm::perspective(60.0f/aspect,aspect,0.1f,100.0f);
// model matrix
glm::mat4 model_matrix = glm::mat4(1.0f);// identity
//ModelViewProjection
glm::mat4 model_view_projection = projection_matrix * view_matrix * model_matrix;
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &model_view_projection[0][0]);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-10.0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
sphere_draw();
glFlush();
glfwSwapBuffers(window);
}
sphere_exit();
glfwTerminate();
return 0;
}
This is my vertex shader file:
#version 410 core
uniform mat4 MVP;
layout(location = 0) in vec3 vertexPosition_modelspace;
out vec4 vertexColor;
void main()
{
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
vertexColor = vec4(0, 1, 0, 1.0);
}
I added error-check function get_log in my shader.h file.
...
...
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
get_log(vertex);
...
...
void get_log(GLuint shader){
GLint isCompiled = 0;
GLchar infoLog[1024];
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
printf("----error--- \n");
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "| ERROR::::" << &infoLog << "\n| -- ------------------ --------------------------------- -- |" << std::endl;
glDeleteShader(shader); // Don't leak the shader.
}else{
printf("---no error --- \n");
}
}
I tested both fragment shader and vertex shader, it both showed ---no error---
As I mentioned in the comments you need to add indices to your mesh VAO/VBO. Not sure why GL_QUADS is not implemented on your machine that makes no sense as it is basic primitive so to make this easy to handle I use only GL_TRIANGLES which is far from ideal but what to heck ... Try this:
//---------------------------------------------------------------------------
const int na=36; // vertex grid size
const int nb=18;
const int na3=na*3; // line in grid size
const int nn=nb*na3; // whole grid size
GLfloat sphere_pos[nn]; // vertex
GLfloat sphere_nor[nn]; // normal
//GLfloat sphere_col[nn]; // color
GLuint sphere_ix [na*(nb-1)*6]; // indices
GLuint sphere_vbo[4]={-1,-1,-1,-1};
GLuint sphere_vao[4]={-1,-1,-1,-1};
void sphere_init()
{
// generate the sphere data
GLfloat x,y,z,a,b,da,db,r=3.5;
int ia,ib,ix,iy;
da=2.0*M_PI/GLfloat(na);
db= M_PI/GLfloat(nb-1);
// [Generate sphere point data]
// spherical angles a,b covering whole sphere surface
for (ix=0,b=-0.5*M_PI,ib=0;ib<nb;ib++,b+=db)
for (a=0.0,ia=0;ia<na;ia++,a+=da,ix+=3)
{
// unit sphere
x=cos(b)*cos(a);
y=cos(b)*sin(a);
z=sin(b);
sphere_pos[ix+0]=x*r;
sphere_pos[ix+1]=y*r;
sphere_pos[ix+2]=z*r;
sphere_nor[ix+0]=x;
sphere_nor[ix+1]=y;
sphere_nor[ix+2]=z;
}
// [Generate GL_TRIANGLE indices]
for (ix=0,iy=0,ib=1;ib<nb;ib++)
{
for (ia=1;ia<na;ia++,iy++)
{
// first half of QUAD
sphere_ix[ix]=iy; ix++;
sphere_ix[ix]=iy+1; ix++;
sphere_ix[ix]=iy+na; ix++;
// second half of QUAD
sphere_ix[ix]=iy+na; ix++;
sphere_ix[ix]=iy+1; ix++;
sphere_ix[ix]=iy+na+1; ix++;
}
// first half of QUAD
sphere_ix[ix]=iy; ix++;
sphere_ix[ix]=iy+1-na; ix++;
sphere_ix[ix]=iy+na; ix++;
// second half of QUAD
sphere_ix[ix]=iy+na; ix++;
sphere_ix[ix]=iy-na+1; ix++;
sphere_ix[ix]=iy+1; ix++;
iy++;
}
// [VAO/VBO stuff]
GLuint i;
glGenVertexArrays(4,sphere_vao);
glGenBuffers(4,sphere_vbo);
glBindVertexArray(sphere_vao[0]);
i=0; // vertex
glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_pos),sphere_pos,GL_STATIC_DRAW);
glEnableVertexAttribArray(i);
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
i=1; // indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,sphere_vbo[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(sphere_ix),sphere_ix,GL_STATIC_DRAW);
glEnableVertexAttribArray(i);
glVertexAttribPointer(i,4,GL_UNSIGNED_INT,GL_FALSE,0,0);
i=2; // normal
glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_nor),sphere_nor,GL_STATIC_DRAW);
glEnableVertexAttribArray(i);
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
/*
i=3; // color
glBindBuffer(GL_ARRAY_BUFFER,sphere_vbo[i]);
glBufferData(GL_ARRAY_BUFFER,sizeof(sphere_col),sphere_col,GL_STATIC_DRAW);
glEnableVertexAttribArray(i);
glVertexAttribPointer(i,3,GL_FLOAT,GL_FALSE,0,0);
*/
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
}
void sphere_exit()
{
glDeleteVertexArrays(4,sphere_vao);
glDeleteBuffers(4,sphere_vbo);
}
void sphere_draw()
{
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glBindVertexArray(sphere_vao[0]);
// glDrawArrays(GL_POINTS,0,sizeof(sphere_pos)/sizeof(GLfloat)); // POINTS ... no indices for debug
glDrawElements(GL_TRIANGLES,sizeof(sphere_ix)/sizeof(GLuint),GL_UNSIGNED_INT,0); // indices (choose just one line not both !!!)
glBindVertexArray(0);
}
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float aspect=float(xs)/float(ys);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0/aspect,aspect,0.1,100.0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-10.0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
sphere_draw();
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
Usage is simple after OpenGL context is created and extensions loaded call sphere_init() before closing app call sphere_exit() (while OpenGL context is still running) and when you want to render call sphere_draw(). I make an gl_draw() example with some settings and here the preview of it:
The point is to create 2D grid of points covering whole surface of sphere (via spherical long,lat a,b angles) and then just create triangles covering whole grid...