Linux环境QT5.9+OpenGL绘制图形与渲染(5)详解系列
这是一个系列的博客,OpenGL是基于计算机图形学为基础发展出来的一个分支,必须理解清楚的是“向量”,由数学向量与C语言结合发展出了shader language,shader封装再结合C++就是UE4和UE5图形引擎了。向量vector(不是C++里面的vector容器啊,不要搞混了),最重要的就是引入数学矩阵Matrix,一个矩阵就是一个向量,即将向量计算变成矩阵变换,同时一个向量vector可以等于两个矩阵计算的结果。
这是向量A,换算成矩阵的样子
矩阵的乘法满足于以下运算律:
结合律:(AB)C = A(BC)
左分配律:(A + B)C = AC + BC
右分配律:C(A + B) = CA + CB
矩阵乘法不满足交换律:
由此我们明白了,矩阵Matrix与向量Vector的关系,但是还没搞明白OpenGL与shader的关系。
OpenGL有vertex shader 和 fragment shader等过程,这些就是封装过的shader在OpenGL里面使用。
关于纹理滤波的问题:
线性插值滤波(GL_LINEAR)==的纹理贴图,这需要机器有相当高的处理能力,但是看起来效果会很好;
最临近值滤波(GL_NEAREST),它只占用很小的处理能力,看起来效果会比较差,但是使用它因为不占用资源,工程在很快和很慢的机器上都可以正常运行;也可以混合使用线性插值滤波和最临近值滤波,纹理看起来效果会好一些;
Mipmap,这是一种创建纹理的新方法;您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失,刚才还很不错的图案变得很难看;当您告诉OPenGL创建一个mipmaped纹理时,OPenGL将选择它已经创建的外观最佳的纹理(带有很多细节)来绘制,而不仅仅是缩放原先的图像(这将导致细节丢失)。
关于光照
(1)当不开启光照时,使用顶点颜色来产生整个表面的颜色。
用glShadeModel可以设置表面内部像素颜色产生的方式。GL_FLAT/GL_SMOOTH.
(2)一般而言,开启光照后,在场景中至少需要有一个光源(GL_LIGHT0.。.GL_LIGHT7)
通过glEnable(GL_LIGHT0) glDisable(GL_LIGHT0) 来开启和关闭指定的光源。
— 全局环境光 —
GLfloat gAmbient[] = {0.6, 0,6, 0,6, 1.0};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, gAmbient);
(3)设置光源的光分量 – 环境光/漫色光/镜面光
默认情况下,GL_LIGHT0.。.GL_LIGHT7 的GL_AMBIENT值为(0.0, 0.0, 0.0, 1.0);
GL_LIGHT0的GL_DIFFUSE和GL_SPECULAR值为(1.0, 1.0, 1.0, 1.0),
GL_LIGHT1.。.GL_LIGHT7 的GL_DIFFUSE和GL_SPECULAR值为(0.0, 0.0, 0.0, 0.0)。
GLfloat lightAmbient[] = {1.0, 1.0, 1.0, 1.0};
GLfloat lightDiffuse[] = {1.0, 1.0, 1.0, 1.0};
GLfloat lightSpecular[] = {0.5, 0.5, 0.5, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
(4)设置光源的位置和方向
– 平行光 – 没有位置只有方向
GLfloat lightPosiTIon[] = {8.5, 5.0, -2.0, 0.0}; // w=0.0
glLightfv(GL_LIGHT0, GL_POSITION, lightPosiTIon);
– 点光源 – 有位置没有方向
GLfloat lightPosiTIon[] = {8.5, 5.0, -2.0, 1.0}; // w不为0
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
– 聚光灯 – 有位置有方向
GLfloat lightPosition[] = {-6.0, 1.0, 3.0, 1.0}; // w不为0
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
GLfloat lightDirection[] = {1.0, 1.0, 0.0};
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightDirection); // 聚光灯主轴方向 spot direction
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0); // cutoff角度 spot cutoff
** 平行光不会随着距离d增加而衰减,但点光源和聚光灯会发生衰减。
attenuation为衰变系数,系数值越大,衰变越快。
默认情况下,c=1.0, l=0.0, q=0.0
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0); // c 系数
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0); // l 系数
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5); // q 系数
.h文件
ifndef MYGLWIDGET_H
define MYGLWIDGET_H
include
include
class MyGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent = nullptr);
~MyGLWidget();
protected:
virtual void paintGL();
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void timerEvent(QTimerEvent *event);
virtual void keyPressEvent(QKeyEvent *event);
private:
bool mFullScreen;
GLfloat mRotateTriangle;
GLfloat mRotateQuad;
};
endif // MYGLWIDGET_H
2.cpp文件
include “myglwidget.h”
include <GL/glu.h>
include
- MyGLWidget::MyGLWidget(QWidget *parent)
- QOpenGLWidget(parent), mFullScreen(false), mRotateTriangle(0.0f), mRotateQuad(0.0f)
{
showNormal();
startTimer(50);
}
MyGLWidget::~MyGLWidget()
{
}
void MyGLWidget::resizeGL(int w, int h)
{
if(h == 0){
h = 1;
}
glViewport(0, 0, w, h);
//下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)w/(GLfloat)h, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void MyGLWidget::initializeGL()
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glClearDepth(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
void MyGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-1.5f, 0.0f, -6.0f);
glRotatef(mRotateTriangle, 0.0f, 1.0f, 0.0f);
glBegin(GL_TRIANGLES);
//上顶点在底面的投影位于底面的中心
glColor3f( 1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
//现在绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
//现在是后侧面。再次切换颜色。左下顶点又回到绿色,因为后侧面与右侧面共享这个角。
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
//最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glEnd();
glLoadIdentity();
glTranslatef(1.5f,0.0f,-7.0f); // 先右移再移入屏幕
glRotatef(mRotateQuad,1.0f,1.0f,1.0f); // XYZ轴上旋转立方体
glBegin(GL_QUADS);
//然后是靠近观察者的左下和右下顶点。就是屏幕往外一单位。
glColor3f(0.0f,1.0f,0.0f); // 颜色改为蓝色
glVertex3f( 1.0f, 1.0f,-1.0f); // 四边形的右上顶点 (顶面)
glVertex3f(-1.0f, 1.0f,-1.0f); // 四边形的左上顶点 (顶面)
glVertex3f(-1.0f, 1.0f, 1.0f); // 四边形的左下顶点 (顶面)
glVertex3f( 1.0f, 1.0f, 1.0f);
//但一旦您进入象纹理映射这样的领域时,忽略绘制次序会导致十分怪异的结果。
glColor3f(1.0f,0.5f,0.0f); // 颜色改成橙色
glVertex3f( 1.0f,-1.0f, 1.0f); // 四边形的右上顶点(底面)
glVertex3f(-1.0f,-1.0f, 1.0f); // 四边形的左上顶点(底面)
glVertex3f(-1.0f,-1.0f,-1.0f); // 四边形的左下顶点(底面)
glVertex3f( 1.0f,-1.0f,-1.0f);
//接着画立方体的前面。保持Z坐标为一单位,前面正对着我们
glColor3f(1.0f,0.0f,0.0f); // 颜色改成红色
glVertex3f( 1.0f, 1.0f, 1.0f); // 四边形的右上顶点(前面)
glVertex3f(-1.0f, 1.0f, 1.0f); // 四边形的左上顶点(前面)
glVertex3f(-1.0f,-1.0f, 1.0f); // 四边形的左下顶点(前面)
glVertex3f( 1.0f,-1.0f, 1.0f);
//立方体后面的绘制方法与前面类似。只是位于屏幕的里面。
glColor3f(1.0f,1.0f,0.0f); // 颜色改成黄色
glVertex3f( 1.0f,-1.0f,-1.0f); // 四边形的右上顶点(后面)
glVertex3f(-1.0f,-1.0f,-1.0f); // 四边形的左上顶点(后面)
glVertex3f(-1.0f, 1.0f,-1.0f); // 四边形的左下顶点(后面)
glVertex3f( 1.0f, 1.0f,-1.0f);
//还剩两个面就完成了。您会注意到总有一个坐标保持不变。这一次换成了X坐标。
glColor3f(0.0f,0.0f,1.0f); // 颜色改成蓝色
glVertex3f(-1.0f, 1.0f, 1.0f); // 四边形的右上顶点(左面)
glVertex3f(-1.0f, 1.0f,-1.0f); // 四边形的左上顶点(左面)
glVertex3f(-1.0f,-1.0f,-1.0f); // 四边形的左下顶点(左面)
glVertex3f(-1.0f,-1.0f, 1.0f);
//您会看见一个非常漂亮的彩色立方体,各种颜色在它的各个表面流淌。
glColor3f(1.0f,0.0f,1.0f); // 颜色改成紫罗兰色
glVertex3f( 1.0f, 1.0f,-1.0f); // 四边形的右上顶点(右面)
glVertex3f( 1.0f, 1.0f, 1.0f); // 四边形的左上顶点(右面)
glVertex3f( 1.0f,-1.0f, 1.0f); // 四边形的左下顶点(右面)
glVertex3f( 1.0f,-1.0f,-1.0f); // 四边形的右下顶点(右面)
glEnd();
}
void MyGLWidget::timerEvent(QTimerEvent *event)
{
//并试着将0.2改成1.0。这个数字越大,物体就转的越快,这个数字越小,物体转的就越慢。
mRotateTriangle += 5.0f; // 增加三角形的旋转变量
mRotateQuad -= 0.3f;
update();
QOpenGLWidget::timerEvent(event);
}
void MyGLWidget::keyPressEvent(QKeyEvent *event)
{
switch(event->key()){
case Qt::Key_F2:{
mFullScreen = !mFullScreen;
if(mFullScreen){
showFullScreen();
}
else{
showNormal();
}
update();
break;
}
case Qt::Key_Escape:{
qApp->exit();
break;
}
}
}