OpenGL入门第六步:材质

news2025/2/24 20:14:54

目录

结果显示

材质介绍

函数解析

具体代码


结果显示

材质介绍

当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。现在,我们再添加一个反光度(Shininess)分量,结合上述的三个颜色,我们就有了全部所需的材质属性了。

为风氏光照模型的每个分量都定义一个颜色向量。ambient材质向量定义了在环境光照下这个表面反射的是什么颜色,通常与表面的颜色相同。diffuse材质向量定义了在漫反射光照下表面的颜色。漫反射颜色(和环境光照一样)也被设置为我们期望的物体颜色。specular材质向量设置的是表面上镜面高光的颜色(或者甚至可能反映一个特定表面的颜色)。最后,shininess影响镜面高光的散射/半径。有这4个元素定义一个物体的材质,我们能够模拟很多现实世界中的材质。devernay.free.fr中的一个表格展示了一系列材质属性,它们模拟了现实世界中的真实材质。下图展示了几组现实世界的材质参数值对我们的立方体的影响:

可以看到,通过正确地指定一个物体的材质属性,我们对这个物体的感知也就不同了。效果非常明显,但是要想获得更真实的效果,我们需要以更复杂的形状替换这个立方体。

函数解析

timerEvent(QTimerEvent *event)函数

initializeGL()函数绘制光源

顶点着色器

片段着色器 

环境光通常被认为是均匀地照亮场景的,所以直接用光源的环境光和材质的环境光相乘来简单表示。

漫反射光的强度取决于光线与表面法向量的夹角。通过归一化法向量和光线方向向量,然后计算它们的点积,就能得到光线与表面的夹角余弦值。夹角越小,漫反射光越强,所以用这个点积值来控制漫反射光的强度。

镜面反射光主要是模拟物体表面的高光效果。通过计算反射方向向量和观察方向向量的点积,并对结果进行幂运算,来模拟高光的集中和锐利程度。材质的 shininess 值越大,高光就越集中和锐利。

光源的绘制和前一次(基础光照)没有区别。 

paintGL()函数:传参进行绘制

具体代码

.h

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLTexture>
#include <QElapsedTimer>
#include "Camera.h"

QT_BEGIN_NAMESPACE
namespace Ui { class openGLWidget; }
QT_END_NAMESPACE

class openGLWidget : public QOpenGLWidget
{
    Q_OBJECT

public:
    openGLWidget(QWidget *parent = nullptr);
    ~openGLWidget();
protected:
    virtual void timerEvent(QTimerEvent *event) override;
    //鼠标事件
    virtual void enterEvent(QEnterEvent *event) override;
    virtual void leaveEvent(QEvent *event) override;
    virtual void mouseMoveEvent(QMouseEvent *event) override;
    virtual void wheelEvent(QWheelEvent *event) override;
    virtual void keyPressEvent(QKeyEvent *event) override;
    virtual void keyReleaseEvent(QKeyEvent *event) override;
    //初始化
    virtual void initializeGL() override;
    virtual void resizeGL(int w, int h) override;
    virtual void paintGL() override;

private:
    QOpenGLShaderProgram lightingShader;
    QOpenGLShaderProgram lightCubeShader;
    QOpenGLBuffer vbo;
    QOpenGLVertexArrayObject cubeVao;
    QOpenGLVertexArrayObject lightVao;
    QMatrix4x4 projection;
    QMatrix4x4 view;
    Camera camera {Camera(QVector3D(0.0f, 0.0f, 3.0f))};
    QVector3D lightPos {QVector3D(1.2f, 1.0f, 2.0f)};
    // 用 0 - 1 的值 直接表示颜色
    QVector3D diffuseColor;
    QVector3D ambientColor;
    QElapsedTimer time;
    float lastFrameTime {0.f};
    struct {
        bool W {false};
        bool S {false};
        bool A {false};
        bool D {false};
    } keys;

private:
    Ui::openGLWidget *ui;
};
#endif // OPENGLWIDGET_H

.cpp

#include "openGLWidget.h"
#include "./ui_openGLWidget.h"

#include <QOpenGLFunctions>
#include <QKeyEvent>
#include <QPainter>
#include <QtMath>

openGLWidget::openGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
    , ui(new Ui::openGLWidget)
{
    ui->setupUi(this);
    setMouseTracking(true);
}

openGLWidget::~openGLWidget()
{
    makeCurrent();
    lightVao.destroy();
    cubeVao.destroy();
    vbo.destroy();
    doneCurrent();
    delete ui;
}

void openGLWidget::timerEvent(QTimerEvent *event)
{
    float s = time.elapsed() / 1000.0;
    float delta = s - lastFrameTime;
    lastFrameTime = s;
    if (keys.W)
        camera.ProcessKeyboard(FORWARD, delta);
    if (keys.S)
        camera.ProcessKeyboard(BACKWARD, delta);
    if (keys.A)
        camera.ProcessKeyboard(LEFT, delta);
    if (keys.D)
        camera.ProcessKeyboard(RIGHT, delta);
    view = camera.GetViewMatrix();
    QVector3D lightColor(qSin(lastFrameTime * 2.0), qSin(lastFrameTime * 0.7), qSin(lastFrameTime * 1.3));
    diffuseColor = lightColor * 0.5;
    ambientColor = lightColor * 0.2;
    update();
}

void openGLWidget::enterEvent(QEnterEvent *event)
{
    // 隐藏鼠标指针,将指针置于窗口中心
    setCursor(Qt::BlankCursor);
    QCursor::setPos(mapToGlobal(rect().center()));
}

void openGLWidget::leaveEvent(QEvent *event)
{

}

void openGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    float xoffset = rect().center().x() - event->x();
    float yoffset = rect().center().y() - event->y();

    float sensitivity = 0.1f; // change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    camera.ProcessMouseMovement(xoffset, yoffset);

    // 将指针置于窗口中心
    QCursor::setPos(mapToGlobal(rect().center()));
}

void openGLWidget::wheelEvent(QWheelEvent *event)
{
    float f = event->angleDelta().y() > 0 ? 1.0f : -1.0f;
    camera.ProcessMouseScroll(f);
    projection.setToIdentity();
    projection.perspective(camera.Zoom, float(width()) / float(height()), 0.1f, 100.f);
}

void openGLWidget::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_W:
        keys.W = true;
        break;
    case Qt::Key_S:
        keys.S = true;
        break;
    case Qt::Key_A:
        keys.A = true;
        break;
    case Qt::Key_D:
        keys.D = true;
        break;
    default:
        return;
    }
}

void openGLWidget::keyReleaseEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_W:
        keys.W = false;
        break;
    case Qt::Key_S:
        keys.S = false;
        break;
    case Qt::Key_A:
        keys.A = false;
        break;
    case Qt::Key_D:
        keys.D = false;
        break;
    default:
        return;
    }
}

void openGLWidget::initializeGL()
{
    // 设置用来清空屏幕的颜色 这里设置为黑色
    QOpenGLFunctions *f = context()->functions();
    f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    lightingShader.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, R"(
        #version 330 core
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec3 aNormal;
        out vec3 vFragPos;
        out vec3 vNormal;
        uniform mat4 uProjection;
        uniform mat4 uView;
        uniform mat4 uModel;

        void main()
        {
            vFragPos = vec3(uModel * vec4(aPos, 1.0));
            vNormal = mat3(transpose(inverse(uModel))) * aNormal;

            gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
        }
    )");
    // 片段着色器
    lightingShader.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, R"(
        #version 330 core
        out vec4 FragColor;

        struct Material {
            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
            float shininess;
        };

        struct Light {
            vec3 position;
            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
        };

        in vec3 vNormal;
        in vec3 vFragPos;

        uniform vec3 uViewPos;
        uniform Material uMaterial;
        uniform Light uLight;

        void main()
        {
            // ambient
            vec3 ambient = uLight.ambient * uMaterial.ambient;

            // diffuse
            vec3 norm = normalize(vNormal);
            vec3 lightDir = normalize(uLight.position - vFragPos);
            float diff = max(dot(norm, lightDir), 0.0);
            vec3 diffuse = uLight.diffuse * (diff * uMaterial.diffuse);

            // specular
            vec3 viewDir = normalize(uViewPos - vFragPos);
            vec3 reflectDir = reflect(-lightDir, norm);
            float spec = pow(max(dot(viewDir, reflectDir), 0.0), uMaterial.shininess);
            vec3 specular = uLight.specular * (spec * uMaterial.specular);

            vec3 result = ambient + diffuse + specular;
            FragColor = vec4(result, 1.0);
        }
    )");
    // 编译链接
    if(!lightingShader.link()) {
        qDebug() << lightingShader.log();
    };

    //光源
    lightCubeShader.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, R"(
        #version 330 core
        layout (location = 0) in vec3 aPos;

        uniform mat4 uModel;
        uniform mat4 uView;
        uniform mat4 uProjection;

        void main()
        {
            gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
        }
    )");

    lightCubeShader.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, R"(
        #version 330 core
        out vec4 FragColor;

        void main()
        {
            FragColor = vec4(1.0); // set all 4 vector values to 1.0
        }
    )");
    // 编译链接
    if(!lightCubeShader.link()) {
        qDebug() << lightCubeShader.log();
    };

    // 顶点数据
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    // 创建VBO
    vbo.create();
    vbo.bind();
    vbo.allocate(vertices, sizeof(vertices));

    cubeVao.create();
    cubeVao.bind();
    lightingShader.enableAttributeArray(0);
    lightingShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
    lightingShader.enableAttributeArray(1);
    lightingShader.setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float));

    lightVao.create();
    lightVao.bind();
    lightCubeShader.enableAttributeArray(0);
    lightCubeShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
    startTimer(1);
    time.start();
}

void openGLWidget::resizeGL(int w, int h)
{
    QOpenGLFunctions *f = context()->functions();
    f->glViewport(0, 0, w, h);
    projection.setToIdentity();
    projection.perspective(camera.Zoom, float(w) / float(h), 0.1f, 100.f);
}

void openGLWidget::paintGL()
{
    QOpenGLFunctions* f = context()->functions();
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // 启用深度测试
    f->glEnable(GL_DEPTH_TEST);

    lightingShader.bind();
    lightingShader.setUniformValue("uViewPos", camera.Position);
    lightingShader.setUniformValue("uLight.position", lightPos);
    lightingShader.setUniformValue("uLight.ambient", ambientColor);
    lightingShader.setUniformValue("uLight.diffuse", diffuseColor);
    lightingShader.setUniformValue("uLight.specular", 1.0f, 1.0f, 1.0f);

    lightingShader.setUniformValue("uMaterial.ambient", 1.0f, 0.5f, 0.31f);
    lightingShader.setUniformValue("uMaterial.diffuse", 1.0f, 0.5f, 0.31f);
    lightingShader.setUniformValue("uMaterial.specular", 0.5f, 0.5f, 0.5f);
    lightingShader.setUniformValue("uMaterial.shininess", 32.0f);

    lightingShader.setUniformValue("uProjection", projection);
    lightingShader.setUniformValue("uView", view);
    lightingShader.setUniformValue("uModel", QMatrix4x4());
    cubeVao.bind();
    f->glDrawArrays(GL_TRIANGLES, 0, 36);

    lightCubeShader.bind();
    lightCubeShader.setUniformValue("uProjection", projection);
    lightCubeShader.setUniformValue("uView", view);
    QMatrix4x4 model;
    model.translate(lightPos);
    model.scale(0.2f);
    lightCubeShader.setUniformValue("uModel", model);
    lightVao.bind();
    f->glDrawArrays(GL_TRIANGLES, 0, 36);
}

Camera.h参考前一篇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1954206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot整合 knife4j 接口文档

第一步&#xff1a;引入依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.4.0</version></dependency> 第二步&#xff1a;写入配置 方…

国内NAT服务器docker方式搭建rustdesk服务

前言 如果遇到10054,就不要设置id服务器!!! 由于遇到大带宽,但是又贵,所以就NAT的啦,但是只有ipv4共享和一个ipv6,带宽50MB(活动免费会升130MB~) https://bigchick.xyz/aff.php?aff322 月付-5 循环 &#xff1a;CM-CQ-Monthly-5 年付-60循环&#xff1a;CM-CQ-Annually-60官方…

2024后端开发面试题总结

一、前言 上一篇离职贴发布之后仿佛登上了热门&#xff0c;就连曾经阿里的师兄都看到了我的分享&#xff0c;这波流量真是受宠若惊&#xff01; 回到正题&#xff0c;文章火之后&#xff0c;一些同学急切想要让我分享一下面试内容&#xff0c;回忆了几个晚上顺便总结一下&#…

SQL数据库:通过在视频监控平台服务器上直接使用SQL存储过程,在海量记录中查询特定时间段内-某个摄像头的所有视频片段

目录 一、背景 1、存储过程 2、视频监控系统 二、需求和数据表 1、具体要求 2、数据表 3、部分数据 三、实现 1、目标 2、创建存储过程 &#xff08;1&#xff09;存储过程代码 &#xff08;2&#xff09;创建成功 3、存储过程的解释 4、SQL命令调用方式 5、调用…

【FunClip】阿里开源AI视频剪辑神器:全面体验与教程

目录 引言1. FunClip概览1.1 什么是FunClip1.2 FunClip的市场定位1.3 FunClip的创新意义 2. FunClip的功能特性3. FunClip的实际应用案例4. FunClip的使用教程4.1 在线体验FunClip4.2 本地部署Gradio版本4.3 命令行运行 结语参考引用 引言 随着数字媒体的蓬勃发展&#xff0c;…

OpenGL笔记十五之GLM叠加实验

OpenGL笔记十五之GLM叠加实验 —— 2024-07-27 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十五之GLM叠加实验1.每一帧都旋转的三角形2.每一帧在旋转前&#xff0c;都重置为一次单位矩阵&#xff0c;这要只会旋转1度3.每一帧旋转前&#xff0c…

centos stream 9安装 Kubernetes v1.30 集群

1、版本说明&#xff1a; 系统版本&#xff1a;centos stream 9 Kubernetes版本&#xff1a;最新版(v1.30) docker版本&#xff1a;27.1.1 节点主机名ip主节点k8s-master172.31.0.10节点1k8s-node1172.31.0.11节点2k8s-node2172.31.0.12 2、首先&#xff0c;使用Vagrant和Virt…

XSS漏洞:xss.haozi.me靶场1-12 | A-F

目录 0x00&#xff08;无限制&#xff09; 0x01&#xff08;闭合标签绕过&#xff09; 0x02&#xff08;双引号闭合绕过&#xff09; 0x03&#xff08;过滤括号&#xff09; 0x04&#xff08;编码绕过&#xff09; 0x05&#xff08;注释闭合绕过&#xff09; 0x06&#…

【网络爬虫技术】(1·绪论)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;网络爬虫开发技术入门_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …

【C语言】两个数组比较详解

目录 C语言中两个数组比较详解1. 逐元素比较1.1 示例代码1.2 输出结果1.3 分析 2. 内置函数的使用2.1 示例代码2.2 输出结果2.3 分析 3. 在嵌入式系统中的应用3.1 示例代码3.2 输出结果3.3 分析 4. 拓展技巧4.1 使用指针优化比较4.2 输出结果4.3 分析 5. 表格总结6. 结论7. 结束…

yolov8 训练模型

一、准备数据 1.1 收集数据 以拳皇为例&#xff0c;将录制的游戏视频进行抽帧。 import cv2 import os# 视频文件路径 video_path 1.mp4# 输出帧的保存目录 output_dir ./output_frames os.makedirs(output_dir, exist_okTrue)# 读取视频 cap cv2.VideoCapture(video_pa…

7-25学习笔记

一、锁对象 Lock接口 1、创建锁对象 ReentrantLock类 Lock locknew ReentrantLock(true); 默认创建的是非公平锁 在创建锁对象时传入一个true参数 便会创建公平锁 先来后到 是重入锁 排他锁 加锁后不允许其它线程进入 2、加锁、解锁 &#xff08;1&#xff09;loc…

服务器搭建总结

服务器搭建好初期要记得开放端口&#xff0c;配置安全组 &#xff0c;主要的有22&#xff0c;80&#xff0c;3389&#xff0c;8888等&#xff0c;宝塔连接的端口在8888上&#xff0c;不开放无法连接 由于时使用的腾讯云服务器&#xff0c;所以在宝塔选择上使用了Windows的面板…

Autodesk Revit v2025 激解锁版下载及安装教程 (三维建模软件)

前言 Revit是欧特克公司知名的三维建模软件&#xff0c;是建筑业BIM体系中使用最广泛的软件之一&#xff0c;其核心功能是三维建筑模型参数化设计、渲染效果图、算量&#xff0c;土建建模、机电建模、用来帮助工程师在施工前精确模拟阶段。 一、下载地址 下载链接&#xff1…

消息摘要算法:MD5加密

&#x1f31f; 主题简介 今天&#xff0c;我们将深入探讨一种经典且广泛应用的加密算法——MD5。通过案例形式了解其原理、实现方法及注意细节。无论你是Python爱好者还是JavaScript高手&#xff0c;这篇内容都将为你揭开MD5的神秘面纱。 &#x1f4da; 内容介绍 MD5&#xf…

大话成像公众号文章阅读学习(一)

系列文章目录 文章目录 系列文章目录前言一、扫射拍摄二、索尼Alpha 9 III2.1. 视频果冻效应2.2 闪光灯同步速度2.3 其他功能 三 A9III 局限性总结 前言 大话成像是一个专注成像的公众号&#xff0c;文章都很好。 今天看的这篇是 特朗普遭枪击后“大片”出自它 文章地址 htt…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑电动汽车动态拥堵的配电网灵活性资源双层优化调度 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

揭秘企业为何钟情定制红酒:品牌形象与不同的礼品的双重魅力

在商务世界的广阔天地里&#xff0c;红酒不仅仅是一种饮品&#xff0c;更是一种传递情感、展示品味的不同媒介。近年来&#xff0c;越来越多的企业开始钟情于定制红酒&#xff0c;其中洒派红酒&#xff08;Bold & Generous&#xff09;通过其品质和个性化的定制服务&#x…

深入源码:解析SpotBugs (4)如何自定义一个 SpotBugs plugin

自定义一个 spotbugs 的插件&#xff0c;官方有比较详细的说明&#xff1a; https://spotbugs.readthedocs.io/en/stable/implement-plugin.html 本篇是跟随官网demo的足迹&#xff0c;略显无聊&#xff0c;可跳过。 创建工程 执行maven 命令 mvn archetype:generate -Darche…

关于Jenkins自动化部署Maven项目:

jenkins介绍: Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 jenkins作用:更好的利于测试工程师测试项目(自动部署) 安装je…