当前位置:首页 > 芯闻号 > 充电吧
[导读]欢迎进入第八课。到现在为止,您应该很好的理解OpenGL了。您已经学会了设置一个OpenGL窗口的每个细节。学会在旋转的物体上贴图并打上光线以及混色(透明)处理。       这一课应该算是第一课中级

欢迎进入第八课。到现在为止,您应该很好的理解OpenGL了。您已经学会了设置一个OpenGL窗口的每个细节。学会在旋转的物体上贴图并打上光线以及混色(透明)处理。

       这一课应该算是第一课中级教程。您将学到如下的知识:在3D场景中移动位图,并去除位图上的黑色象素(使用混色)。接着为黑白纹理上色,最后您将学会创建丰富的色彩,并把上过不同色彩的纹理相互混合,得到简单的动画效果。

lesson8.h

#ifndef LESSON8_H
#define LESSON8_H

#include#include#includetypedef struct		// 为星星创建一个结构
{
    int r, g, b;	// 星星的颜色
    GLfloat dist;	// 星星距离中心的距离
    GLfloat angle;	// 当前星星所处的角度,自转角度
}stars;				// 结构命名为stars

const int starCount = 50; // 星星的个数

class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;

class Lesson8 : public QWindow, QOpenGLFunctions_1_1
{
    Q_OBJECT
public:
    explicit Lesson8(QWindow *parent = 0);
    ~Lesson8();

    virtual void render(QPainter *);
    virtual void render();
    virtual void initialize();

public slots:
    void renderNow();

protected:
    void exposeEvent(QExposeEvent *);
    void resizeEvent(QResizeEvent *);
    void keyPressEvent(QKeyEvent *); // 键盘事件

private:
    void loadGLTexture();

private:
    QOpenGLContext *m_context;

    bool m_twinkle; // twinkle用来跟踪闪烁效果是否启用
    QVectorm_stars;
    GLfloat	m_zoom;	// 星星离观察者的距离
    GLfloat m_tilt;	// 星星的倾角
    GLfloat	m_spin;	// 闪烁星星的公转角度
    GLuint	m_texture[1]; // 存放一个纹理
};

#endif // LESSON8_H

lesson8.cpp

#include "lesson8.h"

#include#include#include#include#include#includeLesson8::Lesson8(QWindow *parent) :
    QWindow(parent)
  , m_context(0)
  , m_twinkle(false)
  , m_zoom(-15.0f)
  , m_tilt(90.0f)
  , m_spin(0.0f)
{
    setSurfaceType(QWindow::OpenGLSurface);
}

Lesson8::~Lesson8()
{
    glDeleteTextures(1, &m_texture[0]);
}

void Lesson8::render(QPainter *painter)
{
    Q_UNUSED(painter);
}

void Lesson8::render()
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glViewport(0,0,(GLint)width(),(GLint)height()); // 重置当前视口
    glMatrixMode(GL_PROJECTION);                    // 选择投影矩阵
    glLoadIdentity();                               // 重置投影矩阵为单位矩阵
    gluPerspective(45.0f,(GLdouble)width()/(GLdouble)height(),0.1f,100.0f);

    glMatrixMode(GL_MODELVIEW);// 选择模型视图矩阵
    glLoadIdentity();          // 重置模型视图矩阵为单位矩阵

    glBindTexture(GL_TEXTURE_2D, m_texture[0]);    // 选择纹理

    for (int i = 0, iend = starCount;i<iend; i++)  // 循环设置所有的星星
    {
        glLoadIdentity();				           // 绘制每颗星星之前,重置模型观察矩阵
        glTranslatef(0.0f,0.0f,m_zoom);			   // 深入屏幕里面
        glRotatef(m_tilt,1.0f,0.0f,0.0f);		   // 倾斜视角,初始为90°,此时Y轴正向是指向观察者的。
        //现在我们来移动星星。星星开始时位于屏幕的中心。我们要做的第一件事是把场景沿Y轴旋转。
        //第二行代码沿x轴移动一个正值。通常x轴上的正值代表移向了屏幕的右侧(也就是通常的x轴的正向),但这里由于我们绕y轴旋转了坐标系,
        //x轴的正向可以是任意方向。
        glRotatef(m_stars[i].angle,0.0f,1.0f,0.0f);	// 旋转至当前所画星星的角度
        glTranslatef(m_stars[i].dist,0.0f,0.0f);	// X轴正向移动
        //接着的代码带点小技巧。星星实际上是一个平面的纹理。现在您在屏幕中心画了个平面的四边形然后贴上纹理,这看起来很不错。
        //但是当经过X轴和Y轴的旋转以后,星星的正面并没对着我们,如果倾斜角我饿90°,那么所有的星星
        //看起来就是一条细线。这不是我们所想要的。我们希望星星永远正面朝着我们,而不管屏幕如何旋转或倾斜。
        //我们通过在绘制星星之前,抵消对星星所作的任何旋转来实现这个愿望。您可以采用逆序来抵消旋转。
        //当我们倾斜屏幕时,我们实际上以当前角度旋转了星星。通过逆序,我们又以当前角度"反旋转"星星。
        //注意,旋转只能改变星星的角度,不能改变星星的位置。
        glRotatef(-m_stars[i].angle,0.0f,1.0f,0.0f);// 取消当前星星的角度
        glRotatef(-m_tilt,1.0f,0.0f,0.0f);		    // 取消屏幕倾斜
        //如果 twinkle 为 TRUE,我们在屏幕上先画一次不旋转的星星:将星星总数(num) 减去当前的星星数(loop)再减去1,
        //来提取每颗星星的不同颜色(这么做是因为循环范围从0到num-1)。举例来说,结果为10的时候,我们就使用10号星星的颜色。
        //这样相邻星星的颜色总是不同的。这不是个好法子,但很有效。最后一个值是alpha通道分量。这个值越小,这颗星星就越暗。
        //由于启用了twinkle,每颗星星最后会被绘制两遍。程序运行起来会慢一些,这要看您的机器性能如何了。
        //但两遍绘制的星星颜色相互融合,会产生很棒的效果。同时由于第一遍的星星没有旋转,启用twinkle后的星星看起来有一种动画效果。
        //(如果您这里看不懂得话,就自己去看程序的运行效果吧。)
        //值得注意的是给纹理上色是件很容易的事。尽管纹理本身是黑白的,纹理将变成我们在绘制它之前选定的任意颜色。
        //此外,同样值得注意的是我们在这里使用的颜色值是byte型的,而不是通常的浮点数。甚至alpha通道分量也是如此。
        if (m_twinkle)				// 启用闪烁效果
        {
            // 使用byte型数值指定一个颜色
            glColor4ub(m_stars[(iend-i)-1].r,m_stars[(iend-i)-1].g,m_stars[(iend-i)-1].b,255);
            glBegin(GL_QUADS);		// 开始绘制纹理映射过的四边形
            glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
            glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
            glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
            glEnd();				// 四边形绘制结束
        }
        //现在绘制第二遍的星星。唯一和前面的代码不同的是这一遍的星星肯定会被绘制,并且这次的星星绕着z轴旋转。
        glRotatef(m_spin,0.0f,0.0f,1.0f); // Z轴旋转星星
        // 使用byte型数值指定一个颜色
        glColor4ub(m_stars[i].r,m_stars[i].g,m_stars[i].b,255);
        glBegin(GL_QUADS);				  // 开始绘制纹理映射过的四边形
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
        glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
        glEnd();					      // 四边形绘制结束

        //以下的代码代表星星的运动。我们增加spin的值来旋转所有的星星(公转)。
        //然后,将每颗星星的自转角度增加loop/num。这使离中心更远的星星转的更快。
        //最后减少每颗星星离屏幕中心的距离。这样看起来,星星们好像被不断地吸入屏幕的中心。
        m_spin+=0.01f;					    // 星星的公转
        m_stars[i].angle+=float(i)/iend;    // 改变星星的自转角度
        m_stars[i].dist-=0.01f;				// 改变星星离中心的距离
        //接着几行检查星星是否已经碰到了屏幕中心。当星星碰到屏幕中心时,
        //我们为它赋一个新颜色,然后往外移5个单位,这颗星星将踏上它回归屏幕中心的旅程。
        if (m_stars[i].dist<0.0f)			// 星星到达中心了么
        {
            m_stars[i].dist+=5.0f;			// 往外移5个单位
            m_stars[i].r=qrand()%256;		// 赋一个新红色分量
            m_stars[i].g=qrand()%256;		// 赋一个新绿色分量
            m_stars[i].b=qrand()%256;		// 赋一个新蓝色分量
        }
    }
}

void Lesson8::initialize()
{
    loadGLTexture();                      // 加载纹理
    glEnable(GL_TEXTURE_2D);              // 启用纹理映射
    glShadeModel(GL_SMOOTH);              // 启用平滑着色
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景
    glClearDepth(1.0f);                   // 设置深度缓存
//    glEnable(GL_DEPTH_TEST);              // 启用深度测试
//    glDepthFunc(GL_LEQUAL);               // 深度测试类型
    // 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    glBlendFunc(GL_SRC_ALPHA,GL_ONE);	  // 基于源象素alpha通道值的半透明混合函数
    glEnable(GL_BLEND);		              // 打开混合

    //以下是新增的代码。设置了每颗星星的起始角度、距离、和颜色。您会注意到修改结构的属性有多容易。
    //全部50颗星星都会被循环设置。
    for (int i = 0, iend = starCount; isetFormat(requestedFormat());
        m_context->create();

        needsInitialize = true;
    }

    m_context->makeCurrent(this);

    if (needsInitialize) {
        initializeOpenGLFunctions();
        initialize();
    }

    render();

    m_context->swapBuffers(this);
}

void Lesson8::loadGLTexture()
{
    QImage image(":/image/Star.bmp");
    image = image.convertToFormat(QImage::Format_RGB888);
    image = image.mirrored();
    glGenTextures(1, &m_texture[0]);// 创建一个纹理
    // 创建一个线性滤波纹理
    glBindTexture(GL_TEXTURE_2D, m_texture[0]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(),
                 0, GL_RGB, GL_UNSIGNED_BYTE, image.bits());
}

void Lesson8::exposeEvent(QExposeEvent *event)
{
    Q_UNUSED(event);

    if (isExposed())
    {
        renderNow();
    }
}

void Lesson8::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);

    if (isExposed())
    {
        renderNow();
    }
}

void Lesson8::keyPressEvent(QKeyEvent *event)
{
    int key=event->key();
    switch(key)
    {
    case Qt::Key_T:
    {
        m_twinkle=!m_twinkle; // 控制闪烁
        break;
    }
    case Qt::Key_Up:
    {
        m_tilt-=0.5f;
        break;
    }
    case Qt::Key_Down:
    {
        m_tilt+=0.5f;
        break;
    }
    case Qt::Key_Right:
    {
        m_zoom-=0.2f;
        break;
    }
    case Qt::Key_Left:
    {
        m_zoom+=0.2f;
        break;
    }

    }
    if(key==Qt::Key_T||key==Qt::Key_Up||key==Qt::Key_Down||key==Qt::Key_Right||key==Qt::Key_Left)
    {
        renderNow();
    }

}

main.cpp

#include#includeint main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QSurfaceFormat format;
    format.setSamples(16);

    Lesson8 window;
    window.setFormat(format);
    window.resize(640, 480);
    window.show();

    return app.exec();
}

运行效果(通过方向键可得到下图)


按键控制

T键:打开和关闭星星闪烁效果

Left和Right:控制星星的远近

Up和Down:控制星星的倾角


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

特朗普集团近日取消了其新推出的T1智能手机“将在美国制造”的宣传标语,此举源于外界对这款手机能否以当前定价在美国本土生产的质疑。

关键字: 特朗普 苹果 AI

美国总统特朗普在公开场合表示,他已要求苹果公司CEO蒂姆·库克停止在印度建厂,矛头直指该公司生产多元化的计划。

关键字: 特朗普 苹果 AI

4月10日消息,据媒体报道,美国总统特朗普宣布,美国对部分贸易伙伴暂停90天执行新关税政策,同时对中国的关税提高到125%,该消息公布后苹果股价飙升了15%。这次反弹使苹果市值增加了4000多亿美元,目前苹果市值接近3万...

关键字: 特朗普 AI 人工智能 特斯拉

3月25日消息,据报道,当地时间3月20日,美国总统特朗普在社交媒体平台“真实社交”上发文写道:“那些被抓到破坏特斯拉的人,将有很大可能被判入狱长达20年,这包括资助(破坏特斯拉汽车)者,我们正在寻找你。”

关键字: 特朗普 AI 人工智能 特斯拉

1月22日消息,刚刚,新任美国总统特朗普放出重磅消息,将全力支持美国AI发展。

关键字: 特朗普 AI 人工智能

特朗普先生有两件事一定会载入史册,一个是筑墙,一个是挖坑。在美墨边境筑墙的口号确保边境安全,降低因非法移民引起的犯罪率过高问题;在中美科技产业之间挖坑的口号也是安全,美国企业不得使用对美国国家安全构成威胁的电信设备,总统...

关键字: 特朗普 孤立主义 科技产业

据路透社1月17日消息显示,知情人士透露,特朗普已通知英特尔、铠侠在内的几家华为供应商,将要撤销其对华为的出货的部分许可证,同时将拒绝其他数十个向华为供货的申请。据透露,共有4家公司的8份许可被撤销。另外,相关公司收到撤...

关键字: 华为 芯片 特朗普

曾在2018年时被美国总统特朗普称作“世界第八奇迹”的富士康集团在美国威斯康星州投资建设的LCD显示屏工厂项目,如今却因为富士康将项目大幅缩水并拒绝签订新的合同而陷入了僵局。这也导致富士康无法从当地政府那里获得约40亿美...

关键字: 特朗普 富士康

今年5月,因自己发布的推文被贴上“无确凿依据”标签而与推特发生激烈争执后,美国总统特朗普签署了一项行政令,下令要求重审《通信规范法》第230条。

关键字: 谷歌 facebook 特朗普

众所周知,寄往白宫的所有邮件在到达白宫之前都会在他地进行分类和筛选。9月19日,根据美国相关执法官员的通报,本周早些时候,执法人员截获了一个寄给特朗普总统的包裹,该包裹内包含蓖麻毒蛋白。

关键字: 美国 白宫 特朗普
关闭