使用着色器绘制 3D 对象的正确方法是什么?
我有一个 OBJ 和从3DS Max导出的MTL文件是一套完整的国际象棋。在我的最终结果中,我希望能够选择一个游戏块和一个游戏块。然后选择一个方块来移动它。我在组合在一起时遇到问题的元素是:
- 对象选择
- 着色器
我的对象导入器以两种方式工作。
1) 将所有内容与类似材质组合到一个名为 Mesh
的结构中,并循环遍历绘制每个帧的所有 model.meshes
。
2) 识别棋盘、每个方格,以及每个游戏块并将相关的顶点、材质等放入名为 GroupObject
的结构中。它们被放置在 model.objects 中,我在其中循环绘制每个帧。
以下是 #1 和 #2 中描述的每个选项的屏幕截图。在 SS 的顶部 1/2 中,我只是绘制了网格,所有阴影看起来都是正确的,但没有什么是可选的。在底部 1/2,我可以选择一个游戏块或一个方块就可以了,但模型看起来很糟糕。
http://img815.imageshack.us/img815/2158/chesst.png
此外,如果我将着色器与 #2 中的代码一起使用,则平移和平移缩放速度减慢。它几乎完全没有反应。
这是每帧绘制对象所调用的方法。前 2 行注释行是我尝试选项 1 的地方,只需绘制我的结构 Mesh
,它看起来不错。旋转&缩放也很平滑,但我无法选择对象,因为我无法执行 Mesh
的 glPushName。现在,代码设置为循环遍历每个 model.objects,因此我可以运行 glPushName( (int)object ); ,这是拾取/选择对象所必需的游戏块或方块。注释掉的部分是我尝试应用着色器的地方,该着色器在循环 model.objects
时非常慢(循环 model.mesh
时没有减慢速度)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//for (int i = 0; i < model.getNumberOfMeshes(); ++i)
//{
for ( int i = 0; i < (int)model.objects.size(); i++ )
{
ModelOBJ::GroupObject *object = model.objects[i];
glPushName( (int)object );
int faceSize = (int)object->faces.size();
for ( int j = 0; j < faceSize; j++ )
{
ModelOBJ::Face *face = object->faces[j];
pMaterial = &model.getMaterial( face->materialId );
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat *)pMaterial->ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat *)pMaterial->diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *)pMaterial->specular);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);
glDisable(GL_TEXTURE_2D);
drawFace( *face );
//if (pMaterial->bumpMapFilename.empty())
//{
// Per fragment Blinn-Phong code path.
// glUseProgram(blinnPhongShader);
// // Bind the color map texture.
// texture = nullTexture;
// if (enableTextures)
// {
// iter = modelTextures.find(pMaterial->colorMapFilename);
// if (iter != modelTextures.end())
// texture = iter->second;
// }
// glActiveTexture(GL_TEXTURE0);
// glEnable(GL_TEXTURE_2D);
// glBindTexture(GL_TEXTURE_2D, texture);
// // Update shader parameters.
// glUniform1i(glGetUniformLocation(
// blinnPhongShader, "colorMap"), 0);
// glUniform1f(glGetUniformLocation(
// blinnPhongShader, "materialAlpha"), pMaterial->alpha);
//}
}
glPopName();
}
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDisable(GL_BLEND);
这里是drawFace 方法(如果相关)
void GLEngine::drawFace(ModelOBJ::Face &face)
{
if ( (int)face.numVertices <= 3 )
glBegin(GL_TRIANGLES);
else
glBegin(GL_POLYGON);
for ( int v = 0; v < (int)face.numVertices; v++ )
{
if ( (int)face.numUVWs > v && face.UVWs != NULL )
glTexCoord2f(face.UVWs[v]->x, face.UVWs[v]->y);
if ( (int)face.numNormals > v && face.normals != NULL )
glTexCoord3d(face.normals[v]->x, face.normals[v]->y, face.normals[v]->z);
if ( (int)face.numVertices > v && face.vertices != NULL )
glVertex3f(face.vertices[v]->x, face.vertices[v]->y, face.vertices[v]->z);
}
glEnd();
}
这是我正在使用的着色器
Per-fragment Blinn-Phong shader for a single directional light source.
[vert]
varying vec3 normal;
void main()
{
normal = normalize(gl_NormalMatrix * gl_Normal);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
}
[frag]
uniform sampler2D colorMap;
uniform float materialAlpha;
varying vec3 normal;
void main()
{
vec3 n = normalize(normal);
float nDotL = max(0.0, dot(n, gl_LightSource[0].position.xyz));
float nDotH = max(0.0, dot(normal, vec3(gl_LightSource[0].halfVector)));
float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess);
vec4 ambient = gl_FrontLightProduct[0].ambient;
vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL;
vec4 specular = gl_FrontLightProduct[0].specular * power;
vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular;
gl_FragColor = color * texture2D(colorMap, gl_TexCoord[0].st);
gl_FragColor.a = materialAlpha;
}
您将如何将所有这些函数合并在一起?我的第一个想法是绘制所有没有任何材质信息的 model.objects
并使它们透明。然后运行第二个循环并绘制所有带有材质和材质的model.meshes
。使用着色器。但后来我想,当我开始将游戏块移动到新的方块时,我将如何确保将正确的阴影应用于该块的新位置。当我选择透明的 GroupObject 并将其转换为新的正方形时,该块是否仍具有正确的阴影?
解决我遇到的问题的最佳方法是什么?
I have an OBJ & MTL file exported from 3DS Max that is a complete chess set. In my end result, I would like to be able to select a game piece & then select a square to move it too. The elements I am having problems tying together are:
- Object selection
- Shaders
I have my object importer working in two ways.
1) Group everything together with like materials into a struct called Mesh
and loop through all the model.meshes
drawing each frame.
2) Identify the board, each square, & each game piece and place the relevant vertices, material, etc into a struct called GroupObject
. They are placed in model.objects
which I loop through drawing each frame.
Here is a screenshot of what each option described in #1 and #2 look like. In the top 1/2 of the SS, I just draw out the meshes and all the shading looks right, but nothing is selectable. In the bottom 1/2, I can select a game piece or a square just fine, but the model looks like crap.
http://img815.imageshack.us/img815/2158/chesst.png
Additionally, if I use the shader with the code from #2, panning & zooming slows down to a crawl. It is almost completely unresponsive.
Here is the method called to draw the objects every frame. The first 2 commented lines is where I try option 1, and just draw my struct Mesh
's and it looks good. Rotating & Zooming are smooth as well, but I cannot select an object b/c I can't do the glPushName of a Mesh
. Right now, the code is setup looping through each model.objects
so I can run the glPushName( (int)object );
which is necessary for the picking/selection of a game piece or square. The commented out section is where I try and apply the shader that is very slow when looping through model.objects
(no slow down when looping model.mesh
's )
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//for (int i = 0; i < model.getNumberOfMeshes(); ++i)
//{
for ( int i = 0; i < (int)model.objects.size(); i++ )
{
ModelOBJ::GroupObject *object = model.objects[i];
glPushName( (int)object );
int faceSize = (int)object->faces.size();
for ( int j = 0; j < faceSize; j++ )
{
ModelOBJ::Face *face = object->faces[j];
pMaterial = &model.getMaterial( face->materialId );
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat *)pMaterial->ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat *)pMaterial->diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *)pMaterial->specular);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);
glDisable(GL_TEXTURE_2D);
drawFace( *face );
//if (pMaterial->bumpMapFilename.empty())
//{
// Per fragment Blinn-Phong code path.
// glUseProgram(blinnPhongShader);
// // Bind the color map texture.
// texture = nullTexture;
// if (enableTextures)
// {
// iter = modelTextures.find(pMaterial->colorMapFilename);
// if (iter != modelTextures.end())
// texture = iter->second;
// }
// glActiveTexture(GL_TEXTURE0);
// glEnable(GL_TEXTURE_2D);
// glBindTexture(GL_TEXTURE_2D, texture);
// // Update shader parameters.
// glUniform1i(glGetUniformLocation(
// blinnPhongShader, "colorMap"), 0);
// glUniform1f(glGetUniformLocation(
// blinnPhongShader, "materialAlpha"), pMaterial->alpha);
//}
}
glPopName();
}
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glDisable(GL_BLEND);
Here is the drawFace method in case it's relevant
void GLEngine::drawFace(ModelOBJ::Face &face)
{
if ( (int)face.numVertices <= 3 )
glBegin(GL_TRIANGLES);
else
glBegin(GL_POLYGON);
for ( int v = 0; v < (int)face.numVertices; v++ )
{
if ( (int)face.numUVWs > v && face.UVWs != NULL )
glTexCoord2f(face.UVWs[v]->x, face.UVWs[v]->y);
if ( (int)face.numNormals > v && face.normals != NULL )
glTexCoord3d(face.normals[v]->x, face.normals[v]->y, face.normals[v]->z);
if ( (int)face.numVertices > v && face.vertices != NULL )
glVertex3f(face.vertices[v]->x, face.vertices[v]->y, face.vertices[v]->z);
}
glEnd();
}
And here is the shader I am using
Per-fragment Blinn-Phong shader for a single directional light source.
[vert]
varying vec3 normal;
void main()
{
normal = normalize(gl_NormalMatrix * gl_Normal);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
}
[frag]
uniform sampler2D colorMap;
uniform float materialAlpha;
varying vec3 normal;
void main()
{
vec3 n = normalize(normal);
float nDotL = max(0.0, dot(n, gl_LightSource[0].position.xyz));
float nDotH = max(0.0, dot(normal, vec3(gl_LightSource[0].halfVector)));
float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess);
vec4 ambient = gl_FrontLightProduct[0].ambient;
vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL;
vec4 specular = gl_FrontLightProduct[0].specular * power;
vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular;
gl_FragColor = color * texture2D(colorMap, gl_TexCoord[0].st);
gl_FragColor.a = materialAlpha;
}
How would you merge all of these functions together? My first thought is to draw all the model.objects
with none of the material information and make them transparent. Then run through a second loop and draw all the model.meshes
complete with material & using the shader. But then I thought when I start moving game pieces to a new square, how will I make sure the proper shading is applied to the piece's new position. When I select the transparent GroupObject
and it translate's to a new square, will the piece still have the proper shading?
What is the best way to fix the problem I am having?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为什么在
GLEngine::drawFace
中将法线作为纹理坐标发送:您的着色器希望它作为
glNormal3d
,正如您在此处看到的:Why do you send your normal as a texture coordinate in
GLEngine::drawFace
:You shader expect it to be as a
glNormal3d
, as you can see here: