我想实现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;
}
答案 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)。然后,只需模仿事件并添加相关的调用即可为每个事件绘制和键控/鼠标事件。
在这里预览外观:
可悲的是,我的GIF捕获器无法捕获鼠标光标,因此您看不到我单击/拖动的位置...但是,您可以看到我的控件相当复杂,而OBB却无济于事,因为圆环和箭头相交很多。波动性是由于GIF捕获编码引起的,但是当使用对数深度缓冲区时,您可能会期望远离znear平面的对象也产生波动性。为了弥补这一点,您可以使用:
在我的示例中,我没有任何对象,只有一个控件,但是您知道了...所以您的每个对象都应具有其矩阵(用于渲染的矩阵相同),因此您只需添加引用它的控件即可。如果动态地添加和删除了对象,则还需要向控件添加其添加/删除...
最重要的东西是函数mouse_select
和mouse_edit
,这些函数将3D全局鼠标位置转换为objetc / control local,从而非常容易检测诸如圆锥体内,圆柱体内,旋转角度之类的东西和翻译大小等...