三维 Siepinski 镂垫
把前面的二维Sierpinski程序转换成一个生成三维Sierpinski镂垫的程序,也就是说要绘制的镂垫不再只是限制在一个平面里。我们可仿效对二维镂垫所使用的两种方法中的任何一种。 即用一个四面体代替初始的三角形
相关代码
-
std::vector 容器比较使用于OpenGL
-
连续绑定 多个数组数据的使用方法
- glBufferData (GL_ARRAY_BUFFER,points.size () * sizeof(QVector3D)* 2,NULL,GL_STATIC_DRAW);
- glBufferSubData (GL_ARRAY_BUFFER, 0, points.size () * sizeof(QVector3D), points.data () );
- glBufferSubData (GL_ARRAY_BUFFER, points.size () * sizeof(QVector3D), colors.size () * sizeof(QVector3D), colors.data () );
- 每个面使用一种颜色
color3 base_colors[4] = { color3(1.0,0.0,0.0), color3(0.0,1.0,0.0), color3(0.0,0.0,1.0), color3(1.0,1.0,0.0), };
-
利用 pos 的 z 值加深显示,修改片元着色器
-
添加 model 矩阵,并旋转动态显示
-
屏幕缩放比例获取
qApp->devicePixelRatio (); -
Qt 的高分辨率设置,见main.cpp
main.cpp
#include "Widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute (Qt::AA_EnableHighDpiScaling);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec ();
}
Help.hpp
#ifndef HELPER_H
#define HELPER_H
#include <QVector3D>
typedef QVector3D point3;
typedef QVector3D color3;
extern std::vector<QVector3D> points;
extern std::vector<QVector3D> colors;
#include <QDebug>
#define qout if( 1 ) qDebug() << __FILE__ << __LINE__ << ": "
int colorindex;
color3 base_colors[4] = {
color3(1.0,0.0,0.0),
color3(0.0,1.0,0.0),
color3(0.0,0.0,1.0),
color3(1.0,1.0,0.0),
};
void triangle(point3 a, point3 b, point3 c) {
static int i = 0;
colors.emplace_back (base_colors[colorindex]);
points.emplace_back (a);
++i;
colors.emplace_back (base_colors[colorindex]);
points.emplace_back (b);
++i;
colors.emplace_back (base_colors[colorindex]);
points.emplace_back (c);
++i;
}
void tetra(point3 a, point3 b, point3 c,point3 d) {
colorindex = 0;
triangle(a,b,c);
colorindex = 1;
triangle(a,c,d);
colorindex = 2;
triangle(a,b,d);
colorindex = 3;
triangle(b,d,c);
}
void divide_tetra(point3 a, point3 b, point3 c,point3 d, int m) {
if(m > 0 )
{
point3 mid[6];
mid[0] = (a+b)/2.0;
mid[1] = (a+c)/2.0;
mid[2] = (a+d)/2.0;
mid[3] = (b+c)/2.0;
mid[4] = (c+d)/2.0;
mid[5] = (b+d)/2.0;
// 通过细分生成4个四面体
divide_tetra (a, mid[0], mid[1], mid[2], m-1);
divide_tetra (mid[0], b, mid[3], mid[5], m-1);
divide_tetra (mid[1], mid[3], c, mid[4], m-1);
divide_tetra (mid[2], mid[5], mid[4], d, m-1);
}
else
tetra (a,b,c,d);
}
#endif // WIDGET_H
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
class Widget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// QOpenGLWidget interface
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void resizeGL(int w, int h) override;
private:
unsigned int VAO,VBO;
QOpenGLShaderProgram shaderProgram;
// void (Widget::* p)();
int min,max;
double ratio; // 存放屏幕的缩放比例,Qt 5.15
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include "Helper.hpp"
#include "qcoreapplication.h"
#include "qelapsedtimer.h"
#include "qmatrix4x4.h"
#include "qvector3d.h"
#include <QThread>
#include <QDebug>
#include <QRandomGenerator>
#include <algorithm>
#include <vector>
#include <QApplication>
#define qRandom QRandomGenerator::global ()
#define qout if( 0 ) qDebug() << __FILE__ << __LINE__ << ": "
constexpr int NumTimesToSubdivide = 3;
// std::vector 是内存连续的容器,比较适合opengl 的数据绑定
std::vector<QVector3D> points;
std::vector<QVector3D> colors;
Widget::Widget(QWidget *parent)
: QOpenGLWidget(parent)
{
setWindowTitle ("04_Siepinski_Recursion_3D");
resize (300,300);
int NumTriangles = std::pow(4,NumTimesToSubdivide+1) * 3;
qout << NumTriangles;
points.reserve (NumTriangles); // 最后优化才添加的代码
colors.reserve (NumTriangles); // 最后优化才添加的代码
qout << points.size () << points.capacity (); // 大小 和 容量
p = &Widget::update;
ratio = qApp->devicePixelRatio ();
}
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);
QVector3D vertices[4] = {
// QVector3D(-1.0,-1.0,-1.0),
// QVector3D( 1.0,-1.0,-1.0),
// QVector3D( 0.0, 1.0,-1.0),
// QVector3D( 0.0, 0.0, 1.0)
QVector3D( 0.0f, 0.0f, -1.0f),
QVector3D( 0.0f, 0.942809f, 0.333333f),
QVector3D(-0.816497f, -0.471405f, 0.333333f),
QVector3D( 0.816497f, -0.471405f, 0.333333f)
};
// 参考 https://blog.csdn.net/zhanxi1992/article/details/105771685中的数据
QElapsedTimer timer;
timer.start ();
// 对初始的4面体进行细分
divide_tetra (vertices[0],vertices[1],vertices[2],vertices[3],NumTimesToSubdivide);
qout << timer.elapsed ();
glGenBuffers (1,&VBO);
glBindBuffer (GL_ARRAY_BUFFER,VBO);
glBufferData (GL_ARRAY_BUFFER,points.size () * sizeof(QVector3D)* 2,NULL,GL_STATIC_DRAW);
glBufferSubData (GL_ARRAY_BUFFER,
0,
points.size () * sizeof(QVector3D),
points.data () );
glBufferSubData (GL_ARRAY_BUFFER,
points.size () * sizeof(QVector3D),
colors.size () * sizeof(QVector3D),
colors.data () );
glGenVertexArrays (1,&VAO);
glBindVertexArray(VAO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), (void*)(points.size () * sizeof(QVector3D)));
glEnableVertexAttribArray(1);
shaderProgram.addShaderFromSourceFile (QOpenGLShader::Vertex, ":/shader.vert");
shaderProgram.addShaderFromSourceFile (QOpenGLShader::Fragment,":/shader.frag");
shaderProgram.link ();
glBindBuffer (GL_ARRAY_BUFFER,0);
// glPolygonMode (GL_FRONT_AND_BACK,GL_LINE);
// 使能 深度测试
glEnable(GL_DEPTH_TEST);
}
int count = 3;
QMatrix4x4 model;
void Widget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置背景色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderProgram.bind ();
shaderProgram.setUniformValue ("u_color",1.0f, 0.5f, 0.2f, 1.0f);
model.rotate ( 2, 1.0, 1.0, 1.0);
glViewport ((max-min)/2 ,0,min,min);
shaderProgram.setUniformValue ("model",model);
glBindVertexArray(VAO);
// 绘制三角形扇形
glDrawArrays (GL_TRIANGLES,0,(int)points.size ());
QThread::currentThread ()->msleep (50);
// (this->*p)();
update ();
}
void Widget::resizeGL(int w, int h)
{
qout << "resizeGL";
min = std::min (w,h) * ratio;
max = std::max (w,h) * ratio;
}
顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec4 Color;
uniform mat4 model;
void main()
{
gl_Position = model * vec4(aPos.x, aPos.y, aPos.z, 1.0);
Color = vec4( aColor.xyz * (1-gl_Position.z)/2,1.0);
};
片元着色器
#version 330 core
out vec4 FragColor;
uniform vec4 u_color;
in vec4 Color;
void main()
{
// FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
FragColor = Color;
}
总结
- std::vector 使用十分方便
- glViewport ((max-min)/2 ,0,min,min); 要在渲染过程调用才有用处【奇怪】
- Qt 的高分辨率设置,windows屏幕的缩放比率获取,见widget.cpp