(本文是LearnOpenGL的学习笔记,教程中文翻译地址https://learnopengl-cn.github.io/(备用地址https://learnopengl-cn.readthedocs.io/zh/latest/),写于 2020-2-4 ,并在 2021-9-5 进行了更新)
1.实现思路(本来想拿扑克的正反面来做练习,奈何没找到免费的资源图片,白嫖不易,于是拿两张明星图片来代替了)
代码主要参考教程 "坐标系统" 一节的源码,把顶点坐标和纹理坐标传递给着色器,然后旋转model矩阵就能实现了翻转效果了。
两个纹理的位置是一样的,那么如何区分正反面呢?在OpenGL中默认逆时针的顶点顺序是正面,所以我们把两个面的顶点顺序反一下,一个顺时针一个逆时针就可以了。有了正反面,还需要把背面隐藏起来,OpenGL中有面剔除的功能,我们把背面剔除即可。(参考:https://www.jianshu.com/p/4e165df3ae26)
glEnable(GL_CULL_FACE); //开启面剔除
glCullFace(GL_BACK); //剔除背面
//glFrontFace(GL_CW); //逆时针顶点为正面GL_CCW(默认),顺时针GL_CW
解决了正反面问题,还有个问题是如何把两张图分别贴到正反面 。开始我想的是用两个着色器程序分别绘制,但是感觉太麻烦了,索性把两张图合并到一起(这样就只有一个纹理贴图了),用纹理坐标来分割两张图贴到正反面。(如果其中一个图左右翻转了,把纹理坐标x翻转下就可以了)
//VAO,VBO(一个面两个三角)360/460(应该用qimage获取宽高,这里设定死了)
const float texture_width=360.0f/460.0f;
float vertices[] = {
-texture_width/2, -0.5f, 0.0f, 0.0f, 0.5f, //左下角
texture_width/2, -0.5f, 0.0f, 1.0f, 0.5f, //右下角
texture_width/2, 0.5f, 0.0f, 1.0f, 1.0f, //右上角
texture_width/2, 0.5f, 0.0f, 1.0f, 1.0f, //右上角
-texture_width/2, 0.5f, 0.0f, 0.0f, 1.0f, //左上角
-texture_width/2, -0.5f, 0.0f, 0.0f, 0.5f, //左下角
-texture_width/2, -0.5f, 0.0f, 1.0f, 0.0f, //左下角
-texture_width/2, 0.5f, 0.0f, 1.0f, 0.5f, //左上角
texture_width/2, 0.5f, 0.0f, 0.0f, 0.5f, //右上角
texture_width/2, 0.5f, 0.0f, 0.0f, 0.5f, //右上角
texture_width/2, -0.5f, 0.0f, 0.0f, 0.0f, //右下角
-texture_width/2, -0.5f, 0.0f, 1.0f, 0.0f, //左下角
};
坐标变换的代码用的教程里的方式,只旋转了model矩阵。
QMatrix4x4 view; //观察矩阵,后退一点
view.translate(QVector3D(0.0f, 0.0f, -1.5f));
_shaderProgram.setUniformValue("view", view);
QMatrix4x4 projection; //透视投影
projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
_shaderProgram.setUniformValue("projection", projection);
QMatrix4x4 model;//模型矩阵
model.rotate(_rotate, QVector3D(0.0f, 1.0f, 0.0f));
_shaderProgram.setUniformValue("model", model);
(代码里没有堆叠的问题,所以可以不用深度缓冲,我复制粘贴没去掉)
2.实现代码(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git)
Poker 类实现效果(左边图片右边GIF):
MyPoker类代码:
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
//扑克反转练习
//QOpenGLWidget窗口上下文
//QOpenGLFunctions访问OpenGL接口,可以不继承作为成员变量使用
class MyPoker
: public QOpenGLWidget
, protected QOpenGLFunctions_3_3_Core
{
public:
explicit MyPoker(QWidget *parent = nullptr);
~MyPoker();
protected:
//【】继承QOpenGLWidget后重写这三个虚函数
//设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
void initializeGL() override;
//渲染OpenGL场景,每当需要更新小部件时使用
void paintGL() override;
//设置OpenGL视口、投影等,每当尺寸大小改变时调用
void resizeGL(int width, int height) override;
private:
//着色器程序
QOpenGLShaderProgram shaderProgram;
//顶点数组对象
QOpenGLVertexArrayObject vao;
//顶点缓冲
QOpenGLBuffer vbo;
//纹理
QOpenGLTexture *texture{ nullptr };
//定时器,做动画效果
QTimer timer{ nullptr };
//旋转角度
int rotate{ 0 };
};
#include "MyPoker.h"
#include
#include
#include
MyPoker::MyPoker(QWidget *parent)
: QOpenGLWidget(parent)
{
connect(&timer,&QTimer::timeout,this,[this](){
rotate+=2;
if(isVisible()){
update();
}
});
timer.setInterval(50);
}
MyPoker::~MyPoker()
{
//initializeGL在显示时才调用,释放未初始化的会异常
if(!isValid())
return;
//QOpenGLWidget
//三个虚函数不需要makeCurrent,对应的操作已由框架完成
//但是释放时需要设置当前上下文
makeCurrent();
vbo.destroy();
vao.destroy();
delete texture;
doneCurrent();
}
void MyPoker::initializeGL()
{
//为当前上下文初始化OpenGL函数解析
initializeOpenGLFunctions();
//着色器代码
//in输入,out输出,uniform从cpu向gpu发送
//因为OpenGL纹理颠倒过来的,所以取反vec2(aTexCoord.x, 1-aTexCoord.y);
const char *vertex_str=R"(#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
})";
const char *fragment_str=R"(#version 330 core
in vec2 TexCoord;
uniform sampler2D texture1;
out vec4 FragColor;
void main()
{
FragColor = texture(texture1, TexCoord);
})";
//将source编译为指定类型的着色器,并添加到此着色器程序
if(!shaderProgram.addCacheableShaderFromSourceCode(
QOpenGLShader::Vertex,vertex_str)){
qDebug()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?