OpenGL ray OBB交集

时间:2018-10-20 07:41:15

标签: c++ opengl

我想实现3D对象拾取,所以我使用Ray方法从屏幕上的某个点到场景有一个glm::unproject,“它返回翻转的Y,所以我使用它的负值”,以下代码始终在对象位于世界原点中心的情况下成功执行,但在另一个对象被转换且相机移动或旋转的情况下,它可能成功,也可能不成功,我模拟了射线,它已经与该对象相交,所有坐标都在世界空间。

bool Engine::IntersectBox(Ray& ray,BoundingBox* boundingBox,GLfloat& distance){
V3* v=boundingBox->getVertices();
glm::vec4 vec(v->x,v->y,v->z,1);
vec=boundingBox->getMatrix()*vec;
GLfloat minX=vec.x;
GLfloat minY=vec.y;
GLfloat minZ=vec.z;
GLfloat maxX=vec.x;
GLfloat maxY=vec.y;
GLfloat maxZ=vec.z;
for(int i=0;i<8;i++){
    v++;
    vec=glm::vec4(v->x,v->y,v->z,1);
    vec=boundingBox->getMatrix()*vec;
    minX=minX<vec.x?minX:vec.x;
    minY=minY<vec.y?minY:vec.y;
    minZ=minZ<vec.z?minZ:vec.z;
    maxX=maxX>vec.x?maxX:vec.x;
    maxY=maxY>vec.y?maxY:vec.y;
    maxZ=maxZ>vec.z?maxZ:vec.z;
}
GLfloat tMin = 0.0f;
GLfloat tMax = 100000.0f;
glm::vec3 delta=glm::vec3(boundingBox->getMatrix()[3])-ray.getOrigin();
{
    glm::vec3 xAxis=boundingBox->getMatrix()[0];
    GLfloat e = glm::dot(xAxis, delta);
    GLfloat f = glm::dot(ray.getDirection(), xAxis);
    if ( fabs(f) > 0.001f ) { // Standard case
        GLfloat min = (e+minX)/f; // Intersection with the "left" plane
        GLfloat max = (e+maxX)/f; // Intersection with the "right" plane
        if(min<max){
            tMin=min;
            tMax=max;
        }
        else{
            tMin=max;
            tMax=min;
        }
        if (tMax < tMin)
            return false;
    }
    else{
        if(-e+minX > 0.0f || -e+maxX < 0.0f)
            return false;
    }
}
{
    glm::vec3 yAxis=boundingBox->getMatrix()[1];
    GLfloat e = glm::dot(yAxis, delta);
    GLfloat f = glm::dot(ray.getDirection(), yAxis);
    if ( fabs(f) > 0.001f ){
        GLfloat min = (e+minY)/f;
        GLfloat max = (e+maxY)/f;
        if(min<max){
            tMin=glm::max(tMin,min);
            tMax=glm::min(tMax,max);
        }
        else{
            tMin=glm::max(tMin,max);
            tMax=glm::min(tMax,min);
        }
        if (tMax < tMin)
            return false;
    }else{
        if(-e+minY > 0.0f || -e+maxY < 0.0f)
            return false;
    }
}
{
    glm::vec3 zAxis=boundingBox->getMatrix()[2];
    GLfloat e = glm::dot(zAxis, delta);
    GLfloat f = glm::dot(ray.getDirection(),zAxis);
    if ( fabs(f) > 0.001f ){
        GLfloat min = (e+minZ)/f;
        GLfloat max = (e+maxZ)/f;
        if(min<max){
            tMin=glm::max(tMin,min);
            tMax=glm::min(tMax,max);
        }
        else{
            tMin=glm::max(tMin,max);
            tMax=glm::min(tMax,min);
        }
        if (tMax < tMin)
            return false;
    }else{
        if(-e+minZ > 0.0f || -e+maxZ < 0.0f)
            return false;
    }
}
distance = tMin;
return true;
}

Screenshot

1 个答案:

答案 0 :(得分:1)

我正在使用:

这个想法是除了渲染到屏幕之外,还将每个对象的索引渲染到单独的看不见的缓冲区(颜色附件,模板,阴影等)中,而不是仅从此缓冲区和深度中选取鼠标位置处的像素...它提供了拾取点的3D位置以及它所属的对象的索引。 O(1)非常快,几乎没有性能损失。

现在,您不需要对象的OBB,也不需要任何交叉检查。而是使用4x4 homogenuous matrix形式的局部坐标系,您可以轻松地将鼠标拾取的3D位置转换为对象局部坐标,从而使像对象的平移/旋转这样的操作非常容易。

这是我的较老的C ++方法:

,不需要任何其他库和其他东西。我现在如何将以上所有内容融合在一起使用,就像这样:

//---------------------------------------------------------------------------
#ifndef _OpenGLctrl3D_h
#define _OpenGLctrl3D_h
//---------------------------------------------------------------------------
#include "gl/OpenGL3D_double.cpp" // vector and matrix math keyboard and mouse handler
//---------------------------------------------------------------------------
static reper NULL_rep;
AnsiString dbg="";
//---------------------------------------------------------------------------
class OpenGLctrl3D      // arrow translation controls (you need one for each objet)
    {
public:
    reper *rep;             // points to bounded object model matrix
    double l[3],r0,r1,r2,a; // l  - size of each straight arrow
                            // r0 - tube radius
                            // r1 - arrow radius
                            // r2 - arced arrow radius
                            // a  - arrowhead size
    double a0,a1,aa;        // start,end, cone size [rad] of the arced arrow

    OpenGLctrl3D()
        {
        rep=&NULL_rep;
        l[0]=3.5; r0=0.05; a0=  0.0*deg; a=0.10;
        l[1]=3.5; r1=0.25; a1=360.0*deg;
        l[2]=3.5; r2=0.50; aa= 15.0*deg;
        }
    OpenGLctrl3D(OpenGLctrl3D& a)   { *this=a; }
    ~OpenGLctrl3D() {}
    OpenGLctrl3D* operator = (const OpenGLctrl3D *a) { *this=*a; return this; }
    //OpenGLctrl3D* operator = (const OpenGLctrl3D &a) { ...copy... return this; }

    void draw(int sel);                 // render arrows
    void mouse_select(void* sys);       // handle [camera local] mouse events (no active button)
    void mouse_edit  (void* sys);       // handle [camera local] mouse events (active button)
    };
//---------------------------------------------------------------------------
class OpenGLctrls3D     // arrow translation controls (you need one for each objet)
    {
public:
    reper *eye;                         // camera matrix
    double per[16],ndc[16];             // perspective and viewport matrices
    TShiftState sh; double mw[3],ms[3]; // actual mouse [buttons],[world units],[camera units]
    bool _redraw;                       // redraw needed?
    int sel0,sel1,_sel;                 // actualy selected item ctrl[sel0].axis=sel1 the _sel is for iteration variable
    double psel[3];                     // selected point [object local units]

    List<OpenGLctrl3D> ctrl;
    OpenGLctrls3D() { eye=&NULL_rep; matrix_one(per); matrix_one(ndc); ctrl.num=0; }
    OpenGLctrls3D(OpenGLctrls3D& a) { *this=a; }
    ~OpenGLctrls3D(){}
    OpenGLctrls3D* operator = (const OpenGLctrls3D *a) { *this=*a; return this; }
    //OpenGLctrls3D* operator = (const OpenGLctrls3D &a) { ...copy... return this; }
    void add(reper &rep,double *l,double r0,double r1,double r2,double a)   // add new control bounded to rep
        {
        // l  - size of each straight arrow
        // r0 - tube radius
        // r1 - arrow radius
        // r2 - arced arrow radius
        // a  - arrowhead size
        ctrl.add();
        OpenGLctrl3D *c=ctrl.dat+ctrl.num-1;
        c->rep=&rep;
        vector_copy(c->l,l);
        c->r0=r0;
        c->r1=r1;
        c->r2=r2;
        c->a=a;
        }
    void resize(int x0,int y0,int xs,int ys)
        {
        matrix_one(ndc);
        ndc[ 0]=+divide(2.0,double(xs));
        ndc[ 5]=-divide(2.0,double(ys));
        ndc[12]=-1.0;
        ndc[13]=+1.0;
        glGetDoublev(GL_PROJECTION_MATRIX,per);
        mouse_refresh();
        }
    void draw()
        {
        int i;
        OpenGLctrl3D *c;
        for (c=ctrl.dat,i=0;i<ctrl.num;i++,c++)
            {
            glPushMatrix();
            c->rep->use_rep();
            glMatrixMode(GL_MODELVIEW);
            glMultMatrixd(c->rep->rep);
            if (i==sel0) c->draw(sel1);
             else        c->draw(-1);
            glMatrixMode(GL_MODELVIEW);
            glPopMatrix();
            }
        }
    bool mouse(double mx,double my,TShiftState _sh) // handle mouse events return if redraw is needed
        {
        // mouse depth [camera units]
        ms[0]=mx; ms[1]=my; sh=_sh;
        ms[2]=glReadDepth(mx,divide(-2.0,ndc[5])-my-1,per);
        // mouse x,y [pixel] ->  <-1,+1> NDC
        matrix_mul_vector(ms,ndc,ms);
        // mouse x,y <-1,+1> NDC -> [camera units]
        scr2world(mw,ms);
        return mouse_refresh();
        }
    bool mouse_refresh()    // call after any view change
        {
        _redraw=false;
        if (!sh.Contains(ssLeft))
            {
            int _sel0=sel0; sel0=-1;
            int _sel1=sel1; sel1=-1;
            for (_sel=0;_sel<ctrl.num;_sel++) ctrl.dat[_sel].mouse_select(this);
            _redraw=((_sel0!=sel0)||(_sel1!=sel1));
            }
        else{
            if ((sel0>=0)&&(sel0<ctrl.num)) ctrl.dat[sel0].mouse_edit(this);
            }
        return _redraw;
        }
    void world2scr(double *s,double *w)
        {
        // camera [LCS]
        eye->g2l(s,w);
        // [camera units] -> <-1,+1> NDC
        s[0]=-divide(s[0]*per[0],s[2]);
        s[1]=-divide(s[1]*per[5],s[2]);
        }
    void scr2world(double *w,double *s)
        {
        // <-1,+1> NDC -> [camera units]
        w[0]=-divide(s[0]*s[2],per[0]);
        w[1]=-divide(s[1]*s[2],per[5]);
        w[2]=s[2];
        // world [GCS]
        eye->l2g(w,w);
        }
    };
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void OpenGLctrl3D::draw(int sel)
    {
    if (sel==0) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.0,0.0); glArrowx(0.0,0.0,0.0,r0,r1,l[0],a);
    if (sel==1) glColor3f(0.0,1.0,0.0); else glColor3f(0.0,0.5,0.0); glArrowy(0.0,0.0,0.0,r0,r1,l[1],a);
    if (sel==2) glColor3f(0.0,0.0,1.0); else glColor3f(0.0,0.0,0.5); glArrowz(0.0,0.0,0.0,r0,r1,l[2],a);
    if (sel==3) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.0,0.0); glCircleArrowyz(0.0,0.0,0.0,r2,r0,r1,a0,a1,aa);
    if (sel==4) glColor3f(0.0,1.0,0.0); else glColor3f(0.0,0.5,0.0); glCircleArrowzx(0.0,0.0,0.0,r2,r0,r1,a0,a1,aa);
    if (sel==5) glColor3f(0.0,0.0,1.0); else glColor3f(0.0,0.0,0.5); glCircleArrowxy(0.0,0.0,0.0,r2,r0,r1,a0,a1,aa);
    }
//---------------------------------------------------------------------------
void OpenGLctrl3D::mouse_select(void *_sys)
    {
    OpenGLctrls3D *sys=(OpenGLctrls3D*)_sys;
    int i,x,y,z; double p[3],q[3],pm[3],t,r;
    // mouse [object local units]
    rep->g2l(pm,sys->mw);
    // straight arrows
    for (i=0;i<3;i++)
        {
        t=pm[i]; pm[i]=0.0; r=vector_len(pm); pm[i]=t;
        t=divide(l[i]-t,a);
        if ((t>=0.0)&&(t<=1.0)&&(r<=r1*t))  // straight cone
            {
            sys->sel0=sys->_sel;
            sys->sel1=i;
            vector_ld(sys->psel,0.0,0.0,0.0); sys->psel[i]=pm[i];
            }
        }
    // arced arrows
    for (i=0;i<3;i++)
        {
        if (i==0){ x=1; y=2; z=0; }
        if (i==1){ x=2; y=0; z=1; }
        if (i==2){ x=0; y=1; z=2; }
        t=atanxy(pm[x],pm[y]);
        p[x]=r2*cos(t);
        p[y]=r2*sin(t);
        p[z]=0.0;
        vector_sub(q,p,pm);
        r=vector_len(q);
        if (r<=r0*2.0)
            {
            sys->sel0=sys->_sel;
            sys->sel1=i+3;
            vector_copy(sys->psel,p);
            }
        }
    }
//---------------------------------------------------------------------------
void OpenGLctrl3D::mouse_edit(void *_sys)
    {
    OpenGLctrls3D *sys=(OpenGLctrls3D*)_sys;
    // drag straight arrows (active button)
    if ((sys->sel1>=0)&&(sys->sel1<3))
        {
        double z0,z1,z2,t0;
        double q[3],q0[3],q1[3],t;
        // q0 = mouse change in 2D screen space
        rep->l2g(q0,sys->psel);                 // selected point position
        sys->world2scr(q0,q0);
        vector_sub(q0,q0,sys->ms); q0[2]=0.0;   // actual mouse position
        // q1 = selected axis step in 2D screen space
        rep->l2g(q,sys->psel);                  // selected point position
        sys->world2scr(q,q);
        vector_copy(q1,sys->psel);              // axis step
        q1[sys->sel1]+=1.0;
        rep->l2g(q1,q1);
        sys->world2scr(q1,q1);
        vector_sub(q1,q1,q); q1[2]=0.0;
        // compute approx change
        t=-vector_mul(q0,q1);                   // dot(q0,q1)
        // enhance precision of t
        int i; double len0,len,dq[3]={0.0,0.0,0.0},dt;
        // selected arrow direction
        dq[sys->sel1]=1.0;
        // closest point on axis to psel
        for (len0=-1.0,dt=0.25*t;fabs(dt)>1e-5;t+=dt)
            {
            // position on axis p(t) = p0 + t*dp
            for (i=0;i<3;i++) q[i]=sys->psel[i]+(t*dq[i]);
            // len = distance to mouse
            rep->l2g(q,q);
            sys->world2scr(q,q);
            vector_sub(q,q,sys->ms); q[2]=0.0;
            len=vector_len2(q);
            // handle iteration step
            if (len0<-0.5) len0=len;
            if (len>len0) dt=-0.1*dt;
            len0=len;
            }
        // translate by change
        double m[16]=
            {
            1.0,0.0,0.0,0.0,
            0.0,1.0,0.0,0.0,
            0.0,0.0,1.0,0.0,
            0.0,0.0,0.0,1.0,
            };
        m[12+sys->sel1]=t;
        rep->use_rep();
        matrix_mul(rep->rep,m,rep->rep);
        rep->_inv=0;
        sys->_redraw=true;
        }
    // rotate arced arrows (active button)
    if ((sys->sel1>=3)&&(sys->sel1<6))
        {
        int i,x,y,z; double t,t0,tt,dt,len,len0,q[3];
        if (sys->sel1==3){ x=1; y=2; z=0; }
        if (sys->sel1==4){ x=2; y=0; z=1; }
        if (sys->sel1==5){ x=0; y=1; z=2; }
        t0=atanxy(sys->psel[x],sys->psel[y]);
        // initial search
        for (i=10,t=0.0,dt=divide(1.0,i),len0=-1.0;i--;t+=dt)
            {
            q[x]=r2*cos(t0+t);
            q[y]=r2*sin(t0+t);
            q[z]=0.0;
            rep->l2g(q,q);
            sys->world2scr(q,q);
            vector_sub(q,q,sys->ms); q[2]=0.0;
            len=vector_len2(q);
            if ((len0<-0.5)||(len<len0)) { len0=len; tt=t; }
            }
        // closest angle to psel
        for (t=tt;fabs(dt)>0.1*deg;t+=dt)
            {
            q[x]=r2*cos(t0+t);
            q[y]=r2*sin(t0+t);
            q[z]=0.0;
            rep->l2g(q,q);
            sys->world2scr(q,q);
            vector_sub(q,q,sys->ms); q[2]=0.0;
            len=vector_len2(q);
            // handle iteration step
            if (len>len0) dt=-0.1*dt; else { tt=t; }
            len0=len;
            }
        // rotate
        if (sys->sel1==3) rep->lrotx(tt);
        if (sys->sel1==4) rep->lroty(tt);
        if (sys->sel1==5) rep->lrotz(tt);
        sys->_redraw=true;
        }
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

与上面的链接中的示例不同,此示例使用了许多未提供的东西(来自我的GL引擎),因此您不能直接使用它,但足以掌握基础知识。这里使用了一些外部的东西(不是全部):

我也使用我的动态列表模板,所以:


List<double> xxx;double xxx[];相同
xxx.add(5);5添加到列表的末尾
xxx[7]访问数组元素(安全)
xxx.dat[7]访问数组元素(不安全但快速的直接访问)
xxx.num是数组的实际使用大小
xxx.reset()清除数组并设置xxx.num=0
xxx.allocate(100)100个项目预分配空间

渲染:

//---------------------------------------------------------------------------
void glArrowx(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r0,GLfloat r1,GLfloat l0,GLfloat l1)
    {
    double pos[3]={ x0, y0, z0};
    double dir[3]={1.0,0.0,0.0};
    glArrow3D(pos,dir,r0,r1,l0,l1);
    }
//---------------------------------------------------------------------------
void glArrowy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r0,GLfloat r1,GLfloat l0,GLfloat l1)
    {
    double pos[3]={ x0, y0, z0};
    double dir[3]={0.0,1.0,0.0};
    glArrow3D(pos,dir,r0,r1,l0,l1);
    }
//---------------------------------------------------------------------------
void glArrowz(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r0,GLfloat r1,GLfloat l0,GLfloat l1)
    {
    double pos[3]={ x0, y0, z0};
    double dir[3]={0.0,0.0,1.0};
    glArrow3D(pos,dir,r0,r1,l0,l1);
    }
//---------------------------------------------------------------------------
void glCircleArrowxy(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa)
    {
    double pos[3]={ x0, y0, z0};
    double nor[3]={0.0,0.0,1.0};
    double bin[3]={1.0,0.0,0.0};
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa);
    }
//---------------------------------------------------------------------------
void glCircleArrowyz(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa)
    {
    double pos[3]={ x0, y0, z0};
    double nor[3]={1.0,0.0,0.0};
    double bin[3]={0.0,1.0,0.0};
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa);
    }
//---------------------------------------------------------------------------
void glCircleArrowzx(GLfloat x0,GLfloat y0,GLfloat z0,GLfloat r,GLfloat r0,GLfloat r1,GLfloat a0,GLfloat a1,GLfloat aa)
    {
    double pos[3]={ x0, y0, z0};
    double nor[3]={0.0,1.0,0.0};
    double bin[3]={0.0,0.0,1.0};
    glCircleArrow3D(pos,nor,bin,r,r0,r1,a0,a1,aa);
    }
//---------------------------------------------------------------------------
void glArrow3D(double *pos,double *dir,double r0,double r1,double l0,double l1)
    {
    int i,n=_glCircleN;
    double nn=1.0,a,da=divide(pi2,n),p[3],dp[3],x[3],y[3],p0[3],p1[3],c,s,q;
    if (l0<0.0) { da=-da; nn=-nn; l1=-l1; }
    // TBN
         if (fabs(dir[0]-dir[1])>1e-6) vector_ld(x,dir[1],dir[0],dir[2]);
    else if (fabs(dir[0]-dir[2])>1e-6) vector_ld(x,dir[2],dir[1],dir[0]);
    else if (fabs(dir[1]-dir[2])>1e-6) vector_ld(x,dir[0],dir[2],dir[1]);
    else                       vector_ld(x,1.0,0.0,0.0);
    vector_one(dir,dir);
    vector_mul(x,x,dir);
    vector_mul(y,x,dir);
    vector_mul(p0,dir,l0-l1); vector_add(p0,pos,p0);
    vector_mul(p1,dir,l0   ); vector_add(p1,pos,p1);
    // disc r0, 0
    vector_len(x,x,r0);
    vector_len(y,y,r0);
    glBegin(GL_TRIANGLE_FAN);
    vector_mul(p,dir,-nn);
    glNormal3dv(p);
    glVertex3dv(pos);
    for (a=0.0,i=0;i<=n;i++,a+=da)
        {
        vector_mul(dp,x,cos(a)); vector_add(p,pos,dp);
        vector_mul(dp,y,sin(a)); vector_add(p,p  ,dp);
        glVertex3dv(p);
        }
    glEnd();

    // tube r0, 0..l0-l1
    q=divide(1.0,r0);
    glBegin(GL_QUAD_STRIP);
    for (a=0.0,i=0;i<=n;i++,a+=da)
        {
        vector_mul( p,x,cos(a));
        vector_mul(dp,y,sin(a)); vector_add(dp,p ,dp); vector_add(p,pos,dp);
        vector_mul(dp,dp,q);
        glNormal3dv(dp);
        glVertex3dv(p);
        vector_sub(p,p,pos);
        vector_add(p,p,p0);
        glVertex3dv(p);
        }
    glEnd();

    // disc r1, l0-l1
    vector_len(x,x,r1);
    vector_len(y,y,r1);
    glBegin(GL_TRIANGLE_FAN);
    vector_mul(p,dir,-nn);
    glNormal3dv(p);
    glVertex3dv(p0);
    for (a=0.0,i=0;i<=n;i++,a+=da)
        {
        vector_mul(dp,x,cos(a)); vector_add(p,p0 ,dp);
        vector_mul(dp,y,sin(a)); vector_add(p,p  ,dp);
        glVertex3dv(p);
        }
    glEnd();

    // cone r1..0, l0-l1..l0
    glBegin(GL_TRIANGLE_STRIP);
    q=divide(1.0,sqrt((l1*l1)+(r1*r1)));
    for (a=0.0,i=0;i<=n;i++,a+=da)
        {
        vector_mul( p,x,cos(a));
        vector_mul(dp,y,sin(a)); vector_add(dp,p ,dp); vector_add(p,p0,dp);
        vector_mul(dp,dp,q);
        glNormal3dv(dp);
        glVertex3dv(p);
        glVertex3dv(p1);
        }
    glEnd();
    }
//---------------------------------------------------------------------------
void glCircleArrow3D(double *pos,double *nor,double *bin,double r,double r0,double r1,double a0,double a1,double aa)
    {
    int e,i,j,N=3*_glCircleN;
    double U[3],V[3],u,v;
    double a,b,da,db=pi2/double(_glCircleN-1),a2,rr;
    double *ptab,*p0,*p1,*n0,*n1,*pp,p[3],q[3],c[3],n[3],tan[3];
    // buffers
    ptab=new double [12*_glCircleN]; if (ptab==NULL) return;
    p0=ptab+(0*_glCircleN);
    n0=ptab+(3*_glCircleN);
    p1=ptab+(6*_glCircleN);
    n1=ptab+(9*_glCircleN);
    // prepare angles
    a2=a1; da=db; aa=fabs(aa);
    if (a0>a1) { da=-da; aa=-aa; }
    a1-=aa;
    // compute missing basis vectors
    vector_copy(U,nor);         // U is normal to arrow plane
    vector_mul(tan,nor,bin);    // tangent is perpendicular to normal and binormal
    // arc interpolation a=<a0,a2>
    for (e=0,j=0,a=a0;e<5;j++,a+=da)
        {
        // end conditions
        if (e==0)   // e=0
            {
            if ((da>0.0)&&(a>=a1)) { a=a1; e++; }
            if ((da<0.0)&&(a<=a1)) { a=a1; e++; }
            rr=r0;
            }
        else{       // e=1,2,3,4
            if ((da>0.0)&&(a>=a2)) { a=a2; e++; }
            if ((da<0.0)&&(a<=a2)) { a=a2; e++; }
            rr=r1*fabs(divide(a-a2,a2-a1));
            }
        // compute actual tube segment center c[3]
        u=r*cos(a);
        v=r*sin(a);
        vector_mul(p,bin,u);
        vector_mul(q,tan,v);
        vector_add(c,p,  q);
        vector_add(c,c,pos);
        // V is unit direction from arrow center to tube segment center
        vector_sub(V,c,pos);
        vector_one(V,V);
        // tube segment interpolation
        for (b=0.0,i=0;i<N;i+=3,b+=db)
            {
            u=cos(b);
            v=sin(b);
            vector_mul(p,U,u);      // normal
            vector_mul(q,V,v);
            vector_add(n1+i,p,q);
            vector_mul(p,n1+i,rr);  // vertex
            vector_add(p1+i,p,c);
            }
        if (e>1)                    // recompute normals for cone
            {
            for (i=3;i<N;i+=3)
                {
                vector_sub(p,p0+i  ,p1+i);
                vector_sub(q,p1+i-3,p1+i);
                vector_mul(p,p,q);
                vector_one(n1+i,p);
                }
            vector_sub(p,p0    ,p1);
            vector_sub(q,p1+N-3,p1);
            vector_mul(p,q,p);
            vector_one(n1,p);
            if (da>0.0) for (i=0;i<N;i+=3) vector_neg(n1+i,n1+i);
            if (e==  3) for (i=0;i<N;i+=3) vector_copy(n0+i,n1+i);
            }
        // render base disc
        if (!j)
            {
            vector_mul(n,V,U);
            glBegin(GL_TRIANGLE_FAN);
            glNormal3dv(n);
            glVertex3dv(c);
            if (da<0.0) for (i=  0;i< N;i+=3) glVertex3dv(p1+i);
            else        for (i=N-3;i>=0;i-=3) glVertex3dv(p1+i);
            glEnd();
            }
        // render tube
        else{
            glBegin(GL_QUAD_STRIP);
            if (da<0.0) for (i=0;i<N;i+=3)
                {
                glNormal3dv(n0+i); glVertex3dv(p0+i);
                glNormal3dv(n1+i); glVertex3dv(p1+i);
                }
            else for (i=0;i<N;i+=3)
                {
                glNormal3dv(n1+i); glVertex3dv(p1+i);
                glNormal3dv(n0+i); glVertex3dv(p0+i);
                }
            glEnd();
            }
        // swap buffers
        pp=p0; p0=p1; p1=pp;
        pp=n0; n0=n1; n1=pp;
        // handle r0 -> r1 edge
        if (e==1) a-=da;
        if ((e==1)||(e==2)||(e==3)) e++;
        }
    // release buffers
    delete[] ptab;
    }
//---------------------------------------------------------------------------
void glLinearArrow3D(double *pos,double *dir,double r0,double r1,double l,double al)
    {
    int e,i,N=3*_glCircleN;
    double U[3],V[3],W[3],u,v;
    double a,da=pi2/double(_glCircleN-1),r,t;
    double *ptab,*p0,*p1,*n1,*pp,p[3],q[3],c[3],n[3];
    // buffers
    ptab=new double [9*_glCircleN]; if (ptab==NULL) return;
    p0=ptab+(0*_glCircleN);
    p1=ptab+(3*_glCircleN);
    n1=ptab+(6*_glCircleN);
    // compute basis vectors
    vector_one(W,dir);
    vector_ld(p,1.0,0.0,0.0);
    vector_ld(q,0.0,1.0,0.0);
    vector_ld(n,0.0,0.0,1.0);
    a=fabs(vector_mul(W,p));            pp=p; t=a;
    a=fabs(vector_mul(W,q)); if (t>a) { pp=q; t=a; }
    a=fabs(vector_mul(W,n)); if (t>a) { pp=n; t=a; }
    vector_mul(U,W,pp);
    vector_mul(V,U,W);
    vector_mul(U,V,W);
    for (e=0;e<4;e++)
        {
        // segment center
        if (e==0) { t=0.0;  r= r0; }
        if (e==1) { t=l-al; r= r0; }
        if (e==2) { t=l-al; r= r1; }
        if (e==3) { t=l;    r=0.0; }
        vector_mul(c,W,t);
        vector_add(c,c,pos);
        // tube segment interpolation
        for (a=0.0,i=0;i<N;i+=3,a+=da)
            {
            u=cos(a);
            v=sin(a);
            vector_mul(p,U,u);      // normal
            vector_mul(q,V,v);
            vector_add(n1+i,p,q);
            vector_mul(p,n1+i,r);   // vertex
            vector_add(p1+i,p,c);
            }
        if (e>2)                    // recompute normals for cone
            {
            for (i=3;i<N;i+=3)
                {
                vector_sub(p,p0+i  ,p1+i);
                vector_sub(q,p1+i-3,p1+i);
                vector_mul(p,p,q);
                vector_one(n1+i,p);
                }
            vector_sub(p,p0    ,p1);
            vector_sub(q,p1+N-3,p1);
            vector_mul(p,q,p);
            vector_one(n1,p);
            }
        // render base disc
        if (!e)
            {
            vector_neg(n,W);
            glBegin(GL_TRIANGLE_FAN);
            glNormal3dv(n);
            glVertex3dv(c);
            for (i=0;i<N;i+=3) glVertex3dv(p1+i);
            glEnd();
            }
        // render tube
        else{
            glBegin(GL_QUAD_STRIP);
            for (i=0;i<N;i+=3)
                {
                glNormal3dv(n1+i);
                glVertex3dv(p0+i);
                glVertex3dv(p1+i);
                }
            glEnd();
            }
        // swap buffers
        pp=p0; p0=p1; p1=pp;
        }
    // release buffers
    delete[] ptab;
    }
//---------------------------------------------------------------------------

向量和矩阵数学:

// cross product: W = U x V
W.x=(U.y*V.z)-(U.z*V.y)
W.y=(U.z*V.x)-(U.x*V.z)
W.z=(U.x*V.y)-(U.y*V.x)
// dot product: a = (U.V)
a=U.x*V.x+U.y*V.y+U.z*V.z
// abs of vector a = |U|
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))


vector_mul(a[3],b[3],c[3])是跨产品a = b x c
a = vector_mul(b[3],c[3])是点积a = (b.c)
vector_one(a[3],b[3])是单位向量a = b/|b|
vector_copy(a[3],b[3])只是副本a = b
vector_add(a[3],b[3],c[3])正在添加a = b + c
vector_sub(a[3],b[3],c[3])减去a = b - c
vector_neg(a[3],b[3])是否定词a = -b
vector_ld(a[3],x,y,z)刚刚加载a = (x,y,z)

reper类仅保留表示3D坐标系的正反4x4矩阵。它的实现取决于您的坐标系和gfx表示法(矩阵行/列的主要顺序,乘法顺序等)。实现它所需的一切都在上面的 4x4均匀矩阵链接中。

现在终于可以使用了:

这是我的BDS2006 C ++ / VCL / OpenGL项目源代码:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "OpenGLctrl3D.h" // only this is important
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1; // this form/window
//---------------------------------------------------------------------------
reper eye,obj; // camera and object matrices
double perspective[16]; // projection matrix
OpenGLscreen scr; // my GL engine can ignore this
OpenGLctrls3D ctrl; // control component (important)
bool _redraw=true; // need repaint ?
//---------------------------------------------------------------------------
void gl_draw()      // main rendering code
    {
    _redraw=false;
    scr.cls();
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);

    // set view
    glMatrixMode(GL_MODELVIEW);
    eye.use_inv();
    glLoadMatrixd(eye.inv);

    // draw all controls
    ctrl.draw();

    // draw all objects
    glPushMatrix();
    obj.use_rep();
    glMatrixMode(GL_MODELVIEW);
    glMultMatrixd(obj.rep);

    glColor3f(1.0,1.0,1.0);
//  glBox(0.0,0.0,0.0,1.0,1.0,1.0);

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    scr.exe();
    scr.rfs();
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    // application init
    scr.init(this);
    scr.views[0].znear=0.1;
    scr.views[0].zfar=100.0;
    scr.views[0].zang=60.0;
    // matrices
    eye.reset();
    eye.gpos_set(vector_ld(0.0,0.0,+5.0));
    eye.lrotz(25.0*deg);
    obj.reset();
    obj.gpos_set(vector_ld(-1.0,-0.5,-1.0));
    obj.lroty(-35.0*deg);
    // controls
    ctrl.eye=&eye;
    ctrl.add(obj,vector_ld(2.5,2.5,2.5),0.04,0.10,1.25,0.5);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    // application exit
    scr.exit();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    // window resize
    scr.resize();
    ctrl.resize(scr.x0,scr.y0,scr.xs,scr.ys);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    // window repaint
    gl_draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
    {
    // mouse wheel translates camera (like zoom)
    GLfloat dz=2.0;
    if (WheelDelta>0) dz=-dz;
    eye.lpos_set(vector_ld(0.0,0.0,dz));
    ctrl.mouse_refresh();
    _redraw=true;
    }
//---------------------------------------------------------------------------
// mouse events
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { _redraw|=ctrl.mouse(X,Y,Shift); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y)   { _redraw|=ctrl.mouse(X,Y,Shift); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)                     { _redraw|=ctrl.mouse(X,Y,Shift); }
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
    {
//  double *p=ctrl.pm; Caption=AnsiString().sprintf("(%7.3lf,%7.3lf,%7.3lf)",p[0],p[1],p[2]);
    Caption=dbg;
    //  obj.lroty(3.0*deg); ctrl.mouse_refresh(); _redraw=true;
    if (_redraw) gl_draw();
    }
//---------------------------------------------------------------------------

您可以忽略VCL和我的引擎相关的内容。对于每个受控对象,您都应具有其4x4变换矩阵(reper)和一个控件组件(OpenGLctrl3D)。然后,只需模仿事件并添加相关的调用即可为每个事件绘制和键控/鼠标事件。

在这里预览外观:

preview

可悲的是,我的GIF捕获器无法捕获鼠标光标,因此您看不到我单击/拖动的位置...但是,您可以看到我的控件相当复杂,而OBB却无济于事,因为圆环和箭头相交很多。波动性是由于GIF捕获编码引起的,但是当使用对数深度缓冲区时,您可能会期望远离znear平面的对象也产生波动性。为了弥补这一点,您可以使用:

在我的示例中,我没有任何对象,只有一个控件,但是您知道了...所以您的每个对象都应具有其矩阵(用于渲染的矩阵相同),因此您只需添加引用它的控件即可。如果动态地添加和删除了对象,则还需要向控件添加其添加/删除...

最重要的东西是函数mouse_selectmouse_edit,这些函数将3D全局鼠标位置转换为objetc / control local,从而非常容易检测诸如圆锥体内,圆柱体内,旋转角度之类的东西和翻译大小等...