`

使用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);
        }
}
分享到:
评论

相关推荐

    使用Eclipse RCP进行桌面程序开发

    ### 使用Eclipse RCP进行桌面程序开发:详细指南 #### 一、Eclipse RCP简介与快速起步 **Eclipse RCP** (Rich Client Platform) 是一种基于Java的开源框架,用于开发桌面应用程序。自从Eclipse 3.0版本以来,RCP...

    Eclipse的RCP开发的入门教程

    最后,"使用Eclipse RCP进行桌面程序开发(六):向OpenGL进军.doc"将带领开发者进入更高级的主题,即如何在RCP应用中集成OpenGL,实现高性能的3D图形渲染。这对于需要强大图形处理能力的应用来说是至关重要的。 ...

    eclipse RCP Plug-in开发自学教程.pdf

    eclipse RCP(Rich Client Platform)是一种基于eclipse的插件式开发平台,允许开发者使用eclipse结构风格设计弹性的可扩展的应用程序。RCP插件式开发方式可以重用eclipse中的方法和编码模式,提高开发效率和代码...

    eclipse 开发桌面程序

    ### Eclipse 开发桌面程序——基于SWT技术及WindowBuilder插件详解 #### 一、SWT技术概述 SWT(Standard Widget Toolkit)是IBM Eclipse项目的一部分,作为一个强大的GUI编程框架,SWT为开发者提供了丰富的组件和...

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

    5. **服务和事件模型**:Eclipse RCP使用服务导向的架构,允许插件之间通过服务接口进行通信。同时,事件模型使得插件可以监听并响应其他插件的事件,增强应用的动态性和响应性。 6. **国际化和本地化**:Eclipse ...

    ECLIPSE+RCP应用系统开发方法与实战(PDF 高岗著)

    《ECLIPSE+RCP应用系统开发方法与实战》这本书是高岗先生关于使用Eclipse RCP(Rich Client Platform)进行应用系统开发的一本实战指南。Eclipse RCP是Eclipse IDE的一部分,它提供了一个框架,使得开发者可以构建...

    Eclipse Rcp 桌面程序开发

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

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

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

    rcp学习好资料

    5. **使用Eclipse RCP进行桌面程序开发(六):向OpenGL进军 - 海边沫沫 - BlogJava.mht** OpenGL是一种强大的图形库,用于创建复杂的3D图形。这篇文章可能涉及如何在Eclipse RCP应用中集成OpenGL,为用户提供高级...

    EclipseRCP教程

    2. RCP(Rich Client Platform):Eclipse RCP 的核心架构,提供了应用程序的入口、视图管理、事件处理和插件管理机制。 3. Plug-in:Eclipse RCP 的插件机制,允许开发者轻松地开发和集成新的功能模块。 4. View:...

    Eclipse RCP.pdf清晰版

    通过以上介绍,我们可以看到Eclipse RCP是一个非常强大且灵活的框架,它不仅能够加速桌面应用程序的开发,还能让开发者专注于业务逻辑而不是繁琐的界面设计。对于希望利用Eclipse平台构建复杂应用的开发者来说,掌握...

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

    #### Eclipse RCP:关键概念 ##### 什么是 RCP? **RCP** 从 Eclipse V3.0 版本开始重新架构,旨在提供一个构建几乎任何类型桌面应用程序的基础平台。它基于 Eclipse 的动态插件模型,但更加灵活和轻便。 - **...

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

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

    Eclipse RCP 应用系统开发方法与实战

    6. **插件开发**:Eclipse RCP的插件开发涉及到定义插件元数据(plugin.xml)、编写插件代码以及实现所需的接口和服务。开发者需要熟悉Extension Points,这是插件之间交互的关键。 7. **SQL在Eclipse RCP中的应用*...

    EclipseRcp 例子程序

    Eclipse RCP(Rich Client Platform)是一个开源框架,由Eclipse基金会开发,用于构建桌面应用程序。这个框架基于Java,提供了一套完整的工具集,包括插件系统、工作台管理、视图、编辑器、透视图等,使得开发者可以...

    Eclipse RCP 插件开发指南

    Eclipse RCP 是一个灵活的平台,它提供了构建桌面应用程序所需的所有组件和服务。通过使用Eclipse RCP,开发者可以创建高度可定制和扩展的应用程序,这些应用不仅具有强大的功能,而且还能与其他Eclipse插件无缝集成...

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

    Eclipse RCP是一种基于Java的框架,用于创建桌面应用程序,它充分利用了Eclipse的插件体系结构和开发环境的强大力量。 Eclipse RCP应用程序的开发涉及多个方面,包括视图、编辑器、透视图、操作和命令等,每个组件...

    ECLIPSE RCP项目源程序

    这个压缩包中的“ECLIPSE RCP项目源程序”包含了使用Eclipse RCP开发的一个项目的完整源代码,并且有中文注释,这对于初学者或有经验的开发者来说都是一个宝贵的资源。 Eclipse RCP的核心概念主要包括以下几个方面...

    Eclipse RCP 软件打包发布方法

    Eclipse Rich Client Platform (RCP) 是一个强大的框架,用于构建桌面应用程序。它提供了一整套工具和功能,使得开发者可以构建出具有丰富用户界面的应用。在开发完成后,我们需要将这些应用打包并发布,以便用户...

    Eclipse RCP开发详解

    Eclipse Rich Client Platform (RCP) 是一个强大的框架,用于构建桌面应用程序,它基于Java语言并利用了Eclipse IDE的核心技术。Eclipse RCP允许开发者创建功能丰富的、可扩展的应用程序,这些应用程序拥有与Eclipse...

Global site tag (gtag.js) - Google Analytics