OpenGLÂ : Chargement et compilation des shaders
Publication : le 06 sept. 2016
Lorsque l’on développe une application OpenGL 3 (ou plus récent), une des premières fonctions à développer est celle permettant de charger et compiler des shaders.
Personnellement, j’ai tendance à placer tout le code relatif aux shaders dans une classe en C++. Mais pour couvrir le C et C++, je vais présenter de manière itératif la procédure.
Pour le moment, le code présenté ne gère que les vertex shaders et les fragment shaders. Pour les impatients ou les partisans du « tout, tout de suite », vous trouverez l’intégralité du code en fin d’article.
La fonction principale
La fonction est déclarée ainsi :
Gluint loadShader(GLchar* vs, Glchar* fs) ;
Nous supposons que les deux shaders sont déjà stockés en RAM et accessibles par les deux pointeurs vs
et fs
.
Compilation
Comme il faut compiler le vertex shader et le fragment shader, autant mettre le procédé de compilation dans une seconde fonction, telle que :
bool compile(GLuint &shaderId, GLenum type, GLchar* src)
{
shaderId = glCreateShader(type);
if (shaderId == 0)
{
std::cout << "ERROR: shader type (" << type << ") does not exist" << std::endl;
return false;
}
glShaderSource(shaderId, 1, &src, 0);
glCompileShader(shaderId);
GLint errorCp(0);
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &errorCp);
if (errorCp != GL_TRUE)
{
std::cout << "ERROR: while compiling Shader :" << std::endl;
GLint errorSize(0);
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &errorSize);
char *error = new char[errorSize + 1];
glGetShaderInfoLog(shaderId, errorSize, &errorSize, error);
error[errorSize] = ' 0';
std::cout << error << std::endl;
delete[] error;
glDeleteShader(shaderId);
return false;
}
return true;
}
On appelle cette fonction deux fois depuis la fonction loadShader() :
Gluint vertexId, fragmentId ;
if (!compile(verterxId, GL_VERTEX_SHADER, vs))
{
std::cout << "ERROR: while compiling vertex shader" << std::endl;
return 0 ; // 0 means null in OpenGL programming
}
if (!compile(fragmentId, GL_FRAGMENT_SHADER, fs))
{
std::cout << "ERROR: while compiling fragment shader" << std::endl;
glDeleteShader(verterxId);
return 0 ; // 0 means null in OpenGL programming
}
Fusion des shaders en un seul programme
Maintenant, il faut dire à OpenGL que nos deux shaders sont liés. Pour cela, il faut créer un programme (ce qu’on appelle communément shader également).
Gluint programId = glCreateProgram();
glAttachShader(programId, verterxId);
glAttachShader(programId, fragmentId);
glLinkProgram(programId);
Contrôle des erreurs
Cette phase n’est pas obligatoire mais fortement recommandée. Il s’agit de vérifier que la fusion s’est correctement déroulée.
GLint errorlk(0);
glGetProgramiv(programId, GL_LINK_STATUS, &errorlk);
if (errorlk != GL_TRUE)
{
std::cout << "ERROR: while linking Shader :" << std::endl;
GLint errorSize(0);
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &errorSize);
char *error = new char[errorSize + 1];
glGetShaderInfoLog(programId, errorSize, &errorSize, error);
error[errorSize] = ' 0';
std::cout << error << std::endl;
delete[] error;
glDeleteProgram(programId);
return 0 ; // 0 means null in OpenGL programming
}
Retourner l’ID du shader (/programme)
Enfin, si tout va bien, on retourne l’ID du shader.
return programID ;
Code complet
bool compile(GLuint &shaderId, GLenum type, GLchar* src)
{
shaderId = glCreateShader(type);
if (shaderId == 0)
{
std::cout << "ERROR: shader type (" << type << ") does not exist" << std::endl;
return false;
}
glShaderSource(shaderId, 1, &src, 0);
glCompileShader(shaderId);
GLint errorCp(0);
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &errorCp);
if (errorCp != GL_TRUE)
{
std::cout << "ERROR: while compiling Shader :" << std::endl;
GLint errorSize(0);
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &errorSize);
char *error = new char[errorSize + 1];
glGetShaderInfoLog(shaderId, errorSize, &errorSize, error);
error[errorSize] = ' 0';
std::cout << error << std::endl;
delete[] error;
glDeleteShader(shaderId);
return false;
}
return true;
}
Gluint loadShader(GLchar* vs, Glchar* fs)
{
Gluint vertexId, fragmentId ;
if (!compile(verterxId, GL_VERTEX_SHADER, vs))
{
std::cout << "ERROR: while compiling vertex shader" << std::endl;
return 0 ; // 0 means null in OpenGL programming
}
if (!compile(fragmentId, GL_FRAGMENT_SHADER, fs))
{
std::cout << "ERROR: while compiling fragment shader" << std::endl;
glDeleteShader(verterxId);
return 0 ; // 0 means null in OpenGL programming
}
Gluint programId = glCreateProgram();
glAttachShader(programId, verterxId);
glAttachShader(programId, fragmentId);
glLinkProgram(programId);
GLint errorlk(0);
glGetProgramiv(programId, GL_LINK_STATUS, &errorlk);
if (errorlk != GL_TRUE)
{
std::cout << "ERROR: while linking Shader :" << std::endl;
GLint errorSize(0);
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &errorSize);
char *error = new char[errorSize + 1];
glGetShaderInfoLog(programId, errorSize, &errorSize, error);
error[errorSize] = ' 0';
std::cout << error << std::endl;
delete[] error;
glDeleteProgram(programId);
return 0 ; // 0 means null in OpenGL programming
}
return programID ;
}