Related
I'm having trouble implementing both point light and sunlight in openGl upon a 3d pyramid. Do I need a separate diffuse component for the sunlight portion?
I'm also not sure how to go about implementing a specular component as well.
From my understanding it seems that both light sources will be mixed and sent to the shader, however I'm not sure how to implement them simultaneously.
Here is what I have for now.
// GLEW header
#include <GL/glew.h> // This must appear before freeglut.h
// Freeglut header
#include <GL/freeglut.h>
// GLM header files
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
//#include <glm/gtx/transform2.hpp>
#include <glm/gtc/matrix_access.hpp>
//#include <glm/gtx/projection.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/type_ptr.hpp>
// Simple OpenGL Image Library header file
#include <SOIL.h>
// C++ header files
#include <iostream>
using namespace std;
using namespace glm;
#define BUFFER_OFFSET(offset) ((GLvoid *) offset)
GLfloat pyramidVertices[][4] = {
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 1
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 1.0f, -1.0f, -1.0f, 1.0f },
{ 1.0f, -1.0f, -1.0f, 1.0f }, // face 2
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 3
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ -1.0f, -1.0f, 1.0f, 1.0f },
{ -1.0f, -1.0f, 1.0f, 1.0f }, // face 4
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f, 1.0f }, // face 5
{ 1.0f, -1.0f, -1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f }, // face 6
{ -1.0f, -1.0f, 1.0f, 1.0f },
{ -1.0f, -1.0f, -1.0f, 1.0f }
};
int numVertices = 18;
GLfloat pyramidNormals[][4] = {
{ 0.0f, -1.0f, 0.0f, 1.0f }, // normal 1
{ 0.0f, -1.0f, 0.0f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f },
{ 0.8944f, 0.4472f, 0.0f, 1.0f }, // normal 2
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{ 0.8944f, 0.4472f, 0.0f, 1.0f },
{ -0.0f, 0.4472f, 0.8944f, 1.0f }, // normal 3
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{ -0.0f, 0.4472f, 0.8944f, 1.0f },
{ -0.8944f, 0.4472f, 0.0f, 1.0f }, // normal 4
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{ -0.8944f, 0.4472f, 0.0f, 1.0f },
{ 0.0f, 0.4472f, -0.8944f, 1.0f }, // normal 5
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, 0.4472f, -0.8944f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }, // normal 6
{ 0.0f, -1.0f, 0.0f, 1.0f },
{ 0.0f, -1.0f, 0.0f, 1.0f }
};
GLfloat pyramidTextureCoords[][2] = {
{ 0.0319, 0.4192 }, // textureCoord 1
{ 0.3546, 0.0966 },
{ 0.3546, 0.4192 },
{ 0.4223, 0.5177 }, // textureCoord 2
{ 0.2541, 0.8753 },
{ 0.0996, 0.5116 },
{ 0.8047, 0.5250 }, // textureCoord 3
{ 0.6434, 0.8857 },
{ 0.4820, 0.5250 },
{ 0.6637, 0.0981 }, // textureCoord 4
{ 0.5130, 0.4184 },
{ 0.3748, 0.0926 },
{ 0.8416, 0.4227 }, // textureCoord 5
{ 0.6922, 0.0988 },
{ 0.9834, 0.0954 },
{ 0.0319, 0.4192 }, // textureCoord 6
{ 0.0319, 0.0966 },
{ 0.3546, 0.0966 }
};
// Update the texture image file path for your computer.
const char *imageFileName = "marble.jpg"; //This will apply the marble texture to the picture
// VBO buffer IDs
GLuint vertexArrayBufferID = 0;
GLuint normalArrayBufferID = 0;
GLuint texCoordArrayBufferID = 0;
GLuint program; // shader program ID
// Shader variable IDs
GLint vPos; // vertex attribute: position
GLint normalID; // vertex attribute: normal
GLint textureCoordID; // vertex attribute: texture coordinates
GLint mvpMatrixID; // uniform variable: model, view, projection matrix
GLint modelMatrixID; // uniform variable: model, view matrix
GLint normalMatrixID; // uniform variable: normal matrix for transforming normals
GLint lightSourcePositionID; // uniform variable: for lighting calculation
GLint diffuseLightProductID; // uniform variable: for lighting calculation
GLint ambientID;
GLint attenuationAID;
GLint attenuationBID;
GLint attenuationCID;
GLint textureSamplerID; // texture sampler ID
// Texture object ID
GLuint textureID;
// Texture unit ID
// These two values must be consistent.
GLenum textureUnitID = GL_TEXTURE1;
// Sometimes, setting this to 0 will connect the sampler to any active texture unit.
// But it's better to avoid using this "trick".
GLuint textureSamplerValue = 1;
// Transformation matrices
mat4 projMatrix;
mat4 mvpMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
mat3 normalMatrix; // Normal matrix for transforming normals
// Light parameters
vec4 lightSourcePosition = vec4(0.0f, 4.0f, 0.0f, 1.0f);
vec4 diffuseMaterial = vec4(0.5f, 0.5f, 0.5f, 1.0f);
vec4 diffuseLightIntensity = vec4(1.0f, 1.0f, 1.0f, 1.0f);
vec4 ambient = vec4(0.0f, 0.0f, 1.0f, 1.0f);
float attenuationA = 1.0f;
float attenuationB = 0.2f;
float attenuationC = 0.0f;
vec4 diffuseLightProduct;
// Camera parameters
vec3 eyePosition = vec3(0.0f, 0.0f, 4.0f);
vec3 lookAtCenter = vec3(0.0f, 0.0f, 0.0f);
vec3 upVector = vec3(0.0f, 1.0f, 0.0f);
float fieldOfView = 30.0f;
float nearPlane = 0.1f;
float farPlane = 1000.0f;
// Mouse controlled rotation angles
float rotateX = 0;
float rotateY = 0;
//---------------------------------------------------------------
// Initialize vertex arrays and VBOs
void prepareVBOs() {
// Define a 3D pyramid.
// Get an unused buffer object name. Required after OpenGL 3.1.
glGenBuffers(1, &vertexArrayBufferID);
// If it's the first time the buffer object name is used, create that buffer.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Allocate memory for the active buffer object.
// 1. Allocate memory on the graphics card for the amount specified by the 2nd parameter.
// 2. Copy the data referenced by the third parameter (a pointer) from the main memory to the
// memory on the graphics card.
// 3. If you want to dynamically load the data, then set the third parameter to be NULL.
glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidVertices), pyramidVertices, GL_STATIC_DRAW);
glGenBuffers(1, &normalArrayBufferID);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidNormals), pyramidNormals, GL_STATIC_DRAW);
// Create a buffer object to store the texture coordinates.
// Later we will passt his over to the shader.
glGenBuffers(1, &texCoordArrayBufferID);
glBindBuffer(GL_ARRAY_BUFFER, texCoordArrayBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidTextureCoords), pyramidTextureCoords, GL_STATIC_DRAW);
}
//---------------------------------------------------------------
// Print out the output of the shader compiler
void printLog(GLuint obj)
{
int infologLength = 0;
char infoLog[1024];
if (glIsShader(obj)) {
glGetShaderInfoLog(obj, 1024, &infologLength, infoLog);
}
else {
glGetProgramInfoLog(obj, 1024, &infologLength, infoLog);
}
if (infologLength > 0) {
cout << infoLog;
}
}
//-------------------------------------------------------------------
void prepareShaders() {
// Vertex shader source code
// The vertex position and normal vector are transformed and then passed on to the fragment shader.
const char* vSource = {
"#version 330\n"
"in vec4 vPos;"
"in vec4 normal;"
"in vec2 vTextureCoord;"
"uniform mat4x4 mvpMatrix;"
"uniform mat4x4 modelMatrix;"
"uniform mat3x3 normalMatrix;"
"out vec4 transformedPosition;"
"out vec3 transformedNormal;"
"out vec2 textureCoord;"
"void main() {"
" gl_Position = mvpMatrix * vPos;"
// Transform the vertex position to the world space.
" transformedPosition = modelMatrix * vPos;"
// Transform the normal vector to the world space.
" transformedNormal = normalize(normalMatrix * normal.xyz);"
" textureCoord = vec2(vTextureCoord.x, vTextureCoord.y);"
"}"
};
// Fragment shader source code
// A point light source is implemented.
// For simplicity, only the ambient and diffuse components are implemented.
// The lighting is calculated in world space, not in camera space.
// Note that the transformedPosition and transformedNormal in the fragment shader are for each fragment (pixel).
// They are interpolated from the vertex positions and vertex normals in the (invisible) rasterization stage.
const char* fSource = {
"#version 330\n"
"in vec4 transformedPosition;"
"in vec3 transformedNormal;"
"in vec2 textureCoord;"
"uniform vec4 lightSourcePosition;"
"uniform vec4 diffuseLightProduct;"
"uniform vec4 ambient;"
"uniform float attenuationA;"
"uniform float attenuationB;"
"uniform float attenuationC;"
"uniform sampler2D tex;"
"out vec4 fragColor;"
"void main() {"
// Light direction
" vec3 lightVector = normalize(transformedPosition.xyz - lightSourcePosition.xyz);"
// Distance between the light source and vertex
" float dist = distance(lightSourcePosition.xyz, transformedPosition.xyz);"
// Attenuation factor
" float attenuation = 1.0f / (attenuationA + (attenuationB * dist) + (attenuationC * dist * dist));"
// Calculate the diffuse component of the lighting equation.
" vec4 diffuse = attenuation * (max(dot(transformedNormal, lightVector), 0.0) * diffuseLightProduct);"
" vec4 textureColor = texture(tex, textureCoord);"
// Combine the ambient component and diffuse component.
" fragColor = mix((ambient + diffuse), textureColor, 0.6f);"
"}"
};
// Declare shader IDs
GLuint vShader, fShader;
// Create empty shader objects
vShader = glCreateShader(GL_VERTEX_SHADER);
fShader = glCreateShader(GL_FRAGMENT_SHADER);
// Attach shader source code the shader objects
glShaderSource(vShader, 1, &vSource, NULL);
glShaderSource(fShader, 1, &fSource, NULL);
// Compile shader objects
glCompileShader(vShader);
printLog(vShader);
glCompileShader(fShader);
printLog(fShader);
// Create an empty shader program object
program = glCreateProgram();
// Attach vertex and fragment shaders to the shader program
glAttachShader(program, vShader);
glAttachShader(program, fShader);
// Link the shader program
glLinkProgram(program);
printLog(program);
}
//---------------------------------------------------------------
// Retrieve the IDs of the shader variables. Later we will
// use these IDs to pass data to the shaders.
void getShaderVariableLocations(GLuint shaderProgram) {
// Retrieve the ID of a vertex attribute, i.e. position
vPos = glGetAttribLocation(shaderProgram, "vPos");
normalID = glGetAttribLocation(shaderProgram, "normal");
// get the ID of the texture coordinate variable in the shader.
textureCoordID = glGetAttribLocation(shaderProgram, "vTextureCoord");
mvpMatrixID = glGetUniformLocation(shaderProgram, "mvpMatrix");
modelMatrixID = glGetUniformLocation(shaderProgram, "modelMatrix");
normalMatrixID = glGetUniformLocation(shaderProgram, "normalMatrix");
lightSourcePositionID = glGetUniformLocation(shaderProgram, "lightSourcePosition");
diffuseLightProductID = glGetUniformLocation(shaderProgram, "diffuseLightProduct");
ambientID = glGetUniformLocation(shaderProgram, "ambient");
attenuationAID = glGetUniformLocation(shaderProgram, "attenuationA");
attenuationBID = glGetUniformLocation(shaderProgram, "attenuationB");
attenuationCID = glGetUniformLocation(shaderProgram, "attenuationC");
// get the ID of the texture sampler in the shader.
textureSamplerID = glGetUniformLocation(shaderProgram, "tex");
}
//---------------------------------------------------------------
void setShaderVariables() {
// value_ptr is a glm function
glUniformMatrix4fv(mvpMatrixID, 1, GL_FALSE, value_ptr(mvpMatrix));
glUniformMatrix4fv(modelMatrixID, 1, GL_FALSE, value_ptr(modelMatrix));
glUniformMatrix3fv(normalMatrixID, 1, GL_FALSE, value_ptr(normalMatrix));
glUniform4fv(lightSourcePositionID, 1, value_ptr(lightSourcePosition));
glUniform4fv(diffuseLightProductID, 1, value_ptr(diffuseLightProduct));
glUniform4fv(ambientID, 1, value_ptr(ambient));
glUniform1f(attenuationAID, attenuationA);
glUniform1f(attenuationBID, attenuationB);
glUniform1f(attenuationCID, attenuationC);
// Associate texture sampler ID in the shader with the active texture unit number.
// glActiveTexture() function and glUniform1i(textUnit, ...) function calls must be consistent.
// If you change texture unit number in one function all, you must change the texture unit
// in the other function call.
glUniform1i(textureSamplerID, textureSamplerValue);
}
//---------------------------------------------------------------
// Set lighting related parameters
void setLightingParam() {
diffuseLightProduct = diffuseMaterial * diffuseLightIntensity;
}
void prepareTextureImage() {
// Use SOIL to load a picture.
textureID = SOIL_load_OGL_texture(imageFileName,
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); // Adding SOIL_FLAG_MIPMAPS is also fine.
// Specify the current active texture unit number.
//
// There are certain number of texture units on the graphics card (for example 32).
// Check OpenGL extension viewer to find out how many texture units are on your card.
//
// The shader accesses the texture through texture units, not the texture IDs.
// In other words, the shader does not know the texture IDs; it only sees
// (for example) 32 texture units.
// You can have more than 32 texture images stored in the graphics card's memory, but
// at any given time, the shader only sees 32 texture units. In the OpenGL program,
// you can associate a texture unit with any texture ID, and change it at any time.
// This is how you can transfer potentially large numbers of texture images to the
// shader through a limited channel.
//
glActiveTexture(textureUnitID);
// Bind the texture ID with the active texture unit number.
// In this case, texture ID "textureID" is associated with texture unit #2.
// When the shader reads texture unit #2, it will read the texture identified by ID textureID.
glBindTexture(GL_TEXTURE_2D, textureID);
}
//---------------------------------------------------------------
// Build the model matrix. This matrix will transform the 3D object to the proper place.
mat4 buildModelMatrix() {
mat4 rotationXMatrix = rotate(mat4(1.0f), radians(rotateX), vec3(1.0f, 0.0f, 0.0f));
mat4 rotationYMatrix = rotate(mat4(1.0f), radians(rotateY), vec3(0.0f, 1.0f, 0.0f));
mat4 matrix = rotationYMatrix * rotationXMatrix;
return matrix;
}
//---------------------------------------------------------------
void buildMatrices() {
modelMatrix = buildModelMatrix();
mvpMatrix = projMatrix * viewMatrix * modelMatrix;
normalMatrix = column(normalMatrix, 0, vec3(modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2]));
normalMatrix = column(normalMatrix, 1, vec3(modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2]));
normalMatrix = column(normalMatrix, 2, vec3(modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2]));
// Use glm::inverseTranspose() to create a normal matrix, which is used to transform normal vectors.
normalMatrix = inverseTranspose(normalMatrix);
}
//---------------------------------------------------------------
// Handles the display event
void display()
{
// Clear the window with the background color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
buildMatrices();
setShaderVariables();
// Activate the shader program
glUseProgram(program);
// If the buffer object already exists, make that buffer the current active one.
// If the buffer object name is 0, disable buffer objects.
glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBufferID);
// Associate the vertex array in the buffer object with the vertex attribute: "position"
glVertexAttribPointer(vPos, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// Enable the vertex attribute: "position"
glEnableVertexAttribArray(vPos);
glBindBuffer(GL_ARRAY_BUFFER, normalArrayBufferID);
glVertexAttribPointer(normalID, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(normalID);
// Associate the texture coordinate array in the buffer object with the vertex attribute "vTextureCoord",
// which is identified by texCoordID.
glBindBuffer(GL_ARRAY_BUFFER, texCoordArrayBufferID);
glVertexAttribPointer(textureCoordID, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(textureCoordID);
// Start the shader program. Draw the object. The third parameter is the number of vertices.
glDrawArrays(GL_TRIANGLES, 0, numVertices);
// Refresh the window
glutSwapBuffers();
}
//---------------------------------------------------------------
// Handles the reshape event
void reshape(int width, int height)
{
// Specify the width and height of the picture within the window
glViewport(0, 0, width, height);
projMatrix = perspective(fieldOfView, (float)width / (float)height, nearPlane, farPlane);
viewMatrix = lookAt(eyePosition, lookAtCenter, upVector);
}
//---------------------------------------------------------------
// Read mouse motion data and convert them to rotation angles.
void passiveMotion(int x, int y) {
rotateY = (float)x * -0.5f;
rotateX = (float)y * 0.5f;
// Generate a dislay event to force refreshing the window.
glutPostRedisplay();
}
//-----------------------------------------------------------------
void init() {
prepareVBOs();
prepareShaders();
getShaderVariableLocations(program);
setLightingParam();
prepareTextureImage();
// Specify the background color
glClearColor(1, 1, 1, 1);
glEnable(GL_DEPTH_TEST);
}
//---------------------------------------------------------------
void main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("Texture mapping Demo");
glutReshapeWindow(800, 800);
glewInit();
init();
// Register the display callback function
glutDisplayFunc(display);
// Register the reshape callback function
glutReshapeFunc(reshape);
// Register the passive mouse motion call back function
// This function is called when the mouse moves within the window
// while no mouse buttons are pressed.
glutPassiveMotionFunc(passiveMotion);
// Start the event loop
glutMainLoop();
}
With OpenGL shaders, I want to draw a triangle on the screen, where the vertex colours are specified in the data structure alongside the vertex coordinates. The structure has 7 floats for each vertex -- 3 for coordinates, followed by 4 for colour:
static std::vector<GLfloat> vertices = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f
};
I then tell OpenGL how to interpret this structure by using glVertexAttribPointer():
// Vertex coordinates
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), 0);
// Vertex colour
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
And then tell my vertex shader to read the coordinates and colour:
layout (location = 0) in vec3 position;
layout (location = 1) in vec4 vertex_colour;
However, no matter what values I use for the colour component, the triangle is always drawn in red. Changing the coordinates in the structure affects the image as expected, but changing the colour in the structure does nothing.
I believe that this is a problem with my C++ code, rather than the shader code, because I have debugged the shader and it is always reading (1.0, 0.0, 0.0, 1.0) for the colour, even though I am passing it (0.0, 0.0, 1.0, 1.0) for each vertex.
Any ideas on what I'm doing wrong?
Here is my complete code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cmath>
#include <assert.h>
#include <vector>
#include <GL/glew.h>
#include <GL/glut.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/ext.hpp>
GLuint buffer;
GLuint projection_matrix_location;
GLuint view_matrix_location;
glm::mat4 view_matrix;
glm::mat4 projection_matrix;
int num_vertices = 0;
static void RenderScene()
{
// Clear the buffers.
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
// Set the matrices
glUniformMatrix4fv(projection_matrix_location, 1, GL_FALSE, glm::value_ptr(projection_matrix));
glUniformMatrix4fv(view_matrix_location, 1, GL_FALSE, glm::value_ptr(view_matrix));
// Specify how to read the vertex buffer
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Vertex coordinates
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), 0);
// Vertex colour
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
// Draw the vertex buffer
glDrawArrays(GL_TRIANGLES, 0, num_vertices);
glDisableVertexAttribArray(0);
// Swap the buffers
glutSwapBuffers();
}
static void MakeBuffers()
{
// Set the vertices
static std::vector<GLfloat> vertices = {
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f
};
num_vertices = (1.0 / 7.0) * vertices.size();
// Fill the buffer with the vertices
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, num_vertices * 7 * sizeof(GL_FLOAT), &vertices[0], GL_STATIC_DRAW);
}
static GLuint MakeShader(GLenum shader_type, std::string shader_filename)
{
// Load the source code
std::ifstream file_in;
file_in.open(&shader_filename[0]);
std::stringstream file_stream;
file_stream << file_in.rdbuf();
std::string file_string = file_stream.str();
const GLchar* ptr_file_string = &file_string[0];
const GLchar** ptr_file_strings = &ptr_file_string;
int string_lengths[] = {(int)file_string.length()};
// Compile the shader
GLuint shader = glCreateShader(shader_type);
glShaderSource(shader, 1, ptr_file_strings, &string_lengths[0]);
glCompileShader(shader);
// Check
GLint is_success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &is_success);
if (!is_success)
{
std::cerr << "Error" << std::endl;
return -1;
}
return shader;
}
static void MakeShaderProgram()
{
// Make the shaders
GLuint vertex_shader = MakeShader(GL_VERTEX_SHADER, "../src/vertex-shader.glsl");
GLuint fragment_shader = MakeShader(GL_FRAGMENT_SHADER, "../src/fragment-shader.glsl");
// Create the program
GLuint program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
// Check
GLint is_success = 0;
glGetProgramiv(program, GL_LINK_STATUS, &is_success);
if (!is_success)
{
std::cout << "Error" << std::endl;
return;
}
glValidateProgram(program);
glGetProgramiv(program, GL_VALIDATE_STATUS, &is_success);
if (!is_success)
{
std::cout << "Error" << std::endl;
return;
}
// Use the program
glUseProgram(program);
// Get the location of the uniform variables
view_matrix_location = glGetUniformLocation(program, "view_matrix");
assert(view_matrix_location != 0xFFFFFFFF);
projection_matrix_location = glGetUniformLocation(program, "projection_matrix");
assert(projection_matrix_location != 0xFFFFFFFF);
}
int main(int argc, char** argv)
{
// Initialize GLUT
glutInit(&argc, argv);
// Configure some GLUT display options:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
// Specify the GLUT window parameters and create the window
glutInitWindowSize(1000, 750);
glutInitWindowPosition(500, 200);
glutCreateWindow("Test");
// Specify the display callback
glutDisplayFunc(RenderScene);
// Initialize GLEW, which must be done after GLUT is initialized.
GLenum glut_result = glewInit();
if (glut_result != GLEW_OK)
{
std::cout << "Error" << std::endl;
return -1;
}
// Set the clear colour.
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
// Enable depth testing so that only the nearest vertex is sent to the colour buffer (also needed to read the depth of each pixel using glReadPixels())).
glEnable(GL_DEPTH_TEST);
// Make the vertex and index buffers.
MakeBuffers();
// Make the shader program.
MakeShaderProgram();
// Create the view matrix.
glm::vec3 eye(0.0f, 0.0f, -3.0f);
glm::vec3 centre(0.0f, 0.0f, 0.0f);
glm::vec3 up(0.0f, 1.0f, 0.0f);
view_matrix = glm::lookAt(eye, centre, up);
// Create the projection matrix.
float fov_y = 45.0;
float aspect_ratio = 1.5;
float near_clip = 1;
float far_clip = 1000;
projection_matrix = glm::perspective(fov_y, aspect_ratio, near_clip, far_clip);
// Start the GLUT internal loop.
glutMainLoop();
}
And here is my shader code:
// Vertex shader
#version 330
layout (location = 0) in vec3 position;
layout (location = 1) in vec4 vertex_colour;
uniform mat4 projection_matrix;
uniform mat4 view_matrix;
out vec4 frag_colour;
void main()
{
gl_Position = projection_matrix * view_matrix * vec4(position, 1.0f);
frag_colour = vertex_colour;
}
// Fragment shader
#version 330
in vec4 frag_colour;
void main()
{
gl_FragColor = frag_colour;
}
I can't debug your code now,but based on what I see here you forgot to enable second attribute array.See this example for reference.
But even if I am wrong,I would like to point you to some bad practices you have in your code.You are using GL 3.3 which is good.This is modern OpenGL baseline.But you are still mixing old API (pre 3.3) with the new one.From the end:
In your fragment shader you should use custom output attribute rather than the GLSL built in gl_FragColor.
#version 330
in smooth vec4 frag_colour;
out vec4 frag_out;
void main()
{
frag_out = frag_colour;
}
Now,regarding your OpenGL commands.You shouldn't bind vertex buffers directly but use VAO and bind it to the context.In fact some driver version (I experienced on NVIDIA) produce no rendering results at all when VAO is not used with core profile.
I'm trying to rotate a triangle with OpenGL 4 using matrixes, but the triangle appears to reverse direction when halfway. It's obviously not rotating directions, so I must have the depth buffer wrong somewhere. I've tried resolving this but I haven't found any examples of a rotation with OpenGL 4, only ones that use depreciated functions. Here is my code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <math.h>
#include <iostream>
#include "maths_funcs.h"
#include "maths_funcs.cpp"
const char* vertex_shader =
"#version 400\n"
"layout(location = 0) in vec3 vertex_position;"
"layout(location = 1) in vec3 vertex_colour;"
"uniform mat4 pmatrix;"
"uniform mat4 rmatrix;"
"uniform mat4 smatrix;"
"out vec3 colour;"
"void main () {"
" colour = vertex_colour;"
" gl_Position = smatrix * rmatrix * vec4 (vertex_position, 1.0);"
"}";
const char* fragment_shader =
"#version 400\n"
"in vec3 colour;"
"out vec4 frag_colour;"
"void main () {"
" frag_colour = vec4 (colour, 1.0);"
"}";
float angle = 0.0f;
int main () {
// start GL context and O/S window using the GLFW helper library
if (!glfwInit ()) {
fprintf (stderr, "ERROR: could not start GLFW3\n");
return 1;
}
GLFWwindow* window = glfwCreateWindow (640, 480, "Triangle Rotation test", NULL, NULL);
if (!window) {
fprintf (stderr, "ERROR: could not open window with GLFW3\n");
glfwTerminate();
return 1;
}
glfwMakeContextCurrent (window);
// start GLEW extension handler
glewExperimental = GL_TRUE;
glewInit ();
// get version info
const GLubyte* renderer = glGetString (GL_RENDERER); // get renderer string
const GLubyte* version = glGetString (GL_VERSION); // version as a string
printf ("Renderer: %s\n", renderer);
printf ("OpenGL version supported %s\n", version);
// tell GL to only draw onto a pixel if the shape is closer to the viewer
glEnable (GL_DEPTH_TEST); // enable depth-testing
glDepthFunc (GL_LESS); // depth-testing interprets a smaller value as "closer"
float points[] = {
0.0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f
};
float colours[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
float pmatrix[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float rmatrix[] = {
cos(10), 0.0f, sin(10), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sin(10), 0.0f, cos(10), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float smatrix[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, cos(10), sin(10), 0.0f,
0.0f, -sin(10), cos(10), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
GLuint vbo = 0;
glGenBuffers (1, &vbo);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, 9 * sizeof (float), points, GL_STATIC_DRAW);
unsigned int colours_vbo = 0;
glGenBuffers (1, &colours_vbo);
glBindBuffer (GL_ARRAY_BUFFER, colours_vbo);
glBufferData (GL_ARRAY_BUFFER, 9 * sizeof (float), colours, GL_STATIC_DRAW);
GLuint vao = 0;
glGenVertexArrays (1, &vao);
glBindVertexArray (vao);
glEnableVertexAttribArray (0);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer (GL_ARRAY_BUFFER, colours_vbo);
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
GLuint vs = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vs, 1, &vertex_shader, NULL);
glCompileShader (vs);
GLuint fs = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fs, 1, &fragment_shader, NULL);
glCompileShader (fs);
GLuint shader_programme = glCreateProgram ();
glAttachShader (shader_programme, fs);
glAttachShader (shader_programme, vs);
glLinkProgram (shader_programme);
int pmatrix_location = glGetUniformLocation (shader_programme, "pmatrix");
int rmatrix_location = glGetUniformLocation (shader_programme, "rmatrix");
int smatrix_location = glGetUniformLocation (shader_programme, "smatrix");
while (!glfwWindowShouldClose (window)) {
// wipe the drawing surface clear
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram (shader_programme);
rmatrix[0] = cos(angle);
rmatrix[2] = sin(angle);
rmatrix[8] = -sin(angle);
rmatrix[10] = cos(angle);
smatrix[5] = cos(angle);
smatrix[6] = sin(angle);
smatrix[9] = -sin(angle);
smatrix[10] = cos(angle);
glUniformMatrix4fv (pmatrix_location, 1, GL_FALSE, pmatrix);
glUniformMatrix4fv (rmatrix_location, 1, GL_FALSE, rmatrix);
glUniformMatrix4fv (smatrix_location, 1, GL_FALSE, smatrix);
glBindVertexArray (vao);
// draw points 0-3 from the currently bound VAO with current in-use shader
glDrawArrays (GL_TRIANGLES, 0, 3);
// update other events like input handling
glfwPollEvents ();
// put the stuff we've been drawing onto the display
glfwSwapBuffers (window);
angle+=0.01f;
if (angle >= 3.14f) {
angle = -3.14f;
}
}
// close GL context and any other GLFW resources
glfwTerminate();
return 0;
}
Since you are not using a projection matrix, you have to invert the z-coordinate in your vertex shader. Change this line:
gl_Position = smatrix * rmatrix * vec4 (vertex_position, 1.0);
To something like this:
vec4 eyePos = smatrix * rmatrix * vec4 (vertex_position, 1.0);
gl_Position = vec4(eyePos.xy, -eyePos.z, eyePos.w);
The gl_Position value is in clip coordinates, which after the division by w results in NDC (normalized device coordinates). While world coordinates are typically specified in a right-handed coordinate system, with the z-axis pointing out of the screen, NDC is a left-handed coordinate system, with the z-axis pointing into the screen.
Typical projection transformations take care of flipping the z-coordinates from out-of-screen to into-screen. But if you're not using a projection transformation, you have to flip the z-coordinate yourself.
Also, you may want to double check that you really got a depth buffer. Maybe add this before glfwCreateWindow():
glfwWindowHint(GLFW_DEPTH_BITS, 24);
Based on what I could find in the GLFW documentation, this is probably the default. But it couldn't hurt to specify it explicitly.
I am struggling with coding this simple boat scene. It is a homework question but I am truly stumped. I have two models which I upload. One is a cube which has a fragment shader to color itself blue. The second is a boat which has a fragment shader to color itself white. When I use two different shaders the boat model is invisible. With much struggling and searching
I am stumped so I am trying plan B which is find a way to change the colors of a fragment shader between rendering the two objects, but I can't find out how to do so.
void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//BOAT START
//Rocks the ship, simulating ocean rocking
if(shiprocker < 5000){
shiprocker++;
theta += 0.00002f;
}
if(shiprocker ==15000){
shiprocker = -5000;
}
if(shiprocker > 4999){
shiprocker++;
theta -= 0.00002f;
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
boat_model->mR = glm::rotate(
glm::mat4( 1.0f ),
glm::degrees( theta ),
glm::vec3(0.0f, 0.0f, 1.0f)
);
glm::mat4 mtx_trans = glm::translate(
glm::mat4(1.0f),
glm::vec3( 0.0f, 0.0f, -15.0f )
);
boat_model->mM = mtx_trans * boat_model->mR;
boat_model->render();
//BOAT END
//OCEAN CUBE START
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glm::mat4 mtx_trans_cube = glm::translate(
glm::mat4( 1.0f ),
glm::vec3( -20.0f, -50.0f, -50.0f )
);
plane_model->mM = glm::scale(
mtx_trans_cube,
glm::vec3( 10.0f, 10.0f, 10.0f )
);
//gl_FragColor = (0.0f, 0.0f, 1.0f , 1.0f); wont compile
//glColor3f(0.0f, 0.0f, 1.0f); changes nothing
camera->set_uniform_view_perspective( plane_model->shader->program_ID );
plane_model->render();
//OCEAN CUBE END
glutSwapBuffers();
}
int main (int argc, char** argv) {
// Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_MULTISAMPLE | GLUT_RGB);
//WINDOW SETTINGS
glutInitWindowSize(1000, 1000);
glutCreateWindow("SailBoat");
//PASSIVE FUNCS
glutReshapeFunc(change_viewport);
glutDisplayFunc(render);
glutIdleFunc( animate );
//ERROR CHECK
if (GLEW_OK != glewInit()) {
exit(1);
}
//Clear color used for sky tones.
glClearColor(0.529f, 0.807f, .98f, 1.0f);
glEnable (GL_DEPTH_TEST);
glEnable (GL_MULTISAMPLE);
Shader* boatShader = new Shader( VERTEX_SHADER, BOAT_FRAGMENT_SHADER );
Shader* cubeShader = new Shader( VERTEX_SHADER, CUBE_FRAGMENT_SHADER );
//BOAT
boat_model = new Model(boatShader);
load_model(boat_model ,MODEL_BOAT_FILENAME);
boat_model->upload_2_server();
//OCEAN CUBE
plane_model = new Model( boatShader );
load_model(plane_model ,MODEL_PLANE_FILENAME);
plane_model->upload_2_server();
// Set up the camera
theta = 0.0f;
shiprocker = 0;
camera = new FPSCamera(60.0f, 1.0f, 1.0f, 10000.0f, true);
glutMainLoop();
return 0;
}
FRAGMENT SHADER
#version 150
in vec4 color;
out vec4 fColor;
void main () {
fColor = vec4(1.0, 1.0, 1.0, 0.5);
}
You can use uniform variables in GLSL to pass in values in between render calls. Set up your fragment shader like so:
#version 150
in vec4 color;
out vec4 fColor;
uniform vec3 boatColor;
void main () {
fColor = vec4(boatColor, 0.5);
}
And in your C++ code:
glUseProgram(program); // Where program is your shader program
GLint uniform = glGetUniformLocation(program, "boatColor"); // since boatColor is what we called the uniform variable in our fragment shader
glUniform3f(uniform, 1.0f, 0.0f, 0.0f); // Set the boatColor variable to be a solid red
// Render your first boat
glUniform3f(uniform, 0.0f, 0.0f, 1.0f); // Set the second boat to be a solid blue
// Render your second boat
glUseProgram(0);
And that should work nicely. If anyone else here has a better way, please let me know; I'm relatively new to GLSL.
I have created a basic shader program to brush up on my openGL GLSL.
on the cpu side i have properly created my shader program .vp and .fp linked properly and error checked
my result when i render is always a black Triangle
Right before i link my program but after i attach both shaders i do this
glBindAttribLocation( program, 0, "vVertexPos" );
glBindAttribLocation( program, 1, "vColor" );
both my position variables and my color variable in the shaders
all of this is just a quick run through so im not worried about ugly code besides the openGL calls and shader setup
struct PosColVertex
{
float pos[3];
float color[4];
};
PosColVertex verts[3];
float vPos1[3] = { 0.5f, 0.0f, -1.0f };
float vPos2[3] = { 0.0f, 1.0f, -1.0f };
float vPos3[3] = { -0.5f, 0.0f, -1.0f };
memcpy( verts[0].pos, vPos1, sizeof(float)*3 );
memcpy( verts[1].pos, vPos2, sizeof(float)*3 );
memcpy( verts[2].pos, vPos3, sizeof(float)*3 );
float vColor1[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float vColor2[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
float vColor3[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
memcpy( verts[0].color, vColor1, sizeof(float)*4 );
memcpy( verts[1].color, vColor2, sizeof(float)*4 );
memcpy( verts[2].color, vColor3, sizeof(float)*4 );
glGenBuffers( 1, &vboHandle );
glBindBuffer( GL_ARRAY_BUFFER, vboHandle );
glBufferData( GL_ARRAY_BUFFER, sizeof(PosColVertex)*3, verts, GL_DYNAMIC_READ );
for my rendering this is what i do
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
//use our shader program
glUseProgram( program );
//set which vertices we will be using
glBindBuffer( GL_ARRAY_BUFFER, vboHandle );
glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );
//specify our vertex attribute
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( PosColVertex ), (void*)(0) );
//specify our texture attribute
glVertexAttribPointer( 1, 4, GL_FLOAT, GL_FALSE, sizeof( PosColVertex ), (void*)(12) );
glPushMatrix();
//draw our rectangle
glDrawArrays( GL_TRIANGLES, 0, 3 );
glPopMatrix();
glDisableVertexAttribArray( 0 );
glDisableVertexAttribArray( 1 );
did i do something wrong with the glVertexAttribPointer calls? i've checked my shader it works and it does change the values since i hard coded values in there before to test it. but im assuming im not telling openGL on the CPU side how to read my verts properly. any help?
tri.vp
#version 330
in vec3 vVertexPos;
void main(void)
{
gl_Position = vec4( vVertexPos.x, vVertexPos.y, vVertexPos.z, 1 );
}
tri.fp
#version 330
out vec4 vFragColor;
in vec4 vColor;
void main(void)
{
vFragColor = vColor;
}
You can't access attributes inside of fragment shaders, only inside of vertex shaders. This makes sense because you are specifying a color for each vertex, and not for each fragment. So, I'd recommend changing your code to read in the color in the vertex shader and smoothly output it to your fragment shader:
Vertex shader:
in vec3 vVertexPos;
in vec4 vColor;
smooth out vec4 fColor;
void main(void)
{
gl_Position = vec4( vVertexPos.x, vVertexPos.y, vVertexPos.z, 1 );
fColor = vColor;
}
Fragment shader:
smooth in vec4 fColor;
out vec4 vFragColor;
void main(void)
{
vFragColor = fColor;
}