在openGL中挑选颜色

时间:2012-01-08 20:13:32

标签: opengl graphics 3d lwjgl picking

我一直在尝试实施颜色选择,它只是正常工作。问题是,如果最初用不同颜色绘制我的模型用于采摘(我的意思是,我给每个三角形不同的颜色,这是他的id颜色),它工作正常(没有纹理或任何东西..),但如果我放置模型的纹理,并且单击鼠标时我通过给每个三角形赋予不同的颜色来绘制模型,它不起作用.. 这是代码:

public int selection(int x, int y) {        
    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glDisable(GL11.GL_TEXTURE_2D);

    IntBuffer viewport = BufferUtils.createIntBuffer(16); 
    ByteBuffer pixelbuff = BufferUtils.createByteBuffer(16);

    GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);              

    this.render(this.mesh);

    GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, pixelbuff);

    for (int m = 0; m < 3; m++)
        System.out.println(pixelbuff.get(m));

   GL11.glEnable(GL11.GL_TEXTURE_2D);
   GL11.glEnable(GL11.GL_LIGHTING);

    return 0;
}


public void render(GL_Mesh m, boolean inPickingMode)
{
    GLMaterial[] materials = m.materials;   // loaded from the .mtl file
    GLMaterial mtl;
    GL_Triangle t;
    int currMtl = -1;
    int i = 0;

    // draw all triangles in object
    for (i=0; i < m.triangles.length; ) {
        t = m.triangles[i];

        // activate new material and texture
        currMtl = t.materialID;
        mtl = (materials != null && materials.length>0 && currMtl >= 0)? materials[currMtl] : defaultMtl;
        mtl.apply();
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, mtl.textureHandle);

        // draw triangles until material changes
        for ( ; i < m.triangles.length && (t=m.triangles[i])!=null && currMtl == t.materialID; i++) {
                drawTriangle(t, i, inPickingMode);

        }
    }
}

private void drawTriangle(GL_Triangle t,int i,boolean inPickingMode){

    if (inPickingMode) {
        byte[] triColor = this.triangleToColor(i);

        GL11.glColor3ub((byte)triColor[2], (byte)triColor[1], (byte)triColor[0]);
    }

    GL11.glBegin(GL11.GL_TRIANGLES);

    GL11.glTexCoord2f(t.uvw1.x, t.uvw1.y);
    GL11.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z);
    GL11.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z);

    GL11.glTexCoord2f(t.uvw2.x, t.uvw2.y);
    GL11.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z);
    GL11.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z);

    GL11.glTexCoord2f(t.uvw3.x, t.uvw3.y);
    GL11.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z);
    GL11.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z);

    GL11.glEnd();
}

正如你所看到的,我有一个选择函数,每次点击鼠标时都会调用它,然后我禁用亮化和纹理,然后我再次以独特的颜色渲染场景,然后读取像素缓冲区,呼吁: GL11.glReadPixels(x,y,1,1,GL11.GL_RGB,GL11.GL_UNSIGNED_BYTE,pixelbuff); 给了我错误的价值 ..它驱使我疯狂! 顺便说一句,主渲染函数是渲染(mesh m,boolean inPickingMode),你可以看到,你也可以看到鼠标点击之前模型上有纹理..

1 个答案:

答案 0 :(得分:1)

这个例子有几个问题。

首先,单击鼠标时不会清除颜色和深度缓冲区(这会导致带有颜色多边形的场景混合到带有纹理多边形的场景中 - 然后它不起作用)。你需要打电话:

GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

其次,在采色时使用材料可能是个坏主意。我不熟悉GLMaterial类,但它可能会启用GL_COLOR_MATERIAL或其他一些修改最终颜色的东西,即使禁用了光照也是如此。试试这个:

if(!inPickingMode) { // === add this line ===
    // activate new material and texture
    currMtl = t.materialID;
    mtl = (materials != null && materials.length>0 && currMtl >= 0)? materials[currMtl] : defaultMtl;
    mtl.apply();
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, mtl.textureHandle);
} // === and this line ===

接下来,这与颜色选择无关,你经常无缘无故地调用glBegin()。您可以在三角形绘制循环之前在render()中调用它(但不应该改变结果的样子):

GL11.glBegin(GL11.GL_TRIANGLES);
// draw triangles until material changes
for ( ; i < m.triangles.length && (t=m.triangles[i])!=null && currMtl == t.materialID; i++) {
        drawTriangle(t, i, inPickingMode);
}
GL11.glEnd();

---现在我回答的问题超出原来的问题---

关于颜色选择的问题是,渲染器只有有限的位数来表示颜色(例如每个通道只有5位),因此您需要使用没有设置这些位的颜色。在移动设备上执行此操作可能是个坏主意。

如果您的对象足够简单(可以用球体表示,用于拾取),那么使用光线追踪来拾取对象可能是个好主意。这很简单,你的想法是你采用模型视图 - 投影矩阵的逆,并通过它转换点(mouse_x,mouse_y,-1)和(mouse_x,mouse_y,+ 1),这将给你鼠标的位置在远处的视图平面附近,在物体空间中。您需要做的就是减去它们以获得光线的方向(原点位于近平面),然后您可以使用此光线(google ray - sphere intersection)选择对象。

float[] mvp = new float[16]; // this is your modelview-projection
float mouse_x, mouse_y; // those are mouse coordinates (in -1 to +1 range)
// inputs

float[] mvp_inverse = new float[16];
Matrix.invertM(mvp_inverse, 0, mvp, 0);
// inverse the matrix

float nearX = mvp_inverse[0 * 4 + 0] * mouse_x +
              mvp_inverse[1 * 4 + 0] * mouse_y +
              mvp_inverse[2 * 4 + 0] * -1 +
              mvp_inverse[3 * 4 + 0];
float nearY = mvp_inverse[0 * 4 + 1] * mouse_x +
              mvp_inverse[1 * 4 + 1] * mouse_y +
              mvp_inverse[2 * 4 + 1] * -1 +
              mvp_inverse[3 * 4 + 1];
float nearZ = mvp_inverse[0 * 4 + 2] * mouse_x +
              mvp_inverse[1 * 4 + 2] * mouse_y +
              mvp_inverse[2 * 4 + 2] * -1 +
              mvp_inverse[3 * 4 + 2];
float nearW = mvp_inverse[0 * 4 + 3] * mouse_x +
              mvp_inverse[1 * 4 + 3] * mouse_y +
              mvp_inverse[2 * 4 + 3] * -1 +
              mvp_inverse[3 * 4 + 3];
// transform the near point

nearX /= nearW;
nearY /= nearW;
nearZ /= nearW;
// dehomogenize the coordinate

float farX = mvp_inverse[0 * 4 + 0] * mouse_x +
             mvp_inverse[1 * 4 + 0] * mouse_y +
             mvp_inverse[2 * 4 + 0] * +1 +
             mvp_inverse[3 * 4 + 0];
float farY = mvp_inverse[0 * 4 + 1] * mouse_x +
             mvp_inverse[1 * 4 + 1] * mouse_y +
             mvp_inverse[2 * 4 + 1] * +1 +
             mvp_inverse[3 * 4 + 1];
float farZ = mvp_inverse[0 * 4 + 2] * mouse_x +
             mvp_inverse[1 * 4 + 2] * mouse_y +
             mvp_inverse[2 * 4 + 2] * +1 +
             mvp_inverse[3 * 4 + 2];
float farW = mvp_inverse[0 * 4 + 3] * mouse_x +
             mvp_inverse[1 * 4 + 3] * mouse_y +
             mvp_inverse[2 * 4 + 3] * +1 +
             mvp_inverse[3 * 4 + 3];
// transform the far point

farX /= farW;
farY /= farW;
farZ /= farW;
// dehomogenize the coordinate

float rayX = farX - nearX, rayY = farY - nearY, rayZ = farZ - nearZ;
// ray direction

float orgX = nearX, orgY = nearY, orgZ = nearZ;
// ray origin

最后 - 一个调试建议:尝试使用inPickingMode设置为true进行渲染,这样你就可以在屏幕上看到你实际绘制的是什么。如果你看到纹理或灯光,那就出问题了。