计算机图形学 实验二 三维模型读取与控制

目录

一、实验内容

二、具体内容

(在实验2.3的基础上进行修改)

1、OFF格式三维模型文件的读取

2、三维模型的旋转动画

3、键盘鼠标的交互

4、模型的修改

三、代码


一、实验内容

  1. 读取实验提供的off格式三维模型,并对其赋色。利用鼠标和键盘的交互,控制动画效果,模型的颜色自己可以自行设置,好看就行。

                                                 

二、具体内容

(在实验2.3的基础上进行修改)

1、OFF格式三维模型文件的读取

参考上机实验2.2的内容,完成对OFF格式三维模型文件的读取与显示,可改变物体的显示颜色,尽量特别,但不要太难看。

        1)修改init()方法,读取OFF格式三维模型文件cow.off。

                                        

        2)修改颜色:

        在readoff()方法中:将坐标值([-1,1])映射到颜色值([0,1])

        方法1:坐标值加1,除2(结果和实验给的类似)

                       

                                   

    方法2:坐标值取绝对值

                                  

                                          

(应该还不算难看)

2、三维模型的旋转动画

参考实验2.1中动画的生成方式,并结合实验2.3中对模型进行旋转变换的过程,生成旋转动画。

默认绕X轴旋转,每1000毫秒转一下rotateDelta角度。

定义相关变量:

                     

在mian函数中定义相关操作:

                      

3、键盘鼠标的交互

参考实验2.1中鼠标与键盘的交互,通过键盘设定选择绕x、y、z轴进行旋转,通过鼠标左右键控制动画的开始与暂停。

        1)通过键盘设定选择绕x、y、z轴进行旋转:

        在key_callback函数内增加增加如下代码,通过按“X”、“Y”、“Z”控制。

                                

        2)通过鼠标左右键控制动画的开始与暂停:

        增加mouse_button_callback函数:

                

        然后在main函数中绑定。

        3)修改提示语:

                     

4、模型的修改

参考以下代码,通过键盘设定可以在cow.off和cube.off之间切换。

清除顶点数组缓存:

        glBindVertexArray(0);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

二维向量:

std::vector<std::vector<?>>

其他STL相关代码可以查阅C++ STL 教程 | 菜鸟教程 (runoob.com)

1)添加变量表示绘制的是牛还是方块:

                                        

2)修改init()函数:通过判断currentModel选择读取的文件:

                        

3)在key_callback函数中添加如下代码:

        通过按下“N”绘制牛,按下“M”绘制方块。

                                

4)并修改相关提示语。

           效果如下:

三、代码

1、main.cpp

#include "Angel.h"
#include "TriMesh.h"
#include <vector>
#include <string>
//#include "main.h"
using namespace std;const int X_AXIS = 0;
const int Y_AXIS = 1;
const int Z_AXIS = 2;const int TRANSFORM_SCALE = 0;
const int TRANSFORM_ROTATE = 1;
const int TRANSFORM_TRANSLATE = 2;const double DELTA_DELTA = 0.3;		// Delta的变化率
const double DEFAULT_DELTA = 0.5;	// 默认的Delta值double scaleDelta = DEFAULT_DELTA;
double rotateDelta = DEFAULT_DELTA;
double translateDelta = DEFAULT_DELTA;glm::vec3 scaleTheta(1.0, 1.0, 1.0);		// 缩放控制变量
glm::vec3 rotateTheta(0.0, 0.0, 0.0);    // 旋转控制变量
glm::vec3 translateTheta(0.0, 0.0, 0.0);	// 平移控制变量int currentTransform = TRANSFORM_ROTATE;	// 设置当前变换
int mainWindow;//------------------------------------------------------------------------
bool isplaying = true;	// 动画状态
int rotationAxis = X_AXIS;	// 当前旋转轴,默认为 X 轴
int rotationTime = 1000;		// 每1000帧旋转一次string currentModel ="cow";struct openGLObject
{// 顶点数组对象GLuint vao;// 顶点缓存对象GLuint vbo;// 着色器程序GLuint program;// 着色器文件std::string vshader;std::string fshader;// 着色器变量GLuint pLocation;GLuint cLocation;GLuint matrixLocation;GLuint darkLocation;
};openGLObject cube_object;TriMesh* cube = new TriMesh();void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}void bindObjectAndData(TriMesh* mesh, openGLObject& object, const std::string& vshader, const std::string& fshader) {// 创建顶点数组对象glGenVertexArrays(1, &object.vao);  	// 分配1个顶点数组对象glBindVertexArray(object.vao);  	// 绑定顶点数组对象// 创建并初始化顶点缓存对象glGenBuffers(1, &object.vbo);glBindBuffer(GL_ARRAY_BUFFER, object.vbo);glBufferData(GL_ARRAY_BUFFER,mesh->getPoints().size() * sizeof(glm::vec3) + mesh->getColors().size() * sizeof(glm::vec3),NULL,GL_STATIC_DRAW);// @TODO: Task3-修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错glBufferSubData(GL_ARRAY_BUFFER, 0, mesh->getPoints().size() * sizeof(glm::vec3), &mesh->getPoints()[0]);glBufferSubData(GL_ARRAY_BUFFER, mesh->getPoints().size() * sizeof(glm::vec3), mesh->getColors().size() * sizeof(glm::vec3), &mesh->getColors()[0]);object.vshader = vshader;object.fshader = fshader;object.program = InitShader(object.vshader.c_str(), object.fshader.c_str());// 从顶点着色器中初始化顶点的位置object.pLocation = glGetAttribLocation(object.program, "vPosition");glEnableVertexAttribArray(object.pLocation);glVertexAttribPointer(object.pLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));// 从顶点着色器中初始化顶点的颜色object.cLocation = glGetAttribLocation(object.program, "vColor");glEnableVertexAttribArray(object.cLocation);glVertexAttribPointer(object.cLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(mesh->getPoints().size() * sizeof(glm::vec3)));// 获得矩阵存储位置object.matrixLocation = glGetUniformLocation(object.program, "matrix");}void init()
{std::string vshader, fshader;// 读取着色器文件路径vshader = "shaders/vshader.glsl";fshader = "shaders/fshader.glsl";//cube->generateCube();//cube->readOff("./Models/cow.off");if (currentModel == "cow") {cube->readOff("./Models/cow.off");}else {cube->generateCube();}bindObjectAndData(cube, cube_object, vshader, fshader);// 设置背景色为黑色glClearColor(0.0, 0.0, 0.0, 1.0);
}// 渲染函数
void display()
{// 清空颜色缓冲和深度缓冲glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glUseProgram(cube_object.program);glBindVertexArray(cube_object.vao);// 初始化变换矩阵  glm::mat4表示 4x4 矩阵glm::mat4 m(1.0, 0.0, 0.0, 0.0,0.0, 1.0, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0);// @TODO: Task4-在此处修改函数,计算最终的变换矩阵// 调用函数传入三种变化的变化量,累加得到变化矩阵// 注意三种变化累加的顺序// 构建旋转矩阵glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0), rotateTheta.x, glm::vec3(1.0, 0.0, 0.0))* glm::rotate(glm::mat4(1.0), rotateTheta.y, glm::vec3(0.0, 1.0, 0.0))* glm::rotate(glm::mat4(1.0), rotateTheta.z, glm::vec3(0.0, 0.0, 1.0));// 构建缩放矩阵glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0), glm::vec3(scaleTheta.x, scaleTheta.y, scaleTheta.z));// 构建平移矩阵glm::mat4 translateMatrix = glm::translate(glm::mat4(1.0), glm::vec3(translateTheta.x, translateTheta.y, translateTheta.z));// 按照平移、旋转、缩放的顺序相乘得到最终的变换矩阵m = translateMatrix * rotationMatrix * scaleMatrix;// 传递变换矩阵到着色器glUniformMatrix4fv(cube_object.matrixLocation, 1, GL_FALSE, glm::value_ptr(m));// 绘制立方体中的各个三角形glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}// 通过Delta值更新Theta
// axis 表示坐标轴,sign 表示增加或减少
// currentTransform 表示当前变换类型
void updateTheta(int axis, int sign) {switch (currentTransform) {// 根据变换类型,增加或减少某种变换的变化量case TRANSFORM_SCALE://增加或减少缩放的 Theta 值scaleTheta[axis] += sign * scaleDelta;break;case TRANSFORM_ROTATE://增加或减少旋转的 Theta 值rotateTheta[axis] += sign * rotateDelta;break;case TRANSFORM_TRANSLATE://增加或减少平移的 Theta 值translateTheta[axis] += sign * translateDelta;break;}
}// 复原Theta和Delta
void resetTheta()
{scaleTheta = glm::vec3(1.0, 1.0, 1.0);	//scaleTheta 表示缩放变换的角度rotateTheta = glm::vec3(0.0, 0.0, 0.0);translateTheta = glm::vec3(0.0, 0.0, 0.0);scaleDelta = DEFAULT_DELTA;				//缩放变换的单位变化量rotateDelta = DEFAULT_DELTA;translateDelta = DEFAULT_DELTA;
}// 更新变化Delta值
void updateDelta(int sign)
{switch (currentTransform) {// 根据变化类型增加或减少每一次变化的单位变化量case TRANSFORM_SCALE:scaleDelta += sign * DELTA_DELTA;break;case TRANSFORM_ROTATE:rotateDelta += sign * DELTA_DELTA;break;case TRANSFORM_TRANSLATE:translateDelta += sign * DELTA_DELTA;break;}
}void cleanData();// 处理键盘输入的回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{switch (key){// 退出。case GLFW_KEY_ESCAPE:if (action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE);break;// 1:缩放模式case GLFW_KEY_1:if (action == GLFW_PRESS) currentTransform = TRANSFORM_SCALE;break;// 2: 旋转模式case GLFW_KEY_2:if (action == GLFW_PRESS) currentTransform = TRANSFORM_ROTATE;break;// 3: 移动模式case GLFW_KEY_3:if (action == GLFW_PRESS) currentTransform = TRANSFORM_TRANSLATE;break;// 4: 绘制线。case GLFW_KEY_4:if (action == GLFW_PRESS) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);break;// 5: 绘制面。case GLFW_KEY_5:if (action == GLFW_PRESS) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);break;// Q: 增加 x。case GLFW_KEY_Q:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(X_AXIS, 1);break;// A: 减少 x。case GLFW_KEY_A:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(X_AXIS, -1);break;// W: 增加 y。case GLFW_KEY_W:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Y_AXIS, 1);break;// S: 减少 y。case GLFW_KEY_S:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Y_AXIS, -1);break;// E: 增加 z。case GLFW_KEY_E:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Z_AXIS, 1);break;// D: 减少 z。case GLFW_KEY_D:if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Z_AXIS, -1);break;// R: 增加变化量。case GLFW_KEY_R:if (action == GLFW_PRESS) updateDelta(1);break;// F: 减少变化量。case GLFW_KEY_F:if (action == GLFW_PRESS) updateDelta(-1);break;// T: 所有值重置。case GLFW_KEY_T:if (action == GLFW_PRESS) resetTheta();break;//-------------------------------------------------------------------// 选择绕 X 轴旋转case GLFW_KEY_X:if (action == GLFW_PRESS) rotationAxis = X_AXIS;break;// 选择绕 Y 轴旋转case GLFW_KEY_Y:if (action == GLFW_PRESS) rotationAxis = Y_AXIS;break;// 选择绕 Z 轴旋转case GLFW_KEY_Z:if (action == GLFW_PRESS) rotationAxis = Z_AXIS;break;// N: 加载cow.off模型case GLFW_KEY_N:if (action == GLFW_PRESS) {glBindVertexArray(0);//清除顶点数组缓存glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);currentModel = "cow";init();}break;// M: 加载cube模型case GLFW_KEY_M:if (action == GLFW_PRESS) {glBindVertexArray(0);//清除顶点数组缓存glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);currentModel = "cube";init();}break;}}//-------------------------------------------------------------------------------
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {isplaying = true; // 左键按下,开始动画}if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {isplaying = false; // 右键按下,暂停动画}
}// 输出帮助信息
void printHelp() {printf("%s\n\n", "3D Transfomations");printf("Keyboard options:\n");printf("n: Draw cow\n");printf("m: Draw block\n");printf("left mouse button: start playing\n");printf("right mouse button: Pause playback\n");printf("x: Rotate around the X-axis\n");printf("y: Rotate around the Y-axis\n");printf("z: Rotate around the Z-axis\n");printf("The following are the operations previously used:\n");printf("1: Transform Scale\n");printf("2: Transform Rotate\n");printf("3: Transform Translate\n");printf("q: Increase x\n");printf("a: Decrease x\n");printf("w: Increase y\n");printf("s: Decrease y\n");printf("e: Increase z\n");printf("d: Decrease z\n");printf("r: Increase delta of currently selected transform\n");printf("f: Decrease delta of currently selected transform\n");printf("t: Reset all transformations and deltas\n");
}// 清理数据
void cleanData() {cube->cleanData();// 释放内存delete cube;cube = NULL;// 删除绑定的对象glDeleteVertexArrays(1, &cube_object.vao);glDeleteBuffers(1, &cube_object.vbo);glDeleteProgram(cube_object.program);
}int main(int argc, char** argv)
{// 初始化GLFW库,必须是应用程序调用的第一个GLFW函数glfwInit();// 配置GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif//设置字符格式#pragma execution_character_set("GBK");GLFWwindow* window = glfwCreateWindow(600, 600, "homework", NULL, NULL);if (window == NULL) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetKeyCallback(window, key_callback);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 调用任何OpenGL的函数之前初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}init();// 输出帮助信息printHelp();// 启用深度测试glEnable(GL_DEPTH_TEST);//------------------------------------------------------------------------glfwSetMouseButtonCallback(window, mouse_button_callback); // 设置鼠标回调int flag = clock();while (!glfwWindowShouldClose(window)){display();// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)glfwSwapBuffers(window);glfwPollEvents();//--------------------------------------------------------------------------------//处理动画int now = clock();if (isplaying) {// 每1000帧旋转一定的角度if ((now - flag) >= rotationTime) {if (rotationAxis == X_AXIS) {rotateTheta.x += rotateDelta; // 沿X轴旋转}else if (rotationAxis == Y_AXIS) {rotateTheta.y += rotateDelta; // 沿Y轴旋转}else if (rotationAxis == Z_AXIS) {rotateTheta.z += rotateDelta; // 沿Z轴旋转}flag = now;}}}cleanData();return 0;
}

2、TriMesh.cpp

#include "TriMesh.h"// 一些基础颜色
const glm::vec3 basic_colors[8] = {glm::vec3(1.0, 1.0, 1.0),	// Whiteglm::vec3(1.0, 1.0, 0.0),	// Yellowglm::vec3(0.0, 1.0, 0.0),	// Greenglm::vec3(0.0, 1.0, 1.0),	// Cyanglm::vec3(1.0, 0.0, 1.0),	// Magentaglm::vec3(1.0, 0.0, 0.0),	// Redglm::vec3(0.0, 0.0, 0.0),	// Blackglm::vec3(0.0, 0.0, 1.0)		// Blue
};// 立方体的各个点
const glm::vec3 cube_vertices[8] = {glm::vec3(-0.5, -0.5, -0.5),glm::vec3(0.5, -0.5, -0.5),glm::vec3(-0.5,  0.5, -0.5),glm::vec3(0.5,  0.5, -0.5),glm::vec3(-0.5, -0.5,  0.5),glm::vec3(0.5, -0.5,  0.5),glm::vec3(-0.5,  0.5,  0.5),glm::vec3(0.5,  0.5,  0.5)
};TriMesh::TriMesh()
{
}TriMesh::~TriMesh()
{
}std::vector<glm::vec3> TriMesh::getVertexPositions()
{return vertex_positions;
}std::vector<glm::vec3> TriMesh::getVertexColors()
{return vertex_colors;
}std::vector<vec3i> TriMesh::getFaces()
{return faces;
}std::vector<glm::vec3> TriMesh::getPoints()
{return points;
}std::vector<glm::vec3> TriMesh::getColors()
{return colors;
}void TriMesh::cleanData() {vertex_positions.clear();vertex_colors.clear();faces.clear();points.clear();colors.clear();glBindVertexArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}void TriMesh::storeFacesPoints() {// @TODO: Task-2修改此函数在points和colors容器中存储每个三角面片的各个点和颜色信息// 根据每个三角面片的顶点下标存储要传入GPU的数据// 清空points和colors容器  points.clear();colors.clear();// 遍历每个面  for (const auto& face : faces) {// 根据索引获取顶点的位置和颜色  unsigned int x = face.x;unsigned int y = face.y;unsigned int z = face.z;glm::vec3 pos1 = vertex_positions[x];glm::vec3 col1 = vertex_colors[x];glm::vec3 pos2 = vertex_positions[y];glm::vec3 col2 = vertex_colors[y];glm::vec3 pos3 = vertex_positions[z];glm::vec3 col3 = vertex_colors[z];// 将顶点位置和颜色添加到points和colors容器中  points.push_back(pos1);colors.push_back(col1);points.push_back(pos2);colors.push_back(col2);points.push_back(pos3);colors.push_back(col3);}}// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube() {// 创建顶点前要先把那些vector清空cleanData();// @TODO: Task1-修改此函数,存储立方体的各个面信息// vertex_positions和vertex_colors先保存每个顶点的数据for (int i = 0; i < 8; ++i) {vertex_positions.push_back(cube_vertices[i]);// 这里简单使用基本颜色数组中的颜色,每个顶点按顺序分配颜色  vertex_colors.push_back(basic_colors[i % 8]);}// faces再记录每个面片上顶点的下标// 立方体12个面的顶点索引  // 每个面由两个三角形组成  faces.push_back(vec3i(1, 3, 7)); // 前面  faces.push_back(vec3i(1, 7, 5));faces.push_back(vec3i(0, 2, 6)); // 后面  faces.push_back(vec3i(0, 6, 4));faces.push_back(vec3i(2, 6, 7)); // 右面  faces.push_back(vec3i(2, 7, 3));faces.push_back(vec3i(0, 4, 5)); // 左面  faces.push_back(vec3i(0, 5, 1));faces.push_back(vec3i(4, 5, 7)); // 顶面  faces.push_back(vec3i(4, 7, 6));faces.push_back(vec3i(0, 1, 3)); // 底面  faces.push_back(vec3i(0, 3, 2));storeFacesPoints();
}void TriMesh::readOff(const std::string& filename)
{// fin打开文件读取文件信息if (filename.empty()){return;}std::ifstream fin;fin.open(filename);if (!fin){printf("File on error\n");return;}else{printf("File open success\n");cleanData();int nVertices, nFaces, nEdges;// 读取OFF字符串std::string str;fin >> str;// 读取文件中顶点数、面片数、边数fin >> nVertices >> nFaces >> nEdges;// 根据顶点数,循环读取每个顶点坐标for (int i = 0; i < nVertices; i++){glm::vec3 tmp_node;fin >> tmp_node.x >> tmp_node.y >> tmp_node.z;vertex_positions.push_back(tmp_node);//vertex_colors.push_back(tmp_node);// 将坐标值([-1,1])映射到颜色值([0,1])/*//方法1:加1,除2(结果和实验给的一样)glm::vec3 color = (tmp_node + glm::vec3(1.0f)) * 0.5f;*///方法2:取坐标绝对值float a = tmp_node.x>0? tmp_node.x:-tmp_node.x;float b = tmp_node.y>0? tmp_node.y:-tmp_node.y;float g = tmp_node.z>0? tmp_node.z:-tmp_node.z;glm::vec3 color(a,b,g);vertex_colors.push_back(color);}// 根据面片数,循环读取每个面片信息,并用构建的vec3i结构体保存for (int i = 0; i < nFaces; i++){int num, a, b, c;// num记录此面片由几个顶点构成,a、b、c为构成该面片顶点序号fin >> num >> a >> b >> c;faces.push_back(vec3i(a, b, c));}}fin.close();storeFacesPoints();
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 中 Bitmap 原理和应用

Bitmap Redis中的Bitmap&#xff08;位图&#xff09;是一种较为特殊数据类型&#xff0c;它以最小单位bit来存储数据&#xff0c;我们知道一个字节由 8个 bit 组成&#xff0c;和传统数据结构用字节存储相比&#xff0c;这使得它在处理大量二值状态&#xff08;true、false 或…

Springboot3.3.5 启动流程(源码分析)

一图搞懂 SpringBoot 启动流程&#xff08;清晰明了&#xff09;&#xff1a; createWebServer &#xff08;ServletWebApplicationContext&#xff09;流程 finishBeanFactoryInitialization&#xff08;ServletWebApplicationContext&#xff09;Bean装配流程 真正干活的&am…

CSS实现图片3D立体效果

概述 本文主要讲述如何通过 CSS 简单的设置就可以实现图片的 3D 立体效果。 3D 立体效果 当鼠标移入某一个图片上时,其余图片会像该图片倾斜。 具体实现 静图如下: 倒影效果图片会有一个倒影效果,其代码如下: <style>img {-webkit-box-reflect: below 1px linea…

java: 无法访问org.springframework.web.bind.annotation.RequestMapping

一、报错问题 java: 无法访问org.springframework.web.bind.annotation.RequestMapping 二、原因分析 SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17。所以仅需要将SpringBoot版本降低为3.0以下即可&#xff08;或…

Node.js:Express 服务 路由

Node.js&#xff1a;Express 服务 & 路由 创建服务处理请求req对象 静态资源托管托管多个资源挂载路径前缀 路由模块化 Express是Node.js上的一个第三方框架&#xff0c;可以快速开发一个web框架。本质是一个包&#xff0c;可以通过npm直接下载。 创建服务 Express创建一…

知识中台赋能法律咨询服务:八大核心优势

法律咨询服务领域&#xff0c;知识中台以其独特的功能和优势&#xff0c;为行业发展注入了新的活力。以下是知识中台在法律咨询服务中展现的八大核心优势&#xff1a; 一、法律知识资源的全面整合 知识中台致力于收集、整理和整合各类法律知识资源&#xff0c;包括法律法规、…

【青牛科技】GC5931:工业风扇驱动芯片的卓越替代者

在工业领域&#xff0c;工业风扇的稳定高效运行对于维持良好的生产环境至关重要。而驱动芯片作为工业风扇控制系统的核心元件&#xff0c;其性能直接影响风扇的工作状态。芯麦 GC5931 作为一款新型驱动芯片&#xff0c;在替代 A5931/Allegro 应用于工业风扇中展现出了非凡的优势…

使用Netty实现一个简单的聊天服务器

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;Netty &#x1f96d;本文内容&a…

【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (二)

【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 &#xff08;二&#xff09; 一、前言 目前鸿蒙应用的实现逻辑&#xff0c;基本都是参考和移植Android端来实现。针对BLE低功耗蓝牙来说&#xff0c;在鸿蒙化的实现过程中。我们发现了&#xff0c;鸿蒙独有的优秀点&#xff0c…

第六十三周周报 GCN-CNNGA

文章目录 week 63 GCN-CNNGA摘要Abstract1. 题目2. Abstract3. 文献解读3.1 Introduction3.2 创新点 4. 网络结构4.1 数据分析4.2 混合深度学习框架的发展4.3 Mul4.4 CNN block4.5 GCN block4.6 GRU block4.7 注意力机制4.8 模型评估标准 5. 实验结果5.1 不同邻接矩阵的性能评价…

geoserver+postgis 最短路径规划常见问题记录

一、说明 具体实现步骤可参考其他博文&#xff0c;下面的这个博主写的很详细&#xff0c;步骤很清晰&#xff0c;注释也很全。geoserverpostgis 最短路径规划_geoserver 最短路径 存储过程-CSDN博客 本次文章&#xff0c;仅记录过程中需要注意的方面。 二、数据预处理 目标&a…

石油安全理论知识题库 考试宝在线刷题

一、单选题&#xff08;每题有4个选项&#xff0c;其中只有1个是正确的&#xff0c;将正确的选项号填入括号内&#xff09; 1.新修订的《中华人民共和国安全生产法》于&#xff08; &#xff09;正式实施。 A、2014年1月1日 B、2014年12月1日 C、2015年1月1日 D、2015年…

航空标志灯技术革新:提升夜间飞行安全

航空标志灯 随着低空飞行活动的增多和新型飞行器&#xff08;如无人机、热气球和直升机&#xff09;的普及&#xff0c;地面重要设施的安全面临前所未有的挑战。因此&#xff0c;航空标志灯的安装变得尤为重要。它们通过提升城市天际线、广袤乡村、跨河桥梁及电力网络等复杂地…

前后端交互接口(三)

前后端交互接口&#xff08;三&#xff09; 前言 前两集我们先做了前后端交互接口的约定以及浅浅的阅读了一些proto代码。那么这一集我们就来看看一些重要的proto代码&#xff0c;之后把protobuffer给引入我们的项目当中&#xff01; gateway.proto 我们来看一眼我们的网关…

机器学习—sigmoid的替代品

Z状结肠激活函数&#xff0c;在隐藏层中&#xff0c;在输出层&#xff0c;因为用逻辑回归建立神经网络&#xff0c;创造了大量的逻辑回归单元&#xff0c;但是如果你使用其他激活函数&#xff0c;神经网络可以变得更加强大。 以需求预测为例&#xff0c;给定价格&#xff0c;航…

数据分析-44-时间序列预测之深度学习方法TCN

文章目录 1 TCN简介1.1 网络示意图1.2 TCN优点2 模拟应用2.1 模拟数据2.2 预处理创建滞后特征2.3 划分训练集和测试集2.4 创建TCN模型2.5 模型训练2.6 模型预测3 自定义my_TCN模型3.1 my_TCN()函数3.2 训练模型3.3 模型预测3.4 改进4 参考附录1 TCN简介 时间卷积网络(TCN)是…

2024最新AI绘画系统软件(Midjourney)+GPT4文档分析总结,多模态识图理解,AI文生图/图生图/混图生图(图像混合)

一、前言 人工智能的快速发展已成为全球关注的焦点&#xff0c;其应用领域广泛&#xff0c;涵盖绘图、语言处理、视频编辑等。前沿技术不仅推动科技创新&#xff0c;还在艺术创作、内容生产和商业实践等方面展示出巨大潜力。例如&#xff0c;AI语言模型显著提升了内容自动生成、…

input file检验成功之后才可以点击

input file检验成功之后才可以点击 需求 在上传发票前需要先填写发票号&#xff0c;然后点击选择文件直接完成上传功能 实现思路 在没有输入发票号之前&#xff0c;file按钮不可用不能点击&#xff0c;输入之后&#xff0c;按钮可用&#xff0c;点击之后选择文件&#xff…

每日OJ题_牛客_AB31活动安排_区间贪心_C++_Java

目录 牛客_AB31活动安排_区间贪心 题目解析 C代码 Java代码 牛客_AB31活动安排_区间贪心 活动安排_牛客题霸_牛客网 描述&#xff1a; 给定n个活动&#xff0c;每个活动安排的时间为[ai,bi)。求最多可以选择多少个活动&#xff0c;满足选择的活动时间两两之间没有重合。 …

购物车-多元素组合动画css

学习 渡一课程 多元素组合动画 练习。 在我们开发购物车功能时&#xff0c;经常会有点击添加按钮&#xff0c;就会有一个小圆点掉进购物车的动画&#xff0c;如下图所示&#xff0c;今天我们通过css来实现。 首先实现多元素组合动画 直接上代码&#xff0c;可以复制到本地使用…