码迷,mamicode.com
首页 > Windows程序 > 详细

在之前的基础上继续分析openglwindow例子——了解视角

时间:2016-04-29 19:30:47      阅读:293      评论:0      收藏:0      [点我收藏+]

标签:

      在写第二个QOpenGLWidget的时候,参考了openglwindow例子的顶点设置和颜色设置,及项目对象的使用。大致对整个过程有所了解了。第二个程序实现了绘制三角形并着色,但并没有像openglwindow那样做旋转展示,还是静态的。

      现在来分析分析这个例子,学习视角。


main.cpp

#include "openglwindow.h"

#include <QtGui/QGuiApplication>
#include <QtGui/QMatrix4x4>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QScreen>

#include <QtCore/qmath.h>

//! [1]
class TriangleWindow : public OpenGLWindow
{
public:
    TriangleWindow();

    void initialize() Q_DECL_OVERRIDE;
    void render() Q_DECL_OVERRIDE;

private:
    GLuint m_posAttr;
    GLuint m_colAttr;
    GLuint m_matrixUniform;

    QOpenGLShaderProgram *m_program;
    int m_frame;
};

TriangleWindow::TriangleWindow()
    : m_program(0)
    , m_frame(0)
{
}
//! [1]

//! [2]
int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    QSurfaceFormat format;
    format.setSamples(16);

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

    window.setAnimating(true);

    return app.exec();
}
//! [2]


//! [3]
static const char *vertexShaderSource =
    "attribute highp vec4 posAttr;\n"
    "attribute lowp vec4 colAttr;\n"
    "varying lowp vec4 col;\n"
    "uniform highp mat4 matrix;\n"
    "void main() {\n"
    "   col = colAttr;\n"
    "   gl_Position = matrix * posAttr;\n"
    "}\n";

static const char *fragmentShaderSource =
    "varying lowp vec4 col;\n"
    "void main() {\n"
    "   gl_FragColor = col;\n"
    "}\n";
//! [3]

//! [4]
void TriangleWindow::initialize()
{
    m_program = new QOpenGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_program->link();
    m_posAttr = m_program->attributeLocation("posAttr");
    m_colAttr = m_program->attributeLocation("colAttr");
    m_matrixUniform = m_program->uniformLocation("matrix");
}
//! [4]

//! [5]
void TriangleWindow::render()
{
    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, width() * retinaScale, height() * retinaScale);

    glClear(GL_COLOR_BUFFER_BIT);

    m_program->bind();

    QMatrix4x4 matrix;
    matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
    matrix.translate(0, 0, -2);
    matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);

    m_program->setUniformValue(m_matrixUniform, matrix);

    GLfloat vertices[] = {
        0.0f, 0.707f,
        -0.5f, -0.5f,
        0.5f, -0.5f
    };

    GLfloat colors[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f
    };

    glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

    m_program->release();

    ++m_frame;
}
//! [5]

      相比正常应用的main.cpp,这个显得比较特别,把习惯分开的两个文件合并在一起了。

      main()中

QSurfaceFormat format;
format.setSamples(16);

     format设置,setSamples(16)不知道什么意思。

     然后是继承自OpenGLWindow的窗体,实现了initializerender。

     片段着色器,以顶点着色器的颜色输出做输入,然后直接输出。

     顶点着色器,输入的顶点颜色做输出,输入一个常量矩阵与位置属性,输出相乘后的位置结果。

     initialize初始化,link之后,获取获取三个属性的位置,没有初始化。

     render位置变化,首先

const qreal retinaScale = devicePixelRatio();
QWindow::devicePixelRatio
QWindow继承自QSurface

     获取设备的像素比。找到了之前的说明

QGLWidget

    OpenGL是在设备像素空间中工作的。例如,传递给glViewport的宽和高应该是设备像素。QGLWidget::resizeGL()中的宽和高也是设备像素的。

    不管怎样,QGLWidget::width()实际上就是QWidget::width(),它返回的是设备无关像素的值。如果需要,用它来乘以widget->windowHandle()->devicePixelRatio()可以解决很多问题。

     获得像素比后,按像素比设置视窗大小。

    设置一个矩阵做透视投影矩阵,

matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
     帮助文档是这样说的

void QMatrix4x4::perspective(floatverticalAngle,floataspectRatio,floatnearPlane,floatfarPlane)

Multiplies this matrix by another that applies a perspective projection. The vertical field of view will beverticalAngle degrees within a window with a givenaspectRatio that determines the horizontal field of view. The projection will have the specifiednearPlane andfarPlane clipping planes which are the distances from the viewer to the corresponding planes.

See alsoortho() andfrustum().

     第一句,纠结了很久。先翻译第二句,拆分一下,aspectRatio决定了view视野的水平部分,而垂直部分将呈现一个window视窗verticalAngle 个角度。这样翻译,看不懂,断错了。在一个给定aspectRatio 像素比的窗口内垂直部分将呈现verticalAngle 个角度,而aspectRatio 决定了水平部分(?)。

     对于投影面来说,它的宽和高大多数情况下不同,即宽高比不为1,比 如640/480。而CVV的宽高是相同的,即宽高比永远是1。这就造成了多边形的失真现象,比如一个投影面上的正方形在CVV的面上可能变成了一个长方形。解决这个问题的方法就是在对多变形进行透视变换、裁剪、透视除法之后,在归一化的设备坐标(Normalized Device Coordinates)上进行的视口(viewport)变换中进行校正,它会把归一化的顶点之间按照和投影面上相同的比例变换到视口中,从而解除透视投影变换带来的失真现象。进行校正前提就是要使投影平面的宽高比和视口的宽高比相同。

     第一句应该翻译为使用另一个矩阵来乘以这个矩阵可以来应用透视投影。第三句,(这样的话,)投影(的效果)将会有近平面和远平面两种指定的剪切平面,即观察者和相应的面的距离不同。

     所以在设置视口(其实我喜欢翻译为视窗,这样才形象,窗,联想到的才是眼睛,眼睛是心灵的窗户嘛)时先获取像素比,再按像素比设置。

     但还是不太理解

matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);这样设置会怎样,即为什么这样设置

void QMatrix4x4::translate(floatx,floaty,floatz)

This is an overloaded function.

Multiplies this matrix by another that translates coordinates by the componentsx,y, andz.

See alsoscale() androtate().

转换坐标系?

void QMatrix4x4::rotate(floatangle,floatx,floaty,floatz = 0.0f)

This is an overloaded function.

Multiplies this matrix by another that rotates coordinates throughangle degrees about the vector (x,y,z).

See alsoscale() andtranslate().

    旋转坐标系,这个就产生了旋转的效果。旋转后的三角形样子就需要上面两个做了。


http://www.cnblogs.com/hisiqi/p/3155813.html

透视投影(Perspective Projection)变换推导

     不是数学系的估计粗略看一眼都烦了。

     所以,我把旋转的函数注释掉,其他不动的,perspective的第一个参数角度,因为会循环刷新的,所以添加了一个倍数变量,从0.0度到360.0度变化,增幅为10度,也就是0度、10度、20度这样变化,看到的效果是0度,看到整窗的颜色,相当于0距离看三角形;0到180之间,三角形由大到小变换,即感觉三角形由近到远渐渐离去;180度,看不到了,从理论上将应该是缩小到一个点了;180到360之间,三角形是绕中心点旋转了180度,然后由远到近出现,不是上下翻转了。
     从结果来看,应该这样想,你要看到物体,就要物体投影到你的视网膜上。(从光学角度来说,是光线汇聚到一点,所以模型是视网膜上的一点开始向外界延伸出去的立锥体。)但是,这里,刚好相反。显示的窗口,即视口(能看到外界的范围),也是近平面,第三个参数设置为0.1f,很小,可以忽略,但不能忽视,为什么不设置0 ,其实设置为0,就什么都看不到了。“我们做这样一个假设,我们从窗户看外面的场景,而窗户上挂着个小物件,第三个参数就是我们离窗户的距离,假设是个-1,也就是我们的头有点伸出窗户了,就看不到小物件了。0时也一样。”
      第四个参数设置远平面。这样的效果模型就是,远平面上的一个点开始像近平面延伸过来立锥体,就像这样。
技术分享
      坐标原点是远平面的一个点,当你的眼睛将一个物体看作一个点的距离时的面就是右边第二个平面,大小(近平面大小)几乎为
glViewport(0, 0, width() * retinaScale, height() * retinaScale);

      设置的视口,即能看到外界的范围。在这个范围内,perspective的第二个参数,比例,按照这个比例将物体投影。

      一个实际三角形大小是设定好的,不会变,现在看作是这个正立锥体的一个等宽等高的截面。而perspective的第一个参数,看做是这个正立锥体顶点的角度。当顶点角度为60度时,假设那个截面的位置是图中的小正方形截面,此时再用平行光线将这个小正方形截面投影到后面的大正方形(即近平面)上,再将这个小正方形换回等宽等高的正三角形,此时的大正方形(即近平面)上的画面就是看到的程序窗口的效果。但要注意,是从z处向大正方形看去。

     当角度逐渐变小,这个小正方形截面会远离坐标原点,同时也会慢慢按这个角度变大,投影出来的三角形也变大。当这个小正方形截面在大正方形右面时,投影出来是三角形的一部分了。继续想想,当0度时,要得到这样的小正方形截面,需要很远很远的地方了,也就是这个小正方形截面无限大,但投影到大正方形(即近平面)上,就只有大正方形(即近平面)的大小。所以从0度到180度的效果就是这个截面从无限大、无穷远处逐渐先原点靠近并缩小到一点。

     至于180度到360度为什么会旋转180度,就不容易像这样能想像出来了。

     经过这样的想像,就有了上面那张图的透视投影的模型图了。

matrix.translate(0, 0, z);
     同样使用倍数变量看变化

     z从-100到0,看到的效果也是大小的缩放。但是z >=0时就看不到什么东西了

matrix.translate(10, 0, z);

     z从-100到0,效果就是三角形从右前方到你的右后方经过。但是z >=0时就看不到什么东西了

matrix.translate(0, 10, z);

     z从-100到0,效果就是三角形从前方到你的上方经过。但是z >=0时就看不到什么东西了

     知道了,其实移动的是投影面(在我理解中,近平面就是投影面),translate(0,0, z);相当于是在设置投影面相对于视口的位置。所以当z>=0是投影面在视口后方,从视口往里看就看不到投影面了。OK,理解了。


matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);

      看不懂,不过可以知道,这样是绕Y轴旋转

     (x, 1, 0, 0)绕X轴,这个有点划桨的感觉(x, 0, 0, 1)绕Z轴,这个效果也明显

http://stackoverflow.com/questions/25919378/qt-transform-matrix

The math Qt is performing is correct, but the frame of reference that Qt is using doesn‘t match up with what you are thinking.

Matrix Math:

技术分享

So the components you are seeing in the math are adding shear in the x and y directions of image.

But the rotation that Qt does is about one of those axis. So if you want to do a standard 2D rotation, without shearing, you need to either not specify an axis or you specify the z axis.

The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis, as the default axis is the z axis (axis { x: 0; y: 0; z: 1 }).

http://qt-project.org/doc/qt-5/qml-qtquick-rotation.html

The rotation about the y axis looks like this:

技术分享



      下面就是OpenGLWindow这个东西了

#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>

QT_BEGIN_NAMESPACE
class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;
QT_END_NAMESPACE

//! [1]
class OpenGLWindow : public QWindow, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenGLWindow(QWindow *parent = 0);
    ~OpenGLWindow();

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

    virtual void initialize();

    void setAnimating(bool animating);

public slots:
    void renderLater();
    void renderNow();

protected:
    bool event(QEvent *event) Q_DECL_OVERRIDE;

    void exposeEvent(QExposeEvent *event) Q_DECL_OVERRIDE;

private:
    bool m_update_pending;
    bool m_animating;

    QOpenGLContext *m_context;
    QOpenGLPaintDevice *m_device;
};
//! [1]

#include "openglwindow.h"

#include <QtCore/QCoreApplication>

#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLPaintDevice>
#include <QtGui/QPainter>

//! [1]
OpenGLWindow::OpenGLWindow(QWindow *parent)
    : QWindow(parent)
    , m_update_pending(false)
    , m_animating(false)
    , m_context(0)
    , m_device(0)
{
    setSurfaceType(QWindow::OpenGLSurface);
}
//! [1]

OpenGLWindow::~OpenGLWindow()
{
    delete m_device;
}
//! [2]
void OpenGLWindow::render(QPainter *painter)
{
    Q_UNUSED(painter);
}

void OpenGLWindow::initialize()
{
}

void OpenGLWindow::render()
{
    if (!m_device)
        m_device = new QOpenGLPaintDevice;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    m_device->setSize(size());

    QPainter painter(m_device);
    render(&painter);
}
//! [2]

//! [3]
void OpenGLWindow::renderLater()
{
    if (!m_update_pending) {
        m_update_pending = true;
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}

bool OpenGLWindow::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::UpdateRequest:
        m_update_pending = false;
        renderNow();
        return true;
    default:
        return QWindow::event(event);
    }
}

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

    if (isExposed())
        renderNow();
}
//! [3]

//! [4]
void OpenGLWindow::renderNow()
{
    if (!isExposed())
        return;

    bool needsInitialize = false;

    if (!m_context) {
        m_context = new QOpenGLContext(this);
        m_context->setFormat(requestedFormat());
        m_context->create();

        needsInitialize = true;
    }

    m_context->makeCurrent(this);

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

    render();

    m_context->swapBuffers(this);

    if (m_animating)
        renderLater();
}
//! [4]

//! [5]
void OpenGLWindow::setAnimating(bool animating)
{
    m_animating = animating;

    if (animating)
        renderLater();
}
//! [5]


在之前的基础上继续分析openglwindow例子——了解视角

标签:

原文地址:http://blog.csdn.net/fu851523125/article/details/51228344

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!