Мне удалось интегрировать рендеринг Nehe's Cel-Shading с моими загрузчиками моделей и заставить их рисовать с использованием тени и контура Toon И их исходной текстуры одновременно. В результате получается очень приятный эффект Cel Shading текстуры модели, но он влияет на скорость программы, он очень медленный даже при наличии только 3 моделей на экране ...
Поскольку результат был отчасти взломан, я думаю, что, возможно, я выполняю некоторые дополнительные шаги или дополнительные задачи рендеринга, которые, возможно, не нужны, и замедляют игру?
Что-то ненужное, что вы, ребята, могли бы заметить?
В загрузчике MD2 и 3DS есть функция InitToon (), вызываемая при создании для загрузки шейдера
initToon(){
int i; // Looping Variable ( NEW )
char Line[255]; // Storage For 255 Characters ( NEW )
float shaderData[32][3]; // Storate For The 96 Shader Values ( NEW )
FILE *In = fopen ("Shader.txt", "r"); // Open The Shader File ( NEW )
if (In) // Check To See If The File Opened ( NEW )
{
for (i = 0; i < 32; i++) // Loop Though The 32 Greyscale Values ( NEW )
{
if (feof (In)) // Check For The End Of The File ( NEW )
break;
fgets (Line, 255, In); // Get The Current Line ( NEW )
shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = float(atof (Line)); // Copy Over The Value ( NEW )
}
fclose (In); // Close The File ( NEW )
}
else
return false; // It Went Horribly Horribly Wrong ( NEW )
glGenTextures (1, &shaderTexture[0]); // Get A Free Texture ID ( NEW )
glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // Bind This Texture. From Now On It Will Be 1D ( NEW )
// For Crying Out Loud Don't Let OpenGL Use Bi/Trilinear Filtering! ( NEW )
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage1D (GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB , GL_FLOAT, shaderData); // Upload ( NEW )
}
Это рисунок для анимированной модели MD2:
void MD2Model::drawToon() {
float outlineWidth = 3.0f; // Width Of The Lines ( NEW )
float outlineColor[3] = { 0.0f, 0.0f, 0.0f }; // Color Of The Lines ( NEW )
// ORIGINAL PART OF THE FUNCTION
//Figure out the two frames between which we are interpolating
int frameIndex1 = (int)(time * (endFrame - startFrame + 1)) + startFrame;
if (frameIndex1 > endFrame) {
frameIndex1 = startFrame;
}
int frameIndex2;
if (frameIndex1 < endFrame) {
frameIndex2 = frameIndex1 + 1;
}
else {
frameIndex2 = startFrame;
}
MD2Frame* frame1 = frames + frameIndex1;
MD2Frame* frame2 = frames + frameIndex2;
//Figure out the fraction that we are between the two frames
float frac =
(time - (float)(frameIndex1 - startFrame) /
(float)(endFrame - startFrame + 1)) * (endFrame - startFrame + 1);
// I ADDED THESE FROM NEHE'S TUTORIAL FOR FIRST PASS (TOON SHADE)
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); // Use The Good Calculations ( NEW )
glEnable (GL_LINE_SMOOTH);
// Cel-Shading Code //
glEnable (GL_TEXTURE_1D); // Enable 1D Texturing ( NEW )
glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // Bind Our Texture ( NEW )
glColor3f (1.0f, 1.0f, 1.0f); // Set The Color Of The Model ( NEW )
// ORIGINAL DRAWING CODE
//Draw the model as an interpolation between the two frames
glBegin(GL_TRIANGLES);
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
glNormal3f(normal[0], normal[1], normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
glVertex3f(pos[0], pos[1], pos[2]);
}
}
glEnd();
// ADDED THESE FROM NEHE'S FOR SECOND PASS (OUTLINE)
glDisable (GL_TEXTURE_1D); // Disable 1D Textures ( NEW )
glEnable (GL_BLEND); // Enable Blending ( NEW )
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // Set The Blend Mode ( NEW )
glPolygonMode (GL_BACK, GL_LINE); // Draw Backfacing Polygons As Wireframes ( NEW )
glLineWidth (outlineWidth); // Set The Line Width ( NEW )
glCullFace (GL_FRONT); // Don't Draw Any Front-Facing Polygons ( NEW )
glDepthFunc (GL_LEQUAL); // Change The Depth Mode ( NEW )
glColor3fv (&outlineColor[0]); // Set The Outline Color ( NEW )
// HERE I AM PARSING THE VERTICES AGAIN (NOT IN THE ORIGINAL FUNCTION) FOR THE OUTLINE AS PER NEHE'S TUT
glBegin (GL_TRIANGLES); // Tell OpenGL What We Want To Draw
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
glNormal3f(normal[0], normal[1], normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
glTexCoord2f(texCoord->texCoordX, texCoord->texCoordY);
glVertex3f(pos[0], pos[1], pos[2]);
}
}
glEnd (); // Tell OpenGL We've Finished
glDepthFunc (GL_LESS); // Reset The Depth-Testing Mode ( NEW )
glCullFace (GL_BACK); // Reset The Face To Be Culled ( NEW )
glPolygonMode (GL_BACK, GL_FILL); // Reset Back-Facing Polygon Drawing Mode ( NEW )
glDisable (GL_BLEND);
}
Принимая во внимание, что это функция drawToon в загрузчике 3DS
void Model_3DS::drawToon()
{
float outlineWidth = 3.0f; // Width Of The Lines ( NEW )
float outlineColor[3] = { 0.0f, 0.0f, 0.0f }; // Color Of The Lines ( NEW )
//ORIGINAL CODE
if (visible)
{
glPushMatrix();
// Move the model
glTranslatef(pos.x, pos.y, pos.z);
// Rotate the model
glRotatef(rot.x, 1.0f, 0.0f, 0.0f);
glRotatef(rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(rot.z, 0.0f, 0.0f, 1.0f);
glScalef(scale, scale, scale);
// Loop through the objects
for (int i = 0; i < numObjects; i++)
{
// Enable texture coordiantes, normals, and vertices arrays
if (Objects[i].textured)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (lit)
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
// Point them to the objects arrays
if (Objects[i].textured)
glTexCoordPointer(2, GL_FLOAT, 0, Objects[i].TexCoords);
if (lit)
glNormalPointer(GL_FLOAT, 0, Objects[i].Normals);
glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertexes);
// Loop through the faces as sorted by material and draw them
for (int j = 0; j < Objects[i].numMatFaces; j ++)
{
// Use the material's texture
Materials[Objects[i].MatFaces[j].MatIndex].tex.Use();
// AFTER THE TEXTURE IS APPLIED I INSERT THE TOON FUNCTIONS HERE (FIRST PASS)
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); // Use The Good Calculations ( NEW )
glEnable (GL_LINE_SMOOTH);
// Cel-Shading Code //
glEnable (GL_TEXTURE_1D); // Enable 1D Texturing ( NEW )
glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // Bind Our Texture ( NEW )
glColor3f (1.0f, 1.0f, 1.0f); // Set The Color Of The Model ( NEW )
glPushMatrix();
// Move the model
glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);
// Rotate the model
glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);
// Draw the faces using an index to the vertex array
glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);
glPopMatrix();
}
glDisable (GL_TEXTURE_1D); // Disable 1D Textures ( NEW )
// THIS IS AN ADDED SECOND PASS AT THE VERTICES FOR THE OUTLINE
glEnable (GL_BLEND); // Enable Blending ( NEW )
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // Set The Blend Mode ( NEW )
glPolygonMode (GL_BACK, GL_LINE); // Draw Backfacing Polygons As Wireframes ( NEW )
glLineWidth (outlineWidth); // Set The Line Width ( NEW )
glCullFace (GL_FRONT); // Don't Draw Any Front-Facing Polygons ( NEW )
glDepthFunc (GL_LEQUAL); // Change The Depth Mode ( NEW )
glColor3fv (&outlineColor[0]); // Set The Outline Color ( NEW )
for (int j = 0; j < Objects[i].numMatFaces; j ++)
{
glPushMatrix();
// Move the model
glTranslatef(Objects[i].pos.x, Objects[i].pos.y, Objects[i].pos.z);
// Rotate the model
glRotatef(Objects[i].rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(Objects[i].rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(Objects[i].rot.x, 1.0f, 0.0f, 0.0f);
// Draw the faces using an index to the vertex array
glDrawElements(GL_TRIANGLES, Objects[i].MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects[i].MatFaces[j].subFaces);
glPopMatrix();
}
glDepthFunc (GL_LESS); // Reset The Depth-Testing Mode ( NEW )
glCullFace (GL_BACK); // Reset The Face To Be Culled ( NEW )
glPolygonMode (GL_BACK, GL_FILL); // Reset Back-Facing Polygon Drawing Mode ( NEW )
glDisable (GL_BLEND);
glPopMatrix();
}
Наконец, это функция tex.Use (), которая загружает текстуру BMP и каким-то образом идеально смешивается с затенением Toon
void GLTexture::Use()
{
glEnable(GL_TEXTURE_2D); // Enable texture mapping
glBindTexture(GL_TEXTURE_2D, texture[0]); // Bind the texture as the current one
}
EDIT ---------------------------------------------- ------------------------------
Спасибо всем за совет. Следуя совету Коса, я пересмотрел функцию, чтобы не использовать glBegin / End .... поэтому я сохраняю вершины в std :: vectors после того, как их позиции были рассчитаны, а затем использую glDrawArrays для чтения из вектора. .... Теоретически это должно быть быстрее, но это дает мне гораздо более низкую частоту кадров, чем раньше ... Правильно ли это реализовано?
for(int i = 0; i < numTriangles; i++) {
MD2Triangle* triangle = triangles + i;
for(int j = 0; j < 3; j++) {
MD2Vertex* v1 = frame1->vertices + triangle->vertices[j];
MD2Vertex* v2 = frame2->vertices + triangle->vertices[j];
Vec3f pos = v1->pos * (1 - frac) + v2->pos * frac;
Vec3f normal = v1->normal * (1 - frac) + v2->normal * frac;
if (normal[0] == 0 && normal[1] == 0 && normal[2] == 0) {
normal = Vec3f(0, 0, 1);
}
normals.push_back(normal[0]);
normals.push_back(normal[1]);
normals.push_back(normal[2]);
MD2TexCoord* texCoord = texCoords + triangle->texCoords[j];
textCoords.push_back(texCoord->texCoordX);
textCoords.push_back(texCoord->texCoordY);
vertices.push_back(pos[0]);
vertices.push_back(pos[1]);
vertices.push_back(pos[2]);
}
}
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &textCoords[0]);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3);
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
vertices.clear();
textCoords.clear();
normals.clear();