荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: contonazhao (烦恼的松鼠仔), 信区: Visual
标  题: 利用OpenGL实现三维绘图
发信站: 荔园晨风BBS站 (Wed Jul 30 15:57:42 2003), 站内信件

 [ 转载文档 本文适合初级读者 已阅读1076次 ]

利用OpenGL实现三维绘图
作者:王刚

提交者:eastvc 发布日期:2003-7-1 23:02:21
原文出处:http://cgd.pages.com.cn/cgd/develop/opengl/200112/Draw3DFace.
htm


目 录
  1 初始化OpenGL绘图环境
  2 定义与Windows接口的系统函数
  3 在VC下实现程序编译


------------------------------------------------------------------------
--------

  在三维绘图蓬勃发展的过程中,计算机公司推出了大量的三维绘图软件包。其
中SGI公司推出的OpenGL,作为一个性能优越的图形应用程序设计界面(API)异军
突起,取得了很大的成就。它以高性能的交互式三维图形建模能力和易于编程开发
,得到了Microsoft、IBM、DEC、Sun、HP等大公司的认同。因此,OpenGL已经成为
一种三维图形开发标准,是从事三维图形开发工作的必要工具。

1、初始化OpenGL绘图环境

  1.1 定义颜色格式和缓冲模式
  OpenGL提供两种颜色模式:RGB(RGBA)模式和颜色索引模式(调色板)。在
RGBA模式下所有颜色的定义用RGB三个值来表示,有时也加上Alpha值(表示透明度
)。RGB三个分量值的范围都在0和1之间,它们在最终颜色中所占的比例与它们的
值成正比。如:(1、1、0)表示黄色,(0、0、1)表示蓝色。颜色索引模式下每
个象素的颜色是用颜色索引表中的某个颜色索引值表示(类似于从调色板中选取颜
色)。由于三维图形处理中要求颜色灵活,而且在阴影,光照,雾化,融合等效果
处理中RGBA的效果要比颜色索引模式好,所以,在编程时大多采用RGBA模式。
  OpenGL提供了双缓存来绘制图像。即在显示前台缓存中的图像同时,后台缓存
绘制第二幅图像。当后台绘制完成后,后台缓存中的图像就显示出来,此时原来的
前台缓存开始绘制第三幅图像,如此循环往复,以增加图像的输出速度。
  设置窗口显示模式函数:

  void auxInitDisplayMode(
    AUX_DOUBLE |   // 双缓存方式
    AUX_RGBA     // RGBA颜色模式
  );

  1.2 设置光源
  OpenGL的光源大体分为三种:环境光(Ambient light),即来自于周围环境
没有固定方向的光。漫射光(Diffuse light)来自同一个方向,照射到物体表面
时在物体的各个方向上均匀发散。镜面光(Specular light)则是来自于同一方向
,也沿同一个方向反射。全局环境光是一种特殊的环境光,它不来自特于某种定光
源,通常做为场景的自然光源。
  指定光源函数:

  void glLightfv(
    Glenum light,  // 光源号
    Glenum pname,  // 指明光源类型:
             // GL_DIFFUSE 光源为漫射光光源
             // GL_AMBIENT 光源为环境光光源
             // GL_SPECULAR 光源为镜面光光源
    const Glfloat* params  // 指向颜色向量的指针
  );

  设置全局环境光函数:

  void glLightModelfv(
    GL_LIGHT_MODEL_ AMBIENT,
    const Glfloat* param  // param:指向颜色向量的指针
  );

  起用光源函数:

  void glEnable(GL_LIGHTING);
  void glEnable(GL_enum cap);  // cap:指明光源号

  1.3 设置材质
  在OpenGL中,用材料对光的三原色(红绿蓝)的反射率大小来定义材料的颜色
。与光源相对应,材料的颜色,也分为环境色,漫反射色和镜面反射色,由此决定
该材料对应不同的光呈现出不同的反射率。由于人所看到物体的颜色是光源发出的
光经物体反射后进入眼睛的颜色。所以,物体的颜色是光源的环境光,漫反射光和
镜面反射光与材料的环境色,漫反射色和镜面反射色的综合。例如:OpenGL的光源
色是(LR、LG、LB),材质色为(MR、MG、MB),那么,在忽略其他反射效果的情
况下,最终进入眼睛的颜色是(LR*MR、LG*MG、LB*MB)。
  材质定义函数:

  void glMaterialfv(
    GLenum face,  // 指明在设置材质的哪个表面的颜色。
            // 可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK
    GLenum pname,  // 与光源的pname参数相似
    const float* params  // 指向材质的颜色向量
  );

  1.4 定义投影方式
  也即选择观察物体的角度和范围。由于我们是三维绘图,所以采用不同的视点
和观察范围,就会产生不同的观察效果。由于计算机只能显示二维图形,所以在表
示真实世界中的三维图形时,需将三维视景转换成二维视景。这是产生三维立体效
果的关键。OpenGL提供了两种将3D图形转换成2D图形的方式。正投影(
Orthographic Projection)和透视投影(Perspective Projection)。其中,正
投影指投影后物体的大小与视点的远近无关,通常用于CAD设计;而透视投影则符
合人的心理习惯,离视点近的物体大,离视点远的物体小。此外,在OpenGL中还要
定义投影范围,只有在该范围中的物体才会被投射到计算机屏幕上,投影范围外的
物体将被裁减掉。
  定义投影范围(不同的投影方式对应不同函数):

  void glOrtho(
    GLdouble left, GLdouble right,  // (left,bottom,near)及(
right,top,far)分别给出正射投
    GLdouble bottom, GLdouble top,  // 影投影范围的左下角和右上角
的坐标。
    GLdouble near,GLdouble far);

2、定义与Windows接口的系统函数

  2.1 定义绘图窗口的位置

  // (x,y)给出窗口左上角坐标
  // width及heigh给出窗口的宽高
  void auxInitPosition(GLint x,GLint y,GLsizei width, GLsizei heigh);


  2.2 定义绘图窗口的标题

  // STR表示窗口标题字串
  void auxInitWindow(GLbyte* STR);

  2.3 定义绘图窗口改变时的窗口刷新函数

  // 当窗口改变形状时调指定的回调函数
  // NAME表示回调函数名称
  void auxReshapeFunc(NAME);

  2.4 定义空闲状态的空闲状态函数以实现动画

  // 当系统空闲时调用指定的回调函数
  // NAME表示回调函数名称
  void auxIdleFunc(NAME);

  2.5 定义场景绘制函数(当窗口更新或场景改变时调用)

  // 当窗口需要更新或场景变化时调用
  // NAME表示回调函数名称
  void auxMainLoop(NAME);

3、在VC下实现程序编译
  在VC编辑器下键入下述代码后,保存为后缀是.cpp的C++文件。开始编译,在
“The build command requires an active project workspace”。“Would
you like to create a default project workspace”? 的提示后,选择“是(
Y)”。进入“Project”菜单,选择“Setting”项,弹出“Project Setting”对
话框,选择“Link”项,在“Libaray”栏目中加入OpenGL提供的函数库:“
opengl32.lib glu32.lib glaux.lib”。(注意:在执行程序时,Windows的
system目录下要包含opengl32.dll及glu32.dll两个动态连接库)。附源程序代码


  #include "windows.h"
  #include "gl/gl.h"
  #include "gl/glaux.h"
  #include "gl/glu.h"
  #include "math.h"

  void myinit()
  {
    glClearColor(1,1,0,0);
    GLfloat ambient[]={.5,.5,.5,0};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
    GLfloat mat_ambient[]={.8,.8,.8,1.0};
    GLfloat mat_diffuse[]={.8,.0,.8,1.0};
    GLfloat mat_specular[]={1.0,.0,1.0,1.0};
    GLfloat mat_shininess[]={50.0};
    GLfloat light_diffuse[]={0,0,.5,1};
    GLfloat light_position[]={0,0,1.0,0};
    glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK,GL_SHININESS,mat_shininess);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0,GL_POSITION, light_position);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
  }

  void CALLBACK display()
  {
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);  // 绘制半径为1.0的实体球
    glFlush();       // 强制输出图像
    auxSwapBuffers();    // 交换绘图缓存
    _sleep(100);
  }

  void CALLBACK Idledisplay()
  {
    // x,y满足x2+y2=0.01。这样可以使物体沿该圆轨迹运动。
    static float x=-.1,y=0.0;
    static BOOL mark=TRUE;
    static float step=.01;
    x+=step;
    if(x<=.1&&x>=-.1)
    {
      if(step>0)
        y=sqrt(.01-x*x);
      else
        y=-sqrt(.01-x*x);
      glTranslatef(x,y,0);
    }
    else
    {
      step=0-step;
    }
    display();
  }

  void CALLBACK myReshape(GLsizei w,GLsizei h)
  {
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if(w<=h)
      glOrtho(-3.5,3.5,-3.5*(GLfloat)w/(GLfloat)h, 3.
5*(GLfloat)w/(GLfloat)h,-10,10);
    else
      glOrtho(-3.5*(GLfloat)w/(GLfloat)h,3.5*
(GLfloat)w/(GLfloat)h,-3.5,3.5,-10,10);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
  }

  void main()
  {
    auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA);
    auxInitPosition(0,0,400,400);
    auxInitWindow(" circle ");
    myinit();
    auxReshapeFunc(myReshape);
    auxIdleFunc(Idledisplay);
    auxMainLoop(display);
  }
--
做人,就不要做事,做事,就不要做人!

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.40.158]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店