I'm currently porting an old OpenGL 1.1 application which makes use of wireframe models to OpenGL 3.0.
In 1.1 following code is used to create a dashed line:
glPushAttrib(GL_ENABLE_BIT);
glLineStipple(1, 0x0F0F);
glEnable(GL_LINE_STIPPLE);
Here as usual the parameters are pushed to the stack in order to influence all following drawing operations.
My question: how is this done in OpenGL3 where this stack is no longer used? How can I set up my lines to be dashed (probably before handing the coordinates over to glBufferData()?
For separate line segments, this is not very complicated at all. For example drawing the GL_LINES primitives.
The trick is to know the start of the line segment in the fragment shader. This is quite simple by using the flat interpolation qualifier.
The vertex shader has to pass the normalized device coordinate to the fragment shader. Once with default interpolation and once with no (flat) interpolation. This causes that in the fragment shade, the first input parameter contains the NDC coordinate of the actual position on the line and the later the NDC coordinate of the start of the line.
#version 330
layout (location = 0) in vec3 inPos;
flat out vec3 startPos;
out vec3 vertPos;
uniform mat4 u_mvp;
void main()
{
vec4 pos = u_mvp * vec4(inPos, 1.0);
gl_Position = pos;
vertPos = pos.xyz / pos.w;
startPos = vertPos;
}
Additionally to the varying inputs, the fragment shader has uniform variables. u_resolution contains the width and the height of the viewport. u_dashSize contains the length of the line and u_gapSize the length of a gap in pixel.
So the length of the line from the start to the actual fragment can be calculated:
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
And fragments on the gap can be discarded, by the discard command.
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
Fragment shader:
#version 330
flat in vec3 startPos;
in vec3 vertPos;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main()
{
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
fragColor = vec4(1.0);
}
For the following simple demo program I've used the GLFW API for creating a window, GLEW for loading OpenGL and GLM -OpenGL Mathematics for the math. I don't provide the code for the function CreateProgram, which just creates a program object, from the vertex shader and fragment shader source code:
#include <GL/glew.h>
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <GLFW/glfw3.h>
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
int main(void)
{
if (glfwInit() == GLFW_FALSE)
return 0;
GLFWwindow *window = glfwCreateWindow(400, 300, "OGL window", nullptr, nullptr);
if (window == nullptr)
return 0;
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK)
return 0;
GLuint program = CreateProgram(vertShader, fragShader);
GLint loc_mvp = glGetUniformLocation(program, "u_mvp");
GLint loc_res = glGetUniformLocation(program, "u_resolution");
GLint loc_dash = glGetUniformLocation(program, "u_dashSize");
GLint loc_gap = glGetUniformLocation(program, "u_gapSize");
glUseProgram(program);
glUniform1f(loc_dash, 10.0f);
glUniform1f(loc_gap, 10.0f);
std::vector<float> varray{
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1
};
std::vector<unsigned int> iarray{
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
};
GLuint bo[2], vao;
glGenBuffers(2, bo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iarray.size()*sizeof(*iarray.data()), iarray.data(), GL_STATIC_DRAW);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 project;
int vpSize[2]{0, 0};
while (!glfwWindowShouldClose(window))
{
int w, h;
glfwGetFramebufferSize(window, &w, &h);
if (w != vpSize[0] || h != vpSize[1])
{
vpSize[0] = w; vpSize[1] = h;
glViewport(0, 0, vpSize[0], vpSize[1]);
project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
glUniform2f(loc_res, (float)w, (float)h);
}
static float angle = 1.0f;
glm::mat4 modelview( 1.0f );
modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -3.0f) );
modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
angle += 0.5f;
glm::mat4 mvp = project * modelview;
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_LINES, (GLsizei)iarray.size(), GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
Things get a bit more complicated, if the goal is to draw a dashed line along a polygon. For example drawing a GL_LINE_STRIP primitive.
The length of the line cannot be calculated in the shader program, without knowing all the primitives of the line. Even if all the primitives would be known (e.g. SSBO), then the calculation would have to be done in a loop.
I decided to add an additional attribute to the shader program, which contains the "distance" from the start of the line to the vertex coordinate. By "distance" is meant the length of the projected polygon on to the viewport.
This causes that the vertex shader and fragment shader is even simpler:
Vertex shader:
#version 330
layout (location = 0) in vec3 inPos;
layout (location = 1) in float inDist;
out float dist;
uniform mat4 u_mvp;
void main()
{
dist = inDist;
gl_Position = u_mvp * vec4(inPos, 1.0);
}
Fragment shader:
#version 330
in float dist;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main()
{
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
fragColor = vec4(1.0);
}
In the demo program the inDist attribute is calculated on the CPU. Each vertex coordinate is transformed by the model, view, projection matrix. Finally it is transformed from normalized device space to window space. The XY distance between adjacent coordinates of the line strip is calculated and the lengths are summed along the line strip and assigned to the corresponding attribute value:
int w = [...], h = [...]; // window widht and height
glm::mat4 mpv = [...]; // model view projection matrix
std::vector<glm::vec3> varray{ [...] }; // array of vertex
std::vector<float> darray(varray.size(), 0.0f); // distance attribute - has to be computed
glm::mat4 wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));
glm::vec2 vpPt(0.0f, 0.0f);
float dist = 0.0f;
for (size_t i=0; i < varray.size(); ++i)
{
darray[i] = dist;
glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
glm::vec4 ndc = clip / clip.w;
glm::vec4 vpC = wndmat * ndc;
float len = i==0 ? 0.0f : glm::length(vpPt - glm::vec2(vpC));
vpPt = glm::vec2(vpC);
dist += len;
}
Demo program:
int main(void)
{
if (glfwInit() == GLFW_FALSE)
return 0;
GLFWwindow *window = glfwCreateWindow(800, 600, "OGL window", nullptr, nullptr);
if (window == nullptr)
return 0;
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK)
return 0;
GLuint program = CreateProgram(vertShader, fragShader);
GLint loc_mvp = glGetUniformLocation(program, "u_mvp");
GLint loc_res = glGetUniformLocation(program, "u_resolution");
GLint loc_dash = glGetUniformLocation(program, "u_dashSize");
GLint loc_gap = glGetUniformLocation(program, "u_gapSize");
glUseProgram(program);
glUniform1f(loc_dash, 10.0f);
glUniform1f(loc_gap, 10.0f);
std::vector<glm::vec3> varray;
for (size_t u=0; u <= 360; ++u)
{
double a = u*M_PI/180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec3((float)c, (float)s, 0.0f));
}
std::vector<float> darray(varray.size(), 0.0f);
GLuint bo[2], vao;
glGenBuffers(2, bo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, bo[1] );
glBufferData(GL_ARRAY_BUFFER, darray.size()*sizeof(*darray.data()), darray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 project, wndmat;
int vpSize[2]{0, 0};
while (!glfwWindowShouldClose(window))
{
int w, h;
glfwGetFramebufferSize(window, &w, &h);
if (w != vpSize[0] || h != vpSize[1])
{
vpSize[0] = w; vpSize[1] = h;
glViewport(0, 0, vpSize[0], vpSize[1]);
project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
glUniform2f(loc_res, (float)w, (float)h);
wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));
}
static float angle = 1.0f;
glm::mat4 modelview( 1.0f );
modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -2.0f) );
modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
angle += 0.5f;
glm::mat4 mvp = project * modelview;
glm::vec2 vpPt(0.0f, 0.0f);
float dist = 0.0f;
for (size_t i=0; i < varray.size(); ++i)
{
darray[i] = dist;
glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
glm::vec4 ndc = clip / clip.w;
glm::vec4 vpC = wndmat * ndc;
float len = i==0 ? 0.0f : glm::length(vpPt - glm::vec2(vpC));
vpPt = glm::vec2(vpC);
dist += len;
}
glBufferSubData(GL_ARRAY_BUFFER, 0, darray.size()*sizeof(*darray.data()), darray.data());
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)varray.size());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
See also
glLineStipple deprecated in OpenGL 3.1
OpenGL ES - Dashed Lines
Related
I'm trying to setup an orthographic camera in C++ and I have some problems getting the rotation correctly.
When I try to rotate the camera around the z axis by 45 degrees, everything works as expected (model is defined from -0.5f to 0.5f).
However, when trying to rotate around x axis, the angle gets really amplified (i.e. rotation around 1 radian makes it almost disappear).
Here is the related code:
int main(int argc, char** argv)
{
sdl2::Context context;
sdl2::Window window("myWindow", 800, 600);
sdl2::GLContext glContext(window);
//make the shader
Shader shader("shaders/quad.vert", "shaders/quad.frag");
Quad quad;
//create a orthographic projection
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
auto zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
glUseProgram(shader.getID());
auto projectionLoc = glGetUniformLocation(shader.getID(), "projection");
glUniformMatrix4fv(projectionLoc, 1, false, glm::value_ptr(projection));
auto viewLoc = glGetUniformLocation(shader.getID(), "view");
auto viewTransform = glm::mat4(1.0f);
auto zRot = glm::quat(glm::vec3(glm::radians(1.0f), glm::radians(0.0f), glm::radians(0.0f)));
viewTransform *= glm::toMat4(zRot);
glUniformMatrix4fv(viewLoc, 1, true, glm::value_ptr(viewTransform));
bool quit = false;
SDL_Event e;
while (!quit)
{
while (SDL_PollEvent(&e) != 0)
{
if (e.type == SDL_QUIT)
{
quit = true;
}
}
glClear(GL_COLOR_BUFFER_BIT);
quad.render(shader);
SDL_GL_SwapWindow(window());
}
SDL_Delay(3000);
return 0;
}
#version 430 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 uv;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
gl_Position = projection * view * model * vec4(pos, 1.0);
}
Quad::Quad() :
_position(0, 0, 0)
{
glCreateBuffers(1, &_vbo);
glCreateVertexArrays(1, &_vao);
glGenBuffers(1, &_ebo);
float vertices[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};
uint32_t indices[] = {
0, 1, 3,
1, 2, 3
};
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glEnableVertexAttribArray(0);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
}
Quad::~Quad()
{
glDeleteVertexArrays(1, &_vao);
glDeleteBuffers(1, &_vbo);
}
void Quad::render(const Shader& shader)
{
glBindVertexArray(_vao);
glUseProgram(shader.getID());
auto modelView = glm::mat4(1.0f);
//modelView = glm::translate(modelView, glm::vec3(400.0f, 300.0f, 0.0f));
modelView = glm::scale(modelView, glm::vec3(400.0f, 400.0f, 1.0f));
//modelView = glm::scale(modelView, glm::vec3(800, 600, 0));
auto modelLoc = glGetUniformLocation(shader.getID(), "model");
auto viewLoc = glGetUniformLocation(shader.getID(), "view");
auto projectionLoc = glGetUniformLocation(shader.getID(), "projection");
glUniformMatrix4fv(modelLoc, 1, false, glm::value_ptr(modelView));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
When you rotate a round the x or y axis, then the objects is clipped by the near and far plane of the Orthographic projection. By default near is -1.0 and far is 1.0. Increase the distance to the near and far plane (see glm::ortho). For instance:
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, -500.0f, 500.0f);
I have sprites in an atlas rendering in OpenGL properly with the code below. My problem comes from trying to add a secondary "texture" to sample so I can do some multitexturing magic. The problem I think it's that the second sprite is also in an atlas and it's being affected by the VAO offsets so I can't really pick the right UVs to get the exact point I need. I've tried adding some calculations to reverse engineer the correct UVs for this sprite inside the other texture (you can see my attempt at the bottom) but this doesn't seem to work. What would be the best approach to do this?
Preparation:
glm::vec4 fRect;
fRect.x = static_cast<float>(iRect.x) / textureWidth;
fRect.y = static_cast<float>(iRect.y) / textureHeight;
fRect.z = (static_cast<float>(iRect.z) / textureWidth) + fRect.x;
fRect.w = (static_cast<float>(iRect.w) / textureHeight) + fRect.y;
// Configure VAO/VBO
GLuint VBO;
GLfloat vertices[] = {
// Pos // Tex
0.0f, 1.0f, fRect.x, fRect.w,
1.0f, 0.0f, fRect.z, fRect.y,
0.0f, 0.0f, fRect.x, fRect.y,
0.0f, 1.0f, fRect.x, fRect.w,
1.0f, 1.0f, fRect.z, fRect.w,
1.0f, 0.0f, fRect.z, fRect.y
};
GLuint VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(VAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
Rendering:
// Prepare transformations
glm::mat4 modelMatrix;
modelMatrix = glm::translate(modelMatrix, position);
// modelMatrix = glm::translate(modelMatrix, -glm::vec3(spriteOffset.x, spriteOffset.y, 0.0f));
modelMatrix = glm::rotate(modelMatrix, rotate.x, glm::vec3(1.0f, 0.0f, 0.0f));
// modelMatrix = glm::rotate(modelMatrix, rotate.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMatrix = glm::rotate(modelMatrix, rotate.z, glm::vec3(0.0f, 0.0f, 1.0f));
modelMatrix = glm::translate(modelMatrix, glm::vec3(-spriteOffset.x, -spriteOffset.y, 0.0f));
modelMatrix = glm::scale(modelMatrix, glm::vec3(size, 1.0f));
//(...)
glUniformMatrix4fv(modelMatrixLocation, 1, false, glm::value_ptr( modelMatrix ) );
glUniformMatrix4fv(viewMatrixLocation, 1, false, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(projectionMatrixLocation, 1, false, glm::value_ptr(projectionMatrix));
ASSERT( !HasOpenGLErrors(), "OpenGL error!" );
glUniform3f(multColorLocation, multColour.x, multColour.y, multColour.z );
glUniform3f(addColorLocation, addColour.x, addColour.y, addColour.z );
ASSERT( !HasOpenGLErrors(), "OpenGL error!" );
// Bind Texture, etc
glDrawArrays(GL_TRIANGLES, 0, 6);
Vertex shader:
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
out vec2 TexCoords;
uniform mat4 model_matrix, view_matrix, projection_matrix;
void main()
{
TexCoords = vec2(vertex.z, 1.0 - vertex.w);
gl_Position = projection_matrix*view_matrix*model_matrix*vec4(vertex.xyz,1);
}
Fragment shader:
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D texture;
uniform vec3 multColour;
uniform vec3 addColour;
void main()
{
vec4 minColour = vec4(0.0, 0.0, 0.0, 0.0);
vec4 maxColour = vec4(1.0, 1.0, 1.0, 1.0);
vec4 texColour = texture(texture, TexCoords);
if(texColour.a < 0.01)
discard;
color = clamp(vec4(multColour, 1.0) * texColour + vec4(addColour,0.0), minColour, maxColour);
}
Failed attempt at reading the right UVs in fragment shader:
float normU = (TexCoords.x - currUVs.x) / (currUVs.z - currUVs.x);
float icU = mix(icUVs.x, icUVs.z, normU);
float normV = (TexCoords.y - currUVs.y) / (currUVs.w - currUVs.y);
float icV = mix(icUVs.y, icUVs.w, normV);
vec2 UV = vec2(icU, icV );
vec4 initial = texture(initialColor, UV);
Where currUVs are the values of fRect passed in the VAO above and the icUVs are the UV bounds (min and max values) for the second sprite within the atlas texture.
So far it seems like all sprites that have no offset applied will render properly but if I passed in any kind of spriteOffset into the rendering, then it will render wrong.
How can I solve this? Is there a way of applying the VAO rects in the shaders and then be able to get the second sprite correctly?
I am making an empty room with 4 walls, 1 floor and 1 ceiling.
I have added an array of uniform variables in the fragment shader for each wall/floor/ceiling.
I'm adjusting this section of code (below), and trying to get the far wall to appear red but nothing is changing after I execute. It stays blue. Why is that?
static void init(GLFWwindow* window)
{
...
// Wall 1 (Far)
g_materialProperties_plane[1].ambient = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
g_materialProperties_plane[1].diffuse = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
g_materialProperties_plane[1].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
...
}
Full program
#define MAX_MATERIALS 6
// struct for lighting properties
struct LightProperties
{
vec4 position;
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
vec3 attenuation;
float cutoffAngle;
vec3 direction;
};
// struct for material properties
struct MaterialProperties
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
};
LightProperties g_lightProperties;
MaterialProperties g_materialProperties_plane[6];
// struct for vertex attributes
struct Vertex_plane
{
GLfloat position[3];
GLfloat normal[3];
};
GLuint g_VBO; // vertex buffer object identifier
GLuint g_VAO = 0; // vertex array object identifier
GLuint g_shaderProgramID = 0; // shader program identifier
// locations in shader
GLuint g_MVP_Index;
GLuint g_M_Index = 0;
GLuint g_viewPointIndex = 0;
GLuint g_lightPositionIndex = 0;
GLuint g_lightAmbientIndex = 0;
GLuint g_lightDiffuseIndex = 0;
GLuint g_lightSpecularIndex = 0;
GLuint g_lightShininessIndex = 0;
GLuint g_lightAttenuationIndex = 0;
GLuint g_lightCutoffAngleIndex = 0;
GLuint g_lightDirectionIndex = 0;
GLuint g_materialAmbientIndex[MAX_MATERIALS];
GLuint g_materialDiffuseIndex[MAX_MATERIALS];
GLuint g_materialSpecularIndex[MAX_MATERIALS];
glm::mat4 g_modelMatrix_plane[6]; // object's model matrix (4 walls + 1 ceiling + 1 floor)
glm::mat4 g_viewMatrix; // view matrix
glm::mat4 g_projectionMatrix; // projection matrix
glm::vec3 g_viewPoint; // view point
Camera g_camera; // camera
GLuint g_windowWidth = 1200; // window dimensions
GLuint g_windowHeight = 1000;
bool g_wireFrame = false; // wireframe on or off
static void init(GLFWwindow* window)
{
glEnable(GL_DEPTH_TEST); // enable depth buffer test
// create and compile our GLSL program from the shader files
g_shaderProgramID = loadShaders("PerFragLightingVS.vert", "PerFragLightingFS.frag");
// find the location of shader variables
GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
GLuint normalIndex = glGetAttribLocation(g_shaderProgramID, "aNormal");
g_MVP_Index = glGetUniformLocation(g_shaderProgramID, "uModelViewProjectionMatrix");
g_M_Index = glGetUniformLocation(g_shaderProgramID, "uModelMatrix");
g_viewPointIndex = glGetUniformLocation(g_shaderProgramID, "uViewPoint");
// Material
g_materialAmbientIndex[0] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[0].ambient");
g_materialDiffuseIndex[0] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[0].diffuse");
g_materialSpecularIndex[0] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[0].specular");
g_materialAmbientIndex[1] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[1].ambient");
g_materialDiffuseIndex[1] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[1].diffuse");
g_materialSpecularIndex[1] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[1].specular");
g_materialAmbientIndex[2] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[2].ambient");
g_materialDiffuseIndex[2] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[2].diffuse");
g_materialSpecularIndex[2] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[2].specular");
g_materialAmbientIndex[3] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[3].ambient");
g_materialDiffuseIndex[3] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[3].diffuse");
g_materialSpecularIndex[3] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[3].specular");
g_materialAmbientIndex[4] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[4].ambient");
g_materialDiffuseIndex[4] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[4].diffuse");
g_materialSpecularIndex[4] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[4].specular");
g_materialAmbientIndex[5] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[5].ambient");
g_materialDiffuseIndex[5] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[5].diffuse");
g_materialSpecularIndex[5] = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties[5].specular");
// initialise model matrix to the identity matrix
g_modelMatrix_plane[0] = g_modelMatrix_plane[1] = g_modelMatrix_plane[2] = g_modelMatrix_plane[3]
= g_modelMatrix_plane[4] = g_modelMatrix_plane[5] = g_modelMatrix_plane[6] = glm::mat4(1.0f);
...
// Material Properties - Planes
// Floor
g_materialProperties_plane[0].ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
g_materialProperties_plane[0].diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
g_materialProperties_plane[0].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// Wall 1 (Far)
g_materialProperties_plane[1].ambient = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
g_materialProperties_plane[1].diffuse = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
g_materialProperties_plane[1].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// Wall 2 (Left)
g_materialProperties_plane[2].ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
g_materialProperties_plane[2].diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
g_materialProperties_plane[2].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// Wall 3 (Right)
g_materialProperties_plane[3].ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
g_materialProperties_plane[3].diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
g_materialProperties_plane[3].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// Wall 4 (Near)
g_materialProperties_plane[4].ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
g_materialProperties_plane[4].diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
g_materialProperties_plane[4].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// Ceiling
g_materialProperties_plane[5].ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
g_materialProperties_plane[5].diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
g_materialProperties_plane[5].specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
// generate identifier for VBOs and copy data to GPU
glGenBuffers(1, &g_VBO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices_plane), g_vertices_plane, GL_STATIC_DRAW);
// generate identifiers for VAO
glGenVertexArrays(1, &g_VAO);
// create VAO and specify VBO data
glBindVertexArray(g_VAO);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO);
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex_plane), reinterpret_cast<void*>(offsetof(Vertex_plane, position)));
glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex_plane), reinterpret_cast<void*>(offsetof(Vertex_plane, normal)));
glEnableVertexAttribArray(positionIndex); // enable vertex attributes
glEnableVertexAttribArray(normalIndex);
}
// function used to render the scene
static void render_scene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear colour buffer and depth buffer
glUseProgram(g_shaderProgramID); // use the shaders associated with the shader program
glBindVertexArray(g_VAO); // make VAO active
// set uniform shader variables
glm::mat4 MVP = glm::mat4(1.0f);
// Floor
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[0];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[0][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Wall 1 (Far wall)
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[1];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[1][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Wall 2 (Left wall)
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[2];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[2][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Wall 3 (Right wall)
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[3];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[3][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Wall 4 (Near wall)
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[4];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[4][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Ceiling
MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_plane[5];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_plane[5][0][0]);
glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUniform4fv(g_lightPositionIndex, 1, &g_lightProperties.position[0]);
glUniform4fv(g_lightAmbientIndex, 1, &g_lightProperties.ambient[0]);
glUniform4fv(g_lightDiffuseIndex, 1, &g_lightProperties.diffuse[0]);
glUniform4fv(g_lightSpecularIndex, 1, &g_lightProperties.specular[0]);
glUniform1fv(g_lightShininessIndex, 1, &g_lightProperties.shininess);
glUniform3fv(g_lightAttenuationIndex, 1, &g_lightProperties.attenuation[0]);
glUniform1fv(g_lightCutoffAngleIndex, 1, &g_lightProperties.cutoffAngle);
glUniform3fv(g_lightDirectionIndex, 1, &g_lightProperties.direction[0]);
// Material Properties - Planes
// Floor
glUniform4fv(g_materialAmbientIndex[0], 1, &g_materialProperties_plane[0].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[0], 1, &g_materialProperties_plane[0].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[0], 1, &g_materialProperties_plane[0].specular[0]);
// Wall 1 (Far)
glUniform4fv(g_materialAmbientIndex[1], 1, &g_materialProperties_plane[1].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[1], 1, &g_materialProperties_plane[1].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[1], 1, &g_materialProperties_plane[1].specular[0]);
// Wall 2 (Left)
glUniform4fv(g_materialAmbientIndex[2], 1, &g_materialProperties_plane[2].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[2], 1, &g_materialProperties_plane[2].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[2], 1, &g_materialProperties_plane[2].specular[0]);
// Wall 3 (Right)
glUniform4fv(g_materialAmbientIndex[3], 1, &g_materialProperties_plane[3].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[3], 1, &g_materialProperties_plane[3].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[3], 1, &g_materialProperties_plane[3].specular[0]);
// Wall 4 (Near)
glUniform4fv(g_materialAmbientIndex[4], 1, &g_materialProperties_plane[4].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[4], 1, &g_materialProperties_plane[4].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[4], 1, &g_materialProperties_plane[4].specular[0]);
// Ceiling
glUniform4fv(g_materialAmbientIndex[5], 1, &g_materialProperties_plane[5].ambient[0]);
glUniform4fv(g_materialDiffuseIndex[5], 1, &g_materialProperties_plane[5].diffuse[0]);
glUniform4fv(g_materialSpecularIndex[5], 1, &g_materialProperties_plane[5].specular[0]);
glFlush(); // flush the pipeline
}
int main(void)
{
GLFWwindow* window = NULL; // pointer to a GLFW window handle
TwBar *TweakBar; // pointer to a tweak bar
glfwSetErrorCallback(error_callback); // set error callback function
// initialise GLFW
if (!glfwInit())
{
// if failed to initialise GLFW
exit(EXIT_FAILURE);
}
// minimum OpenGL version 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// create a window and its OpenGL context
window = glfwCreateWindow(g_windowWidth, g_windowHeight, "Tutorial", NULL, NULL);
// if failed to create window
if (window == NULL)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window); // set window context as the current context
glfwSwapInterval(1); // swap buffer interval
// initialise GLEW
if (glewInit() != GLEW_OK)
{
// if failed to initialise GLEW
cerr << "GLEW initialisation failed" << endl;
exit(EXIT_FAILURE);
}
// set key callback function
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
// use sticky mode to avoid missing state changes from polling
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
// use mouse to move camera, hence use disable cursor mode
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// initialise AntTweakBar
TwInit(TW_OPENGL_CORE, NULL);
// give tweak bar the size of graphics window
TwWindowSize(g_windowWidth, g_windowHeight);
TwDefine(" TW_HELP visible=false "); // disable help menu
TwDefine(" GLOBAL fontsize=3 "); // set large font size
// create a tweak bar
TweakBar = TwNewBar("Main");
TwDefine(" Main label='Controls' refresh=0.02 text=light size='220 200' ");
// create display entries
TwAddVarRW(TweakBar, "Wireframe", TW_TYPE_BOOLCPP, &g_wireFrame, " group='Display' ");
// display a separator
TwAddSeparator(TweakBar, NULL, NULL);
// create spotlight entries
TwAddVarRW(TweakBar, "Cutoff", TW_TYPE_FLOAT, &g_lightProperties.cutoffAngle, " group='Spotlight' min=-180.0 max=180.0 step=1.0 ");
TwAddVarRW(TweakBar, "Direction: x", TW_TYPE_FLOAT, &g_lightProperties.direction[0], " group='Spotlight' min=-1.0 max=1.0 step=0.1");
TwAddVarRW(TweakBar, "Direction: y", TW_TYPE_FLOAT, &g_lightProperties.direction[1], " group='Spotlight' min=-1.0 max=1.0 step=0.1");
TwAddVarRW(TweakBar, "Direction: z", TW_TYPE_FLOAT, &g_lightProperties.direction[2], " group='Spotlight' min=-1.0 max=1.0 step=0.1");
// initialise rendering states
init(window);
// the rendering loop
while (!glfwWindowShouldClose(window))
{
g_camera.update(window); // update camera
if (g_wireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
render_scene(); // render the scene
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
TwDraw(); // draw tweak bar(s)
glfwSwapBuffers(window); // swap buffers
glfwPollEvents(); // poll for events
}
// clean up
glDeleteProgram(g_shaderProgramID);
glDeleteBuffers(1, &g_VBO);
glDeleteVertexArrays(1, &g_VAO);
// uninitialise tweak bar
TwTerminate();
// close the window and terminate GLFW
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
Fragment Shader
#version 330 core
#define MAX_MATERIALS 6
// interpolated values from the vertex shaders
in vec3 vNormal;
in vec3 vPosition;
// uniform input data
struct LightProperties
{
vec4 position;
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
vec3 attenuation;
float cutoffAngle;
vec3 direction;
};
struct MaterialProperties
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
};
uniform LightProperties uLightingProperties;
uniform MaterialProperties uMaterialProperties[MAX_MATERIALS];
uniform vec3 uViewPoint;
// output data
out vec3 fColor;
void main()
{
// calculate vectors for lighting
vec3 N = normalize(vNormal);
vec3 L;
float attenuation = 1.0f;
// calculate the attenuation based on distance
L = (uLightingProperties.position).xyz - vPosition;
float distance = length(L);
L = normalize(L);
attenuation = 1/(uLightingProperties.attenuation.x
+ uLightingProperties.attenuation.y * distance
+ uLightingProperties.attenuation.z * distance * distance);
vec3 V = normalize(uViewPoint - vPosition);
vec3 R = reflect(-L, N);
// the direction of the spotlight
vec3 direction = normalize(uLightingProperties.direction);
// the angle between the vector from the light to the fragment’s position and the spotlight’s direction
float angle = degrees(acos(dot(-L, direction)));
vec3 colour = vec3(0.0f, 0.0f, 0.0f);
// only compute if angle is less than the cutoff angle
if(angle <= uLightingProperties.cutoffAngle)
{
for(int i = 0; i < MAX_MATERIALS; i++){
// calculate Phong lighting
vec4 ambient = uLightingProperties.ambient * uMaterialProperties[i].ambient;
vec4 diffuse = uLightingProperties.diffuse * uMaterialProperties[i].diffuse * max(dot(L, N), 0.0);
vec4 specular = vec4(0.0f, 0.0f, 0.0f, 1.0f);
if(dot(L, N) > 0.0f)
{
specular = uLightingProperties.specular * uMaterialProperties[i].specular
* pow(max(dot(V, R), 0.0), uLightingProperties.shininess);
}
colour = (attenuation * (diffuse + specular)).rgb + ambient.rgb;
// fade the spotlight's intensity linearly with angle
colour *= 1.0f - angle/uLightingProperties.cutoffAngle;
}
}
// set output color
fColor = colour;
}
The issue is inside the Fragment shader:
for(int i = 0; i < MAX_MATERIALS; i++){
....
colour = (attenuation * (diffuse + specular)).rgb + ambient.rgb;
// fade the spotlight's intensity linearly with angle
colour *= 1.0f - angle/uLightingProperties.cutoffAngle;
}
In each iteration of the for loop assign the variable color.
At the end the content of color os the was calculated in the last iteration of the loop (i=5).
You don't need a loop, but you have to set the appropriate material index to a uniform variable before you draw a mesh.
// The index of the material which should be applied to the mesh,
// which is currently drawn.
uniform int uMaterialIndex;
.....
void main()
{
.....
vec3 colour = vec3(0.0f, 0.0f, 0.0f);
if (angle <= uLightingProperties.cutoffAngle)
{
int i = uMaterialIndex;
// calculate Phong lighting
vec4 ambient = uLightingProperties.ambient * uMaterialProperties[i].ambient;
vec4 diffuse = uLightingProperties.diffuse * uMaterialProperties[i].diffuse * max(dot(L, N), 0.0);
vec4 specular = vec4(0.0f, 0.0f, 0.0f, 1.0f);
if (dot(L, N) > 0.0f)
{
specular = uLightingProperties.specular * uMaterialProperties[i].specular
* pow(max(dot(V, R), 0.0), uLightingProperties.shininess);
}
colour = (attenuation * (diffuse + specular)).rgb + ambient.rgb;
// fade the spotlight's intensity linearly with angle
colour *= 1.0f - angle/uLightingProperties.cutoffAngle;
}
.....
}
When you draw a mesh, you need to update the uniform variable:
GLuint g_materialIndex;
g_materialIndex = glGetUniformLocation(g_shaderProgramID, "uMaterialIndex");
glUniform1i(g_materialIndex, materialIndex); // materialIndex in range 0 to 5
glDrawArrays(GL_TRIANGLES, 0, 6);
current state:
What I am trying to achieve is to create a fading effect (periodically black to full color) on the second cube/model matrix, without changing any of the global vertices. What I have gathered so far is the need to declare a uniform variable in the fragment shader and play with the float values. I have since added to the fragment shader:
uniform float uAlpha;
void main()
{
// set output color
fColor = vec4(vColor, uAlpha);
}
I don't know what to do next with my source code. Add something along the lines of this?
GLuint g_uAlpha = glGetUniformLocation(g_shaderProgramID, "uAlpha");
vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
GLfloat alpha = color.a;
glUniform1fv(g_uAlpha, 1, &alpha);
It really doesn't do anything I know. I'm really clueless about how to implement this and I'm hoping someone can shed some light, thanks.
Source code:
#include <cstdio>
#include <iostream>
#include <cstddef>
#include <Windows.h>
#include <time.h>
using namespace std;
#define GLEW_STATIC
#include <GLEW/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include "shader.h"
#include "Camera.h"
struct Vertex
{
GLfloat position[3];
GLfloat color[3];
};
Vertex g_vertices[] = {
// vertex 1
-0.2f, 0.2f, 0.2f,
1.0f, 0.0f, 1.0f,
// vertex 2
-0.2f, -0.2f, 0.2f,
1.0f, 0.0f, 0.0f,
// vertex 3
0.2f, 0.2f, 0.2f,
1.0f, 1.0f, 1.0f,
// vertex 4
0.2f, -0.2f, 0.2f,
1.0f, 1.0f, 0.0f,
// vertex 5
-0.2f, 0.2f, -0.2f,
0.0f, 0.0f, 1.0f,
// vertex 6
-0.2f, -0.2f, -0.2f,
0.0f, 0.0f, 0.0f,
// vertex 7
0.2f, 0.2f, -0.2f,
0.0f, 1.0f, 1.0f,
// vertex 8
0.2f, -0.2f, -0.2f,
0.0f, 1.0f, 0.0f,
};
GLuint g_indices[] = {
0, 1, 2,
2, 1, 3,
4, 5, 0,
0, 5, 1,
2, 3, 6,
6, 3, 7,
4, 0, 6,
6, 0, 2,
1, 5, 3,
3, 5, 7,
5, 4, 7,
7, 4, 6,
};
GLuint g_IBO[1];
GLuint g_VBO[1];
GLuint g_VAO[1];
GLuint g_shaderProgramID = 0;
GLuint g_MVP_Index = 0;
mat4 g_modelMatrix[2];
mat4 g_viewMatrix;
mat4 g_projectionMatrix;
Camera g_camera;
static void init(GLFWwindow* window)
{
srand(time(NULL));
glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
g_shaderProgramID = loadShaders("Vertex_Shader.vert", "Fragment_Shader.frag");
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
// find the location of shader variables
GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
GLuint colorIndex = glGetAttribLocation(g_shaderProgramID, "aColor");
g_MVP_Index = glGetUniformLocation(g_shaderProgramID, "uModelViewProjectionMatrix");
// initialise model matrix to the identity matrix
g_modelMatrix[0] = mat4(1.0f);
g_modelMatrix[1] = mat4(1.0f);
// set camera's view matrix
g_camera.setViewMatrix(vec3(0, 1, 5), vec3(0, 0, 2), vec3(0, 1, 0));
// get the framebuffer width and height in order to calculate the aspect ratio
int width, height;
glfwGetFramebufferSize(window, &width, &height);
float aspectRatio = static_cast<float>(width) / height;
// initialise the projection matrix
g_camera.setProjectionMatrix(perspective(45.0f, aspectRatio, 0.1f, 100.0f));
glGenBuffers(1, g_VBO);
glGenVertexArrays(1, g_VAO);
glGenBuffers(1, g_IBO);
// draw cubes
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices), g_vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_indices), g_indices, GL_STATIC_DRAW);
glBindVertexArray(g_VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[0]);
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
glVertexAttribPointer(colorIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, color)));
glEnableVertexAttribArray(positionIndex);
glEnableVertexAttribArray(colorIndex);
}
static void render_scene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(g_shaderProgramID);
glBindVertexArray(g_VAO[0]);
mat4 MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[0];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
mat4 MVP1 = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix[1];
glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP1[0][0]);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
glFlush();
}
static void update_scene(GLFWwindow* window)
{
g_modelMatrix[1] = glm::translate(glm::vec3(1.0f, 0.0f, 0.0f));
}
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);
return;
}
}
int main(void)
{
GLFWwindow* window = NULL;
if (!glfwInit())
{
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
window = glfwCreateWindow(1028, 768, "Test", NULL, NULL);
if (window == NULL)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
if (glewInit() != GLEW_OK)
{
cerr << "GLEW initialisation failed" << endl;
exit(EXIT_FAILURE);
}
glfwSetKeyCallback(window, key_callback);
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
init(window);
float lastUpdateTime = glfwGetTime();
float currentTime = lastUpdateTime;
// rendering loop
while (!glfwWindowShouldClose(window))
{
currentTime = glfwGetTime();
if (currentTime - lastUpdateTime > 0.02)
{
g_camera.update(window);
update_scene(window);
render_scene();
glfwSwapBuffers(window);
glfwPollEvents();
lastUpdateTime = currentTime;
}
}
// clean up
glDeleteProgram(g_shaderProgramID);
glDeleteBuffers(1, g_IBO);
glDeleteBuffers(1, g_VBO);
glDeleteVertexArrays(1, g_VAO);
// close the window and terminate GLFW
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
Camera.h
#ifndef __CAMERA_H
#define __CAMERA_H
#include <GLFW/glfw3.h> // include GLFW (which includes the OpenGL header)
#include <glm/glm.hpp> // include GLM (ideally should only use the GLM headers that are actually used)
#include <glm/gtx/transform.hpp>
#include <glm/gtx/rotate_vector.hpp>
using namespace glm; // to avoid having to use glm::
#define MOVEMENT_SENSITIVITY 0.05f // camera movement sensitivity
#define ROTATION_SENSITIVITY 0.05f // camera rotation sensitivity
class Camera {
public:
Camera();
~Camera();
void update(GLFWwindow* window);
void updateYaw(float yaw);
void updatePitch(float pitch);
void setViewMatrix(glm::vec3 position, glm::vec3 lookAt, glm::vec3 up);
void setProjectionMatrix(glm::mat4& matrix);
glm::mat4 getViewMatrix();
glm::mat4 getProjectionMatrix();
private:
float mYaw;
float mPitch;
glm::vec3 mPosition;
glm::vec3 mLookAt;
glm::vec3 mUp;
glm::mat4 mViewMatrix;
glm::mat4 mProjectionMatrix;
};
#endif
Camera.cpp
#include "Camera.h"
Camera::Camera()
{
// initialise camera member variables
mPosition = glm::vec3(0.0f, 0.0f, 1.0f);
mLookAt = glm::vec3(0.0f, 0.0f, 0.0f);
mUp = glm::vec3(0.0f, 1.0f, 0.0f);
mYaw = 0.0f;
mPitch = 0.0f;
mViewMatrix = glm::lookAt(mPosition, mLookAt, mUp);
mProjectionMatrix = glm::perspective(45.0f, 1.0f, 0.1f, 100.0f);
}
Camera::~Camera()
{}
void Camera::update(GLFWwindow* window)
{
// variables to store forward/back and strafe movement
float moveForward = 0;
float strafeRight = 0;
// update variables based on keyboard input
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
moveForward += MOVEMENT_SENSITIVITY;
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
moveForward -= MOVEMENT_SENSITIVITY;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
strafeRight -= MOVEMENT_SENSITIVITY;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
strafeRight += MOVEMENT_SENSITIVITY;
// rotate the respective unit vectors about the y-axis
glm::vec3 rotatedForwardVec = glm::rotateY(glm::vec3(0.0f, 0.0f, -1.0f), mYaw);
glm::vec3 rotatedRightVec = glm::rotateY(glm::vec3(1.0f, 0.0f, 0.0f), mYaw);
// rotate the rotated forward vector about the rotated right vector
rotatedForwardVec = glm::vec3(glm::rotate(mPitch, rotatedRightVec)*glm::vec4(rotatedForwardVec, 0.0f));
// update position, look-at and up vectors
mPosition += rotatedForwardVec * moveForward + rotatedRightVec * strafeRight;
mLookAt = mPosition + rotatedForwardVec;
mUp = glm::cross(rotatedRightVec, rotatedForwardVec); // cross product
// compute the new view matrix
mViewMatrix = glm::lookAt(mPosition, mLookAt, mUp);
}
void Camera::updateYaw(float yaw)
{
mYaw -= yaw * ROTATION_SENSITIVITY;
}
void Camera::updatePitch(float pitch)
{
mPitch -= pitch * ROTATION_SENSITIVITY;
}
void Camera::setViewMatrix(glm::vec3 position, glm::vec3 lookAt, glm::vec3 up)
{
mPosition = position;
mLookAt = lookAt;
mUp = up;
mViewMatrix = glm::lookAt(mPosition, mLookAt, mUp);
}
void Camera::setProjectionMatrix(glm::mat4& matrix)
{
mProjectionMatrix = matrix;
}
glm::mat4 Camera::getViewMatrix()
{
return mViewMatrix;
}
glm::mat4 Camera::getProjectionMatrix()
{
return mProjectionMatrix;
}
Vertex Shader
#version 330 core
// input data (different for all executions of this shader)
in vec3 aPosition;
in vec3 aColor;
// ModelViewProjection matrix
uniform mat4 uModelViewProjectionMatrix;
// output data (will be interpolated for each fragment)
out vec3 vColor;
void main()
{
// set vertex position
gl_Position = uModelViewProjectionMatrix * vec4(aPosition, 1.0);
// the color of each vertex will be interpolated
// to produce the color of each fragment
vColor = aColor;
}
Fragment Shader
#version 330 core
// interpolated values from the vertex shaders
in vec3 vColor;
// output data
out vec4 fColor;
uniform float uAlpha;
void main()
{
// set output color
fColor = vec4(vColor, uAlpha);
}
You need to control the fade in the Host Code, and pass the state along to the shader[s] at runtime. Since you're using GLFW as your window manager, that's relatively simple:
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
constexpr float factor = 30; //Higher == faster fade, lower == slower fade
float alpha = (float(std::sin(glfwGetTime() * factor) + 1) / 2; //Generates a Sine Wave in range [0, 1].
//float alpha = float(glfwGetTime() * factor - std::floor(glfwGetTime() * factor); //Sawtooth fade
glUniform1f(glGetUniformLocation(g_shaderProgramID, "uAlpha"), alpha);
update_scene(window);
render_scene();
/*Whatever else needs to happen*/
}
The other thing I'm going to recommend is that you do the blending manually in the [fragment] shader, not automatically using OpenGL blending.
#version 330 core
// interpolated values from the vertex shaders
in vec3 vColor;
// output data
out vec4 fColor;
uniform float uAlpha;
void main()
{
// set output color
vec4 fade_color = vec4(0,0,0,1); //Black fade
fColor = mix(vec4(vcolor, 1), fade_color, uAlpha);
}
The GLSL function mix will blend two vectors together using a float value to choose how much of either color to use. Using this function, you can set the "fade" color to be whatever you want.
#version 330 core
// interpolated values from the vertex shaders
in vec3 vColor;
// output data
out vec4 fColor;
uniform float uAlpha;
uniform vec4 fade_color;
void main()
{
// set output color
fColor = mix(vec4(vcolor, 1), fade_color, uAlpha);
}
If you actually intend the object to be transparent when fading (which is how the alpha parameter is usually used), then you can use the same host code I provided, with an additional function call in the setup code:
glEnable(GL_BLEND);
And then you can use your original Fragment Shader code. The only restriction is that if you do this (like any rendering involving transparency) the ordering of draw calls becomes extremely important. I would advise you look around for tutorials on how to do transparency using alpha-blending, since getting into the dirt of that is beyond the scope of this question.
I can get two images to display as expected when not applying any kind of orthographic projection. However, when trying to add an orthographic projection matrix to my verts using glm::ortho(), my sprites disappear. My code is as follows (tried removing irrelevant code):
Sprite.cpp
Sprite::Sprite()
{
vboID = 0;
}
Sprite::~Sprite()
{
//Clean up sprite buffers on GPU when done with sprite
if (vboID != 0)
glDeleteBuffers(1, &vboID);
}
void Sprite::Init(float x, float y, float width, float height, Blz::string imageFilePath)
{
this->x = x;
this->y = y;
this->width = width;
this->height = height;
if (vboID == 0)
glGenBuffers(1, &vboID);
texture = Blz::OpenGL::LoadImage(imageFilePath);
Vector3D vertexData[6]{
Vector3D {x + width, y + height, 0.0f},
Vector3D {x, y + height, 0.0f },
Vector3D {x, y, 0.0f},
Vector3D {x, y, 0.0f},
Vector3D {x + width, y, 0.0f},
Vector3D {x + width, y + height, 0.0f},
};
for (int i = 0; i < 6; ++i)
{
vertexData[i].color.r = 0;
vertexData[i].color.g = 155;
vertexData[i].color.b = 200;
};
vertexData[0].setUV(1.0f, 1.0f);
vertexData[1].setUV(0.0f, 1.0f);
vertexData[2].setUV(0.0f, 0.0f);
vertexData[3].setUV(0.0f, 0.0f);
vertexData[4].setUV(1.0f, 0.0f);
vertexData[5].setUV(1.0f, 1.0f);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Sprite::Draw()
{
glBindTexture(GL_TEXTURE_2D, texture.id);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3D), (void*)offsetof(Vector3D, position));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vector3D), (void*)offsetof(Vector3D, textCoordinate));
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
main.cpp
int main(int agrc, char** argv)
{
window.Initialize();
glm::mat4 projectionMatrix = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, -1.0f, 1.0f);
p_sprites.push_back(new Sprite());
p_sprites.back()->Init(0.0f, 0.0f, .5f, .5f, "CharImage.png");
p_sprites.push_back(new Sprite());
p_sprites.back()->Init(-0.4f, 0.0f, .5f, .5f, "CharImage.png");
GameState gamestate{ GameState::PLAY };
SDL_Event evnt;
Blz::OpenGL::ShaderProgram colorShaderProgram("Source/GameEngine/Shaders/VertexShader.glsl", "Source/GameEngine/Shaders/FragmentShader.glsl");
colorShaderProgram.Compile();
colorShaderProgram.AddAttribute("vertexPosition");
colorShaderProgram.AddAttribute("textCoord");
colorShaderProgram.Link();
colorShaderProgram.Bind();
//Get uniform from Shader and send texture info to shader
GLuint uniformLocation = colorShaderProgram.GetUniformLocation("basicTexture");
glUniform1i(uniformLocation, 0);
//Get uniform location from shader
GLuint ProjectMatrixUniformLocation = colorShaderProgram.GetUniformLocation("projectionMatrix");
while (gamestate != GameState::EXIT)
{
ProcessInput();
//Send down projectionMatrix resulting from glm::ortho() call
glUniformMatrix4fv(ProjectMatrixUniformLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (uint32 i = 0; i < p_sprites.size(); ++i)
p_sprites[i]->Draw();
SDL_GL_SwapWindow(window)
}
return 0;
}
Vertex Shader
#version 430
in vec3 vertexPosition;
in vec2 textCoord;
out vec2 TextureCoord;
uniform mat4 projectionMatrix;
void main()
{
vec4 position = vec4(vertexPosition, 1.0f);
gl_Position = projectionMatrix * position;
TextureCoord = textCoord;
};
Fragment Shader
#version 430
out vec4 daColor;
in vec2 TextureCoord;
uniform sampler2D basicTexture;
void main()
{
vec4 texel = texture(basicTexture, TextureCoord);
daColor = texel;
};