文章目录
- 在着色器中实现旋转的彩色正方体
- 旋转矩阵
- 沿x轴旋转:
- 沿y轴旋转:
- 沿z轴旋转:
- 在顶点着色器中实现
- 顶一个vec3的变量 theta
- 计算余弦和正弦值
- 定义3个旋转矩阵
- 最终代码
在着色器中实现旋转的彩色正方体
一直觉得用OpenGL 画一个立方体或者彩色的立方体或者贴图的立方体甚至旋转着的立方体都是一件很容易的事情。具体方法可以参考 [变换 - LearnOpenGL CN (learnopengl-cn.github.io)](https://learnopengl-cn.github.io/01 Getting started/07 Transformations/) 这一章。简单的旋转矩阵就能轻松实现,用Qt的内容替换一下,轻轻松松。所以也没去总结该章笔记。
在《交互式计算机图形学 基于OpenGL着色器的自顶向下方法 第6版_中文版》有个代码案例,是在 GLSL 中实现的旋转,实现的方法挺巧妙,而且代码也有利于 GLSL 的熟悉和学习。
旋转矩阵
这部分内容 就参考 《 LearnOpenGL CN 》中的内容吧。这是旋转的思路。
在3D空间中旋转需要定义一个角 和 一个旋转轴(Rotation Axis)。物体会沿着给定的旋转轴旋转特定角度。
使用三角函数,给定一个角度,可以把一个向量变换为一个经过旋转的新向量。通常是使用正弦和余弦函数(一般简称sin和cos)各种巧妙的组合得到的。
旋转矩阵在3D空间中每个单位轴都有不同定义,旋转角度用θ表示:
【csdn 不支持 latex 公式,还是截图】
沿x轴旋转:
沿y轴旋转:
沿z轴旋转:
在顶点着色器中实现
顶一个vec3的变量 theta
uniform vec3 theta; // 传入的是角度
计算余弦和正弦值
vec3 angles = radians(theta); // 转成弧度
vec3 c = cos(angles);
vec3 s = sin(angles);
定义3个旋转矩阵
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, -s.x, 0.0,
0.0, s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 ry = mat4( c.y, 0.0, s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
-s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 rz = mat4( c.z, -s.z, 0.0, 0.0,
s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
gl_Position = rx * ry * rz * vPosition; // 输出顶点位置
最终代码
gif的问题,色彩过渡不够线性
#include "Widget.h"
#include "qnamespace.h"
#include <QApplication>
#include <QMouseEvent>
#include <QThread>
#include <QVector3D>
#include <vcruntime.h>
#include <vector>
#define qRandom QRandomGenerator::global ()
#define qout if( 1 ) qDebug() << __FILE__ << __LINE__ << ": "
int rotateAngle = 0;
typedef QVector4D color4;
//typedef QVector3D point3;
typedef QVector4D point4;
const int NumVertices = 36;
point4 points[NumVertices];
point4 colors[NumVertices];
// 中心位于坐标原点,边与坐标轴平行的单位立方体的顶点
point4 vertices_points[8] = {
{ -0.5, -0.5, 0.5, 1.0 },
{ -0.5, 0.5, 0.5, 1.0 },
{ 0.5, 0.5, 0.5, 1.0 },
{ 0.5, -0.5, 0.5, 1.0 },
{ -0.5, -0.5, -0.5, 1.0 },
{ -0.5, 0.5, -0.5, 1.0 },
{ 0.5, 0.5, -0.5, 1.0 },
{ 0.5, -0.5, -0.5, 1.0 },
};
point4 vertex_colors[8] = {
point4( 0.0, 0.0, 0.0, 1.0),
point4( 1.0, 0.0, 0.0, 1.0),
point4( 1.0, 1.0, 0.0, 1.0),
point4( 0.0, 1.0, 0.0, 1.0),
point4( 0.0, 0.0, 1.0, 1.0),
point4( 1.0, 0.0, 1.0, 1.0),
point4( 1.0, 1.0, 1.0, 1.0),
point4( 0.0, 1.0, 1.0, 1.0),
};
// 相对于每个坐标轴的旋转角度(°)
enum {Xaxis, Yaxis, Zaxis, NumAxes};
int Axis = Zaxis;
//float Theta[NumAxes] = { 0.0, 0.0, 0.0 };
QVector3D Theta = { 0.0, 0.0, 0.0 };
//GLuint theta; // 顶点着色器中 uniform 变量 theta 的存储位置
//--------------------------------------
// 立方体的每个面生成两个三角形,并且将颜色赋给每个顶点
int Index = 0;
void quad( int a, int b, int c, int d )
{
colors[Index] = vertex_colors[a]; points[Index] = vertices_points[a]; ++Index;
colors[Index] = vertex_colors[b]; points[Index] = vertices_points[b]; ++Index;
colors[Index] = vertex_colors[c]; points[Index] = vertices_points[c]; ++Index;
colors[Index] = vertex_colors[a]; points[Index] = vertices_points[a]; ++Index;
colors[Index] = vertex_colors[c]; points[Index] = vertices_points[c]; ++Index;
colors[Index] = vertex_colors[d]; points[Index] = vertices_points[d]; ++Index;
}
//--------------------------------------
// 生成12个三角形:36个顶点(每个顶点赋一种颜色)
void colorcube(void)
{
quad(1,0,3,2);
quad(2,3,7,6);
quad(3,0,4,7);
quad(6,5,1,2);
quad(4,5,6,7);
quad(5,4,0,1);
}
Widget::Widget(QWidget *parent)
: QOpenGLWidget(parent)
{
setWindowTitle ("10_cube");
resize (200,200);
ratio = qApp->devicePixelRatio ();
colorcube();
// qout << sizeof(vertices);
// qout << sizeof(point4);
}
Widget::~Widget()
{
makeCurrent ();
glDeleteBuffers (1,&VBO);
glDeleteVertexArrays (1,&VAO);
doneCurrent ();
}
void Widget::initializeGL()
{
initializeOpenGLFunctions ();
const char *version =(const char *) glGetString (GL_VERSION);
qout << QString(version);
// ---------------------------------
glGenBuffers (1,&VBO);
glBindBuffer (GL_ARRAY_BUFFER,VBO);
glBufferData (GL_ARRAY_BUFFER,
sizeof(points) + sizeof(colors),
nullptr,
GL_STATIC_DRAW);
glBufferSubData (GL_ARRAY_BUFFER, 0 , sizeof(points) , points );
glBufferSubData (GL_ARRAY_BUFFER, sizeof(points) , sizeof(colors) , colors );
// ---------------------------------
glGenVertexArrays (1,&VAO);
glBindVertexArray(VAO);
glVertexAttribPointer(0, // vao 索引,
4, // 变量中元素的个数,
GL_FLOAT, // 类型
GL_FALSE, // 标准化,
4 * sizeof(float), // 步长,
(void*)0 ); // 变量的偏移量
glEnableVertexAttribArray(0); // 使用 location = 0 的索引
glVertexAttribPointer(1, // vao 索引,
4, // 变量中元素的个数,
GL_FLOAT, // 类型
GL_FALSE, // 标准化
4 * sizeof(float), // 步长
(void*)sizeof(points) ); // 变量的偏移量
glEnableVertexAttribArray(1); // 使用 location = 1 的索引
// ---------------------------------
QString filename = ":/shader";
shaderProgram.addShaderFromSourceFile (QOpenGLShader::Vertex, filename+".vert");
shaderProgram.addShaderFromSourceFile (QOpenGLShader::Fragment, filename+".frag");
shaderProgram.link ();
glBindBuffer (GL_ARRAY_BUFFER,0);
// 控制多边形的正面和背面的绘图模式
// glPolygonMode (GL_FRONT_AND_BACK,GL_LINE);
// 深度测试
glEnable(GL_DEPTH_TEST);
}
void Widget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置背景色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport ((max-min)/2 ,0,min,min);
glBindVertexArray(VAO);
shaderProgram.bind ();
shaderProgram.setUniformValue ("theta",Theta);
qout << Theta;
// shaderProgram.setUniformValueArray ("theta",Theta,1,3);
glDrawArrays (GL_TRIANGLES, 0, NumVertices );
if( 1 ){
QThread::currentThread ()->msleep (50);
Theta[Axis] += 3;
if( Theta[Axis] > 360.0 )
Theta[Axis] = - 360.0;
update ();
}
}
void Widget::resizeGL(int w, int h)
{
qout << "resizeGL";
min = std::min (w,h) * ratio;
max = std::max (w,h) * ratio;
}
// 利用鼠标键切换旋转角度
void Widget::mousePressEvent(QMouseEvent *e)
{
auto b = e->button ();
switch (b) {
case Qt::LeftButton:
Axis = 0;
// Theta[1] = 0.0;
// Theta[2] = 0.0;
break;
case Qt::MiddleButton:
Axis = 1;
// Theta[0] = 0.0;
// Theta[2] = 0.0;
break;
case Qt::RightButton:
Axis = 2;
// Theta[0] = 0.0;
// Theta[1] = 0.0;
break;
default:
break;
}
}