I am trying to detect silhouette edges and render some textures (varies based on the diffuse term) on these edges using OpenGL and shaders. I am rendering a quad using the geometry shader and also assign the texture coordinates here. In the fragment shader, i am trying to use the diffuse term calculated in vertex shader to render different textures based on diffTerm's value. There are two issues with my code.
1) The diffuse term should vary from (-1,1) but it seems to be stuck at 0 when i rotate the model and it reaches negative values at certain positions.
2) The textures are always black and I cant seem to find out what is causing this issue.
"MeshViewer.cpp" - The main file
Mesh* mesh;
GLuint* texID = new GLuint[5];
float rotn_x = 0.0, rotn_y = 0.0, fov;
GLuint matrixLoc1, matrixLoc2, matrixLoc3,texLoc1, texLoc2, texLoc3, texLoc4, texLoc5;
float cam_near, cam_far; //Near and far planes of the camera
const float PI = 3.14159265f;
glm::mat4 view; //View and projection matrices
void loadTextures()
{
glGenTextures(5, texID); //Generate 1 texture ID
glActiveTexture(GL_TEXTURE0); //Texture unit 0
glBindTexture(GL_TEXTURE_2D, texID[0]);
loadTGA("Pencil0.tga");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE1); //Texture unit 0
glBindTexture(GL_TEXTURE_2D, texID[1]);
loadTGA("Pencil1.tga");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE2); //Texture unit 0
glBindTexture(GL_TEXTURE_2D, texID[2]);
loadTGA("Pencil2.tga");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE3); //Texture unit 0
glBindTexture(GL_TEXTURE_2D, texID[3]);
loadTGA("Brick.tga");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE4); //Texture unit 0
glBindTexture(GL_TEXTURE_2D, texID[4]);
loadTGA("Brick.tga");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void initialise()
{
// --- Mesh object ---
GLuint lgtLoc;
mesh = new Mesh();
if(!mesh->loadMeshOFF("Camel.off")) cout << "Error reading mesh data file." << endl;
// --- Camera parameters ---
float win_width = (mesh->_xmax - mesh->_xmin) * 1.5f;
float win_height = (mesh->_ymax - mesh->_ymin) * 1.5f;
if(win_width > win_height) win_height = win_width; //Maintain aspect ratio = 1
cam_near = 2*(mesh->_zmax) - mesh->_zmin;
cam_far = 2*(mesh->_zmin) - mesh->_zmax;
float cam_posx = (mesh->_xmax + mesh->_xmin) * 0.5f;
float cam_posy = (mesh->_ymax + mesh->_ymin) * 0.5f;
float cam_posz = cam_near + win_height;
fov = 27.0f; //Approx. atan(0.5)
// --- Uniform locations ---
GLuint program = createShaderProg("MeshViewer.vert", "MeshViewer.frag", "MeshViewer.geom");
matrixLoc1 = glGetUniformLocation(program, "mvMatrix");
matrixLoc2 = glGetUniformLocation(program, "mvpMatrix");
matrixLoc3 = glGetUniformLocation(program, "norMatrix");
lgtLoc = glGetUniformLocation(program, "lightPos");
GLint lineWidth = glGetUniformLocation(program, "HalfWidth");
if (lineWidth > -1)
glUniform1f(lineWidth, 0.005f);
GLint overhangLength = glGetUniformLocation(program, "OverhangLength");
if (overhangLength > -1)
glUniform1f(overhangLength, 0.15f);
texLoc1 = glGetUniformLocation (program, "tex1");
glUniform1i(texLoc1, 0);
texLoc2 = glGetUniformLocation (program, "tex2");
glUniform1i(texLoc2, 1);
texLoc3 = glGetUniformLocation (program, "tex3");
glUniform1i(texLoc3, 2);
texLoc4 = glGetUniformLocation (program, "tex4");
glUniform1i(texLoc4, 3);
texLoc5 = glGetUniformLocation (program, "tex5");
glUniform1i(texLoc5, 4);
view = glm::lookAt(glm::vec3(cam_posx, cam_posy, cam_posz), glm::vec3(cam_posx, cam_posy, 0.0), glm::vec3(0.0, 1.0, 0.0)); //view matrix
glm::vec4 light = glm::vec4(100.0, 50.0, 100.0, 1.0); //Light's position
glm::vec4 lightEye = view*light; //Light position in eye coordinates
glUniform4fv(lgtLoc, 1, &lightEye[0]);
// --- OpenGL ---
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //Wireframe
mesh->setColor(0, 0, 1); //Mesh color = blue.
mesh->createVAO(); //Create buffer objects for the mesh
}
void display()
{
glm::mat4 proj;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 matrix = glm::mat4(1.0);
matrix = glm::rotate(matrix, rotn_x, glm::vec3(1.0, 0.0, 0.0)); //rotation about x
matrix = glm::rotate(matrix, rotn_y, glm::vec3(0.0, 1.0, 0.0)); //rotation about y
glm::mat4 prodMatrix1 = view*matrix; //Model-view matrix
proj = glm::perspective(fov, 1.0f, cam_near, cam_far); //perspective projection matrix
glm::mat4 prodMatrix2 = proj*prodMatrix1; //The model-view-projection transformation
glm::mat4 invMatrix = glm::inverse(prodMatrix1); //Inverse of model-view matrix for normal transformation
glUniformMatrix4fv(matrixLoc1, 1, GL_FALSE, &prodMatrix1[0][0]);
glUniformMatrix4fv(matrixLoc2, 1, GL_FALSE, &prodMatrix2[0][0]);
glUniformMatrix4fv(matrixLoc3, 1, GL_TRUE, &invMatrix[0][0]); //Use transpose matrix here
mesh->render();
glFlush();
}
void specialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_LEFT) rotn_y -= 5.0;
else if(key == GLUT_KEY_RIGHT) rotn_y += 5.0;
else if(key == GLUT_KEY_UP) rotn_x -= 5.0;
else if(key == GLUT_KEY_DOWN) rotn_x += 5.0;
else if(key == GLUT_KEY_PAGE_UP) fov --;
else if(key == GLUT_KEY_PAGE_DOWN) fov ++;
if(fov < 1.0) fov = 1.0;
else if(fov > 80.0) fov = 80.0;
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (600, 600);
glutInitWindowPosition (20, 10);
glutCreateWindow ("Mesh Viewer");
glutInitContextVersion (4, 2);
glutInitContextProfile ( GLUT_CORE_PROFILE );
if(glewInit() == GLEW_OK)
{
cout << "GLEW initialization successful! " << endl;
cout << " Using GLEW version " << glewGetString(GLEW_VERSION) << endl;
}
else
{
cerr << "Unable to initialize GLEW ...exiting." << endl;
exit(EXIT_FAILURE);
}
initialise ();
glutDisplayFunc(display);
glutSpecialFunc(specialKeys);
glutMainLoop();
return 0;
}
Vertex Shader:
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec3 cols;
layout (location = 3) in vec2 texC;
uniform mat4 mvMatrix;
uniform mat4 mvpMatrix;
uniform mat4 norMatrix;
uniform vec4 lightPos;
out float diffTerm;
out vec4 vColour;
out float viewTerm;
out float silhoutte;
out vec2 TexC;
void main()
{
vec4 grey = vec4(0.2, 0.2, 0.2, 1.0);
vec4 posnEye = mvMatrix * position;
vec4 normalEye = norMatrix * vec4(normal, 0);
vec4 lgtVec = normalize(lightPos - posnEye);
vec4 viewVec = normalize(vec4(-posnEye.xyz, 0));
float viewTerm = max(dot(viewVec, normalEye),0);
vec4 material = vec4(cols, 1.0);
vec4 lgtAmb = grey * material;
diffTerm = max(dot(lgtVec, normalEye), 0);
vec4 lgtDiff = material * diffTerm;
silhoutte = dot(viewVec, normalEye);
gl_Position = mvpMatrix * position;
vColour = vec4(cols, 1);
TexC = texC;
}
Geometry Shader:
#version 430 core
layout(triangles_adjacency) in;
layout(triangle_strip, max_vertices = 6) out;
in vec2 texC[];
out vec2 TexCoord;
in vec4 vColour[];
out vec4 colorv;
in float viewTerm[];
out float viewTermg;
in float diffTerm[];
out float diffTermg;
in vec2 TexC[];
out vec2 TexCg;
uniform float HalfWidth;
uniform float OverhangLength;
out float gDist;
out vec3 gSpine;
bool IsFront(vec3 A, vec3 B, vec3 C)
{
float area = (A.x * B.y - B.x * A.y) + (B.x * C.y - C.x * B.y) + (C.x * A.y - A.x * C.y);
return area > 0;
}
void EmitEdge(vec3 P0, vec3 P1)
{
vec3 E = OverhangLength * vec3(P1.xy - P0.xy, 0);
vec2 V = normalize(E.xy);
vec3 N = vec3(-V.y, V.x, 0) * 0.005;
vec3 S = -N;
float D = HalfWidth;
gSpine = P0;
gl_Position = vec4(P0 + S - E, 1); gDist = +D; TexCoord=vec2(0.0,0.0); colorv = vColour[0]; EmitVertex();
gl_Position = vec4(P0 + N - E, 1); gDist = -D; TexCoord=vec2(1.0,0.0); colorv = vColour[1]; EmitVertex();
gSpine = P1;
gl_Position = vec4(P1 + S + E, 1); gDist = +D; TexCoord=vec2(1.0,1.0); colorv = vColour[0]; EmitVertex();
gl_Position = vec4(P1 + N + E, 1); gDist = -D; ; TexCoord=vec2(0.0,1.0); EmitVertex();
EndPrimitive();
}
void main()
{
vec3 v0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
vec3 v1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;
vec3 v2 = gl_in[2].gl_Position.xyz / gl_in[2].gl_Position.w;
vec3 v3 = gl_in[3].gl_Position.xyz / gl_in[3].gl_Position.w;
vec3 v4 = gl_in[4].gl_Position.xyz / gl_in[4].gl_Position.w;
vec3 v5 = gl_in[5].gl_Position.xyz / gl_in[5].gl_Position.w;
if (IsFront(v0, v2, v4)) {
if (!IsFront(v0, v1, v2)) EmitEdge(v0, v2);
//if (!IsFront(v2, v3, v4)) EmitEdge(v2, v4);
//if (!IsFront(v0, v4, v5)) EmitEdge(v4, v0);
}
}
Fragment Shader:
#version 330
in vec4 vColourg;
in float diffTermg;
in float silhoutte;
in vec2 TexCg;
in vec2 TexCoord;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D tex3;
uniform sampler2D tex4;
uniform sampler2D tex5;
void main()
{
vec4 texColor1 = texture(tex1, TexCoord);
vec4 texColor2 = texture(tex2, TexCoord);
vec4 texColor3 = texture(tex3, TexCoord);
vec4 texColor4 = texture(tex4, TexCoord);
vec4 texColor5 = texture(tex5, TexCoord);
vec4 blue = vec4(0.0,0.0,1.0,0.0);
vec4 red = vec4(1.0,0.0,0.0,0.0);
vec4 yellow = vec4(1.0,1.0,0.0,0.0);
if (diffTermg<0)
{
gl_FragColor = blue;
}
else if (diffTermg ==0)
{
gl_FragColor = texColor5;
}
else if (diffTermg > 0 && diffTermg < 0.2)
gl_FragColor = yellow;
else if (diffTermg > 100)
gl_FragColor = blue;
}
EDIT:
Shader.h
GLuint loadShader(GLenum shaderType, string filename)
{
ifstream shaderFile(filename.c_str());
if(!shaderFile.good()) cout << "Error opening shader file." << endl;
stringstream shaderData;
shaderData << shaderFile.rdbuf();
shaderFile.close();
string shaderStr = shaderData.str();
const char* shaderTxt = shaderStr.c_str();
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &shaderTxt, NULL);
glCompileShader(shader);
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint infoLogLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog);
const char *strShaderType = NULL;
cerr << "Compile failure in shader: " << strInfoLog << endl;
delete[] strInfoLog;
}
return shader;
}
GLuint createShaderProg(string vertShader, string fragShader, string geomShader)
{
GLuint shaderv = loadShader(GL_VERTEX_SHADER, vertShader);
GLuint shaderf = loadShader(GL_FRAGMENT_SHADER, fragShader);
GLuint shaderg = loadShader(GL_GEOMETRY_SHADER, geomShader);
GLuint program = glCreateProgram();
glAttachShader(program, shaderv);
glAttachShader(program, shaderf);
glAttachShader(program, shaderg);
glLinkProgram(program);
GLint status;
glGetProgramiv (program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "Linker failure: %s\n", strInfoLog);
delete[] strInfoLog;
program = 0;
}
glUseProgram(program);
return program;
}
loadTGA.h
void loadTGA(string filename)
{
char id, cmap, imgtype, bpp, c_garb;
char* imageData, temp;
short int s_garb, wid, hgt;
int nbytes, size, indx;
ifstream file( filename.c_str(), ios::in | ios::binary);
if(!file)
{
cout << "*** Error opening image file: " << filename.c_str() << endl;
exit(1);
}
file.read (&id, 1);
file.read (&cmap, 1);
file.read (&imgtype, 1);
if(imgtype != 2 && imgtype != 3 ) //2= colour (uncompressed), 3 = greyscale (uncompressed)
{
cout << "*** Incompatible image type: " << (int)imgtype << endl;
exit(1);
}
//Color map specification
file.read ((char*)&s_garb, 2);
file.read ((char*)&s_garb, 2);
file.read (&c_garb, 1);
//Image specification
file.read ((char*)&s_garb, 2); //x origin
file.read ((char*)&s_garb, 2); //y origin
file.read ((char*)&wid, 2); //image width
file.read ((char*)&hgt, 2); //image height
file.read (&bpp, 1); //bits per pixel
file.read (&c_garb, 1); //img descriptor
nbytes = bpp / 8; //No. of bytes per pixels
size = wid * hgt * nbytes; //Total number of bytes to be read
imageData = new char[size];
file.read(imageData, size);
//cout << ">>>" << nbytes << " " << wid << " " << hgt << endl;
if(nbytes > 2) //swap R and B
{
for(int i = 0; i < wid*hgt; i++)
{
indx = i*nbytes;
temp = imageData[indx];
imageData[indx] = imageData[indx+2];
imageData[indx+2] = temp;
}
}
switch (nbytes)
{
case 1:
glTexImage2D(GL_TEXTURE_2D, 0, 1, wid, hgt, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData);
break;
case 3:
glTexImage2D(GL_TEXTURE_2D, 0, 3, wid, hgt, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);
break;
case 4:
glTexImage2D(GL_TEXTURE_2D, 0, 4, wid, hgt, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
break;
}
delete imageData;
}
You are requesting a core profile context:
glutInitContextProfile(GLUT_CORE_PROFILE);
But your glTexImage2D() calls are not compatible with the core profile:
glTexImage2D(GL_TEXTURE_2D, 0, 1, wid, hgt, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imageData);
glTexImage2D(GL_TEXTURE_2D, 0, 3, wid, hgt, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);
glTexImage2D(GL_TEXTURE_2D, 0, 4, wid, hgt, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
Using the number of components as the internal format (argument 3) is legacy from OpenGL 1.0, and was finally eliminated when the core profile was introduced. GL_LUMINANCE is also gone. The modern (core profile) equivalent of those calls is:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, wid, hgt, 0, GL_RED, GL_UNSIGNED_BYTE, imageData);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, wid, hgt, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, wid, hgt, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
The post contains a lot of code, and I did not study it in detail to see if there are other problems. I strongly recommend the use of glGetError(), which would have reported these invalid arguments, and should also be helpful to check if there are any additional problems.
Related
So I'm drawing some lines by just using a fragment shader. The vertex it's just an empty quad.
The issue I have is that when I zoom out the camera and the lines get smaller they sometimes appear and disappear and I don't understand why.
This is how it looks without zooming
and this is how it looks when the camera is far away from them
The more far away I get from them the more artefacts appear.
This is how my vertex shader looks
#version 330 core
layout(location = 0) in vec2 _position;
out vec2 position;
uniform mat4 uCameraView;
void main() {
gl_Position = uCameraView * vec4(_position.x, _position.y, 0.0f, 1.0f);
position = _position;
}
And this is the fragment
#version 330 core
in vec2 position;
uniform vec4 uGridColor;
uniform float uTileSize;
uniform float uGridBorderSize;
out vec4 fragColor;
void main() {
vec2 uv = mod(position, uTileSize);
vec2 border = mod(uv + (uGridBorderSize / 2.0), uTileSize);
border -= mod(uv - (uGridBorderSize / 2.0), uTileSize);
if (length(border) > uTileSize - uGridBorderSize) {
fragColor = uGridColor;
} else {
fragColor = vec4(0.0);
}
}
Why it's this happening? maybe is something related to antialiasing? My OpenGL setup it's just the default one.
Your current code is making a binary decision "yes line" / "no line". However beyond a certain point (line width < pixel width) you're effectivly dealing with spatial frequencies above the Nyquist limit.
Instead of using a binary "yes"/"no" you need to calculate the pixel coverage, i.e. how much line is inside a pixel. For that you'd normally use a unsigned distance function (UDF). Here's some GLSL code for UDF lines in pixel space (you can also use them in normalized space, but then you'll have to adjust the smoothstep parameters). Try this on https://shadertoy.com
float lsd(vec2 a, vec2 b, vec2 p, float w){
w *= 0.5;
vec2 n = normalize(b-a);
float l = length(b-a);
float t = dot((p-a),n);
float d = length((a-p)+t*n);
float e = min(length(p-a)+w, length(p-b)+w);
return (t > w && t < l-w) ? d : e;
}
float line(vec2 a, vec2 b, float width, vec2 fragcoord){
return max(0., 1.-smoothstep(0., 1., lsd(a, b, fragcoord, width)-0.5*width));
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float l =
line(vec2(8.,8.), vec2(128.,33.), 1., fragCoord)
+ line(vec2(33.,220.), vec2(260.,20.), 4., fragCoord);
fragColor = vec4(l,l,l,1.0);
}
It is hard to tell based on your shaders alone, so here is an example of how to zoom and pan around a line grid. It uses a projection matrix to zoom which looks slightly different to how you implemented the zoom, but the important bit is that it doesn't have any artefacts of lines thinning when zooming/panning.
Here is a demonstration, hopefully the GIF shows it, but the grid lines are constant thickness as you zoom in and out:
#include <iostream>
#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
using std::vector;
using glm::mat4;
using glm::vec3;
using glm::vec4;
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;
vec3 rayCast(double xpos, double ypos, mat4 projection, mat4 view) {
// converts a position from the 2d xpos, ypos to a normalized 3d direction
float x = (2.0f * xpos) / SCR_WIDTH - 1.0f;
float y = 1.0f - (2.0f * ypos) / SCR_HEIGHT;
float z = 1.0f;
vec3 ray_nds = vec3(x, y, z);
vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0f, 1.0f);
// eye space to clip we would multiply by projection so
// clip space to eye space is the inverse projection
vec4 ray_eye = inverse(projection) * ray_clip;
// convert point to forwards
ray_eye = vec4(ray_eye.x, ray_eye.y, -1.0f, 0.0f);
// world space to eye space is usually multiply by view so
// eye space to world space is inverse view
vec4 inv_ray_wor = (inverse(view) * ray_eye);
vec3 ray_wor = vec3(inv_ray_wor.x, inv_ray_wor.y, inv_ray_wor.z);
ray_wor = normalize(ray_wor);
return ray_wor;
}
class Line {
int shaderProgram;
unsigned int VBO, VAO;
vector<float> vertices;
vec3 startPoint;
vec3 endPoint;
mat4 MVP;
vec3 lineColor;
public:
Line(vec3 start, vec3 end) {
startPoint = start;
endPoint = end;
lineColor = vec3(1,1,1);
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform mat4 MVP;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec3 color;\n"
"void main()\n"
"{\n"
" FragColor = vec4(color, 1.0f);\n"
"}\n\0";
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// link shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
vertices = {
start.x, start.y, start.z,
end.x, end.y, end.z,
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
int setMVP(mat4 mvp) {
MVP = mvp;
}
int setColor(vec3 color) {
lineColor = color;
}
int draw() {
glUseProgram(shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]);
glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, 2);
return 0;
}
~Line() {
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
}
};
vec3 cameraPos = glm::vec3(0.0f, 0.0f, 15.0f);
vec3 cameraFront = glm::vec3(0,0,-1);
mat4 model = mat4(1.0);
glm::mat4 view;
glm::mat4 projection;
float scrollSpeed = 2.0f;
float fov = 45.0f;
int main()
{
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);
#endif
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "grid", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Line x(vec3(0,0,0), vec3(1,0,0));
x.setColor(vec3(1,0,0));
Line y(vec3(0,0,0), vec3(0,1,0));
y.setColor(vec3(0,1,0));
std::vector<Line*> grid = {};
for (int i = -5; i < 6; i++) {
grid.push_back(new Line(vec3(-5, i, 0), vec3(5,i, 0)));
}
for (int j = -5; j < 6; j++) {
grid.push_back(new Line(vec3(j, -5, 0), vec3(j,5, 0)));
};
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
view = glm::lookAt(cameraPos, cameraPos + cameraFront, vec3(0,1,0));
projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
for (int i = 0; i < grid.size(); i++) {
grid[i]->setMVP(projection * view * model);
grid[i]->draw();
}
glfwSwapBuffers(window);
glfwPollEvents();
}
for (int i = 0; i < grid.size(); i++) {
delete grid.at(i);
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE);
if (state == GLFW_PRESS)
{
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
cameraPos -= scrollSpeed * glm::vec3(xoffset/(float)SCR_WIDTH, yoffset/(float)SCR_WIDTH, 0);
} else {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
firstMouse = true;
}
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
cameraPos += (float)yoffset * rayCast(lastX, lastY, projection, view);
}
Hey,
For some reason the bloom is appearing as yellow, I am not entirely sure why the bloom is yellow, its not a blending problem and the code seems correct.
I have researched on this problem and knowone appears to have the same problem, I'm thinking it may be todo with the fbo texture filtering but I'm not to sure. I need some help.
Here is the code..
Final Gather code
#version 330 core
out vec4 FragColor;
in vec2 _texcoord;
in float color;
uniform sampler2D scene;
uniform sampler2D bloom;
uniform float bloom_intensity;
void main()
{
const float gamma = 1.3;
float exposure = 1.0;
vec3 hdrColor = texture(scene, _texcoord).rgb;
vec3 bloomColor = texture(bloom, _texcoord).rgb;
hdrColor += bloomColor; /// bloom_intensity;
// tone mapping
vec3 result = vec3(1.0) - exp(-hdrColor * exposure);
// also gamma correct while we're at it
result = pow(result, vec3(1.0 / gamma));
FragColor = vec4(result, 1.0);
}
Bloom in light fragment shader
// final lighting calculations
vec3 result = calc_directional_light(Diffuse, Specular, Metalness, AO);
for (int i = 0; i < 5; i++)
{
result += calc_point_light(pointLights[i], Diffuse, Specular);
result += calc_spot_light(spotLights[i], Diffuse, Specular);
}
float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 0.7)
BrightColor = vec4(result, 1.0);
else
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
FragColor = vec4(result, 1.0);
Bloom setup
inline void Create(std::vector<GLuint> shader_programs, size_t width, size_t height, float intensity)
{
_shader_programs = shader_programs;
_intensity = intensity;
_fbo = new Fbo(width, height, { new FboAttachment(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_COLOR_ATTACHMENT0),
new FboAttachment(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_COLOR_ATTACHMENT1) }, true);
_h_blur = new Fbo(width, height, { new FboAttachment(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_COLOR_ATTACHMENT0) }, false);
_v_blur = new Fbo(width, height, { new FboAttachment(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_COLOR_ATTACHMENT0) }, false);
_u_texturemap[0] = glGetUniformLocation(_shader_programs[0], "textureMap");
_u_texturemap[1] = glGetUniformLocation(_shader_programs[1], "textureMap");
_u_blurres[0] = glGetUniformLocation(_shader_programs[0], "blur_resolution");
_u_blurres[1] = glGetUniformLocation(_shader_programs[0], "blur_resolution");
}
Bloom Render
inline virtual void Render()
{
_h_blur->Bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(_shader_programs[0]);
glUniform1i(_u_texturemap[0], 0);
glUniform1f(_u_blurres[0], 128.0f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _fbo->GetAttachments()[1]->_texture);
_screen_rect->Render(1);
_h_blur->Unbind();
_v_blur->Bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(_shader_programs[1]);
glUniform1i(_u_texturemap[1], 0);
glUniform1f(_u_blurres[1], 128.0f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _h_blur->GetAttachments()[0]->_texture);
_screen_rect->Render(1);
_v_blur->Unbind();
}
I have a simple vertex shader
static const char *vertexShader=
"attribute vec4 vPosition; \n"
"void main(){\n"
"gl_Position = vPosition;\n"
"}";
Also I have a shader which creates a "Billboard" effect on an image.
static const char *fragmentShader=
"uniform float grid;\n"
"uniform float dividerValue;\n"
"uniform float step_x;\n"
"uniform float step_y;\n"
"uniform sampler2D source;\n"
"uniform lowp float qt_Opacity;\n"
"uniform vec2 qt_TexCoord0;\n"
"void main(){\n"
"vec2 uv = qt_TexCoord0.xy;\n"
"float offx = floor(uv.x / (grid * step_x));\n"
"float offy = floor(uv.y / (grid * step_y));\n"
"vec3 res = texture2D(source, vec2(offx * grid * step_x , offy * grid * step_y)).rgb;\n"
"vec2 prc = fract(uv / vec2(grid * step_x, grid * step_y));\n"
"vec2 pw = pow(abs(prc - 0.5), vec2(2.0));\n"
"float rs = pow(0.45, 2.0);\n"
"float gr = smoothstep(rs - 0.1, rs + 0.1, pw.x + pw.y);\n"
"float y = (res.r + res.g + res.b) / 3.0;\n"
"vec3 ra = res / y;\n"
"float ls = 0.3;\n"
"float lb = ceil(y / ls);\n"
"float lf = ls * lb + 0.3;\n"
"res = lf * res;\n"
"vec3 col = mix(res, vec3(0.1, 0.1, 0.1), gr);\n"
"if (uv.x < dividerValue)\n"
"gl_FragColor = qt_Opacity * vec4(col, 1.0);\n"
"else\n"
"gl_FragColor = qt_Opacity * texture2D(source, uv);\n"
"}";
What I'd like to do is to use this shader to apply this effect on an image in QtOpenGlWidget. But I dont get how to set my image as a texture and pass it to a shader and then to return it modified with a shader effect. What is I want to achieve is: https://imgur.com/a/NSY0u But my shader doesn't affect image https://imgur.com/a/dgSfq . My GLWidget class:
GLWidget::GLWidget(Helper *helper, QWidget *parent)
: QOpenGLWidget(parent), helper(helper)
{
QImage img("E:\\pictures\\0151.jpg");
image = img;
image = image.convertToFormat(QImage::Format_RGBA8888);
setFixedSize(512, 512);
setAutoFillBackground(false);
targetWidth = width();
targetHeight = height();
qDebug() << "targetWidth="<<targetWidth;
qDebug() << "targetHeight ="<<targetHeight ;
//this values i am trying to pass to my fragment shader
grid = 5.0;//grid on image
dividerValue = 0.5;
step_x = 0.0015625;
step_y = height() ? (2.5 * step_x * targetWidth / targetHeight) : 0.0;
}
void GLWidget::initializeGL()
{
initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader);//?
m_program->link();
m_program->bind();
m_program->release();
}
//we can use paintEvent to display our image with opengl
void GLWidget::paintEvent(QPaintEvent *event)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_program->bind();
QPainter painter;
painter.begin(this);
painter.drawImage(0,0,image);
QOpenGLTexture texture(image); //I dont know how to setUniformValue(m_program->uniformLocation("source"),texture) to my shader
GLuint m_texture;
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
glGenerateMipmap(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
//open an image
m_program->setUniformValue("grid", grid);
m_program->setUniformValue("dividerValue",dividerValue);
m_program->setUniformValue("step_x", step_x);
m_program->setUniformValue("step_y", step_y);
m_program->setUniformValue(m_program->uniformLocation("source"),m_texture);
painter.end();
m_program->release();
}
When you bind a texture, it is bound to the currently active texture image unit (See Binding textures to samplers).
The active texture unit can be selected by glActiveTexture. The default texture unit is GL_TEXTURE0.
The value which you have to provide to the texture sampler uniform is not the name of a texture, it is the texture unit (number), where the texture is bound to:
int texture_unit = 0; // <----- e.g. texture unit 0
glActiveTexture( GL_TEXTURE0 + texture_unit );
glBindTexture( GL_TEXTURE_2D, m_texture );
.....
m_program->bind();
m_program->setUniformValue( "source", texture_unit ); // <----- texture unit
For a QOpenGLTexture object the texture unit can be selected by QOpenGLTexture::bind:
int texture_unit = 1; // <----- e.g. texture unit 1
QOpenGLTexture texture(image);
texture.bind( texture_unit );
m_program->bind();
m_program->setUniformValue( "source", texture_unit ); // <----- texture unit
Note, since OpenGL 4.2 the texture unit can be initialized within the shader, by a Binding point:
layout(binding = 0) uniform sampler2D source; // binding = 0 -> texture unit 0
Extension to the answer:
The following code will draw the a image to the entire widget with processing it by your shader. Finally the rendered image is read back from the GPU:
class GLWidget : public QOpenGLWidget
{
.....
QOpenGLShaderProgram * m_program = nullptr;
QOpenGLTexture * m_texture = nullptr;
};
void GLWidget::initializeGL()
{
initializeOpenGLFunctions();
QImage img("E:\\pictures\\0151.jpg");
m_texture = new QOpenGLTexture( img );
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader);
m_program->bindAttributeLocation("vPosition", 0);
m_program->link();
}
void GLWidget::paintEvent(QPaintEvent *event)
{
// celar the framebuffer
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// bind the texture
uint texture_unit = 1;
m_texture->bind( texture_unit );
// activate the shader
m_program->bind();
m_program->setUniformValue( "source", texture_unit );
m_program->setUniformValue( "grid", grid );
m_program->setUniformValue( "dividerValue", dividerValue );
m_program->setUniformValue( "step_x", step_x );
m_program->setUniformValue( "step_y", step_y );
// draw a quad over the entire widget
GLfloat vertices[]{ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
m_program->enableAttributeArray(0);
m_program->setAttributeArray(0, GL_FLOAT, vertices, 2);
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
m_program->disableAttributeArray(0);
// release the shader
m_program->release();
// read the rendered image
int width = ....;
int height = ....;
unsigned char *pixels = new unsigned char[width * height * 4];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
QImage *img = new QImage( pixels, width, height, QImage::Format_RGBA8888 );
.....
}
Further you have to do some changes to the vertex shader and fragment shader. In the vertex shader you have to pass the vertex position to the fragment shader:
attribute vec2 vPosition;
varying vec2 v_pos;
void main()
{
v_pos = vPosition.xy;
gl_Position = vec4(vPosition.xy, 0.0, 1.0);
}
In the fragment shader you have to calcualte the texute coordinate fromt the vertex position:
varying vec2 v_pos;
void main()
{
vec2 uv = v_pos.xy * 0.5 + 0.5;
....
}
See also glwidget.cpp Example File.
Is there a reason as to why my mesh isn't drawing correctly? I loaded an .OBJ model from a file and tried to draw it but it always comes out deformed. Any help would be appreciated.
Here is my code:
void Renderer::draw_model(const std::vector<Vector3>& vertex_array, const std::vector<unsigned int>& element_array, const std::vector<Vector2>& uv_array, const std::vector<Vector3>& normal_array, double x, double y, double z, double rx, double ry, double rz, double sx, double sy, double sz, const std::vector<Texture *>& texture_array, double red, double green, double blue, double alpha)
{
#ifdef DOKUN_OPENGL // OpenGL is defined
if(get_current_rendering_API() != "OpenGL") // but it is not set as the current rendering API
return;
#ifdef __windows__
if(!wglGetCurrentContext())
{
Logger("Rendering Failed : No OpenGL Context found");
return;
}
#endif
#ifdef __gnu_linux__
#ifdef DOKUN_X11
if(!glXGetCurrentContext())
{
Logger("Rendering failed : No OpenGL Context found");
return;
}
#endif
#endif
if(vertex_array.empty())
{
Logger("Rendering failed : Vertex list is empty!");
return;
}
if(element_array.empty())
{
Logger("Rendering failed : Element list is empty!");
return;
}
const GLchar * vertex_source[] =
{
"#version 400\n"
"layout(location = 0) in vec3 position;\n"
"layout(location = 1) in vec2 tex_coord;\n"
"layout(location = 2) in vec3 normal;\n"
" \n"
" \n"
" \n"
"uniform mat4 proj; // zooming \n"
"uniform mat4 view; // camera pos \n"
"uniform mat4 model; // object \n"
" \n"
" \n"
" \n"
" \n"
"out vec3 Normal ;\n"
"out vec2 Texcoord;\n"
"\n"
"out vec3 frag_position;\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"void main(void) \n"
"{ \n"
"//frag_position = vec3(model * vec4(position, 1));\n"
"Texcoord = tex_coord;\n"
"Normal = mat3(transpose(inverse(model))) * normal;//normal;\n"
"gl_Position = proj * view * model * vec4(position, 1.0);\n"
"} \n"
};
const GLchar * fragment_source[] = // holds all color
{
"#version 400 \n"
" \n"
"out vec4 out_color; \n"
"uniform vec4 color;\n"
"\n"
"\n"
"in vec3 Normal ;\n"
"in vec2 Texcoord;\n"
"\n"
"in vec3 frag_position;\n"
"\n"
"\n"
"\n"
"uniform vec3 light_color ;\n"
"uniform vec3 light_position;\n"
"\n"
"\n"
"\n"
"void main(void) \n"
"{ \n"
"\n"
"vec3 light_direction = normalize(light_position - frag_position);\n"
"vec3 ambient = 0.1f * light_color;\n"
"vec3 diffuse = max(dot(normalize(Normal), light_direction), 0.0) * light_color;\n"
"\n"
"\n"
"out_color = vec4((ambient + diffuse) * color.xyz, 1.0); \n"
"} \n"
};
glEnable(GL_DEPTH_TEST); // enable depth
glEnable(GL_CULL_FACE );
// Set mode
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // GL_POINT, GL_LINE, GL_FILL,
//--------------
// shaders
Shader vertex_shader ;
Shader fragment_shader;
vertex_shader.create(DOKUN_SHADER_VERTEX);
vertex_shader.set_source(vertex_source);
vertex_shader.compile();
fragment_shader.create(DOKUN_SHADER_FRAGMENT);
fragment_shader.set_source(fragment_source);
fragment_shader.compile();
// program
Program program;
program.create();
program.attach(vertex_shader );
program.attach(fragment_shader);
program.link();
// delete shaders after linking them to the program
vertex_shader.destroy ();
fragment_shader.destroy();
// use program
program.use ();
//---------------
// set uniforms
////////////////////////
// light
if(program.get_uniform("light_color" ) != -1) program.set_uniform("light_color", 1.0f, 1.0f, 1.0f);//, (alpha / 255));
if(program.get_uniform("light_position" ) != -1) program.set_uniform("light_position", 1,1,1);//static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));//, (alpha / 255));
////////////////////////
// camera
glm::vec3 eye = glm::vec3(camera->get_position().x, camera->get_position().y, camera->get_position().z);
glm::vec3 center = glm::vec3(camera->get_position().x + camera->get_view().x, camera->get_position().y + camera->get_view().y, camera->get_position().z + camera->get_view().z);
glm::vec3 up = glm::vec3(camera->get_up().x , camera->get_up().y , camera->get_up().z);
////////////////////////
#ifdef use_glm
glm::mat4 model;
model = glm::scale(model, glm::vec3(static_cast<float>(sx),
static_cast<float>(sy),
static_cast<float>(sz)));
model = glm::rotate(model, static_cast<float>(rx), glm::vec3(1, 0, 0));
model = glm::rotate(model, static_cast<float>(ry), glm::vec3(0, 1, 0));
model = glm::rotate(model, static_cast<float>(rz), glm::vec3(0, 0, 1));
model = glm::translate(model, glm::vec3(static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)));
glm::mat4 view = glm::lookAt(eye, center, up);
glm::mat4 proj = glm::perspective(67.5f, static_cast<float>(800 / 600), 1.0f, 1024.0f); // average fov = 67.5 45 + 90 = 135 / 2 = 67.5 | znear=1 zfar=1024
glm::mat4 modelview = model * view;
glUniformMatrix4fv(glGetUniformLocation((GLuint)program.get_id(), "model"), 1, false, glm::value_ptr(model) ); // object
glUniformMatrix4fv(glGetUniformLocation((GLuint)program.get_id(), "view"), 1, false, glm::value_ptr(view) ); // camera
glUniformMatrix4fv(glGetUniformLocation((GLuint)program.get_id(), "proj"), 1, false, glm::value_ptr(proj) ); // zoom
#endif
////////////////////////
if(program.get_uniform("color") != -1) program.set_uniform("color", (red / 255), (green / 255), (blue / 255), (alpha / 255));
//program.get_default()->set_uniform("base", static_cast<int>(0)); // bind to texture unit 0
////////////////////////
// texture data
/*
GLuint * texture = new GLuint[8]; // 256 would be ideal
for(unsigned int i = 0; i < texture_array.size(); i++) // for each texture
{
std::cout << "GENERATING TEXTURES...." << std::endl;
glGenTextures(8, texture); // generate 256 textures
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture( GL_TEXTURE_2D, (GLuint)texture[i] );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texture_array[i]->get_width(), texture_array[i]->get_height(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, texture_array[i]->get_data() );
glGenerateMipmap(GL_TEXTURE_2D);
}
*/
///////////////////////
///////////////////////
// vertex array obj
GLuint vertex_array_obj;
glGenVertexArrays(1, &vertex_array_obj);
// vertex buffer obj - for drawing
glBindVertexArray(vertex_array_obj); // bind vertex array obj to vertex attrib ptr
GLuint vertex_buffer_obj;
glGenBuffers(1, & vertex_buffer_obj);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_obj);
glBufferData(GL_ARRAY_BUFFER, vertex_array.size() , &vertex_array[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 0, static_cast<void*>(0));
glBindVertexArray(0); // unbind
// tex_coord buffer obj - for texture mapping
GLuint uv_buffer_obj;
if(!uv_array.empty())
{
glBindVertexArray(vertex_array_obj); // bind
glGenBuffers(1, & uv_buffer_obj);
glBindBuffer(GL_ARRAY_BUFFER, uv_buffer_obj);
glBufferData(GL_ARRAY_BUFFER, uv_array.size() , &uv_array[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, 0, static_cast<void*>(0));
glBindVertexArray(0); // unbind
}
// normal buffer - for lighting
GLuint normal_buffer_obj;
if(!normal_array.empty())
{
glBindVertexArray(vertex_array_obj); // bind
glGenBuffers(1, & normal_buffer_obj);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer_obj);
glBufferData(GL_ARRAY_BUFFER, normal_array.size(), &normal_array[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_DOUBLE, GL_FALSE, 0, static_cast<void*>(0));
glBindVertexArray(0); // unbind
}
// element buffer - specifies order in which vertices are to be drawn
glBindVertexArray(vertex_array_obj); // bind
GLuint index_buffer_obj;
glGenBuffers(1, & index_buffer_obj);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_obj);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, element_array.size(), &element_array[0], GL_STATIC_DRAW);
glBindVertexArray(0);
// Draw the model !
glBindVertexArray(vertex_array_obj); // bind
glDrawElements(GL_TRIANGLES, element_array.size(), GL_UNSIGNED_INT, 0);//glDrawElements(GL_TRIANGLES, element_array.size(), GL_UNSIGNED_INT, &element_array[0]); // elements - order in which vertices are to be drawn
glBindVertexArray(0); // unbind
////////////////////////
// cleanup
// textures
//if(texture_array.size() > 0)
// glDeleteTextures(8, texture);
// attributes
glDisableVertexAttribArray(0); // drawing
glDisableVertexAttribArray(1); // texture mapping
glDisableVertexAttribArray(2); // lighting
// buffers
glDeleteBuffers(1, & vertex_buffer_obj );
glDeleteBuffers(1, & uv_buffer_obj );
glDeleteBuffers(1, & normal_buffer_obj );
glDeleteBuffers(1, & index_buffer_obj );
// arrays
glDeleteVertexArrays(1, & vertex_array_obj);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// program
program.disable();
#endif
}
And this is the result of the following code:
The size parameter in all glBufferData calls is wrong. This function expects the amount of data in byte, but are supplied, for example, in this line:
glBufferData(GL_ARRAY_BUFFER, vertex_array.size() , &vertex_array[0], GL_STATIC_DRAW);
as the number of elements. What you actually need is something like this:
glBufferData(GL_ARRAY_BUFFER, vertex_array.size() * sizeof(Vector3), &vertex_array[0], GL_STATIC_DRAW);
(assuming that Vector3 contains exactly 3 doubles).
As a side note: It is rather uncommon to supply vertex information in double precision. So if there is no special reason for doing this, I would recommend using float instead.
As the title says, I've been having troubles with normal mapping. First, a bit of background...
I was following TheCPlusPlusGuy's tutorials, using LWJGL, and ran into errors. I put a post on here, but it got no replies. I then moved into C++, and, once again, got back into normal mapping, and found more issues. The problem is, that when I move in the X-axis, the light appears to move as though I'm moving in the Y-axis. I've rendered the normal map texture, as a regular texture, and it works normally, so the bug therefore should be in the shaders.
I made a video of the bug, on YouTube here.
Here's the code:
vertex.vert:
#version 120
precision highp float;
varying vec2 texCoord;
varying vec3 position;
varying vec3 normal;
uniform vec3 lightPos;
varying vec3 tangentSurface2light;
varying vec3 tangentSurface2view;
mat3 getTangentMatrix(){
vec3 tangent;
vec3 v1 = cross(gl_Normal, vec3(0.0, 1.0, 0.0));
vec3 v2 = cross(gl_Normal, vec3(0.0, 0.0, 1.0));
if(length(v1) > length(v2)){
tangent = v1;
}else{
tangent = v2;
}
vec3 n = normalize(gl_NormalMatrix * gl_Normal);
vec3 t = normalize(gl_NormalMatrix * tangent);
vec3 b = cross(n, t);
mat3 mat = mat3(t.x, b.x, n.x,
t.y, b.y, n.y,
t.z, b.x, n.z);
return transpose(mat);
}
void main(){
mat3 tangent = getTangentMatrix();
texCoord = gl_MultiTexCoord0.xy;
gl_Position = ftransform();
position = vec3(gl_ModelViewMatrix * gl_Vertex);
normal = gl_NormalMatrix * gl_Normal;
vec3 vector = normalize(lightPos - position);
tangentSurface2light = tangent * vector;
vector = normalize(-position);
tangentSurface2view = tangent * vector;
}
fragment.frag:
#version 120
precision highp float;
varying vec2 texCoord;
uniform sampler2D texture;
uniform sampler2D normalMap;
varying vec3 position;
varying vec3 normal;
varying vec3 tangentSurface2light;
varying vec3 tangentSurface2view;
uniform vec3 lightPos;
uniform vec3 mambient;
uniform vec3 mdiffuse;
uniform vec3 mspecular;
uniform float shininess;
uniform vec3 lambient;
uniform vec3 ldiffuse;
uniform vec3 lspecular;
void main(){
float dist = length(position - lightPos);
float att = 1.0/(0.005*dist*dist);
vec3 tdiffuse = vec3(texture2D(texture, texCoord)) * mdiffuse;
vec3 ambient = mambient * lambient;
vec3 surf2light = normalize(tangentSurface2light);
vec3 norm = normalize(texture2D(normalMap, texCoord).xyz * 2.0 - 1.0);
float dcont = max(0.0, dot(norm, surf2light));
vec3 diffuse = dcont * tdiffuse * ldiffuse;
vec3 surf2view = normalize(tangentSurface2view);
vec3 reflection = reflect(-surf2light, norm);
float scont = pow(max(0.0, dot(surf2view, reflection)), shininess);
scont = max(0.0, dot(surf2view, reflection));
vec3 specular = scont * mspecular * lspecular;
gl_FragColor=vec4((ambient + diffuse + specular )* att, 1.0);
}
And just in case, main.cpp (Note functions.h is just the first person camera):
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include "GLee.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL_opengl.h>
#include <SDL/SDL_mouse.h>
#include <math.h>
#include <SDL/SDL_opengl.h>
#include <vector>
#include <string>
#include <algorithm>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include "functions.h"
//#include <SDL/SDL_video.h>
#define PI 3.14159265358979323846264338327
using namespace std;
unsigned int frame, texture, normalMap;
long double lPos = 0;
bool mousein = false;
#include "GLee.h"
void loadFile(const char* fn, string& str){
ifstream in(fn);
if(!in.is_open()){
cout<<"The file "<<fn<<"could not be opened!"<<endl;
return;
}
char tmp[300];
while(!in.eof()){
in.getline(tmp, 300);
str += tmp;
str += '\n';
}
}
unsigned int loadShader(string& source, unsigned int mode){
unsigned int id;
id = glCreateShader(mode);
const char* csource = source.c_str();
glShaderSource(id, 1, &csource, NULL);
glCompileShader(id);
char error[1000];
glGetShaderInfoLog(id, 1000, NULL, error);
cout<<"Compile Status: \n"<<error<<endl;
return id;
}
unsigned int vs, fs, program;
void initShader(const char* vname, const char* fname, unsigned int& program){//20:30 into vid...
string source;
loadFile(vname, source);
vs = loadShader(source, GL_VERTEX_SHADER);
source = "";
loadFile(fname, source);
fs = loadShader(source, GL_FRAGMENT_SHADER);
program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glUseProgram(program);
}
void clean(const int& program){
glDetachShader(program, vs);
glDetachShader(program, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glDeleteProgram(program);
}
void gluPerspective( GLdouble fovY, GLdouble aspect, GLdouble zNear, GLdouble zFar ){
const GLdouble pi = 3.1415926535897932384626433832795;
GLdouble fW, fH;
fH = tan( fovY / 360 * pi ) * zNear;
fW = fH * aspect;
glFrustum( -fW, fW, -fH, fH, zNear, zFar );
}
unsigned int loadTexture(const char* fileName){
//IMG_Init(0);
GLint numberOfColours = 0;
GLenum format;
SDL_Surface* img = IMG_Load(fileName);
img = SDL_DisplayFormatAlpha(img);
cout<<IMG_GetError();
//numberOfColours = img->format->BytesPerPixel;
if(!(&img)){
cout<<"Failed To Find Image!"<<endl;
}
cout<<"Reached load!"<<endl;
if(((img->w & (img->w - 1)) != 0)){cout<<"Warning: Image width not a power of 2!"<<endl;}
if(((img->h & (img->h - 1)) != 0)){cout<<"Warning: Image height not a power of 2!"<<endl;}
if(img->format->BytesPerPixel == 4){
format = GL_RGBA;
}else{
format = GL_RGB;
}
cout<<"GL_RGB: "<<GL_RGB<<"\nFormat: "<<format<<endl;
// SDL_PixelFormat pf;
//pf.palette = 0;
//pf.BitsPerPixel = 32;
//pf.BytesPerPixel = 4;
//pf.alpha = 255;
//pf.Rshift = pf.Rloss = pf.Gloss = pf.Bloss = pf.Aloss = pf.colorkey = 0;
//pf.Rmask = 0x000000ff;
//pf.Gshift = 8;
//pf.Gmask = 0x0000ff00;
//pf.Bshift = 16;
//pf.Bmask = 0x00ff0000;
//pf.Ashift = 24;
//pf.Amask = 0xff000000;
//SDL_Surface* glSurface = SDL_ConvertSurface(img, &pf, SDL_SWSURFACE);
unsigned int id;
glGenTextures(1, &id);
cout<<"ID: "<<id<<endl;
glBindTexture(GL_TEXTURE_2D, id);
cout<<"IMG Error: "<<IMG_GetError()<<endl;
cout<<"SDL Error: "<<SDL_GetError()<<endl;
glTexImage2D(GL_TEXTURE_2D, 0, format, img->w, img->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, img->pixels);
cout<<"IMG Error: "<<IMG_GetError()<<endl;
cout<<"SDL Error: "<<SDL_GetError()<<endl;
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,GL_TRUE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
SDL_FreeSurface(img);
return id;
}
SDL_Surface* loadTextureData(const char* fileName){
SDL_Surface* tex;
if((tex = IMG_Load(fileName))){
cout<<"Texture Found! Tex: "<<fileName<<endl;
}
return tex;
}
void init(){
//glEnable(GL_CULL_FACE);
//glCullFace(GL_BACK);
const unsigned char* text = glGetString(GL_VERSION);
SDL_WM_GrabInput(SDL_GRAB_ON);
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
SDL_WM_SetCaption("OGL!", "OGL!!");
//SDL_WM_IconifyWindow();
SDL_Surface* icon = loadTextureData("Logo.png");
SDL_WM_SetIcon(icon, NULL);
glClearColor(0.0, 0.0, 0.0, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1280.0/720.0, 0.1, 5000.0);
glMatrixMode(GL_MODELVIEW);
SDL_FreeSurface(icon);
texture = loadTexture("Moon.png");
normalMap = loadTexture("Normal Map.png");
SDL_ShowCursor(false);
initShader("vertex.vert", "fragment.frag", program);
}
void display(){
//frame++;
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUniform1i(glGetUniformLocation(program, "texture"), 0);
glUniform1i(glGetUniformLocation(program, "normalMap"), 1);
glUniform3f(glGetUniformLocation(program, "lightPos"), 0, 0, 0);
glUniform3f(glGetUniformLocation(program, "mambient"), 0.2, 0.2, 0.2);
glUniform3f(glGetUniformLocation(program, "mdiffuse"), 0.6, 0.6, 0.6);
glUniform3f(glGetUniformLocation(program, "mspecular"), 1, 1, 1);
glUniform1f(glGetUniformLocation(program, "shininess"), 0);
glUniform3f(glGetUniformLocation(program, "lambient"), 0.2, 0.2, 0.2);
glUniform3f(glGetUniformLocation(program, "ldiffuse"), 0.4, 0.4, 0.4);
glUniform3f(glGetUniformLocation(program, "lspecular"), 1, 1, 1);
glUniform3f(glGetUniformLocation(program, "lightPos"), /*lPos*/0, 0, 0);
glLoadIdentity();
control(0.2, 0.2, mousein);
updateCamera();
//glTranslatef(0.0, 0.0, -6);
//glRotatef(frame, 0, 1, 0);
//glRotatef(90, 0, 1, 0);
/*glBegin(GL_TRIANGLES);
glScalef(15, 15, 15);
glColor4f(1.0, 0.0, 0.0, 1);
glVertex3f(0.0, 2.0, 0.0);
glColor4f(0.0, 1.0, 0.0, 1);
glVertex3f(-2.0, -2.0, 0.0);
glColor4f(0.0, 0.0, 1.0, 1);
glVertex3f(2.0, -2.0, 0.0);
glEnd();*/
//glRotatef(90, 0, 1, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, normalMap);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normalMap);
glBegin(GL_QUADS);
glNormal3f(0.0, 0.0, 1.0);
glColor4f(1.0,1.0,1.0, 1.0);
glTexCoord2i(0, 0);
glVertex3f(-2.0, 2.0, 0.0);
glTexCoord2i(1, 0);
glVertex3f(-2.0, -2.0, 0.0);
glTexCoord2i(1, 1);
glVertex3f(2.0, -2.0, 0.0);
glTexCoord2i(0, 1);
glVertex3f(2.0, 2.0, 0.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
}
void update(){
frame++;
lPos = sin((180/PI)*(frame/10))*5;
}
int main(int args, char* argv[]){
SDL_Init(SDL_INIT_EVERYTHING);
IMG_Init(IMG_INIT_PNG);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
SDL_Surface* screen = SDL_SetVideoMode(1280, 720, 32, SDL_SWSURFACE|SDL_OPENGL);
bool running = true;
Uint32 start;
SDL_Event event;
init();
while(running){
start = SDL_GetTicks();
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
default:
break;
case SDLK_ESCAPE:
running = false;
break;
case SDLK_p:
mousein = false;
SDL_ShowCursor(SDL_ENABLE);
break;
}
break;
case SDL_KEYUP:
break;
case SDL_MOUSEBUTTONDOWN:
mousein = true;
SDL_ShowCursor(SDL_DISABLE);
break;
}
}
update();
display();
SDL_GL_SwapBuffers();
if(1000/60 > (SDL_GetTicks() - start)){
SDL_Delay(1000/60 - (SDL_GetTicks() - start));
}
}
clean(program);
SDL_Quit();
//cout << "Hello world!" << endl;
return 0;
}