我正在使用openGL进行maya模拟器应用程序,一切进展顺利。只有一个麻烦,我不能用鼠标计算100%移动物体的准确度。 我使用3D坐标中的箭头缩放和屏幕坐标中的箭头,并将其与一些缩放相乘,以计算屏幕坐标的x和y方向上的运动程度。 。 但是这些计算不能提供高精度,鼠标偏离箭头。我总是在移动的箭头上需要它,这意味着精度几乎是100% 任何对此问题有想法或意见的人都将不胜感激
这是我尝试的一些代码:
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenVertex, &yScreenVertex, &zScreenVertex);
if (axis == 0)
{
gluProject((GLdouble)x3DVertex + 1, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else if (axis == 1)
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex + 1, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex + 1, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
float totalScaleXY = abs(xScreenArrow - xScreenVertex) + abs(yScreenArrow - yScreenVertex);
scaleX = abs(xScreenArrow - xScreenVertex) / totalScaleXY;
scaleY = abs(yScreenArrow - yScreenVertex) / totalScaleXY;
float lengthArrowOnScreen = sqrt(pow((xScreenArrow - xScreenVertex), 2) + pow((yScreenArrow - yScreenVertex), 2));
scale3dAndScreen = 1 / lengthArrowOnScreen;
这是我测试的代码,它不是很准确
答案 0 :(得分:0)
这里有一些丑陋但非常简单的 C ++ 示例,其中包括:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double mview[16],mmodel[16],mproj[16]; // OpenGL matrices
//---------------------------------------------------------------------------
// vector/matrix math
//---------------------------------------------------------------------------
double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); } // = |vector[3]|
void vector_sub(double *c,double *a,double *b) { for (int i=0;i<3;i++) c[i]=a[i]-b[i]; } // c[3] = a[3] - b[3]
void matrix_mul(double *c,double *a,double *b) // c[4x4] = a[4x4] * b [4x4]
{
double q[16];
q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
for(int i=0;i<16;i++) c[i]=q[i];
}
void matrix_mul_vector(double *c,double *a,double *b) // c[3] = a[4x4] * (b[3],1)
{
double q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
for(int i=0;i<3;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
class DragDrop3D // arrow translation controls (you need one for each objet)
{
public:
double *mm; // model matrix
double *mv; // view matrix
double *mp; // projection matrix
double o[3]; // start point
double l[3]; // length of the arrows
double a,b; // size of the arrow head a length, b width/2
int sel; // selected axis
DragDrop3D()
{
mm=NULL; mv=NULL; mp=NULL;
o[0]=-1.0; l[0]=3.5; a=0.5;
o[1]=-1.0; l[1]=3.5; b=0.25;
o[2]=-1.0; l[2]=3.5; sel=-1;
}
DragDrop3D(DragDrop3D& a) { *this=a; }
~DragDrop3D() {}
DragDrop3D* operator = (const DragDrop3D *a) { *this=*a; return this; }
//DragDrop3D* operator = (const DragDrop3D &a) { ...copy... return this; }
void project(double *scr,double *mmvp,double *obj) // obj -> scr
{
matrix_mul_vector(scr,mmvp,obj);
if (fabs(scr[2])>1e-10) scr[2]=1.0/scr[2]; else scr[2]=0.0;
scr[0]*=scr[2];
scr[1]*=scr[2];
}
void draw() // render arrows
{
int e;
double ang,dang=2.0*M_PI/36.0,u0,u1,v0,v1,q=1.0/sqrt((a*a)+(b*b));
// axis lines
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); glVertex3dv(o); glVertex3d(o[0]+l[0],o[1],o[2]);
glColor3f(0.0,1.0,0.0); glVertex3dv(o); glVertex3d(o[0],o[1]+l[1],o[2]);
glColor3f(0.0,0.0,1.0); glVertex3dv(o); glVertex3d(o[0],o[1],o[2]+l[2]);
glEnd();
// cones
glBegin(GL_TRIANGLES);
u1=b*cos(-dang);
v1=b*sin(-dang);
for (e=1,ang=0.0;e;ang+=dang)
{
if (ang>=2.0*M_PI) { ang=2.0*M_PI; e=0; }
u0=u1; u1=b*cos(ang);
v0=v1; v1=b*sin(ang);
// X
if (sel==0) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.1,0.1);
glNormal3f(a*q,(u0+u1)*0.5*q,(v0+v1)*0.5*q);
glVertex3d(o[0]+l[0] ,o[1] ,o[2] );
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glNormal3f(-1.0,0.0,0.0);
glVertex3d(o[0]+l[0]-a,o[1],o[2]);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
// Y
if (sel==1) glColor3f(0.0,1.0,0.0); else glColor3f(0.1,0.5,0.1);
glNormal3f((u0+u1)*0.5*q,a*q,(v0+v1)*0.5*q);
glVertex3d(o[0] ,o[1]+l[1] ,o[2] );
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glNormal3f(0.0,-1.0,0.0);
glVertex3d(o[0] ,o[1]+l[1]-a,o[2] );
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
// Z
if (sel==2) glColor3f(0.0,0.0,1.0); else glColor3f(0.1,0.1,0.5);
glNormal3f((v0+v1)*0.5*q,(u0+u1)*0.5*q,a*q);
glVertex3d(o[0] ,o[1] ,o[2]+l[2] );
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glNormal3f(0.0,0.0,-1.0);
glVertex3d(o[0] ,o[1] ,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
}
glEnd();
}
bool mouse(double mx,double my,TShiftState sh) // handle mouse events return if redraw is needed
{
if ((mm==NULL)||(mv==NULL)||(mp==NULL)) return false;
int ql; double p[3],mmvp[16]; bool _redraw=false;
// MVP = M*V*P
matrix_mul(mmvp,mm,mv);
matrix_mul(mmvp,mmvp,mp);
// convert screen coords to <-1,+1> GL NDC
mx= (2.0*mx/double(xs))-1.0;
my=1.0-(2.0*my/double(ys)) ;
// mouse left button state (last,actual)
ql=sh.Contains(ssLeft);
// select (no mouse button)
if (!ql)
{
int sel0=sel; sel=-1;
// arrowhead center screen x,y distance to mouse select if close
p[0]=o[0]+l[0]-0.5*a; p[1]=o[1]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=0; // X
p[1]=o[1]+l[1]-0.5*a; p[0]=o[0]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=1; // Y
p[2]=o[2]+l[2]-0.5*a; p[1]=o[1]; p[0]=o[0]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=2; // Z
_redraw=(sel0!=sel);
}
// drag arrowhead center into mouse position (active button)
if ((ql)&&(sel>=0)&&(sel<3))
{
int i;
double len0,len;
double p0[3],dp[3]={0.0,0.0,0.0},t,dt,q[3]={mx,my,0.0};
// selected arrowhead position,direction
for (i=0;i<3;i++) p0[i]=o[i];
p0[sel]+=l[sel]-0.5*a;
dp[sel]=1.0;
// "closest" intersection between axis/mouse_ray
for (len0=-1.0,t=0.0,dt=1.0;fabs(dt)>1e-5;t+=dt)
{
// position on axis p(t) = p0 + t*dp
for (i=0;i<3;i++) p[i]=p0[i]+(t*dp[i]);
// len = distance to mouse
project(p,mmvp,p);
vector_sub(p,p,q); p[2]=0.0;
len=vector_len(p);
// handle iteration step
if (len0<-0.5) len0=len;
if (len>len0) dt=-0.1*dt;
len0=len;
}
// translate by t
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+sel]=t;
matrix_mul(mm,m,mm);
_redraw=true;
}
return _redraw;
}
};
//---------------------------------------------------------------------------
DragDrop3D ctrl; // I got single cube so single arrow drag drop control suffice
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
// init
static bool _init=true;
if (_init)
{
_init=false;
// M,V init matrices
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(0.0,0.0,-25.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glLoadIdentity();
glTranslated(-2.0,-1.0,-1.0);
glRotated(-35.0,0.0,1.0,0.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mmodel);
glPopMatrix();
// matrices -> ctrl
glGetDoublev(GL_PROJECTION_MATRIX,mproj);
ctrl.mp=mproj;
ctrl.mv=mview;
ctrl.mm=mmodel;
}
// matrices -> OpenGL
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(mview);
glMultMatrixd(mmodel);
// draw VAO cube
ctrl.draw();
vao_draw(); // here render your object instead
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
vao_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
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;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixd(mview);
glTranslatef(0,0,dz);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glPopMatrix();
gl_draw();
}
//---------------------------------------------------------------------------
// mouse events
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
//---------------------------------------------------------------------------
它基于以下内容:simple complete GL+VAO/VBO+GLSL+shaders example in C++您可以在其中找到更多信息以及我正在使用的gl_simple.h
。代码基于 VCL ,因此请忽略 VCL 内容并将事件移植到您的编程类型中。
该程序仅使用 OpenGL 1.0 API (表示我的对象的VAO / VBO多维数据集的appart),因此math.h
函数除sin,cos,fabs
之外不需要任何其他内容。
这个想法是在世界/场景中为每个可控对象设置一个控件DragDrop3D
对象。首先是每个初始化,然后在每个鼠标事件上为每个鼠标事件调用mouse
,并在需要时重绘。
这里有小预览:
可悲的是,我的 GIF 捕获 SW 并没有捕获鼠标光标,但它与运动完全匹配。
我太懒了直接计算轴之间的交集,而是迭代找到最接近的匹配(for
循环与dt
)。那部分可以用交叉方程代替。
轴与主体坐标系(mm,mmodel
)的x,y,z轴有界。
对于更多对象,您还应添加锁定,以便只能选择一个对象...
PS。见相关: - OpenGL ray OBB intersection