标签:
在写第二个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的窗体,实现了initialize和render。
片段着色器,以顶点着色器的颜色输出做输入,然后直接输出。
顶点着色器,输入的顶点颜色做输出,输入一个常量矩阵与位置属性,输出相乘后的位置结果。
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.
第一句,纠结了很久。先翻译第二句,拆分一下,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.
转换坐标系?
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
glViewport(0, 0, width() * retinaScale, height() * retinaScale);
一个实际三角形大小是设定好的,不会变,现在看作是这个正立锥体的一个等宽等高的截面。而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