I'm taking a Computer Graphics course at my university. I need to implement basic line drawing algorithms in modern OpenGL(3.3+) to draw primitives on the screen. Here's the function for Bresenham's Line Drawing Algorithm that I want to implement -
void bresenham(GLint xStart, GLint yStart, GLint xEnd, GLint yEnd) {
if (!(xStart < xEnd)) {
swap(xStart, xEnd);
swap(yStart, yEnd);
}
GLint dx = xEnd - xStart;
GLint dy = yEnd - yStart;
GLint p = 2 * dy - dx;
GLint x = xStart;
GLint y = yStart;
setPixel(x,y);
while (x < xEnd) {
x += 1;
if (p < 0)
p += (2 * dy);
else {
p += (2 * (dy - dx));
y += 1;
setPixel(x,y);
}
}
}
I'm clueless on how to realise the setPixel() function. Most answers I found here and elsewhere use older OpenGL functions -
void setPixel(int x, int y)
{
glColor3f(0.0, 0.0, 0.0); //Set pixel to black
glBegin(GL_POINTS);
glVertex2i(x, y); //Set pixel coordinates
glEnd();
glFlush(); //Render pixel
}
What is the equivalent way to do this in OpenGl 3.3+?
Assuming I can add the "pixels" to an std::vector array, how do I initialise the vertex buffer array to store this data?
Another problem I ran into while trying to plot a point using GL_POINTS is that due to clipping during conversion to normalised device coordinates, points beyond the range [-1,1] in either direction do not show on the window.
For example, only the first three points show up on the window screen. See the initialize() function -
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <fstream>
// Read a shader source from a file
// store the shader source in a std::vector<char>
void read_shader_src(const char* fname, std::vector<char> &buffer);
// Compile a shader
GLuint load_and_compile_shader(const char *fname, GLenum shaderType);
// Create a program from two shaders
GLuint create_program(const char *path_vert_shader, const char *path_frag_shader);
// Render scene
void display(GLuint &vao, GLFWwindow* window);
// Initialize the data to be rendered
void initialize(GLuint &vao);
//GLFW Callbacks
static void error_callback(int error, const char* description) {
fprintf(stderr, "Error: %s\n", description);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
int main() {
glfwSetErrorCallback(error_callback);
//Initialize GLFW
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW.\n");
return -1;
}
//Set GLFW window settings and create window
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);
GLFWwindow* window = glfwCreateWindow(500, 500, "My window", NULL, NULL);
if(!window) {
fprintf(stderr, "Window or context creation failed.\n");
return -1;
}
glfwSetKeyCallback(window, key_callback);
glfwMakeContextCurrent(window);
//Initialize GLEW
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize glew");
glfwTerminate();
return -1;
}
//Create a vertex array object
GLuint vao;
//Initialize the data to be rendered
initialize(vao);
while (!glfwWindowShouldClose(window)) {
display(vao, window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
//Render scene
void display(GLuint &vao, GLFWwindow* window) {
//Red background
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, 12);
// Swap front and back buffers
glfwSwapBuffers(window);
}
void initialize(GLuint &vao) {
glEnable(GL_PROGRAM_POINT_SIZE);
// Use a Vertex Array Object
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//Store verex positions in an array
GLfloat vertices[24] = {
0.0, 0.0, // Only these
0.5, 0.5, //three points show up
1.0, 1.0, //on the window screen
4.0, 4.0,
5.0, 5.0,
6.0, 6.0,
7.0, 7.0,
8.0, 8.0,
9.0, 9.0,
10.0, 10.0,
11.0, 11.0,
12.0, 12.0,
};
//Create a vertex buffer object to store the vertex data
GLuint vbo;
//Generates 1 buffer object name and stores it in vbo
glGenBuffers(1, &vbo);
//Bind the buffer object to the buffer binding target
glBindBuffer(GL_ARRAY_BUFFER, vbo);
//Creates and initializes the buffer object's data store(with data from vertices)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint shaderProgram = create_program("/Users/.../vert.shader", "/Users/.../frag.shader"); //path to shader files
// Get the location of the attributes that enters in the vertex shader
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
// Specify how the data for position can be accessed
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
//Enable the attribute
glEnableVertexAttribArray(posAttrib);
}
// Read a shader source from a file
// store the shader source in a std::vector<char>
void read_shader_src(const char *fname, std::vector<char> &buffer) {
std::ifstream in;
in.open(fname, std::ios::binary);
if(in.is_open()) {
// Get the number of bytes stored in this file
in.seekg(0, std::ios::end);
size_t length = (size_t)in.tellg();
// Go to start of the file
in.seekg(0, std::ios::beg);
// Read the content of the file in a buffer
buffer.resize(length + 1);
in.read(&buffer[0], length);
in.close();
// Add a valid C - string end
buffer[length] = '\0';
}
else {
std::cerr << "Unable to open " << fname << " I'm out!" << std::endl;
exit(-1);
}
}
//Compile a shader
GLuint load_and_compile_shader(const char* fname, GLenum shaderType) {
//Load a shader from an external file
std::vector<char> buffer;
read_shader_src(fname, buffer);
const char *src = &buffer[0];
//Create and compile the shader
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);
GLint shader_compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compiled);
if(!shader_compiled) {
GLchar message[1024];
glGetShaderInfoLog(shader, 1024, NULL, message);
std::cerr << "Shader compilation failed.";
std::cerr << "Log: " << &message << std::endl;
glfwTerminate();
exit(-1);
}
return shader;
}
// Create a program from two shaders
GLuint create_program(const char *path_vert_shader, const char *path_frag_shader) {
// Load and compile the vertex and fragment shaders
GLuint vertexShader = load_and_compile_shader(path_vert_shader, GL_VERTEX_SHADER);
GLuint fragmentShader = load_and_compile_shader(path_frag_shader, GL_FRAGMENT_SHADER);
// Attach the above shader to a program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
// Flag the shaders for deletion
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Link and use the program
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
return shaderProgram;
}
What modifications do I make so I can plot the rest of the points?
Vertex Shader -
#version 150 core
in vec4 position;
void main() {
gl_Position = position;
gl_PointSize = 10.0;
}
Fragment Shader -
#version 150 core
out vec4 out_color;
void main() {
out_color = vec4(1.0, 1.0, 1.0, 1.0);
}
From the comments, it sounds like you're trying to use OpenGL in place of a really old graphics library that is required for a class. Since computer graphics have changed so much that what you're trying to do in modern OpenGL is unreasonable, you should try doing this for the current assignment and your later ones:
Disclaimer: This is not a reasonable way to draw a line in modern OpenGL
Create a 2d array of some arbitrary size, large enough that the entire line can be drawn on it.
Draw the line using the original function, create some setPixel function that changes elements in that array
Once you're done drawing the line (or doing whatever else future assignments will have you do), create an OpenGL texture from that array. An excellent guide is available here: https://open.gl/textures
Some rough psuedocode:
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_FLOAT, pixels);
glBindTexture(GL_TEXTURE_2D, 0);
Then you would create a quad (really just two triangles) that draw this texture to screen. Following the open.gl guide should work well since they already do this for their textures. Pulling from their provided code (which does a bit extra, all correct and following the spec though):
GLfloat vertices[] = {
// Position Color Texcoords
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f // Bottom-left
};
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
// Set up buffer objects, create shaders, initialize GL, etc.
//drawing
//bind buffers, enable attrib arrays, etc
glBindTexture(GL_TEXTURE_2D, tex);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
Related
I can't seem to figure out how to add camera movement with WASD and mouse movement. Any directions or help?
I've attached my code below:
#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>
#define STB_IMAGE_IMPLEMENTATION
#include <stb-master/stb_image.h> // Image loading Utility functions
#include "meshes.h"
// Uses the standard namespace for debug output
using namespace std;
// Unnamed namespace for C++ defines //
namespace
{
// Macro for OpenGL window title
const char* const WINDOW_TITLE = "CS330 - Ice Cream";
// Variables for window width and height
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 800;
// Main GLFW window
GLFWwindow* gWindow = nullptr;
// Shader program
GLuint gProgramId1;
GLuint gProgramId2;
// Texture Ids
GLuint gTextureId;
Meshes meshes;
// Front Camera
glm::vec3 cameraPos;
glm::vec3 cameraFront;
glm::vec3 cameraUp;
GLuint gCurrentCameraIndex = 1;
GLfloat gCameraZoom = 45.0f;
GLuint gCameraOrtho = 0;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
}
// User-defined Function prototypes //
bool Initialize(int, char* [], GLFWwindow** window);
void ProcessInput(GLFWwindow* window);
void Render();
bool CreateShaderProgram(const char* vtxShaderSource, const char* fragShaderSource, GLuint& programId);
void DestroyShaderProgram(GLuint programId);
bool CreateTexture(const char* filename, GLuint& textureId);
void DestroyTexture(GLuint textureId);
// Shader program Macro //
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Vertex Shader Source Code //
const GLchar* vertexShaderSource1 = GLSL(440,
// Load vertex data from VBO location 0
layout(location = 0) in vec3 vertex;
// Load vertex normal data from VBO location 1
layout(location = 1) in vec3 vertexNormal;
// Load texture coordinate data from VBO location 2
layout(location = 2) in vec2 textureCoordinate;
// Output vertex normal values to fragment shader
out vec3 vs_vertexNormal;
// Output texture coordinates to fragment shader
out vec2 vs_textureCoordinate;
//Global variables for the model-view-projection
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
// Transforms vertices to clip coordinates
gl_Position = projection * view * model * vec4(vertex, 1.0f);
vs_textureCoordinate = textureCoordinate;
vs_vertexNormal = vertexNormal;
}
);
// Fragment Shader Source Code //
const GLchar* fragmentShaderSource1 = GLSL(440,
// Vertex normal from vertex shader
in vec3 vs_vertexNormal;
// Texture coordinates from vertex shader
in vec2 vs_textureCoordinate;
// Fragment output color
out vec4 fragmentColor;
// Texture image data passed in from outside shader
uniform sampler2D uTextureBase;
uniform vec2 uvScale;
uniform bool ubHasTexture;
uniform vec4 uCustomColor;
void main()
{
// if a texture is passed in, go ahead and use it
if (ubHasTexture)
fragmentColor = texture(uTextureBase, vs_textureCoordinate);// *uvScale);
else // otherwise just use preset color
fragmentColor = uCustomColor;
}
);
// main function. Entry point to the OpenGL program //
int main(int argc, char* argv[])
{
if (!Initialize(argc, argv, &gWindow))
return EXIT_FAILURE;
// Create the mesh, send data to VBO
meshes.CreateMeshes();
// Create the shader program
if (!CreateShaderProgram(vertexShaderSource1, fragmentShaderSource1, gProgramId1))
return EXIT_FAILURE;
// Load texture data from file
//const char * texFilename1 = "../../resources/textures/blue_granite.jpg";
//if (!CreateTexture(texFilename1, gTextureIdBlue))
//{
// cout << "Failed to load texture " << texFilename1 << endl;
// return EXIT_FAILURE;
//}
// Activate the program that will reference the texture
glUseProgram(gProgramId1);
// We set the texture as texture unit 0
glUniform1i(glGetUniformLocation(gProgramId1, "uTextureBase"), 0);
// 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 the camera parameters
cameraPos = glm::vec3(0.0f, .75f, 4.0f);
cameraFront = glm::vec3(0.0f, 0.0f, -2.0f);
cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
// Render loop
while (!glfwWindowShouldClose(gWindow))
{
// Process keyboard input before rendering
ProcessInput(gWindow);
// Render this frame
Render();
glfwPollEvents();
}
// Release mesh data
meshes.DestroyMeshes();
// Release shader program
DestroyShaderProgram(gProgramId1);
// Release the textures
//DestroyTexture(gTextureId);
exit(EXIT_SUCCESS); // Terminates the program successfully
}
// Initialize GLFW, GLEW, and create a window //
bool Initialize(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);
// GLFW: create OpenGL output window, return error if fails
*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;
}
// Set the context for the current window
glfwMakeContextCurrent(*window);
// GLEW: initialize
// ----------------
// Note: if using GLEW version 1.13 or earlier
glewExperimental = GL_TRUE;
GLenum GlewInitResult = glewInit();
// If init fails, output error string, return error
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 keyboard input
void ProcessInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float cameraSpeed = static_cast<float>(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;
}
// Render the next frame to the OpenGL viewport //
void Render()
{
GLuint uHasTextureLoc;
bool ubHasTextureVal;
GLuint uCustomColorLoc;
glm::mat4 scale;
glm::mat4 rotation;
glm::mat4 translation;
glm::mat4 view;
glm::mat4 projection;
glm::mat4 model;
GLint modelLoc;
GLint viewLoc;
GLint projLoc;
// Enable z-depth
glEnable(GL_DEPTH_TEST);
// Clear the background
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set the current view and projection values
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
projection = glm::perspective(glm::radians(gCameraZoom), (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, 0.1f, 1000.0f);
//projection = glm::ortho(-2.0f, 2.0f, -2.0f, 2.0f, 0.1f, 1000.0f);
// Set the program to be used
glUseProgram(gProgramId1);
// Get the has texture location
uHasTextureLoc = glGetUniformLocation(gProgramId1, "ubHasTexture");
// Get the custom color location
uCustomColorLoc = glGetUniformLocation(gProgramId1, "uCustomColor");
ubHasTextureVal = false;
glUniform1i(uHasTextureLoc, ubHasTextureVal);
// Retrieves and passes transform matrices to the Shader program
modelLoc = glGetUniformLocation(gProgramId1, "model");
viewLoc = glGetUniformLocation(gProgramId1, "view");
projLoc = glGetUniformLocation(gProgramId1, "projection");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Bottom of bowl
glBindVertexArray(meshes.gCylinderMesh.vao);
// Set the mesh transfomation values
scale = glm::scale(glm::vec3(0.3f, 0.08f, 0.3f));
rotation = glm::rotate(0.0f, glm::vec3(1.0f, 1.0f, 1.0f));
translation = glm::translate(glm::vec3(0.0f, 0.0f, 0.0f));
model = translation * rotation * scale;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Set the custom color before drawing
glUniform4fv(uCustomColorLoc, 1, glm::value_ptr(glm::vec4(0.87f, 0.87f, 0.69f, 1.0f)));
// glDrawArrays(GL_TRIANGLE_FAN, 0, 36); //bottom
// glDrawArrays(GL_TRIANGLE_FAN, 36, 72); //top
glDrawArrays(GL_TRIANGLE_STRIP, 72, 146); //sides
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// Bowl
glBindVertexArray(meshes.gSphereMesh.vao);
// Set the mesh transfomation values
scale = glm::scale(glm::vec3(1.0f, 0.4f, 1.0f));
rotation = glm::rotate(3.142f, glm::vec3(1.0f, 0.0f, 0.0f));
translation = glm::translate(glm::vec3(0.0f, 0.42f, 0.0f));
model = translation * rotation * scale;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Set the custom color before drawing
glUniform4fv(uCustomColorLoc, 1, glm::value_ptr(glm::vec4(0.87f, 0.87f, 0.69f, 1.0f)));
glDrawElements(GL_TRIANGLES, 720, GL_UNSIGNED_INT, (void*)0);
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// Table
glBindVertexArray(meshes.gPlaneMesh.vao);
// Set the mesh transfomation values
scale = glm::scale(glm::vec3(2.0f, 2.0f, 2.0f));
rotation = glm::rotate(0.0f, glm::vec3(1.0f, 1.0f, 1.0f));
translation = glm::translate(glm::vec3(0.0f, -0.02f, 0.0f));
model = translation * rotation * scale;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Set the custom color before drawing
glUniform4fv(uCustomColorLoc, 1, glm::value_ptr(glm::vec4(0.43f, 0.17f, 0.01f, 1.0f)));
glDrawElements(GL_TRIANGLES, meshes.gPlaneMesh.nIndices, GL_UNSIGNED_INT, (void*)0);
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// Ice Cream scoop#1
glBindVertexArray(meshes.gSphereMesh.vao);
// Set the mesh transfomation values
scale = glm::scale(glm::vec3(-0.5f, -0.25f, -0.5f));
rotation = glm::rotate(0.0f, glm::vec3(1.0f, 1.0f, 1.0f));
translation = glm::translate(glm::vec3(-0.25f, 0.5f, 0.0f));
model = translation * rotation * scale;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Set the custom color before drawing
glUniform4fv(uCustomColorLoc, 1, glm::value_ptr(glm::vec4(0.96f, 0.95f, 0.92f, 1.0f)));
glDrawElements(GL_TRIANGLES, meshes.gSphereMesh.nIndices, GL_UNSIGNED_INT, (void*)0);
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// Ice Cream scoop#2
glBindVertexArray(meshes.gSphereMesh.vao);
// Set the mesh transfomation values
scale = glm::scale(glm::vec3(-0.5f, -0.25f, -0.5f));
rotation = glm::rotate(0.0f, glm::vec3(1.0f, 1.0f, 1.0f));
translation = glm::translate(glm::vec3(0.25f, 0.5f, 0.0f));
model = translation * rotation * scale;
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// Set the custom color before drawing
glUniform4fv(uCustomColorLoc, 1, glm::value_ptr(glm::vec4(0.92f, 0.91f, 0.86f, 1.0f)));
glDrawElements(GL_TRIANGLES, meshes.gSphereMesh.nIndices, GL_UNSIGNED_INT, (void*)0);
// Deactivate the Vertex Array Object
glBindVertexArray(0);
// Flips the the back buffer with the front buffer every frame (refresh)
glfwSwapBuffers(gWindow);
}
//****************************************************
// const char* vtxShaderSource: vertex shader source code
// const char* fragShaderSource: fragment shader source code
// GLuint &programId: unique ID of program associated with shaders
//****************************************************
bool CreateShaderProgram(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);
// Retrieve 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);
// Check for vertex 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;
}
// Compile the fragment shader, and print compilation errors (if any)
glCompileShader(fragmentShaderId);
// Check for fragment 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);
// Links the shader program
glLinkProgram(programId);
// 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;
}
// Uses the shader program
glUseProgram(programId);
return true;
}
// Destroy the linked shader program //
void DestroyShaderProgram(GLuint programId)
{
glDeleteProgram(programId);
}
// Images are loaded with Y axis going down, but OpenGL's Y axis goes up, so let's flip it //
void flipImageVertically(unsigned char* image, int width, int height, int channels)
{
for (int j = 0; j < height / 2; ++j)
{
int index1 = j * width * channels;
int index2 = (height - 1 - j) * width * channels;
for (int i = width * channels; i > 0; --i)
{
unsigned char tmp = image[index1];
image[index1] = image[index2];
image[index2] = tmp;
++index1;
++index2;
}
}
}
// Generate and load the texture //
bool CreateTexture(const char* filename, GLuint& textureId)
{
/*int width, height, channels;
unsigned char *image = stbi_load(filename, &width, &height, &channels, 0);
if (image)
{
flipImageVertically(image, width, height, channels);
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (channels == 3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
else if (channels == 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
else
{
cout << "Not implemented to handle image with " << channels << " channels" << endl;
return false;
}
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(image);
glBindTexture(GL_TEXTURE_2D, 0); // Unbind the texture
return true;
}
// Error loading the image*/
return false;
}
// Release the texture attached to textureId //
void DestroyTexture(GLuint textureId)
{
glGenTextures(1, &textureId);
}
This is what I have so far, I just need to be able to rotate around.
Ice Cream
I am a beginner with OpenGL and C++ and need assistance with implementing a camera into my code, which is below, to move around a 3D cube orbitally. I am unsure as to what else to insert into the code to get the camera to work. The code works but there is no camera movement at this time. I specifically need WASD keys to control the left, right, forward, and backward motions, the QE keys to control the upward and downward movement, and the cursor to control the orientation of the camera. Can someone assist me with what I need to insert into the code to make the camera work?
#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 <learnOpengl/camera.h> // Camera class
using namespace std; // Standard namespace
/*Shader program Macro*/
#ifndef GLSL
#define GLSL(Version, Source) "#version " #Version " core \n" #Source
#endif
// Unnamed namespace
namespace
{
const char* const WINDOW_TITLE = "3D Cube w/ Camera Movement"; // Macro for window title
// Variables for window width and height
const int WINDOW_WIDTH = 800;
const 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 vec4 color; // Color data from Vertex Attrib Pointer 1
out vec4 vertexColor; // variable to transfer color data to the fragment shader
//Global variables for the transform matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f); // transforms vertices to clip coordinates
vertexColor = color; // references incoming color data
}
);
/* Fragment Shader Source Code*/
const GLchar* fragmentShaderSource = GLSL(440,
in vec4 vertexColor; // Variable to hold incoming color data from vertex shader
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(vertexColor);
}
);
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);
// render loop
// -----------
while (!glfwWindowShouldClose(gWindow))
{
// input
// -----
UProcessInput(gWindow);
// Render this frame
URender();
glfwPollEvents();
}
// 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);
}
// 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);
// 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(2.0f, 2.0f, 2.0f));
// 2. Rotates shape by 15 degrees in the x axis
glm::mat4 rotation = glm::rotate(45.0f, glm::vec3(2.0f, 2.0f, 1.0f));
// 3. Place object at the origin
glm::mat4 translation = glm::translate(glm::vec3(-1.0f, 1.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::translate(glm::vec3(0.0f, 0.0f, -5.0f));
// Creates a orthographic projection
glm::mat4 projection = glm::ortho(-5.0f, 5.0f, -5.0f, 5.0f, 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));
// 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[] = {
// Vertex Positions // Colors (r,g,b,a)
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top Right Vertex 0
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // Bottom Right Vertex 1
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Bottom Left Vertex 2
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top Left Vertex 3
0.5f, -0.5f, -1.0f, 0.5f, 0.5f, 1.0f, 1.0f, // 4 br right
0.5f, 0.5f, -1.0f, 1.0f, 1.0f, 0.5f, 1.0f, // 5 tl right
-0.5f, 0.5f, -1.0f, 0.2f, 0.2f, 0.5f, 1.0f, // 6 tl top
-0.5f, -0.5f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f // 7 bl back
};
// Index data to share position data
GLushort indices[] = {
0, 1, 3, // Triangle 1
1, 2, 3, // Triangle 2
0, 1, 4, // Triangle 3
0, 4, 5, // Triangle 4
0, 5, 6, // Triangle 5
0, 3, 6, // Triangle 6
4, 5, 6, // Triangle 7
4, 6, 7, // Triangle 8
2, 3, 6, // Triangle 9
2, 6, 7, // Triangle 10
1, 4, 7, // Triangle 11
1, 2, 7 // Triangle 12
};
const GLuint floatsPerVertex = 3;
const GLuint floatsPerColor = 4;
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);
}
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);
}
I'm new to openGL so I'm testing some things out based on the tutorials from learnopengl.com
I was learning about textures so I decided to try some things out. This is my code.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "Shader.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 400;
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);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
// 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);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader zprogram
// ------------------------------------
Shader ourShader("DVDVertexShader.txt", "DVDFragmentShader.txt");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// load and create a texture
// -------------------------
unsigned int texture;
// texture
// ---------
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true);
unsigned char *data = stbi_load("dvd_logo2.png", &width, &height, &nrChannels, 0);
std::cout << "width and height is " << width << ", " << height << std::endl;
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
glBindTexture(GL_TEXTURE_2D, 0);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// render container
ourShader.use();
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// 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);
glDeleteBuffers(1, &EBO);
// 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);
}
// 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)
{
glViewport(0, 0, width, height);
}
Note that stb_image.h is a header file that allows us to load images from any format. It's an open source code that's available in github.
https://github.com/nothings/stb/blob/master/stb_image.h
This is the Shader.h code just in case.
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include "Shader.h"
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
std::cout << "vertexPath is " << vertexPath<<std::endl;
std::cout << "fragmentPath is " << fragmentPath<<std::endl;
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
int success;
char infoLog[512];
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
My vertex and fragment shader are very basic, so I don't know if these are the reasons for my weird output, but here it is.
DVDVertexShader.txt :
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
DVDFragmentShader.txt :
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
Anyway, I really think the shader isn't really the problem. The image "dvd_logo2.png" is this one.
dvd_logo2.png
However, the output I get from my code is this.
weird_dvd_output
(My original intention was to make the output have the same look as the input so I could do sth to it afterwards like make it move around per frame.)
I don't understand why this is happening... The funny thing is, other images with all kinds of colors work perfectly fine. Maybe the problem is because the photo's black and white..?
Any help and advice would be greatly appreciated. Thanks in advance!
Since it's *.png you need change at last format to GL_RGBA and by format I don't mean internalFormat, so something like that should work glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);. For what I mean by format and internalFormat check reference: glTexImage2D
If you still can't understand why it is then let me make quote from that reference:
format
Specifies the format of the pixel data.
So your pixel data have GL_RGBA value: red, green, blue, and alpha since it's *.png, problem is that now you pass only 3 of them.
EDIT:
I don't note it first time but you need also change in stbi_load number of components to 4:
stbi_load("dvd_logo2.png", &width, &height, &nrChannels, 4);
I want to retrieve the color of the current texel in my fragment shader so I can adapt the output color depending on it.
For example: if the current texel's color is red then set the color to green.
Above, you can see there are two triangles. One triangle is partially drawn on the other. You can see the superimposed portion is in green as, like I said, my fragment shader detected the color "red" so it set the output color to "green".
As you can see, my program works well. However, I'm a very beginner in OpenGL so I don't quite understand everything I wrote and I'm sure I did a few mistakes. I'd like your feedback.
What I did to achieve this:
Draw first triangle in a FBO
Copy the framebuffer to the draw framebuffer (= on screen)
Draw the second triangle directly on screen
At first I tried to draw the two triangles directly on screen without a FBO but then I couldn't get the texel color with texture2D() in my fragment shader.
Here is my code (I use OpenGL 3.3, GLFW 3 and GLEW):
#include <iostream>
#include <string>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const std::string WINDOW_TITLE = "Drawing color depending on current background pixel color";
const GLuint WIDTH = 800, HEIGHT = 600;
const std::string vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position.x, position.y, position.z, 1.0f);
}
)";
const std::string fragmentShaderSource = R"(
#version 330 core
layout(location = 0) out vec4 color;
uniform sampler2D renderedTexture;
void main()
{
vec2 size = textureSize(renderedTexture, 0);
vec2 position = gl_FragCoord.xy / size.xy;
vec4 inputColor = texture2D(renderedTexture, position);
if (inputColor[0] == 0.0f)
{
color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
else if (inputColor[0] == 1.0f)
{
color = vec4(0.1f, 1.0f, 0.0f, 1.0f);
}
else
{
color = vec4(0.8f, 0.0f, 1.0f, 1.0f);
}
}
)";
GLFWwindow *setupWindow()
{
GLFWwindow *window = nullptr;
int width = 0, height = 0;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, WINDOW_TITLE.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
return window;
}
GLuint compileShader(GLenum type, const std::string &source, std::string &errorLog)
{
GLuint shader = 0;
GLint compileSuccess = 0;
const GLchar *sourceC = source.c_str();
const std::size_t ERROR_LOG_BUFFER_SIZE = 512;
GLchar errorLogBuffer[ERROR_LOG_BUFFER_SIZE]{};
errorLog = "";
shader = glCreateShader(type);
glShaderSource(shader, 1, &sourceC, nullptr);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
if (!compileSuccess)
{
glGetShaderInfoLog(shader, ERROR_LOG_BUFFER_SIZE, nullptr, errorLogBuffer);
glDeleteShader(shader);
errorLog = errorLogBuffer;
return 0;
}
return shader;
}
GLuint compileShaderProgram(const std::string &vertexShaderSource, const std::string &fragmentShaderSource, std::string &errorLog)
{
GLuint vertexShader{};
GLuint fragmentShader{};
GLuint shaderProgram{};
GLint linkSuccess{};
const std::size_t ERROR_LOG_BUFFER_SIZE = 512;
GLchar errorLogBuffer[ERROR_LOG_BUFFER_SIZE]{};
errorLog = "";
vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource, errorLog);
if (vertexShader == 0)
return 0;
fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource, errorLog);
if (fragmentShader == 0)
return 0;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkSuccess);
if (!linkSuccess)
{
glGetProgramInfoLog(shaderProgram, ERROR_LOG_BUFFER_SIZE, nullptr, errorLogBuffer);
glDeleteProgram(shaderProgram);
errorLog = errorLogBuffer;
return 0;
}
return shaderProgram;
}
int main(int argc, char *argv[])
{
GLFWwindow *window = setupWindow();
std::string errorLog = "";
GLuint shaderProgram = compileShaderProgram(vertexShaderSource, fragmentShaderSource, errorLog);
GLuint fbo = 0;
GLuint fboTexture = 0;
GLuint vao = 0;
GLuint vbo = 0;
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
GLuint indicesBuffer = 0;
GLushort indices[] = { 0,1,2, 3,4,5 };
GLfloat vertices[] = {
// Bottom triangle (first triangle drawn)
-0.5f, +0.0f,
+0.5f, +0.0f,
+0.0f, +0.5f,
// Top triangle (second triangle drawn, partially ON the first one, and slighly above it)
-0.5f, +0.2f,
+0.5f, +0.2f,
+0.0f, +0.8f,
};
/*
** Setup FBO
*/
glEnable(GL_TEXTURE_2D);
glGenFramebuffers(1, &fbo);
glGenTextures(1, &fboTexture);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindTexture(GL_TEXTURE_2D, fboTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTexture, 0);
glDrawBuffers(sizeof(drawBuffers) / sizeof(GLenum), drawBuffers);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return EXIT_FAILURE;
/*
** Setup VAO and VBO
*/
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &indicesBuffer);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
/*
** Main loop
*/
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
/*
** Draw first triangle in FBO (not on screen)
*/
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
glUseProgram(0);
/*
** Copy the read framebuffer to the draw framebuffer (= on screen)
*/
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0, 0, WIDTH, HEIGHT);
glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
/*
** Draw the second triangle directly on screen
*/
glUseProgram(shaderProgram);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, reinterpret_cast<const void*>(3 * sizeof(GLushort)));
glUseProgram(0);
/*
** Swap buffers
*/
glfwSwapBuffers(window);
}
/**
** Free resources
*/
glDeleteBuffers(1, &indicesBuffer);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
Is it a good way of doing it?
Is it called "multisampling"? From what I understand, you use FBO(s) with functions like glTexImage2DMultisample() to do multisampling. But even if I use an FBO, I use glTexImage2D() instead of glTexImage2DMultisample(). So?
Also, I don't quite understand how the uniform sampler2D renderedTexture; in my fragment shader works. I thought I needed to do something like:
GLuint texID = glGetUniformLocation(shaderProgram, "renderedTexture");
glUniform1i(texID, 0);
to set the uniform variable, but I don't do it and it works. Why?
Finally, you might wonder why I want to do all this. Well, as I just started learning OpenGL, I do that as an "exercise" to solve my real problem: write text on an image in a color with enough contrast so it can be readable (e.g.: if you want to write some text on a white wall, write it in black, not in white, or you won't see it).
I'm very new to OpenGL and at the moment I'm trying to understand VAO and VBO.
The VAO is simply a collection of VBO.
Each VBO is an attribute of an object, coordinates of vertices of the object, color at each object's vertex, etc.
In the code below, the Vertex struct define 2 properties of a vertex, which is position and color of the vertex. Therefore, 2 VBOs are needed to store these information.
My problem is: I'm able to draw the triangle on the screen, but the color at each vertex doesn't seem to be drawn. Why does this happen?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
// create vertex buffer
GLuint vertexbuffer;
struct Vertex{
GLdouble position[3];
GLfloat color[3];
};
static const GLfloat vertex_data[] = {
-0.7f, -0.7f, 0.0f,
0.7f, -0.7f, 0.0f,
0.0f, 1.0f, 0.0f
};
void SetupGeometry(){
const struct Vertex triangle[3] = {
{{-0.7, -0.7, 0.0}, {1.0f, 0.0f, 0.0f}},
{{0.7, -0.7, 0.0}, {0.0f, 1.0f, 0.0f}},
{{0.0, 1.0, 0.0}, {0.0f, 0.0f, 1.0f}}
};
//GLuint VertexArrayID;
//glGenVertexArrays(1, &VertexArrayID);
//glBindVertexArray(VertexArrayID);
//generate 1 buffer, put the resulting identifier in vertex buffer
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
// give our vertices to OpenGL
glBufferData(GL_ARRAY_BUFFER, 3*sizeof(struct Vertex), triangle, GL_STATIC_DRAW);
// GLuint index, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *offset
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, sizeof(struct Vertex), (void*) offsetof(struct Vertex, position));
// any newly created VAO disables array access for all attributes
// array access is enabled by binding the VAO in SetupGeometry and calling:
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*) offsetof(struct Vertex, color));
glEnableVertexAttribArray(1);
}
void SetupShaders(void){
}
void Render(int i){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_LOOP, 0, 3);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
if((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action != GLFW_PRESS){
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
int main( void ) {
/* Create a windowed mode window and its OpenGL context */
GLFWwindow* window;
if( !glfwInit() ) {
printf("Failed to start GLFW\n");
exit( EXIT_FAILURE );
}
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window) {
glfwTerminate();
printf("GLFW Failed to start\n");
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window); // IMPORTANT: Must be done so glew recognises OpenGL
glfwWindowHint(GLFW_SAMPLES, 4);
int err = glewInit();
if (glewInit() != GLEW_OK) {
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err));
}
fprintf(stderr, "Glew done\n");
fprintf(stderr, "GL VERSION INFO %s\n", glGetString(GL_VERSION));
glfwSetKeyCallback(window, key_callback);
SetupGeometry();
while(!glfwWindowShouldClose(window)){
Render(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
You don't seem to have included any shaders. You'll need to use a fragment shader to set the colour values for the triangle. This link should help you.