I'm doing my first steps in OpenGl Shader base programming and computer graphics. I'm trying the following example, but when I try to compile the project I got the following error:
fragment shader failed to link. vertex shader failed to link.
ERROR: Not all shaders has valid object code
i tried running the program in Visual Studio 2012 and 2010. glut library version is 3.7 and glew library version is 1.10.0. what is the problem?
.cpp Program
#include <stdio.h>
#include <stdlib.h>
#include <glew.h>
#include <glut.h>
#include <gl.h>
#pragma comment(lib,"glew32.lib")
#define NoP 50000
GLuint InitShader(char *, char *);
void mydisplay();
struct points
{ GLfloat x,y,z;
};
void init();
void Sierpinski(points Num[]);
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE|GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutInitWindowPosition(0,0);
glutCreateWindow("Sierpinski 3D");
glewInit();
init();
glEnable(GL_DEPTH_TEST);
glutDisplayFunc(mydisplay);
glutMainLoop();
}
void init()
{
points Num[NoP];
Sierpinski(Num);
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);//set the color for clearing the display
glPointSize(2); // set the point size
// Creating a program object containing shader files
GLuint program;
program = InitShader("vshader.glsl","fshader.glsl");
glUseProgram(program);
//Creating a buffer object containing Sirepinski verteces data
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Num), Num, GL_STATIC_DRAW);
//Make a conncetion between data in object buffer and "vPosition in vertex shader
GLuint location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray ( location );
glVertexAttribPointer( location, 3, GL_FLOAT, GL_FALSE,0, 0);//BUFFER_OFFSET(0));
}
void mydisplay()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //the clear call will affect the color buffer
glDrawArrays(GL_POINTS,0,NoP);//Rendering verteces data
glFlush(); //Empty all commands buffer, causing all issued commands to be executed as quickly as they are accepted by the actual rendering engine
}
static char * ReadShaderSource(char * ShaderFile)
{
FILE *fp;
fp = fopen(ShaderFile,"rt");
if (!fp) return NULL;
long size=0;
while (!feof(fp))
{
fgetc (fp);
size++;
}
size--;//EOF should not be counted
fseek(fp, 0, SEEK_SET);
char * buf= new char[size + 1];
fread(buf, 1, size,fp);
buf[size]=0;// string is NULL terminated
fclose(fp);
return buf;
}
GLuint InitShader(char * vShaderFile, char * fShaderFile)
{
char * svs, * sfs;
GLuint program, VertexShader, FragmentShader;
program = glCreateProgram();
VertexShader = glCreateShader(GL_VERTEX_SHADER);
svs=ReadShaderSource(vShaderFile);
glShaderSource(VertexShader,1,(const GLchar **)&svs,NULL);
glCompileShader(VertexShader);
// reading GLSL compiler error messages for vertex shader
GLint compiled;
glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{ printf("/n failed to compile");
GLint logSize;
glGetShaderiv(VertexShader, GL_INFO_LOG_LENGTH,&logSize);
char * logMsg = new char[logSize];
glGetShaderInfoLog(VertexShader, logSize, NULL, logMsg);
printf("\n %s",logMsg);
delete [] logMsg;
getchar();
exit(EXIT_FAILURE);
}
FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
sfs = ReadShaderSource(fShaderFile);
glShaderSource(FragmentShader, 1, (const GLchar **)&sfs, NULL);
glCompileShader(FragmentShader);
// reading GLSL compiler error messages for fragment shader
glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{ printf("\n failed to compile");
GLint logSize2;
glGetShaderiv(FragmentShader, GL_INFO_LOG_LENGTH,&logSize2);
char * logMsg2 = new char[logSize2];
glGetShaderInfoLog(FragmentShader, logSize2, NULL, logMsg2);
printf("\n %s",logMsg2);
delete [] logMsg2;
getchar();
exit(EXIT_FAILURE);
}
glAttachShader(program,VertexShader);
glAttachShader(program, FragmentShader);
glLinkProgram(program);
// reading GLSL linker error messages for program object
GLint linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if(!linked)
{ printf("/n failed to link");
GLint logSize;
glGetProgramiv(program, GL_INFO_LOG_LENGTH,&logSize);
char * logMsg = new char[logSize];
glGetProgramInfoLog(program, logSize, NULL, logMsg);
printf("\n %s",logMsg);
delete [] logMsg;
getchar();
exit(EXIT_FAILURE);
}
glUseProgram(program);
return program;
}
void Sierpinski(points Num[])
{
int j;
points Vertices[4]={{-1.0,-1.0, -1.0},{1.0,-1.0,-1.0},{0.0,1.0,-1.0},{0.0,0.0,1.0}};
Num[0].x = 0;
Num[0].y = 0;
Num[0].z = 0;
for(int i=1;i<NoP;i++)
{
j = rand() % 4;
Num[i].x = (Vertices[j].x + Num[i - 1].x)/2;
Num[i].y = (Vertices[j].y + Num[i - 1].y)/2;
Num[i].z = (Vertices[j].z + Num[i - 1].z)/2;
}
}
vshader.glsl
#version 130
in vec4 vPosition;
out vec4 color;
void main()
{
gl_Position = vPosition;
color = vPosition;
}
fshader.glsl
#version 130
in vec4 color;
void main()
{
gl_FragColor = vec4((1.0 + color.xyz)/2.0,1.0);
}
Seems to work fine:
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
void CheckStatus( GLuint obj )
{
GLint status = GL_FALSE;
if( glIsShader(obj) ) glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
if( glIsProgram(obj) ) glGetProgramiv( obj, GL_LINK_STATUS, &status );
if( status == GL_TRUE ) return;
GLchar log[ 1 << 17 ] = { 0 };
if( glIsShader(obj) ) glGetShaderInfoLog( obj, sizeof(log), NULL, log );
if( glIsProgram(obj) ) glGetProgramInfoLog( obj, sizeof(log), NULL, log );
std::cerr << log << std::endl;
exit( -1 );
}
void AttachShader( GLuint program, GLenum type, const char* src )
{
GLuint shader = glCreateShader( type );
glShaderSource( shader, 1, &src, NULL );
glCompileShader( shader );
CheckStatus( shader );
glAttachShader( program, shader );
glDeleteShader( shader );
}
GLuint LoadProgram( const char* vert, const char* geom, const char* frag )
{
GLuint prog = glCreateProgram();
if( vert ) AttachShader( prog, GL_VERTEX_SHADER, vert );
if( geom ) AttachShader( prog, GL_GEOMETRY_SHADER, geom );
if( frag ) AttachShader( prog, GL_FRAGMENT_SHADER, frag );
glLinkProgram( prog );
CheckStatus( prog );
return prog;
}
#define GLSL(version, shader) "#version " #version "\n" #shader
const char* vert = GLSL
(
130,
in vec4 vPosition;
out vec4 color;
void main()
{
gl_Position = vPosition;
color = vPosition;
}
);
const char* frag = GLSL
(
130,
precision mediump float;
in vec4 color;
void main()
{
gl_FragColor = vec4((1.0 + color.xyz)/2.0,1.0);
}
);
#define NoP 50000
struct points
{
GLfloat x,y,z;
};
void Sierpinski(points Num[])
{
int j;
points Vertices[4]={{-1.0,-1.0, -1.0},{1.0,-1.0,-1.0},{0.0,1.0,-1.0},{0.0,0.0,1.0}};
Num[0].x = 0;
Num[0].y = 0;
Num[0].z = 0;
for(int i=1;i<NoP;i++)
{
j = rand() % 4;
Num[i].x = (Vertices[j].x + Num[i - 1].x)/2;
Num[i].y = (Vertices[j].y + Num[i - 1].y)/2;
Num[i].z = (Vertices[j].z + Num[i - 1].z)/2;
}
}
void init()
{
points Num[NoP];
Sierpinski(Num);
GLuint program = LoadProgram( vert, NULL, frag );
glUseProgram(program);
//Creating a buffer object containing Sirepinski verteces data
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Num), Num, GL_STATIC_DRAW);
//Make a conncetion between data in object buffer and "vPosition in vertex shader
GLuint location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray ( location );
glVertexAttribPointer( location, 3, GL_FLOAT, GL_FALSE,0, 0);//BUFFER_OFFSET(0));
}
void mydisplay()
{
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);//set the color for clearing the display
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //the clear call will affect the color buffer
glPointSize(2); // set the point size
glDrawArrays(GL_POINTS,0,NoP);//Rendering verteces data
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutInitWindowPosition(0,0);
glutCreateWindow("Sierpinski 3D");
glewInit();
init();
glEnable(GL_DEPTH_TEST);
glutDisplayFunc(mydisplay);
glutMainLoop();
}
Make sure you use FreeGLUT, Nate's GLUT is way, way old.
Related
I try to render a triangle to a GtkGLArea but I only see the color with which I cleared the frame with glClearColor().
Please note:
I know that the triangle is so big that it would fill the whole screen, but I also tried smaller ones and it didn't work either.
I also know that I should normally not create the program before each rendering, I only did it here to keep the example short.
I'm fairly certain that the error is neither in LoadShaders nor in the shaders themselves because I've tried the exact same functions with GLFW and they've worked fine their.
Things which might cause the problem:
I'm not flushing the frame currently or swapping framebuffers because the documentation (https://developer.gnome.org/gtk3/stable/GtkGLArea.html) doesn't mention that I have to. I've tried glFlush() but it didn't help either.
I assume that the screen coordinates go from -1 to 1 on all axis like in normal OpenGL. Maybe that's wrong but I couldn't find anything in the documentation there either.
Could somebody help me?
This is how I compile it:
g++ -O3 -s -o main main.cpp -isystem include -Llibs -DNDEBUG `pkg-config --cflags gtk+-3.0` `pkg-config --libs gtk+-3.0` -lepoxy -lm
This is my code:
#include <gtk/gtk.h>
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include <iostream>
#include <vector>
GLuint LoadShaders(char const* vertex, char const* fragment){
// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
glShaderSource(VertexShaderID, 1, &vertex , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
// Compile Fragment Shader
glShaderSource(FragmentShaderID, 1, &fragment , NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
// Link the program
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(ProgramID, VertexShaderID);
glDetachShader(ProgramID, FragmentShaderID);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
return ProgramID;
}
char const* vertShader = R"GLSL(
#version 330 core
void main(){
gl_Position.z = 0.0;
gl_Position.w = 1.0;
if (0 == gl_VertexID) {
gl_Position.x = -100.0;
gl_Position.y = -100.0;
}
if (2 == gl_VertexID) {
gl_Position.x = 0.0;
gl_Position.y = 100.0;
}
if (1 == gl_VertexID) {
gl_Position.x = 100.0;
gl_Position.y = -100.0;
}
}
)GLSL";
char const* fragShader = R"GLSL(
#version 330 core
layout(location = 0) out vec4 color;
void main(){
color = vec4(1.0, 0.0, 0.0, 1.0);
}
)GLSL";
gboolean
render(GtkGLArea*, GdkGLContext*, gpointer) {
glClearColor(0.5, 0.5, 0.5, 0);
glClear(GL_COLOR_BUFFER_BIT);
GLuint programID;
programID = LoadShaders(vertShader, fragShader);
glUseProgram(programID);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glFlush();
glDeleteProgram(programID);
return TRUE;
}
int
main(int argc, char** argv) {
gtk_init(&argc, &argv);
auto window{gtk_window_new(GTK_WINDOW_TOPLEVEL)};
auto glWidget{gtk_gl_area_new()};
gtk_container_add(GTK_CONTAINER(window), glWidget);
g_signal_connect (glWidget, "render", G_CALLBACK(render), nullptr);
gtk_widget_show_all(window);
gtk_main();
return EXIT_SUCCESS;
}
Two things I can think of:
You aren't requesting a Core context from the OS. Looks like you have to override create-context & create + return a gdk_gl_context_set_required_version'd GdkGLContext.
When you do get a Core context up & going I'm pretty sure you still need a VAO bound even if you're generating geometry entirely within your vertex shader.
RE: missing VAOs:
With this GLFW program and the VAO creation/bind commented out:
#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <iostream>
void CheckStatus( GLuint obj, bool isShader )
{
GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
if( status == GL_TRUE ) return;
std::cerr << (GLchar*)log << "\n";
std::exit( EXIT_FAILURE );
}
void AttachShader( GLuint program, GLenum type, const char* src )
{
GLuint shader = glCreateShader( type );
glShaderSource( shader, 1, &src, NULL );
glCompileShader( shader );
CheckStatus( shader, true );
glAttachShader( program, shader );
glDeleteShader( shader );
}
const char* vert = 1 + R"GLSL(
#version 330 core
void main(){
gl_Position.z = 0.0;
gl_Position.w = 1.0;
if (0 == gl_VertexID) {
gl_Position.x = -100.0;
gl_Position.y = -100.0;
}
if (2 == gl_VertexID) {
gl_Position.x = 0.0;
gl_Position.y = 100.0;
}
if (1 == gl_VertexID) {
gl_Position.x = 100.0;
gl_Position.y = -100.0;
}
}
)GLSL";
const char* frag = 1 + R"GLSL(
#version 330 core
layout(location = 0) out vec4 color;
void main(){
color = vec4(1.0, 0.0, 0.0, 1.0);
}
)GLSL";
int main( int, char** )
{
glfwSetErrorCallback( []( int, const char* desc ) { std::cerr << desc << "\n"; std::exit( EXIT_FAILURE ); } );
glfwInit();
glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE );
glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
GLFWwindow* window = glfwCreateWindow( 640, 480, "GLFW", NULL, NULL );
glfwMakeContextCurrent( window );
gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
//GLuint vao = 0;
//glGenVertexArrays( 1, &vao );
//glBindVertexArray( vao );
GLuint prog = glCreateProgram();
AttachShader( prog, GL_VERTEX_SHADER, vert );
AttachShader( prog, GL_FRAGMENT_SHADER, frag );
glLinkProgram( prog );
CheckStatus( prog, false );
while( !glfwWindowShouldClose( window ) )
{
glfwPollEvents();
int w, h;
glfwGetFramebufferSize( window, &w, &h );
glViewport( 0, 0, w, h );
glClearColor( 0.5, 0.5, 0.5, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUseProgram( prog );
glDrawArrays( GL_TRIANGLES, 0, 3 );
glfwSwapBuffers( window );
}
glfwTerminate();
}
Running on Linux with Mesa 13.0.6's llvmpipe backend & the MESA_DEBUG=1 envvar gives me a grey window and this message on stdout:
Mesa: User error: GL_INVALID_OPERATION in glDrawArrays(no VAO bound)
Restoring the VAO gives the expected red window.
Searched Stack Overflow for similar questions in search of my solution , but it doesn't seem to be solved.
main.cpp :
#include"reader.h"
#include"window.h"
#include"shader.h"
int main() {
float vertices[] = {
-0.5f, -0.5f, 0.0f ,
0.5f, -0.5f, 0.0f ,
0.0f, 0.5f, 0.0f
};
Window window;
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
Shader shader;
shader.addShader("./src/shaders/basic.vtx",GL_VERTEX_SHADER);
shader.addShader("./src/shaders/basic.frg", GL_FRAGMENT_SHADER);
shader.compile();
shader.enable();
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,GL_STATIC_DRAW);
GLint pos_in = glGetAttribLocation(shader.getProgram(), "pos_in");
if (pos_in < 0) {
std::cout << "pos_in not found\n";
}
glVertexAttribPointer(pos_in, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(pos_in);
while (!window.closed()) {
window.update();
glDrawArrays(GL_TRIANGLES,0,3);
}
return 0;
}
shader.h :
#pragma once
#include<glad/glad.h>
#include<iostream>
#include<vector>
#include"reader.h"
class Shader {
std::vector<GLuint*> shaders;
GLuint program;
public :
GLuint& getProgram() {
return program;
}
Shader() {
program = glCreateProgram();
}
void addShader(const char * path, GLenum type) {
std::string data = ShaderReader(path).read_shader();
const char * chardata = data.c_str();
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &chardata , nullptr);
glCompileShader(shader);
int success;
char buffer[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader, 512, NULL, buffer);
std::cout << buffer << std::endl;
return;
}
std::cout << "shader inserted into vector\n";
shaders.push_back(&shader);
}
void compile(){
for (int i = 0; i != shaders.size();i++) {
glAttachShader(program, *shaders[i]);
}
glLinkProgram(program);
glValidateProgram(program);
glUseProgram(program);
int status;
glGetProgramiv(program, GL_COMPILE_STATUS, &status);
char buffer[512];
if (!status) {
glGetProgramInfoLog(program,512,NULL,buffer);
std::cout << buffer << std::endl;
return;
}
std::cout << "shader compilation successful\n";
}
void enable() {
glUseProgram(program);
}
void disable() {
glUseProgram(0);
}
~Shader() {
for (int i = 0; i != shaders.size();i++) {
glDeleteShader(*shaders[i]);
}
}
};
vertex shader written in basic.vtx:
#version 400
layout (location = 0 ) in vec3 pos_in ;
void main(){
gl_Position = vec4(pos_in.x , pos_in.y , pos_in.z , 1.0f);
}
fragment shader written in basic.frg :
#version 400
out vec4 color;
void main(){
color = vec4(0.0f, 0.5f , 0.5f , 1.0f);
}
At the time of calling glGetAttribLocation , the vertex shader IS USING pos_in attrib to set gl_Position , yet it returns -1 .
Also the triangle is not rendered when calling glGetAttribLocation() ; or rendered white with direct attrib pointer values like 0 , 1 with an openGL 1281 error.
One thing that is definitely not valid is the shaders.push_back(&shader):
void addShader(const char * path, GLenum type) {
// ....
GLuint shader = glCreateShader(type);
// ....
shaders.push_back(&shader);
}
With the shaders.push_back(&shader) you push back the address of a local variable to the shaders vector. So the glAttachShader(program, *shaders[i]); will result in undefined behavior.
shader holds only an numeric id so there is no need to get a pointer to that, just change the std::vector<GLuint*> shaders to std::vector<GLuint> shaders, use shaders.push_back(shader) and replace all *shaders[i] with shaders[i]
The reason why you don't get an linking error is most likely because the content at the address you get from &shader is not overwritten before you do the glAttachShader(program, *shaders[i]), and that both entries in the shaders vector hold same address of the stack. The result of is that glAttachShader is called each time on the same id, so you bind only the fragement shader to the program.
I just begin my journey in graphical computing, so I started by understanding this tutorial: https://learnopengl.com/Getting-started/Hello-Triangle.
Before adventuring myself to do some "dynamic" drawing I thought transform
this into a "Renderer" class would be interesting this was the result:
#pragma once
#include <fstream>
#include <vector>
#include "pch.h"
class Renderer {
public:
Renderer(GLFWwindow*);
void initVertexShaders(std::vector<std::string>&);
void initFragmentShaders(std::vector<std::string>&);
void load(float*, size_t);
void draw();
const GLubyte* renderer;
const GLubyte* version;
private:
GLFWwindow* window = nullptr;
GLuint vbo;
GLuint vao;
GLuint shaderProgram = 0; //= glCreateProgram();
GLuint vs[100];
GLuint fs[100];
};
pch.h is a precompiled header in order to not compile glm, glfw and glew everytime y build the project.
The idea was to isolate different utilities, I would use load(float*, size_t) to send information to the gpu, and draw() during the main window loop. The idea is to make a first approximation to a 2d, cameraless render engine.
Anyway, source code is:
#include "Renderer.hpp"
Renderer::Renderer(GLFWwindow* window) {
this->window = window;
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
this->renderer = glGetString(GL_RENDERER);
this->version = glGetString(GL_VERSION);
this->vao = 0;
this->vbo = 0;
}
void Renderer::load(float* points, size_t memory) {
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, memory, points, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}
void Renderer::draw() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void Renderer::initVertexShaders(std::vector<std::string>& paths) {
int i = 0;
std::ifstream ifs;
if(this->shaderProgram == 0) shaderProgram = glCreateProgram();
for (const auto& path : paths){
ifs.open(path);
std::string program_str(( std::istreambuf_iterator<char>(ifs)),
( std::istreambuf_iterator<char>() ));
const char* program_src = program_str.c_str();
vs[i] = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs[i], 1, &program_src, NULL);
glCompileShader(vs[i]);
glAttachShader(shaderProgram, vs[i]);
i++;
}
glLinkProgram(shaderProgram);
}
void Renderer::initFragmentShaders(std::vector<std::string>& paths) {
int i = 0;
std::ifstream ifs;
if(this->shaderProgram == 0) shaderProgram = glCreateProgram();
for (const auto& path : paths){
ifs.open();
std::string program_str(( std::istreambuf_iterator<char>(ifs)),
( std::istreambuf_iterator<char>() ));
const char* program_src = program_str.c_str();
fs[i] = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs[i], 1, &program_src, NULL);
glCompileShader(fs[i]);
glAttachShader(shaderProgram, fs[i]);
i++;
}
glLinkProgram(shaderProgram);
}
The problem comes at shader time, I can read the files without problem, but they do nothing.
The questions are: Does store all the shaders in an array make sense?
Can a shader program consist in more than one vertex shader/fragment shader?
Is the error in another spot?
Does the whole class make sense in any way?
Thank you.
edit: Code from main.cpp
#include "Renderer.hpp"
float points[] = {
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
//
0.5f, 0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f
};
int main() {
std::vector<std::string> vertexShaders;
std::vector<std::string> fragmentShaders;
vertexShaders.push_back("shader/vs.glsl");
fragmentShaders.push_back("shader/fs.glsl");
glfwInit(); //glfwGetPrimaryMonitor()
GLFWwindow *window = glfwCreateWindow(960, 540, "Hello Triangle", NULL, NULL);
Renderer Ren(window);
Ren.load(points, sizeof(points));
Ren.initVertexShaders(vertexShaders);
Ren.initFragmentShaders(fragmentShaders);
while (!glfwWindowShouldClose(window)) {
Ren.draw();
glfwPollEvents();
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
It is sufficient to link the shader program object once, after all attached shader objects have been compiled.
OpenGL 4.6 API Core Profile Specification; 7.3. PROGRAM OBJECTS; page 94:
Shader objects may be attached to program objects before source code has been loaded into the shader object, or before the shader object has been compiled or specialized.
Note, it is sufficient that the shader object is successfully compiled, before the shader program object, where it is attached to, gets linked.
OpenGL 4.6 API Core Profile Specification; 7.3. PROGRAM OBJECTS; page 94:
Multiple shader objects of the same type may be attached to a single program object, and a single shader object may be attached to more than one program object.
e.g.
One shader object contains a function (FragColor)
#version 460
uniform sampler2D u_texture;
vec4 FragColor(vec2 uv)
{
return texture(u_texture, uv);
}
A second shader object of the same type contains the function signature (but not the implementation) and the usage of the function.
#version 460
in vec2 vUV;
out vec4 fragColor;
vec4 FragColor(vec2 uv);
void main()
{
fragColor = FragColor(vUV);
}
Both of the above code snippets can be placed to 2 separate shader objects of type GL_FRAGMENT_SHADER. Each of the 2 shader objects can be successfully compiled. And if they are attached to the same shader program object, then the shader program object can be successfully liked.
See also Attaching multiple shaders of the same type in a single OpenGL program?
Further, I recommend to check if a shader object was successfully compiled:
GLuint shaderObj = .... ;
glCompileShader( shaderObj );
GLint status = GL_TRUE;
glGetShaderiv( shaderObj, GL_COMPILE_STATUS, &status );
if ( status == GL_FALSE )
{
GLint logLen;
glGetShaderiv( shaderObj, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetShaderInfoLog( shaderObj, logLen, &written, log.data() );
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
and a shader program object was successfully linked:
GLuint progObj = ....;
glLinkProgram( progObj );
GLint status = GL_TRUE;
glGetProgramiv( progObj, GL_LINK_STATUS, &status );
if ( status == GL_FALSE )
{
GLint logLen;
glGetProgramiv( progObj, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetProgramInfoLog( progObj, logLen, &written, log.data() );
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
I am following the 8Th edition and below is the code I modified from book. it does not give me what I want, but a blank window instead. I wonder what mistake(s) did I make during modifications?
below is the code I modified, hope anyone can point out the mistake.
#include <iostream>
#include <OpenGL/glu.h>
#include <OpenGL/gl3.h>
#include <GLUT/GLUT.h>
GLuint vaoNames[1];
GLuint bufferNames[1];
int vPosition = 0;
GLuint createProgram();
GLuint createShader(GLenum type, const char* src);
GLuint program;
void init()
{
glGenVertexArrays(1, vaoNames);
glBindVertexArray(vaoNames[0]);
GLfloat vertices[6][2] = {
{ -0.90, -0.90 }, // Triangle 1
{ 0.85, -0.90 },
{ -0.90, 0.85 },
{ 0.90, -0.85 }, // Triangle 2
{ 0.90, 0.90 },
{ -0.85, 0.90 }
};
glGenBuffers(1, bufferNames);
glBindBuffer(GL_ARRAY_BUFFER, bufferNames[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glUseProgram(createProgram());
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vPosition);
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoNames[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glFlush();
}
GLuint createProgram()
{
GLuint program = glCreateProgram();
const char* vertexShaderSrc =
"#version 400 core \n"
"layout( location = 0 ) in vec4 vPosition; \n"
"void main() \n"
"{ \n"
"gl_Position = vPosition; \n"
"}";
GLuint vertexShader = createShader(GL_VERTEX_SHADER, vertexShaderSrc);
const char* fragmentShaderSrc =
"#version 400 core \n"
"out vec4 fColor; \n"
"void main() \n"
"{ \n"
"fColor = vec4( 0.0, 0.0, 1.0, 1.0 ); \n"
"}";
GLuint fragmentShader = createShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
return program;
}
GLuint createShader(GLenum type, const char* src){
GLuint shaderID = glCreateShader(type);
glShaderSource(shaderID, 1, &src, 0);
glCompileShader(shaderID);
return shaderID;
}
int main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutInitWindowSize(512, 512);
glutCreateWindow("8877");
init();
glutDisplayFunc(render);
glutMainLoop();
}
When you say 'there is no error during shader compilation and program linking', do you mean that the the program doesn't stop, or do you mean that you actually ran a different program and checked the results of glGetError() at various points, or fetched the compilation or linking log? Your program as is does no actual error checking, and OpenGL doesn't throw exceptions or produce error output as a matter of course. You have to actually request these things.
Simply because glCompileShader() doesn't generate an error doesn't mean your shader actually compiled. You need to be checking the compilation status of shaders and the link status of programs with code similar to this:
GLint compiled;
glGetShaderiv(newShader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
std::string log = getLog(newShader);
SAY(log.c_str());
throw std::runtime_error("Failed to compile shader " + log);
}
where getLog is
static std::string getLog(GLuint shader) {
std::string log;
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char* infoLog = new char[infoLen];
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
log = std::string(infoLog);
delete[] infoLog;
}
return log;
}
This is for checking shaders, and there are equivalent functions for checking the results and getting the logs for program linking. Unless you're calling them you have no guarantee that the shaders are compiling. Assuming you haven't made some simple mistake in the shader syntax, the most likely problem you're facing is a lack of support for OpenGL 4.x, which would cause the shaders to fail right off.
I'm trying to run a program using shaders. In Another question I made, I discovered that I was mixing the fixed pipeline functions with the newer stuff (from OpenGL 2.0), so I tried to remove all the "old stuff" like glMatrixMode, but I'm not sure if I'm doing it correctly. In the doubt I also removed calls like glLightfv which I'm not really sure if they're allowed if I used a programmable pipeline.
This is the main code:
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
#include "utility.hpp"
#include "program.hpp"
GLfloat width=600, height=800;
Program* program_ref;
void init()
{
glewInit();
Shader vertex_shader= Shader("vertex_shader",GL_VERTEX_SHADER);
// This just reads the file "vertex_shader", creates the shader and compiles it
Shader geometry_shader= Shader(); // This means that the shader is empty
// So the program class will be enough smart to recognize it and don't attach it
Shader fragment_shader= Shader("fragment_shader",GL_FRAGMENT_SHADER);
program_ref= new Program(vertex_shader,geometry_shader,fragment_shader);
// This creates a program, attaches the shaders to it, links and uses it
}
void display()
{
vector<GLfloat> quad{-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5};
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2,GL_FLOAT,0,quad.data());
glDrawArrays(GL_QUADS,0,4);
glDisableClientState(GL_VERTEX_ARRAY);
glutSwapBuffers();
cout << glGetError() << endl;
}
void reshape(int w, int h)
{
width=w;
height=h;
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(500,500);
glutCreateWindow("test");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
init();
glewInit();
glutMainLoop();
return 0;
}
(Optional classes to see, in the comments I wrote how are Program and Shader are behaving)
shader.hpp:
#ifndef shader_hpp
#define shader_hpp
#include "utility.hpp"
#include "shader.hpp"
#include <fstream>
#define MAX_SIZE (size_t)1.0e5
class Shader
{
private:
GLuint id;
GLenum type;
bool null;
public:
Shader()
{
id=-1;
type= -1;
null= true;
}
Shader(const string& filename,GLenum type)
{
GLchar* data= new GLchar[MAX_SIZE];
streamsize count;
this->type= type;
ifstream is;
is.open(filename);
is.read(data,MAX_SIZE);
count= is.gcount();
is.close();
id=glCreateShader(type);
glShaderSource(id,1,(const GLchar**)&data,&count);
glCompileShader(id);
delete[] data;
null= false;
}
GLuint getId() const
{
return id;
}
GLenum getType() const
{
return type;
}
bool isNull() const
{
return null;
}
};
#endif
program.hpp:
#ifndef program_hpp
#define program_hpp
#include "utility.hpp"
#include "shader.hpp"
class Program
{
private:
GLuint id;
public:
Program(const Shader& vertex_shader, const Shader& geometry_shader, const Shader& fragment_shader)
{
id= glCreateProgram();
if(!vertex_shader.isNull())
{
glAttachShader(id,vertex_shader.getId());
}
if(!geometry_shader.isNull())
{
glAttachShader(id,geometry_shader.getId());
}
if(!fragment_shader.isNull())
{
glAttachShader(id,fragment_shader.getId());
}
glLinkProgram(id);
glUseProgram(id);
}
GLuint getId()
{
return id;
}
};
#endif
There two classes are just made to simplify everything when using glew to compile the shaders and attach them to the program.
The problem is the following: If I just attach the vertex shader and not the fragment shader, all is fine and I see the quad drawn with the right color (red, because in the vertex shader below I'll set the color to red). If instead I also attach the fragment shader I see that the quad is white. I also tried to see what wasn't right and I printed the result of glGetError(). If I include the fragment shader I get error 1282, if I don't include it I don't get any error (0).
Vertex shader:
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_FrontColor = vec4(1,0,0,1);
// The only relevant thing I do is to set the color to red
// I see the quad red if I attach only the vertex shader and not the fragment shader
gl_TexCoord[0] = gl_MultiTexCoord0;
}
Fragment shader:
void main()
{
gl_fragColor= gl_Color;
}
Give this a shot:
#include <GL/glew.h>
#include <GL/glut.h>
#include <vector>
#include <iostream>
using namespace std;
void CheckStatus( GLuint obj )
{
GLint status = GL_FALSE, len = 10;
if( glIsShader(obj) ) glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
if( glIsProgram(obj) ) glGetProgramiv( obj, GL_LINK_STATUS, &status );
if( status == GL_TRUE ) return;
if( glIsShader(obj) ) glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
if( glIsProgram(obj) ) glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
vector< char > log( len, 'X' );
if( glIsShader(obj) ) glGetShaderInfoLog( obj, len, NULL, &log[0] );
if( glIsProgram(obj) ) glGetProgramInfoLog( obj, len, NULL, &log[0] );
cerr << &log[0] << endl;
exit( -1 );
}
GLuint LoadShader( GLenum type, const char* src )
{
GLuint shader = glCreateShader( type );
glShaderSource( shader, 1, &src, NULL );
glCompileShader( shader );
CheckStatus( shader );
return shader;
}
GLuint LoadProgram( const char* vert, const char* geom, const char* frag )
{
GLuint program = glCreateProgram();
if( vert ) glAttachShader( program, LoadShader( GL_VERTEX_SHADER, vert ) );
if( geom ) glAttachShader( program, LoadShader( GL_GEOMETRY_SHADER, geom ) );
if( frag ) glAttachShader( program, LoadShader( GL_FRAGMENT_SHADER, frag ) );
glLinkProgram( program );
CheckStatus( program );
return program;
}
#define GLSL(version, shader) "#version " #version "\n" #shader
const GLchar* vert = GLSL
(
120,
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_FrontColor = vec4(1.0,0.0,0.0,1.0);
// The only relevant thing I do is to set the color to red
// I see the quad red if I attach only the vertex shader and not the fragment shader
gl_TexCoord[0] = gl_MultiTexCoord0;
}
);
const GLchar* frag = GLSL
(
120,
void main()
{
gl_FragColor= gl_Color;
}
);
void display()
{
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
static GLuint prog = LoadProgram( vert, NULL, frag );
glUseProgram( prog );
glEnableClientState(GL_VERTEX_ARRAY);
GLfloat quad[] = {-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5};
glVertexPointer(2,GL_FLOAT,0,quad);
glDrawArrays(GL_QUADS,0,4);
glDisableClientState(GL_VERTEX_ARRAY);
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(100,100);
glutInitWindowSize(500,500);
glutCreateWindow("test");
glutDisplayFunc(display);
glewInit();
glutMainLoop();
return 0;
}
As Antonie Blom pointed out gl_fragColor should be gl_FragColor. The lower-case f chokes the GLSL compiler on my system.