如何在VC++中用OpenGL绘制NURBS曲线曲面

2024-05-16

1. 如何在VC++中用OpenGL绘制NURBS曲线曲面

基本几何图元是OpenGL进行建模的最基本的方法,但其对较复杂真实物体的建模则比较困难。对于这些复杂物体的建模,需要用到OpenGL基本库和功能库函数(gl库和glu库)以对图元进行扩展并完成法向计算、曲线生成和曲面构造等内容。这种对基本图元的扩展实际也就是对点、线及多边形的扩展。OpenGL中定义的点可具有不同大小的尺寸,其扩展的函数形式为:


void glPointSize(GLfloat size);    

  其参数size以象素为单位设置了点的宽度,其值必须为正,缺省值1.0。对于线的扩展,可通过下面的函数来分别指定其宽度和绘制类型: 


void glLineWidth(GLfloat width);
void glLineStipple(GLint factor,GLushort pattern);    

  glLineWidth()的参数width以象素为单位指定线宽,其值必须为正,缺省值为1.0。glLineStipple()的参数factor为对模式进行拉伸的比例因子,参数pattern指定了线的模式(例如11001100将绘制一条虚线,为1时绘制,为0时不绘制)。该函数只有在启用了函数glEnable(GL_LINE_STIPPLE)后才可以使用,当不再使用时调用glDisable(GL_LINE_STIPPLE)将其关闭。扩展多边形的绘制模式包括全填充式、轮廓点式、轮廓线式及图案填充式等几种。使用时,首先调用glPolygonMode()设置多边形的模式设置:


void glPolygonMode(GLenum face,GLenum mode);    
  
  参数face为GL_FRONT、GL_BACK或GL_FRONT_AND BACK;mode取值可以是GL_POINT、GL_LINE或GL_FILL,分别表示多边型的轮廓点、轮廓线和填充模式的绘制方式。缺省设置为填充模式。设置完成后可进行图案填充的设置:


void glPolygonStipple(const GLubyte *mask);    

  其参数mask必须为一指向32×32大小的位图的指针,值为1时绘制、为0不绘制。该函数的使用同样也需要进行如下启动、关闭设置:


glEnable(GL_POLYGON-STIPPLE);
glDisable(GL_POLYGON_STIPPLE);    

  复杂模型的建模不同与简单模型的建模,在简单模型中一个平面上各点的法向(mormal vector)是一样的,均等于此平面的法向。对于复杂模型中由众多小的平面多边形逼近而成的曲面,其每个顶点的法向量都不一样,因此曲面上每个点的法向计算结果根据采取的不同算法而有不同的结果。OpenGL只提供赋予当前顶点法向量的函数,而不提供对法向量计算的方法,法向量的计算需要由开发者来完成。下面给出一种简单的计算方法:


void getNormal(GLfloat gx[3],GLfloat gy[3],GLfloat gz[3],GLfloat *ddnv)
  {
   GLfloat w0,w1,w2,v0,v1,v2,nr,nx,ny,nz;
   w0=gx[0]-gx[1]; 
   w1=gy[0]-gy[1];
   w2=gz[0]-gz[1];
   v0=gx[2]-gx[1];
   v1=gy[2]-gy[1];
   v2=gz[2]-gz[1];
   nx=(w1*v2-w2*v1);
   ny=(w2*v0-w0*v2);
   nz=(w0*v1-w1*v0);
   nr=sqrt(nx*nx+ny*ny+nz*nz);
   ddnv[0]=nx/nr;
   ddnv[1]=ny/nr; 
   ddnv[2]=nz/nr;
  }    
  
  其参数gx[3],gy[3]和gz[3]为逼近曲面的一个三角形的三个顶点P0,P1和P2。通过计算矢量P0-P1与矢量P2-P1的叉乘而得到其平面法向量,并在归一化后保存到由参数ddnv所指向的数组中。至于顶点法向的计算则多是取邻近平面法向量的均值。OpenGL提供的法向定义函数为:


  void glNormal3{bsifd}(TYPE nx,TYPE ny,TYPE nz);
  void glNormal3{bsifd}v(const TYPE *v);

如何在VC++中用OpenGL绘制NURBS曲线曲面

2. 用Vc++和opengl画图

(2)建立照相机并进行三维观察变换
先做以下约定
1.坐标系:
 (1)右手系,与屏幕对应:
  y轴向上;
  x轴向右;
  z轴向外;
 (2)向限规定:
  a. XOY, YOZ, ZOX 三个基准平面把空间分为八个向限.
  b. ZOX平面( +Y )轴方向四个向限中,YOZ平面( +X )轴方向、XOY平面( +Z )轴方向为0向限,逆时针方向依次为1,2,3向限.
  c. ZOX平面( -Y )轴方向四个向限中,YOZ平面( +X )轴方向、XOY平面( +Z )轴方向为4向限,
    逆时针方向依次为5,6,7向限.
  
3.角度规定:
 (1)右手旋系,姆指指向轴的正向,其余手指弯曲的方向为旋转的正方向.
 (2)方位角Azimuth,标识符A,以Y轴为轴心,+X轴为起点;
 (3)仰角Elevation,标识符E,以X轴为轴心,+Z轴为起点;
 (4)倾角Roll,标识符R,以Z轴为轴心,+Y轴为起点;

4.物体在物体空间中的初始坐标规定:
 (1)初始位置,POS(0,0,0);
 (2)初始角度,A=90,E=180,R=0;
 (3)初始前向量( 0, 0, -1 );
 (4)初始上向量( 0, 1, 0 );
 (5)初始右向量( 0, 0, 1 );
    
5.求把物体变换到世界空间的世界变换矩阵M_world:
 (1)绕Z轴旋转(物体当前R);
 (2)绕X轴旋转(物体当前E - 物体初始E);
 (3)绕Y轴旋转(物体当前A - 物体初始A);
 (4)平移(物体当前POS);
    
6.求物体在世界空间的当前的向前,向上,向右向量:
 (1)绕Z轴旋转(物体当前R);
 (2)绕X轴旋转(物体当前E - 物体初始E);
 (3)绕Y轴旋转(物体当前A - 物体初始A);
 (4)分别用此矩阵变换物体的初始向前向量,初始向上向量,初始向右向量得到.

7.光照计算时,把光源变换到物体空间:
 (1)平移(物体初始POS - 物体当前POS);
 (2)绕Y轴旋转(物体初始A - 物体当前A);
 (3)绕X轴旋转(物体初始E - 物体当前E);
 (4)绕Z轴旋转(物体初始R - 物体当前R);
  也就是M_wrold的逆矩阵,这样物体的法向量就完全正确的了。

    
8.摄像机在世界空间中的初始坐标规定:
 (1)初始位置,POS(0,0,0);
 (2)初始角度,A=90,E=180,R=0;
 (3)初始向前向量( 0, 0, -1 );
 (4)初始向上向量( 0, 1, 0 );
 (5)初始向右向量( 0, 0, 1 );
    
9.求把物体由世界空间变换到摄像机空间的摄像机变换矩阵M_camera:
 (1)平移(摄像机初始POS - 摄像机当前POS);
 (2)绕Y轴旋转(摄像机初始A - 摄像机当前A);
 (3)绕X轴旋转(摄像机初始E - 摄像机当前E);
 (4)绕Z轴旋转(摄像机初始R - 摄像机当前R);
    
10.求摄像机当前的向前,向上,向右向量:
 (1)绕Z轴旋转(摄像机当前R);
 (2)绕X轴旋转(摄像机当前E - 摄像机初始E);
 (3)绕Y轴旋转(摄像机当前A - 摄像机初始A);
 (4)分别用此矩阵变换摄像机的初始向前向量,初始向上向量,初始向右向量得到。

11.物体的最终变换矩阵由:M_world X M_camera X M_projection 构成。

下面是我以此思想实现的照相机类:
class CCamera 
 {
 protected:
  CVertex3f m_position; //位置
  float m_azimuth;  //方位角
  float m_elevation;  //仰角
  float m_roll;   //转角

 private:
  //更新摄影机
  void Update( void );
      
 public:
  CCamera(){ Initialize(); }
  ~CCamera(){}
  
  //初始化摄影机
  void Initialize();
  
  /////////////////////////////
  //操纵摄影机
  ///////////////////////////
  //设置摄影机位置
  void SetPosition( float x, float y, float z );
  void SetPosition( CVertex3f &Pos );
  //设置摄影机方位角
  void  SetAzimuth( float angle );
  //设置摄影机仰角
  void  SetElevation( float angle );
  //设置摄影机倾角 
  void  SetRoll( float angle );
  //跟踪空间某目标点
  void Target( float targetx, float targety, float targetz );
  void Target( CVertex3f &target );
   
  //用空间信息驱动摄影机
  void Drive( CSpaceInfo& SpaceInfo );
  
  //////////////////////////////
  //使用摄影机
  ////////////////////////////
  void GetMatrix( CMatrix& matrix );   //获得变换矩阵
  void GetContraryMatrix( CMatrix& matrix );  //获得逆向变换矩阵
    
  //获得摄影机XYZ轴向量 
  bool GetXYZAxialVector( CVector3f* pVector );
   
  ////////////////////////////////////////
  //获得摄影机各种信息
  //////////////////////////////////////////
  void GetPosition( CVertex3f &Pos );
  float GetAzimuth();
  float GetElevation();
  float GetRoll();
  
 };


//////////////////////////////////
//CCamera implement
//////////////////////////

//摄影机初始化
void CCamera::Initialize()
{
 m_position.Set( 0, 0, 0 );
 m_azimuth = 90;
 m_elevation = 180;
 m_roll = 0;
}

/////////////////////////////
//操纵摄影机
///////////////////////////
//设置摄影机位置
void CCamera::SetPosition( float x, float y, float z )
{
 m_position.Set( x, y, z );
}
void CCamera::SetPosition( CVertex3f &Pos )
{
 m_position.Copy( Pos );
}

//设置方位角
void  CCamera::SetAzimuth( float angle )
{
 m_azimuth = angle;
}

//设置仰角 
void  CCamera::SetElevation( float angle )
{
 m_elevation = angle;

}

//设置倾角
void  CCamera::SetRoll( float angle )
{
 m_roll = angle;
}

//跟踪目标
void CCamera::Target( float targetx, float targety, float targetz )
{
 CVertex3f target( targetx, targety, targetz );
 CVector3f v( 0.0f, 0.0f, 0.0f );
 m_position.GetVector( target, v );
 m_azimuth = v.GetAzimuth();
 m_elevation = v.GetElevation();
}
void CCamera::Target( CVertex3f &target )
{
 CVector3f v( 0.0f, 0.0f, 0.0f );
 m_position.GetVector( target, v );
 m_azimuth = v.GetAzimuth();
 m_elevation = v.GetElevation();
}

//用空间信息驱动摄影机
void CCamera::Drive( CSpaceInfo& SpaceInfo )
{
 m_azimuth = SpaceInfo.m_azimuth;
 m_elevation = SpaceInfo.m_elevation;
 m_roll = SpaceInfo.m_roll;
 m_position.Copy( SpaceInfo.m_position );

 
}

///////////////////////
//使用摄影机
///////////////////////////
//获得变换矩阵
void CCamera::GetMatrix( CMatrix& matrix )
{
 matrix.Identity();
 matrix.Translate( -m_position.X(), -m_position.Y(), -m_position.Z() );
 matrix.RotateY( 90 - m_azimuth );
 matrix.RotateX( 180 - m_elevation );
 matrix.RotateZ( -m_roll );
}
//获得逆向变换矩阵
void CCamera::GetContraryMatrix( CMatrix& matrix )
{
 matrix.Identity();
 matrix.RotateZ( m_roll );
 matrix.RotateX( m_elevation - 180 );
 matrix.RotateY( m_azimuth - 90 );
 matrix.Translate( m_position.X(), m_position.Y(), m_position.Z() );
}

//获得摄影机向前,向右,向上向量 
bool CCamera::GetXYZAxialVector( CVector3f* pVector )
{
 if( NULL == pVector )
 {
  return false;
 }

 pVector[0].Set( 1.0f, 0.0f, 0.0f );
 pVector[1].Set( 0.0f, 1.0f, 0.0f );
 pVector[2].Set( 0.0f, 0.0f, -1.0f );
 
 CMatrix T;
 T.RotateZ( m_roll );
 T.RotateX( m_elevation - 180 );
 T.RotateY( m_azimuth - 90.0f );

 T.Transform( pVector, 3 );

 return true;
}

////////////////////////////////////////
//获得摄影机各种信息
//////////////////////////////////////////
//获得摄影机位置 
void CCamera::GetPosition( CVertex3f &Pos )
{
 Pos.Copy( m_position );
}

//获得方位角
float CCamera::GetAzimuth( )
{
 return m_azimuth;
}

//获得仰角
float CCamera::GetElevation( )
{
 return m_elevation;
}

//获得倾角
float CCamera::GetRoll( )
{
 return m_roll;
}  
////////////////

3. VC++和opengl画图的一点问题

你先看看你用的是不是用双缓冲模式(GLUT_DOUBLE),你可以定在for循环里一个定时器Sleep(100),0.1秒钟刷新一次,这样可以一段一段显示。建议int i 用static ,要不每次移动窗口时候都会再循环100次,画那线了

VC++和opengl画图的一点问题

4. OpenGL如何实现B样条曲线和曲面的绘制(C语言)?

你说的是计算机图形学的课程设计吧,我有C编写的代码,可以运行,是画B样条曲线和曲面的,我发到你邮箱里了,你参考一下吧。

5. 3D中,NURBS曲面及NURBS曲线是什么意思?

具体解释是:
.Non-Uniform(非统一):是指一个控制顶点的影响力的范围能够改变。当创建一个不规则曲面的时候这一点非常有用。同样,统一的曲线和曲面在透视投影下也不是无变化的,对于交互的3D建模来说这是一个严重的缺陷。
.Rational(有理):是指每个NURBS物体都可以用数学表达式来定义。
.B-Spline(B样条):是指用路线来构建一条曲线,在一个或更多的点之间以内插值替换的。

3D中,NURBS曲面及NURBS曲线是什么意思?

6. 怎么在vc++里画图?用opengl吗?

去看看windows编程里关于图形的章节吧
OpenGL是关于3D开发的

7. 在VC中怎样用OpenGL绘制三维图形

不懂哦。反正你时间也到不如把分给我算了。呵呵

在VC中怎样用OpenGL绘制三维图形

8. vc++ opengl绘图

不clear的话原来已经绘制的应该不会没有啊
你看看在回调的display函数里是不是用了glClear(GL_COLOR_BUFFER_BIT)