网站建设系统哪个好做五金有哪些网站推广
网站建设系统哪个好,做五金有哪些网站推广,怎样用代码做网站,网站后台网址在哪输入一#xff1a;环境搭建step1.前置检查先确保Qt安装了OpenGL模块#xff0c;打开Qt的安装路径#xff0c;替换为实际路径#xff1a;D:\Qt\Qt6.9\6.9.3\msvc2022_64\lib\cmake查看是否有以下文件夹#xff0c;如果有就代表你的Qt安装了OpenGL模块step2:项目配置创建一个Con…一环境搭建step1.前置检查先确保Qt安装了OpenGL模块打开Qt的安装路径替换为实际路径D:\Qt\Qt6.9\6.9.3\msvc2022_64\lib\cmake查看是否有以下文件夹如果有就代表你的Qt安装了OpenGL模块step2:项目配置创建一个Console项目即可,因为我们暂时不需要用到Widgets创建完成后打开工程文件CMakeList.txt添加以下行让项目可以找到OpenGL和OpenGLWidgets模块find_package(Qt6 REQUIRED COMPONENTS OpenGL OpenGLWidgets)然后再添加以下行替换为你的实际项目名目的是告诉编译器把我的程序 和 这些库文件 链接到一起让代码能调用库里的函数。target_link_libraries(你的项目名 PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets )step3:验证配置打开main.cpp,删除原全部的QCoreApplication代码替换为以下代码#include QApplication #include QOpenGLWidget int main(int argc, char *argv[]) { QApplication a(argc, argv); QOpenGLWidget glWidget; glWidget.show(); return a.exec(); }然后点击运行成功弹出一个OpenGLWidget窗口二创建一个 OpenGL 绘图窗口类前面只是创建了一个光秃秃的OpenGLWidget 窗口我们看不到任何内容想要在这个GL窗口绘制图像我们就必须创建一个子类继承QOpenGLWidget 然后在子类中重写绘制函数类似于我们普通QWidget项目中重写paintEvent(QPaintEvent *)只不过他们的区别如下对比项QOpenGLWidgetQWidget重写函数initializeGL(); // 初始化时调用一次resizeGL(int w, int h); // 窗口大小改变时调用paintGL(); // 画图时调用窗口第一次显示时窗口大小改变时手动调用update()时paintEvent(QPaintEvent *);//参考paintGL()的调用时机绘制引擎GPU渲染CPU计算适用场景3D、游戏、大量图形、高速渲染界面、文字、简单 2D 图形现在我们开始正式实现这个类step1创建派生类右键项目——添加新文件——选择C Class——填好Class name和Base class基类必须是QOpenGLWidget,参考下图step2实现函数重写GLWidget.h#ifndef GLWIDGET_H #define GLWIDGET_H #include QOpenGLWidget class GLWidget : public QOpenGLWidget { public: explicit GLWidget(QWidget *parent nullptr); //强烈推荐所有 Qt 控件的构造函数都写成这种方式 //好处1后续可正常嵌入到其他窗口里 //好处2Qt父子机制自动管理内存 //好处3explicit防止隐式转换 ~GLWidget(); protected: // 三个必须重写的函数 void initializeGL() override; // 初始化时调用 void resizeGL(int w, int h) override; // 窗口大小改变时调用 void paintGL() override; // 主要绘制调用 }; #endif // GLWIDGET_HGLWidget.cpp#include GLWidget.h #include QDebug GLWidget::GLWidget(QWidget *parent) :QOpenGLWidget(parent) {} GLWidget::~GLWidget() {} void GLWidget::initializeGL() { qDebug()窗口初始化; } void GLWidget::resizeGL(int w, int h) { qDebug()窗口大小改变,宽w 高h; } void GLWidget::paintGL() { qDebug()触发绘制; }main.cpp#include QApplication #include GLWidget.h int main(int argc, char *argv[]) { QApplication a(argc, argv); GLWidget glWidget; glWidget.show(); return a.exec(); }step3运行效果三基础概念引入1.坐标系统OpenGL坐标系统是什么默认坐标屏幕中心是 (0,0)x-1 左1 右y-1 下1 上2.顶点Vertex每个点包含x, y 坐标所有图形都是由点组成的例如矩形 4 个点3.VBO顶点缓冲对象顶点数据存在CPU 内存画图必须把数据送到显卡显存VBO 就是做这件事的一个VBO对象就对应GPU上的一块显存不同的VBO不同显存互不干扰4.Shader着色器Shader GPU 运行的画图程序它有两个顶点着色器告诉 GPU 点在哪里片段着色器告诉 GPU 画什么颜色5.渲染流程定义顶点用 VBO 发给 GPU用 Shader 告诉 GPU 怎么画调用绘制指令四正式开始绘制1.核心头文件/函数讲解#include QOpenGLShaderProgram//着色器头文件#include QOpenGLBuffer//VBO头文件#include QOpenGLFunctions_3_3_Core//OpenGL 3.3 核心版头文件它是调用 OpenGL 原生函数的 “工具箱”包含了很多原生 OpenGL 函数直接继承QOpenGLFunctions_3_3_Core即可使用这些原生函数initializeOpenGLFunctions() glBindBuffer() glBufferData() glEnableVertexAttribArray() glVertexAttribPointer() glDrawArrays() ...为什么不用QOpenGLFunctions它是 Qt 提供的通用版但缺点很大函数太老只包含 OpenGL 1.x ~ 2.0 函数包含废弃旧写法立即模式不支持 VBO / 着色器没有现代 OpenGL 函数没有 glBindBuffer、glVertexAttribPointer…容易写出不兼容现代显卡的代码推荐用QOpenGLFunctions_3_3_Core 干净、现代、安全、标准的工具箱initializeOpenGLFunctions();必须在initializeGL()最开头调用。它的作用只有一个让你能调用所有的 OpenGL 原生函数如 glVertexAttribPointer、glDrawArrays、glClear 等否则程序直接崩溃在Qt OpenGL里OpenGL 的函数地址不是默认加载的你直接写glDrawArrays编译器虽然不报错但运行时找不到函数地址 → 程序崩溃QOpenGLShaderProgram::addShaderFromSourceCodebool addShaderFromSourceCode( QOpenGLShader::ShaderType type, // 1. 着色器类型 const QString source // 2. 你的 GLSL 代码字符串 );把你写的 GLSL 字符串顶点 / 片段着色器代码编译成显卡能识别的着色器模块并附加到程序里。参数type你要加哪种着色器QOpenGLShader::Vertex→ 顶点着色器QOpenGLShader::Fragment→ 片段着色器source自己写的GLSL 代码字符串QOpenGLShaderProgram::link()把编译好的【顶点着色器 片段着色器】“组装 / 绑定 / 链接” 成一个完整、可在 GPU 上运行的着色器程序。QOpenGLBufferQOpenGLBuffer::create(); QOpenGLBuffer::bind(); QOpenGLBuffer::allocate(const void *data, int count); QOpenGLBuffer::release();create 创建显存 → bind 绑定激活 → allocate 上传数据 → release 解绑glVertexAttribPointervoid glVertexAttribPointer( GLuint index, // 1. 顶点属性位置对应着色器 layout(location?) GLint size, // 2. 每个顶点用几个数1/2/3/4 GLenum type, // 3. 数据类型几乎都是 GL_FLOAT GLboolean normalized,// 4. 是否归一化几乎都是 GL_FALSE GLsizei stride, // 5. 两个顶点之间间隔多少字节 const GLvoid *pointer// 6. 从第几个字节开始读偏移量 )告诉 GPU 硬件如何去解析、读取这个VBO顶点数据然后喂给着色器。glVertexAttribPointer做了一件事把 “顶点属性” 绑定到【当前正在绑定的 VBO】上所以我们如果要同时画多个图案就需要在PaintGL()中对每一个VBO都需要执行一次bind然后再对每一个VBO都进行一次顶点属性指针设置很不方便为了解决这个问题就是下一章VAO的内容了glEnableVertexAttribArrayvoid glEnableVertexAttribArray(GLuint index)开启对应位置的顶点属性读取开关GPU 默认关着所有数据通道你必须调用这个函数打开通道数据才能从 VBO 流进着色器。glClearColor和glClear//设置背景颜色 void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) //用刚才设置的颜色把屏幕擦干净 void glClear(GLbitfield mask);mask参数一般都写:GL_COLOR_BUFFER_BITglDrawArrays让GPU执行绘制void glDrawArrays( GLenum mode, // 1. 图元模式画点/线/三角形 GLint first, // 2. 从第几个顶点开始画起始索引 GLsizei count // 3. 一共画多少个顶点 );mode—— 图元模式画什么形状告诉 GPU 怎么把顶点连起来。常用值GL_POINTS画点GL_LINES画线段两两一组GL_LINE_STRIP—— 连续折线GL_LINE_LOOP—— 闭合折线环形GL_TRIANGLES画三角形最常用3 个一组GL_TRIANGLE_STRIP连续三角形连续向前延伸1,2,3,4,5就是1-2-3,2-3-4,3-4-5GL_TRIANGLE_FAN扇形三角形共用第 0 号顶点,1,2,3,4,5就是1-2-3,1-3,4,1-4-52.任务拆解step1:创建着色器程序然后要对这个着色器程序进行一个培训提供执行代码相当于编写图纸/规则告诉这个程序按照什么规范去跑告诉它顶点应该怎么处理片段像素应该怎么着色然后将编译好的 顶点着色器 片段着色器进行焊接 / 组装 / 绑定” 成一个完整可运行的显卡程序关键点片段着色器不能直接访问顶点数据[顶点数据] → [顶点着色器] → [光栅化] → [片段着色器] → [帧缓冲]↑ ↑ ↑ ↑位置 处理顶点 生成片段 处理像素颜色 传递数据 插值属性step2:创建顶点缓冲对象VBOallocate时把CPU 内存里的顶点坐标、颜色等数据一次性打包给GPU显存一个VBO对象对应GPU上的一块显存互不干扰不会相互覆盖。只有同一个VBO对象再次调用allocate时才会覆盖自己之前的那块显存。GPU显存再次调用allocate()会重新分配并覆盖原有数据┌─────────────────────────────────────┐│ VBO 1: 顶点数据 (位置颜色) │←─ allocated in init│ [ -0.5, -0.5, 1.0, 0.0, 0.0 ] │ 永久存储在这里│ [ 0.5, -0.5, 0.0, 1.0, 0.0 ] ││ [ 0.0, 0.5, 0.0, 0.0, 1.0 ] │├─────────────────────────────────────┤│ VBO 2: 索引数据 │←─ allocated in init│ [ 0, 1, 2, 2, 3, 0 ] │ 永久存储├─────────────────────────────────────┤│ 纹理数据 │←─ allocated in init│ [ RGB, RGB, RGB... ] │ 永久存储└─────────────────────────────────────┘step3:设置顶点属性指针告诉GPU如何解析显存里面的数据进行逻辑分通道但不是物理分区然后喂给顶点着色器。// 最常见的方式交错布局一个VBO包含所有属性 float vertices[] { // 位置 // 颜色 -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // 顶点0 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // 顶点1 0.0f, 0.5f, 0.0f, 0.0f, 1.0f // 顶点2 }; 分通道后内存布局不变 ┌─────────────────────────────────────────────────┐ │ 顶点0: [pos] [pos] [col] [col] [col] │ │ ↑通道0 ↑通道1 │ ├─────────────────────────────────────────────────┤ │ 顶点1: [pos] [pos] [col] [col] [col] │ │ ↑通道0 ↑通道1 │ ├─────────────────────────────────────────────────┤ │ 顶点2: [pos] [pos] [col] [col] [col] │ │ ↑通道0 ↑通道1 │ └─────────────────────────────────────────────────┘step4:开始画图paintGL设置清屏颜色然后清空屏幕防止画面重叠使用着色器激活状态机开始绘制OpenGL状态机就像一个只有当前选中的单选按钮【着色器状态】○ Shader1 ● Shader2 ○ Shader3 ← 同时只能激活一个【VBO状态】● VBO1 ○ VBO2 ○ VBO3 ← 同时只能激活一个【纹理状态】○ Texture1 ● Texture2 ○ Texture3 ← 同时只能激活一个3.绘制一个点GLWidget.h#ifndef GLWIDGET_H #define GLWIDGET_H #include QOpenGLWidget #include QOpenGLShaderProgram #include QOpenGLBuffer #include QOpenGLFunctions_3_3_Core class GLWidget : public QOpenGLWidget,private QOpenGLFunctions_3_3_Core //继承QOpenGLFunctions_3_3_Core //便于直接调用OpenGL库函数 { public: explicit GLWidget(QWidget *parent nullptr); ~GLWidget(); protected: // 三个必须重写的函数 void initializeGL() override; // 初始化 void resizeGL(int w, int h) override; // 窗口大小改变 void paintGL() override; // 主要绘制 private: QOpenGLShaderProgram *m_shaderProgram;//创建着色器程序 QOpenGLBuffer m_vbo;//创建vbo对象 }; #endif // GLWIDGET_HGLWidget.cpp#include GLWidget.h #include QDebug GLWidget::GLWidget(QWidget *parent) :QOpenGLWidget(parent) {} GLWidget::~GLWidget() {} void GLWidget::initializeGL() { initializeOpenGLFunctions();//初始化内部函数地址 //编写顶点着色器的代码 //version 字段必须包含 //layout 内表示从某个通道接收 什么类型的数据 存入变量 //gl_Position是固定字段 表示这个点的绘制坐标 //vec4(x,y,z,w)对应x轴y轴z轴2D平面中z0.0w为齐次坐标分量(几乎任何时候都写1.0) const char *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; void main() { gl_Positionvec4(aPos,0.0,1.0); } ); //编写片段着色器的代码 //输出参数就是最终要显示的颜色变量名可以随便取 const char *fragmentShaderR( #version 330 core out vec4 FragColor; void main() { FragColorvec4(1.0,1.0,1.0,1.0);//白色点不透明 } ); //创建shader对象添加顶点着色器和片段着色器并进行组装 m_shaderProgramnew QOpenGLShaderProgram(this); m_shaderProgram-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shaderProgram-addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader); m_shaderProgram-link(); //顶点数据 float vertices[]{ 0.0f,0.0f, }; //create 创建显存 → bind 绑定激活 → allocate 上传数据 m_vbo.create(); m_vbo.bind(); m_vbo.allocate(vertices,sizeof(vertices)); //设置顶点属性指针告诉GPU如何解析数据 glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,2*sizeof(float),(void*)0); glEnableVertexAttribArray(0); //m_vbo的顶点数据已经在GPU显存中了可以直接release m_vbo.release(); } void GLWidget::resizeGL(int w, int h) { } void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); glPointSize(10); // 全局设置点大小(可选) m_shaderProgram-bind(); glDrawArrays(GL_POINTS,0,1); }效果图4.绘制一条线段1.这里我们还是默认白色所以片段着色器fragmentShader的代码无需修改。2.还是两个float表示一个点所以顶点着色器vertexShader的代码也无需修改。3.通常glVertexAttribPointer的修改需要同步修改着色器代码因为两边规则必须100%对应绘制才会成功一个负责告诉GPU解析规则另一个负责按指定规则去执行。只需要修改VBO派送的顶点数据即可一条线段需要两个顶点然后修改GPU绘制方式GL_LINES,0,2float vertices[]{ 0.0f,0.0f, 0.0f,0.5f, }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_LINES,0,2); }效果图5.绘制一个三角形float vertices[]{ 0.0f,0.0f, -0.5f,-0.5f, 0.5f,-0.5f, }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLES,0,3); }6.绘制一个矩形OpenGL没有直接绘制矩形的方式只能用两个三角形拼接起来还可以用4条线段8个点甚至可以用无数个密集的点进行渲染....但是使用三角形拼接是最简单的方式方式1GL_TRIANGLE_FAN绘制只需要4个点 顺序1-2-3 1-3-4// 顶点0 顶点1 // ↓ ↓ // ---------- // | \ | // | \ 1 | // | \ | // | \ | // | 2 \ | // | \ | // ---------- // ↑ ↑ // 顶点3 顶点2 float vertices[]{ -0.5f,0.5f, 0.5f,0.5f, 0.5f,-0.5f, -0.5f,-0.5f }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLE_FAN,0,4); }方式2GL_TRIANGLE_STRIP绘制也只需要4个点 顺序1-2-3 2-3-4float vertices[]{ -0.5f,0.5f, 0.5f,0.5f, -0.5f,-0.5f, 0.5f,-0.5f, }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLE_STRIP,0,4); }方式3GL_TRIANGLES绘制但是需要六个点顺序1-2-3 4-5-6float vertices[]{ -0.5f,0.5f, 0.5f,0.5f, -0.5f,-0.5f, 0.5f,0.5f, -0.5f,-0.5f, 0.5f,-0.5f, }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLES,0,6); }运行效果都如下7.绘制一个彩色三角形这里我们引入了颜色所以需要添加一个通道并且修改原来的步长为5个float大小新通道的偏移量为两个float大小因为前面两个float是位置坐标glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)(2*sizeof(float))); glEnableVertexAttribArray(1);然后修改顶点着色器和片段着色器的代码顶点着色器需要从通道1接收颜色数据然后通过一个新变量进行输出。片段着色器接收顶点着色器输出的颜色数据然后通过一个新变量(vec4)进行输出。const char *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; layout(location1) in vec3 aColor; out vec3 outColor; //也可以out vec4只不过main中需要进行调整 void main() { gl_Positionvec4(aPos,0.0,1.0);//最终位置 gl_Position outColoraColor; } ); const char *fragmentShaderR( #version 330 core in vec3 outColor; out vec4 finalColor;//最终输出颜色 类型必须是vec4 变量名可以随便取 void main() { finalColorvec4(outColor,1.0); } );然后是修改VBO数据在每个位置坐标后面添加对应的颜色数据float vertices[]{ 0.0f,0.5f, 1.0f,0.0f,0.0f, -0.5f,-0.5f, 0.0f,1.0f,0.0f, 0.5f,-0.5f, 0.0f,0.0f,1.0f };绘制逻辑不需要修改了void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLES,0,3); }8.绘制一个彩色矩形只需要在绘制彩色三角形的基础上修改以下部分即可float vertices[]{ -0.5f,0.5f, 1.0f,0.0f,0.0f, 0.5f,0.5f, 0.0f,1.0f,0.0f, 0.5f,-0.5f, 0.0f,0.0f,1.0f, -0.5f,-0.5f, 1.0f,0.0f,1.0f, }; void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_shaderProgram-bind(); glDrawArrays(GL_TRIANGLE_FAN,0,4); }效果图五.补充一个VBO对象对应GPU上的一块显存互不干扰不会相互覆盖只有同一个VBO对象调用allocate时才会覆盖自己的那块显存如果要删除之前分配的那块显存就allocate(0)glVertexAttribPointer和glEnableVertexAttribArray就是让GPU打开通道进行数据传输也是状态机告诉GPU去如何解析当前bind的vbo对应的这块显存然后交给着色器。如果当前没有绑定VBO那么这两个函数就会执行失败画不出东西。这就是为什么在glVertexAttribPointer和glEnableVertexAttribArray之前必须vbo.bind()。一般都是用几个属性就开几个通道比如 只用位置熟悉那我只需要打开0通道即可如果要用 位置熟悉颜色属性那我就再开一个1通道如果要添加纹理贴图那我再开一个2通道以此类推......虽然通道需要根据情况添加但是着色器相关的这两行代码永远只需要写 2 次也必须都写因为着色器程序永远只有两个。那就是顶点着色器和片段着色器两者就决定了顶点在哪里和像素是什么颜色。这两个任何时候都缺一不可。m_shaderProgram-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shaderProgram-addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader);最常用的 6 个通道:1. 位置通道必用作用顶点坐标决定点画在哪里。layout(location0) in vec3 aPos;2.颜色通道常用作用顶点颜色让每个顶点有自己的颜色。layout(location1) in vec3 aColor;3. 纹理坐标通道最常用作用贴图片、贴纹理没有它就不能显示图片、贴图、皮肤、背景图。layout(location2) in vec2 aTexCoord;4. 法线通道3D 必用作用光照计算决定物体哪里亮、哪里暗实现3D 光影效果。layout(location3) in vec3 aNormal;5. 切线通道高级光照作用凹凸贴图、法线贴图让平面看起来有凹凸质感。layout(location4) in vec3 aTangent;6. 顶点索引 / 权重通道骨骼动画作用人物动画、骨骼变形layout(location5) in vec4 aBoneWeights; layout(location6) in vec4 aBoneIndices;最常用的顶点结构长这样行业标准float vertices[] { // 位置 // 颜色 // 纹理坐标 -0.5, 0.5, 0.0, 1.0,0.0,0.0, 0.0,1.0, 0.5, 0.5, 0.0, 0.0,1.0,0.0, 1.0,1.0, ... };为什么连接部分是渐变色呢因为 GPU 会自动在顶点之间做颜色插值渐变比如我们只给了 4 个顶点的颜色GPU 会自动把中间所有像素的颜色 “平滑过渡” 算出来 → 就是渐变。如果不想要渐变想要纯色怎么办只有两个办法每个顶点颜色完全一样使用flat 关键字关闭插值不推荐新手用