How can I use multiple shaders in one mesh?

Started by
2 comments, last by C0lumbo 11 years, 3 months ago
Hi!
I want to apply multiple shader to one mesh. I get the vertex from OBJ file, I load with VAO, IBO and I draw with glElementsDraw()

class Cube,
class Mesh
Mesh::Mesh(){}
Mesh::~Mesh()
{
glDeleteBuffers(2, &BufferIds[1]);
glDeleteVertexArrays(1, &BufferIds[0]);
ExitOnGLError("ERROR: No se pueden destruir los buffer objects");
}
void Mesh::Initialize()
{
// Creamos el VAO
glGenVertexArrays(1, &BufferIds[0]);
ExitOnGLError("ERROR: No se puede generar el VAO");
glBindVertexArray(BufferIds[0]);
ExitOnGLError("ERROR: No se puede bindear el VAO");
// Activamos dos vertex attribute locations
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
ExitOnGLError("ERROR: No se puede activar los vertex attributes");
// Creamos los VBO
glGenBuffers(2, &BufferIds[1]);
ExitOnGLError("ERROR: No se pueden generar los buffer objects");
// Bindeamos el VBO al VAO
glBindBuffer(GL_ARRAY_BUFFER, BufferIds[1]);
glBufferData(GL_ARRAY_BUFFER, vtn.size()*sizeof(VertexTextureNormal), &vtn[0], GL_STATIC_DRAW);
ExitOnGLError("ERROR: No se puede bindear el VBO al VAO");
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(vtn[0]) ,0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vtn[0]) , (GLvoid*) sizeof(vtn[0].position));
ExitOnGLError("ERROR: Could not set VAO attributes");
// Creamos el IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, BufferIds[2]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices.front(), GL_STATIC_DRAW);
ExitOnGLError("ERROR: No se puede bindear el IBO al VAO");
glBindVertexArray(0);
}
void Mesh::Draw()
{
glBindVertexArray(BufferIds[0]);
ExitOnGLError("ERROR: No se puede bindear el VAO para dibujar");
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (GLvoid*)0);
ExitOnGLError("ERROR: No se puede dibujar el mesh");
glBindVertexArray(0);
}
void Mesh::Load(std::string filename)
{
// ifstream: clase para leer archivos que recibe como parametros
//la ruta del archivo y un indicador del tipo de operacion
std::string mesh_source = OpenFile(filename, std::ios::in);
std::istringstream iss(mesh_source);
std::vector<glm::vec4> f_vertices;
std::vector<glm::vec2> f_texcoords;
std::vector<glm::vec3> f_normals;
std::vector<int> f_indices;
// string que representa una linea del archivo
std::string line;
// Avanzamos linea a linea del archivo
while(std::getline(iss, line))
{
// Si es un vertice
if (line.substr(0,2) == "v ")
{
// istringstream: para hacer operaciones de stream sobre una cadena. Recibe como parametro el string
std::istringstream s(line.substr(2));
glm::vec4 t_v;
s >> t_v.x;
s >> t_v.y;
s >> t_v.z;
t_v.w = 1.0f;
f_vertices.push_back(t_v);
}else
if(line.substr(0,2) == "vt") // si es un texcoord
{
// istringstream: para hacer operaciones de stream sobre una cadena. Recibe como parametro el string
std::istringstream s(line.substr(2));
glm::vec2 t_vt;
s >> t_vt.x;
s >> t_vt.y;
f_texcoords.push_back(t_vt);
}else
if(line.substr(0,2) == "vn") // si es un normal
{
// istringstream: para hacer operaciones de stream sobre una cadena. Recibe como parametro el string
std::istringstream s(line.substr(2));
glm::vec3 t_vn;
s >> t_vn.x;
s >> t_vn.y;
s >> t_vn.z;
f_normals.push_back(t_vn);
}else
if(line.substr(0,2) == "f ") // Si son indices
{
std::string s;
int i = 0;
while (i < line.length())
{
s.clear();
while (isdigit(line))
s += line[i++];
if (s.length()>0)
f_indices.push_back(atoi(s.c_str())-1);
i++;
}
}
}
// Asignamos a cada vertice su posicion, texcoord y normal correspondiente
//a partir de su indice
std::vector<VertexTextureNormal> vectorVertex;
int t=0;
for(register int i = 0; i < f_indices.size(); i+=3)
{
// Generamos un vtn temporal
VertexTextureNormal t_vtn;
t_vtn.position = f_vertices[f_indices];
t_vtn.texcoord = f_texcoords[f_indices[i+1]];
vectorVertex.push_back(t_vtn);
std::cout<<f_indices+1<<" ";
t++;
if(t==3)
{
std::cout<<std::endl;
t = 0;
}
}
//ordenamos los vertices y obtenemos los indices
indexVBO(vectorVertex, vtn, indices);
// Inicializamos el mesh en el OpenGL
Initialize();
}
void Mesh::indexVBO(
std::vector<VertexTextureNormal> &in_vtn,
std::vector<VertexTextureNormal> &out_vtn,
std::vector<GLuint> &out_indices
)
{
std::map<VertexTextureNormal,GLuint> VertexToOutIndex;
// Recorremos los vertices
for(register int i = 0; i < in_vtn.size(); i++)
{
VertexTextureNormal temp_vtn = in_vtn;
// Buscamos si hay un vertice similar
GLuint index;
bool found = getSimilarVertexIndex(temp_vtn, VertexToOutIndex, index);
if(found){
out_indices.push_back(index);
}else{
out_vtn.push_back( in_vtn );
GLuint newIndex = (GLuint)out_vtn.size() - 1;
out_indices.push_back( newIndex );
VertexToOutIndex[temp_vtn] = newIndex;
}
}
}
// Funcion que busca un vertice igual al enviado en un mapa y devuelve su valor si lo encuentra
bool Mesh::getSimilarVertexIndex(
VertexTextureNormal &packed,
std::map<VertexTextureNormal,GLuint> &VertexToOutIndex,
GLuint &result
){
// Si encuentra el packed, asigna al index(result) el val
std::map<VertexTextureNormal,GLuint>::iterator it= VertexToOutIndex.find(packed);
if ( it == VertexToOutIndex.end() ){
return false;
}else{
result = it->second;
return true;
}
}
Advertisement

My understanding, although I have never done it personally, is that you can have as many shaders as you like, but only one main function.

So, you could have:


//shader1.fs <- Separate files
SomeShaderFunc1()
{
}

//shader2.fs <- Separate files

SomeShaderFunc2()
{
}

//shader3.fs <- Separate files

SomeShaderFunc3()
{
}

//shader4.fs <- Separate files

main()
{
SomeShaderFunc1();
SomeShaderFunc2();
SomeShaderFunc3();
}

But not:


//shader1.fs <- Separate files

SomeShaderFunc1()
{
}

main()
{
SomeShaderFunc1();
}
 
//shader2.fs <- Separate files

SomeShaderFunc2()
{
}

main()
{
SomeShaderFunc2();
}
 
//shader3.fs <- Separate files

SomeShaderFunc3()
{
}
 
main()
{
SomeShaderFunc3();
}

And that is one main function in your fragment shader(s) and one in your vertex shader(s).

If you mean to combine the results of multiple shader programs into one, I think you will have to render the mesh to a texture with each shader program, and then blend the results together (probably using another shader program).

Did you mean you want parts of your mesh to use one shader and parts of your mesh to use a different one? If that's the problem, then your only real solution is to organise your data in such a way that you can setup the first shader, then draw the required verts. Then setup the second shader and do another draw call.

This topic is closed to new replies.

Advertisement