用唯一的颜色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
这几天为了实现物体的颜色拾取功能,遇到问题了,于是把这个给翻译了一下,希望对大家有所帮助……
用唯一的颜色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
这几天为了实现物体的颜色拾取功能,遇到问题了,于是把这个给翻译了一下,希望对大家有所帮助……
相关文章推荐
- OpenGL Selection Using Unique Color IDs
- 如何实现唯一的ID编号
- 重写Combobox实现颜色带(ColorRamp)Symbol预览与选择——总结
- C# 实现完整功能的截图控件(2)-实现颜色和字体选择控件
- 颜色选择 Color Selection@GTK+ 2.0 中文教程连载
- 重写Combobox实现颜色带(ColorRamp)Symbol预览与选择——总结
- 【转】C# 实现完整功能的截图控件(2)-实现颜色和字体选择控件
- 创建一个如下的窗体,并在窗体上放置colorDialog控件。实现功能:1)程序运行时,单击打开颜色对话框按钮,可选择颜色,并以所选颜色作为窗体背景色
- 数据库的唯一标示符(ID)的选择
- 【JAVA】系统中关于自定义比例选择的功能实现
- 实现图片编辑软件的“填充颜色”功能
- 使用RadioGroup控件实现选项选择功能
- RELIEF Feature Selection(RELIEF特征选择) Python实现
- javascript实现 color颜色格式转换【 rgb和十六进制的转换】
- 【android】TextView文本的 “选择文字”“复制” “粘贴” 功能的实现
- Mysql实现全局唯一ID
- android如何实现文件打开方式可供选择功能。
- 双卡双待 getDeviceId unique device ID IMEI 不唯一 会变问题
- 关于Odoo 选择供应商实现产品过滤功能
- 本文实例讲述了AngularJS+bootstrap实现动态选择商品功能。分享给大家供大家参考,具体如下: 项目中后台一个商品库,新建活动时动态选择所需商品