您的位置:首页 > 产品设计 > UI/UE

用唯一的颜色id编号实现OpenGL选择功能(OpenGL Selection Using Unique Color IDs )

2010-06-17 17:15 417 查看
OpenGL Selection Using Unique Color IDs
用唯一的颜色id编号实现OpenGL选择功能
引言
有好几种方式可以实现物体拾取. 利用 OpenGL你可以利用专用的选择缓冲区,可以让你选择场景里的物体,每个物体已经预先给定唯一编号。. 这种方法的入门可以在这儿找到: OpenGL:导学:拾取
这儿还有另一种方法,这种方法不仅可以用在OpenGL apps下,也可以用在 DirectX apps. 例为了这篇导学,下面给出的代码将使用OpenGL.
它是如何起作用的
本来这种方法就是有效的,是因为每一个物体都被赋予了一个唯一的颜色在我们的场景中. 既然我们用24位颜色结构,这意味着我们可以有很多的物体可以有唯一的颜色。如果我们希望知道那个物体被使用者单击了,我们仅仅需要按照下面的做即可:

把场景纹理渲染,光线,雾效关闭了
用唯一的颜色渲染每一个物体
读取鼠标单击处的颜色缓冲区后面的数据
遍历物体列表来查看颜色id是否匹配
如果找到了一个,我们实现了一次选择
这是极其容易引用的并且不是依赖于API函数。
因此我们必须要做的第一件事,就是为我们场景里的每一物体声明一个基类。这个基类必须要注意初始化每一个物体的颜色id。这个可以在类中声明一个静态变量来实现。
gColorID[3], 并将它设置为黑色(0, 0, 0). 从这儿,每一个物体被建立,gColorID是递增的。第一个元素或红色通道首先递增赋值。然后当它的值递增到255, 它将被重新设回到0 并给第二个元素或绿色通道递增赋值。第三个也就是蓝色通道做相同处理。
class BaseObject
{
private:
unsigned char m_colorID[3];
static unsigned char gColorID[3];

public:
BaseObject()
{
m_colorID[0] = gColorID[0];
m_colorID[1] = gColorID[1];
m_colorID[2] = gColorID[2];

gColorID[0]++;
if(gColorID[0] > 255)
{
gColorID[0] = 0;
gColorID[1]++;
if(gColorID[1] > 255)
{
gColorID[1] = 0;
gColorID[2]++;
}
}
}

~BaseObject() {};
};

unsigned char BaseObject::gColorID[3] = {0, 0, 0};
现在我们在我们的游戏或app中声明的每一个类都应该继承这个基类。所以假设你想要声明一个场景物体类,你可以简单的插入一个函数仅用它的颜色id来渲染物体。因此在下面的示例代码中在picking函数的下部就是来渲染场景物体用一个固定的颜色,物体的颜色id颜色是特别的.
class SceneObject : public BaseObject
{
private:
float m_position[3];
string m_name;

public:
SceneObject();
~SceneObject();

void Render();
void Picking()
{
// 设置网格位置
glPushMatrix();
glTranslatef(m_position[0], m_position[1], m_position[2]);

glColor3f(m_colorID[0]/255.0f, m_colorID[1]/255.0f, m_colorID[2]/255.0f);

// 在这儿渲染物体的顶点

glPopMatrix();
}
};
class SceneObject : public BaseObject
{
private:
float m_position[3];
string m_name;

public:
SceneObject();
~SceneObject();

void Render();
void Picking()
{
//设置网格位置
glPushMatrix();
glTranslatef(m_position[0], m_position[1], m_position[2]);

glColor3f(m_colorID[0]/255.0f, m_colorID[1]/255.0f, m_colorID[2]/255.0f);

//在这儿渲染物体的顶点

glPopMatrix();
}
};
现在和注明的那样在我们需要关闭纹理,光线和雾效之前。我们可能仅仅是想要单击鼠标按键时才显示物体选择。所以在鼠标按键事件中,我们渲染场景中每一个物体到颜色缓冲区,读取颜色信息并搜寻我们的物体列表。
void MouseDownEvent(int x, int y)
{
// 关闭纹理,光线和雾效
glDisable(GL_TEXTURE_2D);
glDisable(GL_FOG);
glDisable(GL_LIGHTING);

// 渲染场景里的每一个物体
// 假设每一个物体被存储在一个叫SceneObjects的容器里
list<SceneObject *>::iterator itr = SceneObjects.begin();
while(itr != SceneObjects.end())
{
(*itr)->Picking();
itr++;
}

// 从帧缓存中获取颜色信息
unsigned char pixel[3];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);

glReadPixels(x, viewport[3] - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);

// 现在我们拾取的屏幕像素被存储到pixel[3]
// 所以我们遍历我们的物体列表来搜寻我们选择选择的物体
itr = SceneObjects.begin();
while(itr != SceneObjects.end())
{
if((*itr)->m_colorID[0] == pixel[0] && (*itr)->m_colorID[1] == pixel[1] && (*itr)->m_colorID[2] == pixel[2])
{
// 选择出标记的物体
SetSelected((*itr);
break;
}
itr++;
}
}
那就是它!程序运行相当迅速,取决于你的场景中有多少物体。它是独立的API并且可以被直接执行。如果因为一些疯狂的原因,你感觉仅仅用24位不能表示你场景里的所有的物体,那么你可以把alpha通道增加到每一个物体的colorI D中,然后再读取像素,你可以把GL_RGB改为GL_RGBA。
unsigned char pixels[4];
glReadPixels(x, viewport[3] - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
同样的,你们中有些人可能会想为什么在glReadPixels的y坐标要这样写,我们传递进去的参数是
viewport[3] - y

这是因为OpenGL 设置窗口的方式的原因, 不同的是窗口的左下角是原点。而在Windows 中左上角是原点,我们必须用窗口的高度减去鼠标的y坐标来获得正确的OpenGL 窗口y坐标。
为了在实践中看一下这项技术的效果,你可以在运行地面编辑软件Freeworld3D。这个软件用了这项技术.不仅仅是为了实现物体选择,还为了当平移,旋转和缩放物体时实现轴线选择(Axis selection不知道翻译的真却不?) This software uses this technique, not only for object selection, but also for Axis selection when translating, rotating and scaling objects. 这是一个很强大并且很灵活的实现拾取的方法。
从这儿可以检索到原文 http://gpwiki.org/index.php/OpenGL_Selection_Using_Unique_Color_IDs

这几天为了实现物体的颜色拾取功能,遇到问题了,于是把这个给翻译了一下,希望对大家有所帮助……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: