OpenGL ray OBB交集 [英] OpenGL ray OBB intersection

查看:96
本文介绍了OpenGL ray OBB交集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在3D中实现对象拾取,所以我使用glm::unproject方法从屏幕上的点到场景有一个Ray它返回翻转的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;
}

解决方案

我正在使用以下方法:

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

现在,您不需要对象的OBB,也不需要任何交叉检查.而是以计算使用箭头和鼠标移动的对象

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

 //---------------------------------------------------------------------------
#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).然后,只需模仿事件并添加相关的调用即可为每个事件绘制和键控/鼠标事件.

在这里预览它的样子:

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

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

最重要的内容是功能mouse_selectmouse_edit,这些功能将3D全局鼠标位置转换为objetc/control local,从而非常容易检测诸如圆锥体内部,圆柱体内部,旋转角度和平移大小等内容...

I want to implement object picking in 3D so I have a Ray from a point on the screen towards the scene using glm::unproject method "it returns the Y flipped so I use its negative value", the following code success always when the object is centered on the world origin but with another object that is transformed and the camera moved or rotated it may success and may not, i simulated the ray and it is already intersect the object, all coordinates are in the world space.

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

解决方案

I am doing this using:

The idea is to apart of rendering to screen also render index of each object into separate unseen buffer (color attachment, stencil, shadow,...) and than just pick pixel at mouse position from this buffer and depth ... which provides 3D position of the picked point and also index of object that it belongs to. This is very fast O(1) at almost no performance cost.

Now You do not need OBB for your objects nor any intersection checking anymore. Instead have a local coordinate system in form of 4x4 homogenuous matrix with which you can easily convert the 3D position picked by mouse into object local coordinates making the manipulation like translation/rotation of the object really easy.

Here is my older C++ approach of mine for this:

which does not require any additional libs and stuff. How ever I do it now using all above in fusion like this:

//---------------------------------------------------------------------------
#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
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

Unlike the example in the link above this uses a lot of stuff not provided (from my GL engine) so you can not use it directly however it should be enough to grasp the basics. Here some external stuff it uses (not all):

I also use mine dynamic list template so:


List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items

Rendering:

//---------------------------------------------------------------------------
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;
    }
//---------------------------------------------------------------------------

vector and matrix math:

// 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]) is cross product a = b x c
a = vector_mul(b[3],c[3]) is dot product a = (b.c)
vector_one(a[3],b[3]) is unit vector a = b/|b|
vector_copy(a[3],b[3]) is just copy a = b
vector_add(a[3],b[3],c[3]) is adding a = b + c
vector_sub(a[3],b[3],c[3]) is substracting a = b - c
vector_neg(a[3],b[3]) is negation a = -b
vector_ld(a[3],x,y,z) is just loading a = (x,y,z)

The reper class is just holding direct and inverse 4x4 matrix representing 3D coordinate system. Its implementation depends on your coordinate system and gfx notation (matrix row/column major order, multiplication order etc...) Everything you need to implement it is in the 4x4 homogenuous matrix link above.

Now finally the usage:

Here is my BDS2006 C++/VCL/OpenGL project source code:

//---------------------------------------------------------------------------
#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();
    }
//---------------------------------------------------------------------------

You can ignore the VCL and my engine related stuff. For each controlled object you should have its 4x4 transform matrix (reper) and a control component (OpenGLctrl3D). Then just mimic the events and add relevant calls to draw and key/mouse events for each.

Here preview how it looks like:

Sadly my GIF capturer does not capture the mouse cursor so you do not see where I click/drag ... But as you can see my control is rather complex and just OBB would not help much as the rings and arrows are intersecting a lot. The choppy ness is due to GIF capture encoding but when using logarithmic depth buffer you might expect chppyness also for object far from znear plane. To remedy that you can use:

In my example I do not have any objects just single control but you get the idea ... so each object of yours should have its matrix (the same that is used for its rendering) so you just add a control referencing it. In case your objects are dynamicaly added and removed you need to add their add/removal to controls too...

The most important stuff are the functions mouse_select and mouse_edit which converts the 3D global mouse position into objetc/control local one making very easy to detect stuff like inside cone, inside cylinder, angle of rotation and translation size etc ...

这篇关于OpenGL ray OBB交集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆