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

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);  

同样的,你们中有些人可能会想为什么在glReadPixelsy坐标要这样写,我们传递进去的参数是

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

这几天为了实现物体的颜色拾取功能,遇到问题了,于是把这个给翻译了一下,希望对大家有所帮助……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值