`

使用Eclipse RCP进行桌面程序开发(六):向OpenGL进军

    博客分类:
  • RCP
阅读更多
使用Eclipse RCP进行桌面程序开发(一):快速起步
使用Eclipse RCP进行桌面程序开发(二):菜单、工具栏和对话框
使用Eclipse RCP进行桌面程序开发(三):视图和透视图
使用Eclipse RCP进行桌面程序开发(四):在Windows中使用Active X控件
使用Eclipse RCP进行桌面程序开发(五):2D绘图

看完这一篇,我们应该可以使用OpenGL绘制如下图的场景了。该场景是一个旋转的三菱锥矩阵,下面是旋转到不同方位的截图:






我整整花了一个星期的时间来研究SWT中的OpenGL,遇到的第一个困难是找不到传说中的GL类和GLU类,最后,通过搜索引擎终于找到了,原来使用Eclipse进行OpenGL开发,还需要另外下载OpenGL插件,如下图:


这里有OpenGL的类库,还有一个示例,把类库下载下来,解压,放到Eclipse的Plugin目录下,然后在我们的项目中添加依赖项,就可以看到我们需要使用的类了,如下图:


我们需要对OpenGL编程的一些基本概念有点了解,在OpenGL中,3D场景不是直接绘制到操作系统的窗口上的,而是有一个称为着色描述表(Rendering Context)的东西,我们这里简称它为context,OpenGL的绘图命令都是在当前context上进行绘制,然后再把它渲染到操作系统的设备描述表(Device Context)上,这里,我们可以简单的理解成把它渲染到窗口控件上(其实也可以渲染到全屏幕)。

在Windows中使用OpenGL编程比较麻烦,因为我们需要设置一个叫做象素格式的东西,大家只要看看下面的这段C代码,就知道我为什么说它麻烦了:
static PIXELFORMATDESCRIPTOR pfd=     //pfd 告诉窗口我们所希望的东东
     {
         sizeof(PIXELFORMATDESCRIPTOR),   //上诉格式描述符的大小
         1,                  // 版本号
         PFD_DRAW_TO_WINDOW |        // 格式必须支持窗口
         PFD_SUPPORT_OPENGL |        // 格式必须支持OpenGL
         PFD_DOUBLEBUFFER,          // 必须支持双缓冲
         PFD_TYPE_RGBA,           // 申请 RGBA 格式
         bits,                // 选定色彩深度
         0, 0, 0, 0, 0, 0,          // 忽略的色彩位
         0,                 // 无Alpha缓存
         0,                 // 忽略Shift Bit
         0,                 // 无聚集缓存
         0, 0, 0, 0,             // 忽略聚集位
         16,                 // 16位 Z-缓存 (深度缓存)
         0,                 // 无模板缓存
         0,                 // 无辅助缓存
         PFD_MAIN_PLANE,           // 主绘图层
         0,                 // 保留
         0, 0, 0               // 忽略层遮罩
     };
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))   // Windows 找到相应的象素格式了吗?
    {
        KillGLWindow();               // 重置显示区
        MessageBox(NULL,"Can't Find A Suitable PixelFormat.",
            "ERROR",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                // 返回 FALSE
    }
if(!SetPixelFormat(hDC,PixelFormat,&pfd))      // 能够设置象素格式么?
    {
        KillGLWindow();               // 重置显示区
        MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;                // 返回 FALSE
    }
if (!(hRC=wglCreateContext(hDC)))         // 能否取得着色描述表?
    {
        KillGLWindow();              // 重置显示区
        MessageBox(NULL,"Can't Create A GL Rendering Context.",
           "ERROR",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;               // 返回 FALSE
    }

在SWT中,我们开发OpenGL应用就要简单得多,这全部要归功于org.eclipse.swt.opengl包下面的GLCanvas类和GLData类,使用GLCanvas类可以直接创建一个用于OpenGL渲染的控件,至于设置象素格式这样复杂的问题,它已经帮我们解决了,不信你看GLCanvas类的构造函数的实现。

GLCanvas类中的几个方法代表了我一开始提到的OpenGL的几个基本概念,setCurrent()方法就是为了把该控件的context设置为OpenGL的当前着色描述表,然后使用GL和GLU类中的方法在当前context上进行绘图,绘制完图形以后,再使用GLCanvas类的swapBuffers()方法交换缓冲区,也就是把context中的3D场景渲染到控件上。

写到这里,大家肯定认为一切问题都应该迎刃而解了,然而,我却碰到了另外一个困难,这个困难就是SWT的OpenGL表现怪异,怎么个怪异呢?请看下面视图类的代码: public void createPartControl(Composite parent) {
        // TODO 自动生成方法存根
        GLData data = new GLData();
        data.depthSize = 1;
        data.doubleBuffer = true;
        GLCanvas canvas = new GLCanvas(parent, SWT.NO_BACKGROUND, data);
        //设置该canvas的context为OpenGL的当前context
        if(!canvas.isCurrent()){
            canvas.setCurrent();
        }
        //这里可以进行OpenGL绘图
       
        //交换缓存,将图形渲染到控件上
        canvas.swapBuffers();
    }
按道理,我们应该可以得到一个经典的3D的黑色场景,但是,我得到的却是这样的效果:


相当的郁闷啊,就是这个问题困扰了我至少一个星期。我把官方网站上的示例看了有看,就是找不到问题的关键所在。直到最后,我用了另外一个线程,每100ms都调用canvas.swapBuffers()把场景渲染一遍问题才解决。由此可见,之所以回出现上面的问题,主要是因为我们渲染的场景很快会被操作系统的其他绘图操作所覆盖,只有不断的渲染我们才能看到连续的3D图形。

我是这样实现让3D场景连续渲染的:
public void createPartControl(Composite parent) {
        // TODO 自动生成方法存根
        GLData data = new GLData();
        data.depthSize = 1;
        data.doubleBuffer = true;
        GLCanvas canvas = new GLCanvas(parent, SWT.NO_BACKGROUND, data);
        //将绘图代码转移到定时器中
        Refresher rf = new Refresher(canvas);
        rf.run();
    }
Refresher类的代码如下:
class Refresher implements Runnable {
    public static final int DELAY = 100;
   
    private GLCanvas canvas;
   
    public Refresher(GLCanvas canvas) {
        this.canvas = canvas;
    }
   
    public void run() {
        if (this.canvas != null && !this.canvas.isDisposed()) {
            if(!canvas.isCurrent()){
                canvas.setCurrent();
            }
            //这里添加OpenGL绘图代码
            canvas.swapBuffers();
            this.canvas.getDisplay().timerExec(DELAY, this);
        }
    }
 
}
问题解决,得到的效果图如下:


OK,下面的任务就是完完全全的使用OpenGL的绘图功能了,不管你的OpenGL教材使用的是什么操作系统什么编程语言,你都能很简单的把它的概念拿到这里来使用。

使用OpenGL的第一件事,就是要设置投影矩阵、透视图和观察者矩阵,如果你不知道为什么要这么做,请查看OpenGL的基础教材,在这里,照搬就行了。为了让我们的控件在每次改变大小的时候都能够做这些设置,我们使用事件监听器,如下:
public void createPartControl(Composite parent) {
        // TODO 自动生成方法存根
        GLData data = new GLData();
        data.depthSize = 1;
        data.doubleBuffer = true;
        canvas = new GLCanvas(parent, SWT.NO_BACKGROUND, data);
        canvas.addControlListener(new ControlAdapter() {
            public void controlResized(ControlEvent e) {
                Rectangle rect = canvas.getClientArea();
                GL.glViewport(0, 0, rect.width, rect.height);
               
                //选择投影矩阵
                GL.glMatrixMode(GL.GL_PROJECTION);
                //重置投影矩阵
                GL.glLoadIdentity();
                //设置窗口比例和透视图
                GLU.gluPerspective(45.0f, (float) rect.width / (float) rect.height, 0.1f, 100.0f);
                //选择模型观察矩阵
                GL.glMatrixMode(GL.GL_MODELVIEW);
                //重置模型观察矩阵
                GL.glLoadIdentity();
               
                //黑色背景
                GL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
                //设置深度缓存
                GL.glClearDepth(1.0f);
                //启动深度测试
                GL.glEnable(GL.GL_DEPTH_TEST);
                //选择深度测试类型
                GL.glDepthFunc(GL.GL_LESS);
                //启用阴影平滑
                GL.glShadeModel(GL.GL_SMOOTH);
                //精细修正透视图
                GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
                //清除屏幕和深度缓存
                GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
                //重置当前的模型观察矩阵
                GL.glLoadIdentity();
            }
        }); 
        canvas.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                dispose();
            }
        });

调用glLoadIdentity()之后,实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。

glTranslatef(x, y, z)是将当前点沿着X,Y和Z轴移动,当我们绘图的时候,不是相对于屏幕中间,而是相对于当前点。

glBegin(GL.GL_TRIANGLES)的意思是开始绘制三角形,glEnd()告诉OpenGL三角形已经创建好了。通常当我们需要画3个顶点时,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果想要画更多的顶点时,可以使用GL_POLYGON。

glVertex(x,y,z)用来设置顶点,如果绘制三角形,这些顶点需要三个一组,如果绘制四边形,则是四个为一组。如果我们要为顶点着色,就需要glColor3f(r,g,b)方法,记住,每次设置以后,这个颜色就是当前颜色,直到再次调用该方法重新设置为止。

最后需要介绍的是glRotatef(Angle,Xvector,Yvector,Zvector)方法,该方法负责让对象围绕指定的轴旋转,Angle参数指转动的角度,注意是浮点数哦。

下面是我的视图类的全部代码,我把3D绘图的任务全部放到了另外一个线程中,并且定义了一个递归方法public void drawPyramid(float x, float y, float z, int n)用来绘制三菱锥矩阵。如下:
package cn.blogjava.youxia.views;

import org.eclipse.opengl.GL;
import org.eclipse.opengl.GLU;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.SWT;

public class OpenGLView extends ViewPart {

    GLCanvas canvas;
    @Override
    public void createPartControl(Composite parent) {
        // TODO 自动生成方法存根
        GLData data = new GLData();
        data.depthSize = 1;
        data.doubleBuffer = true;
        canvas = new GLCanvas(parent, SWT.NO_BACKGROUND, data);
        canvas.addControlListener(new ControlAdapter() {
            public void controlResized(ControlEvent e) {
                Rectangle rect = canvas.getClientArea();
                GL.glViewport(0, 0, rect.width, rect.height);
               
                //选择投影矩阵
                GL.glMatrixMode(GL.GL_PROJECTION);
                //重置投影矩阵
                GL.glLoadIdentity();
                //设置窗口比例和透视图
                GLU.gluPerspective(45.0f, (float) rect.width / (float) rect.height, 0.1f, 100.0f);
                //选择模型观察矩阵
                GL.glMatrixMode(GL.GL_MODELVIEW);
                //重置模型观察矩阵
                GL.glLoadIdentity();
               
                //黑色背景
                GL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
                //设置深度缓存
                GL.glClearDepth(1.0f);
                //启动深度测试
                GL.glEnable(GL.GL_DEPTH_TEST);
                //选择深度测试类型
                GL.glDepthFunc(GL.GL_LESS);
                //启用阴影平滑
                GL.glShadeModel(GL.GL_SMOOTH);
                //精细修正透视图
                GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
                //清除屏幕和深度缓存
                GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
                //重置当前的模型观察矩阵
                GL.glLoadIdentity();
            }
        }); 
        canvas.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                dispose();
            }
        });
        /**//*
       
        */
       
       
        Refresher rf = new Refresher(canvas);
        rf.run();
    }

    @Override
    public void setFocus() {
        // TODO 自动生成方法存根

    }

}

class Refresher implements Runnable {
    public static final int DELAY = 100;
   
    private GLCanvas canvas;
    private float rotate = 0.0f;
   
    public Refresher(GLCanvas canvas) {
        this.canvas = canvas;
    }
   
    public void run() {
        if (this.canvas != null && !this.canvas.isDisposed()) {
            if(!canvas.isCurrent()){
                canvas.setCurrent();
            }
            //这里添加OpenGL绘图代码
            GL.glLoadIdentity();
            GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            GL.glTranslatef(0, 4.5f, -11);
            //围绕y轴转起来
            rotate += 0.5;
            GL.glRotatef(rotate, 0, 1.0f, 0);
            //调用递归函数,绘制三菱锥矩阵
            drawPyramid(0,0,0,4);
            canvas.swapBuffers();
            this.canvas.getDisplay().timerExec(DELAY, this);
        }
    }
       
        public void drawPyramid(float x, float y, float z, int n){
            if(n == 0)return;
            //画一个三菱锥
            GL.glBegin(GL.GL_TRIANGLES);
                //画背面
                GL.glColor3f(1.0f,0.0f,0.0f);
                GL.glVertex3f( x, y, z);
                GL.glColor3f(0.0f,1.0f,0.0f);
                GL.glVertex3f(x+1.0f,y-1.63f,z-0.57f);
                GL.glColor3f(0.0f,0.0f,1.0f);
                GL.glVertex3f( x-1.0f,y-1.63f,z-0.57f);
                //画底面
                GL.glColor3f(1.0f,0.0f,0.0f);
                GL.glVertex3f( x,y-1.63f,z+1.15f);
                GL.glColor3f(0.0f,1.0f,0.0f);
                GL.glVertex3f(x-1.0f,y-1.63f,z-0.57f);
                GL.glColor3f(0.0f,0.0f,1.0f);
                GL.glVertex3f( x+1.0f,y-1.63f,z-0.57f);
                //画左侧面
                GL.glColor3f(1.0f,0.0f,0.0f);
                GL.glVertex3f( x,y,z);
                GL.glColor3f(0.0f,1.0f,0.0f);
                GL.glVertex3f(x-1.0f,y-1.63f,z-0.57f);
                GL.glColor3f(0.0f,0.0f,1.0f);
                GL.glVertex3f( x,y-1.63f,z+1.15f);
                //画右侧面
                GL.glColor3f(1.0f,0.0f,0.0f);
                GL.glVertex3f( x,y,z);
                GL.glColor3f(0.0f,1.0f,0.0f);
                GL.glVertex3f(x,y-1.63f,z+1.15f);
                GL.glColor3f(0.0f,0.0f,1.0f);
                GL.glVertex3f( x+1.0f,y-1.63f,z-0.57f);
            GL.glEnd();
            //递归调用,画多个三菱锥
            drawPyramid(x,y-1.63f,z+1.15f,n-1);
            drawPyramid(x-1.0f,y-1.63f,z-0.57f,n-1);
            drawPyramid(x+1.0f,y-1.63f,z-0.57f,n-1);
        }
}
分享到:
评论

相关推荐

    使用EclipseRCP进行桌面程序开发(一):快速起步

    火龙果软件工程技术中心 所谓RCP,就是...使用RCP,我们可以开发界面象Eclipse这样漂亮的桌面程序,比如医院管理系统啊、CAD软件等等。遗憾的是,目前在国内基本上找不到关于RCP的中文资料,我们只能通过自

    eclipse 开发桌面程序

    利用SWT技术,基于WindowBuilder Eclipse 插件开发 桌面应用小程序详细步骤

    使用EclipseRCP进行桌面程序开发(二):菜单、工具栏和对话框

    火龙果软件工程技术中心 在使用EclipseRCP进行桌面程序开发(一):快速起步中,我们通过Eclipse的插件开发向导,逐步建立了一个RCP应用程序,但是,这个程序没有任何功能,难以激起我们学习的兴趣。在这一节,我们...

    Eclipse Rcp 桌面程序开发

    Eclipse Rcp 桌面程序开发中文教程

    eclipse 3.6 rcp 开发

    Eclipse RCP允许开发者使用eclipse结构风格设计弹性的可扩展的应用程序,可重用Eclipse中已存在的方法和编码模式。俗话说,万事开头难。Eclipse RCP入手可能会比较困难、费时。接下来我们将主要讲述如何让RCP工作. ...

    Eclipse RCP开发教程

    Eclipse RCP开发教程,RCP入门教程,教你如何使用SWT Eclipse RCP开发教程,RCP入门教程,教你如何使用SWT Eclipse RCP开发教程,RCP入门教程,教你如何使用SWT Eclipse RCP开发教程,RCP入门教程,教你如何使用SWT

    使用EclipseRCP进行桌面程序开发(三):视图和透视图

    其中,向视图中添加控件的操作,我们即可以手工编写,也可以使用Designer插件,我这里推荐大家使用Designer插件,该插件对RCP提供功能非常强大的支持,如果使用Designer插件开发视图,则plugin.xml文

    eclipse rcp应用系统开发方法与实战源代码.zip

    eclipse rcp应用系统开发方法与实战源代码.zip

    Eclipse RCP与Spring OSGi技术详解与最佳实践

    高级篇(第6-12章)系统讲解了Eclipse RCP应用开发的基础知识、Eclipse RCP软件产品各个组成部分的构建方法,以及Eclipse RCP扩展的使用和扩展点的开发,掌握这些技术知识的读者将能构建一个结构完整的Eclipse RCP...

    EclipseRcp 例子程序

    EclipseRcp 例子程序

    eclipse icons,用来开发rcp桌面应用程序

    eclipse icons,用来开发rcp桌面应用程序

    eclipse rcp开发入六教程及培训资料

    网络中最全面最合适学习或开发...包含eclipse rcp开发入门教程; eclipse rcp基础教程;eclipse rcp开发自学教程; eclipse rcp开发培训教程及ppt等相关资料;教程中包含一步步操作实例,包含对开发原理的讲解与说明;

    ECLIPSE RCP项目源程序

    ECLIPSE RCP项目开发源程序(中文注释)

    Eclipse RCP 开发资料

    Eclipse RCP 开发资料打包下载 目录如下: RCP程序设计.pdf(推荐) Addison.Wesley.Eclipse.Rich.Client.Platform.Designing.Coding.and.Packaging.Java.Applications.Oct.2005.chm Eclipse+RCP入门.pdf example_...

    使用EclipseRCP进行桌面程序开发(四):在Windows中使用ActiveX控件

    看完这篇文章,可以开发如下的界面:没有嵌入ActiveX控件的...使用Windows系统中丰富的ActiveX控件资源,我们可以实现功能非常强大的程序。在Windows平台下,SWT图形工具包提供了对OLE的支持,ActiveX控件和OLE文档都

    使用EclipseRCP进行桌面程序开发(五):2D绘图

    看完这篇文章,可以实现如下界面:当我第一次看到RCP的时候,我就梦想着有一天能够用它开发界面华丽的2D和3D程序,经历过前面的探索,今天终于可以揭开2D绘图的神秘面纱。在包资源管理器的插件依赖项中,我们一眼就...

    通过例子学习EclipseRCP开发

    本文讲述如何利用Eclipse RCP架构来进行应用开发。本文涵盖了创建RCP应用的各个方面:使用命令、视图、编辑器、对话框及创建与部署Eclipse产品。

    Eclipse4 RCP桌面应用开发教程

    Eclipse4的插件开发教程比较少,之前搜索到的基本是eclipse官网的全英文版本,可能现在做桌面应用的没那么多,搜索到的都是多年前的资料。英文的看的比较吃力,分享一个中文的,部分内容还是有英文,大部分都已经...

    开发您的第一个 Eclipse RCP 应用程序

    Eclipse Rich Client Platform (RCP) 的目标是在各种不是集成开发环境 (IDE) 的最终用户应用程序中使用 Eclipse。随着 Eclipse V3.1 的发布,创建 RCP 应用程序变得容易。本教程将指导您一步步创建自己的 RCP 应用...

Global site tag (gtag.js) - Google Analytics