使用着色器绘制 3D 对象的正确方法是啥?

Posted

技术标签:

【中文标题】使用着色器绘制 3D 对象的正确方法是啥?【英文标题】:What is the proper way to draw my 3D objects with shaders?使用着色器绘制 3D 对象的正确方法是什么? 【发布时间】:2011-01-10 19:59:52 【问题描述】:

我有一个从 3DS Max 导出的 OBJ 和 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's,它看起来不错。旋转和缩放也很流畅,但我无法选择对象 b/c 我无法执行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 并将其转换为一个新的正方形时,这幅作品是否仍具有正确的阴影?

解决我遇到的问题的最佳方法是什么?

【问题讨论】:

【参考方案1】:

为什么要将法线作为纹理坐标发送到GLEngine::drawFace

glTexCoord3d(face.normals[v]->x, face.normals[v]->y, face.normals[v]->z);

您的着色器希望它是 glNormal3d,如您在此处看到的:

normal = normalize(gl_NormalMatrix * gl_Normal);

【讨论】:

嗯。这似乎是一个错误。我今晚会改变它,看看会发生什么 另外,你认为这与为什么在使用带有 drawFace 的着色器时一切都会慢下来有关吗? 我相信当我从 3ds max 导出时,我强迫它们成为三角形,所以我会在 face.size() 循环之外尝试 glBegin 和 glEnd。我今晚回来汇报。非常感谢您的回复! 我的棋子看起来好多了!板上的方块都是白色的,但我想这是一些照明问题。性能更好,但仍然波涛汹涌。我可以尝试使用模板缓冲区来拾取和绘制所有具有相似材料的顶点以提高性能。 img18.imageshack.us/img18/7186/chess2f.png

以上是关于使用着色器绘制 3D 对象的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

模糊边缘的着色器,或如何绘制光线

如何使用OpenGL着色器在平面上绘制没有透视的纹理?

HLSL高级着色器语言怎样在unity3d只用,使用的步骤是啥

如何使用计算着色器计算值并将它们存储在 3D 纹理中?

具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕

Unity3D - 将在 iOS 设备上剪辑纹理的着色器