OpenGL入门006——着色器在纹理混合中的应用

本节将理解顶点和片段着色器在纹理混合中的应用

文章目录

  • 一些概念
    • 纹理
    • 时间依赖动画
  • 实战
    • 简介
    • dependencies
      • shader.fs
      • shader.vs
      • teenager.png
      • tex.png
    • utils
      • windowFactory.h
      • shader.h
      • RectangleModel.h
      • RectangleModel.cpp
    • main.cpp
    • CMakeLists.txt
    • 最终效果

一些概念

纹理

  • 概述: 纹理是一张二维图像或一组数据,用来给3D模型赋予更多细节,模拟出更为复杂和逼真的外观。比如一块简单的立方体可以通过纹理变得像是由木材、金属或石头制作,而不需要为此增加实际的几何复杂度

纹理映射: 是将纹理图像的坐标(通常是二维UV坐标)映射到3D模型的表面,UV坐标系用于指定纹理在模型表面上的位置,其中:

  • U表示横向坐标(从左到右)
  • V表示纵向坐标(从上到下)

每个模型顶点都有一个或多个UV坐标,使得纹理能够精确地贴合到模型表面

纹理类型:

  • 颜色纹理:最常见的纹理类型,用于赋予物体颜色和图案
  • 法线纹理:用于模拟复杂的表面细节,如凹凸感和细小的表面特征,而无需修改几何体
  • 位移纹理:用来改变模型的几何结构,实际移动顶点以产生真实的凹凸效果
  • 环境贴图:用于模拟反射或折射效果,创建逼真的镜面或水面效果

时间依赖动画

概述: 使用时间变量来控制动画的状态变换,以确保动画帧以固定的速度播放或根据外部条件动态调整。

时间步进和概率: 动画通常分为帧,每一帧代表动画在特定时间点上的状态,为了使动画平滑,必须考虑帧率(每秒显示的帧数)和时间步进,常见方法包括固定时间步进和可变时间步进

插值方法: 时间依赖动画通常使用插值技术,在两个关键帧之间平滑过渡:

  • 线性插值:在起始点和结束点之间按线性比例过渡,适合简单的动画
  • 贝塞尔曲线和样条插值:用于创建更复杂、平滑的曲线运行,适合有自然过渡需求的动画,如角色行走或物体掉落

实战

简介

怎么在vscode上使用cmake构建项目,具体可以看这篇Windows上如何使用CMake构建项目 - 凌云行者的博客

目的: 本节将理解顶点和片段着色器在纹理混合中的应用

环境:

  • 编译工具链:使用msys2安装的mingw-gcc
  • 依赖项:glfw3:x64-mingw-static,glad:x64-mingw-static(通过vcpkg安装)

dependencies

shader.fs

// 指定OpenGL着色器语言的版本为3.30
#version 330 core
// 输入变量,表示纹理坐标
in vec2 TexCoord;
// 输出变量,表示片段的最终颜色
out vec4 FragColor;
// uniform变量,表示第一个纹理
uniform sampler2D texture0;
// uniform变量,表示第二个纹理
uniform sampler2D texture1;
// uniform变量,表示混合比例
uniform float blendRatio;void main() {// 使用mix函数根据blendRatio混合两个纹理的颜色,并将结果赋值给输出变量FragColorFragColor = mix(texture(texture0, TexCoord), texture(texture1, TexCoord), blendRatio);
}

shader.vs

#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTex;out vec2 TexCoord;void main() {gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);TexCoord = aTex;
}

teenager.png

在这里插入图片描述

tex.png

在这里插入图片描述

utils

windowFactory.h

#pragma once
#include <glad/glad.h> // gald前面不能包含任何opengl头文件
#include <GLFW/glfw3.h>
#include <functional>
#include <iostream>using std::cout;
using std::endl;class GLFWWindowFactory {
public:// 默认构造函数GLFWWindowFactory() {}// 构造函数,初始化窗口GLFWWindowFactory(int width, int height, const char* title) {// 初始化glfwglfwInit();// 设置opengl版本glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// 使用核心模式:确保不使用任何被弃用的功能glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 创建glfw窗口this->window = glfwCreateWindow(width, height, title, NULL, NULL);if (this->window == NULL) {cout << "Failed to create GLFW window" << endl;glfwTerminate();exit(-1);}// 设置当前窗口的上下文glfwMakeContextCurrent(this->window);// 设置窗口大小改变的回调函数glfwSetFramebufferSizeCallback(this->window, framebuffer_size_callback);// 加载所有opengl函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {cout << "Failed to initialize GLAD" << endl;}// 再次设置当前窗口的上下文,确保当前上下文仍然是刚刚创建的窗口,是一个安全措施glfwMakeContextCurrent(this->window);// 设置窗口大小改变的回调函数glfwSetFramebufferSizeCallback(this->window, framebuffer_size_callback);}// 获取窗口对象GLFWwindow* getWindow() {return this->window;}// 运行窗口,传入一个自定义的更新函数void run(std::function<void()> updateFunc) {// 启用深度测试,opengl将在绘制每个像素之前比较其深度值,以确定该像素是否应该被绘制glEnable(GL_DEPTH_TEST);// 循环渲染while (!glfwWindowShouldClose(this->window)) { // 检查是否应该关闭窗口// 清空屏幕所用的颜色glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 清空颜色缓冲,主要目的是为每一帧的渲染准备一个干净的画布glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 处理输入GLFWWindowFactory::process_input(this->window);// 执行更新函数updateFunc();// 交换缓冲区glfwSwapBuffers(this->window);// 处理所有待处理事件,去poll所有事件,看看哪个没处理的glfwPollEvents();}// 终止GLFW,清理GLFW分配的资源glfwTerminate();}// 窗口大小改变的回调函数static void framebuffer_size_callback(GLFWwindow* window, int width, int height) {// 确保视口与新窗口尺寸匹配,注意在视网膜显示器上,宽度和高度会显著大于指定值glViewport(0, 0, width, height);}// 处理输入static void process_input(GLFWwindow* window) {// 按下ESC键时进入if块if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)// 关闭窗口glfwSetWindowShouldClose(window, true);}private:// 窗口对象GLFWwindow* window;
};

shader.h

#ifndef SHADER_H
#define SHADER_H#include <glad/glad.h>
#include <glm/glm.hpp>#include <string>
#include <fstream>
#include <sstream>
#include <iostream>using std::string;
using std::ifstream;
using std::stringstream;
using std::cout;
using std::endl;class Shader {
public:// 默认构造函数Shader() {}// 着色器程序IDunsigned int ID;// 构造函数Shader(const char* vertexPath, const char* fragmentPath) {string vertexCode;string fragmentCode;ifstream vShaderFile;ifstream fShaderFile;// 确保ifstream对象可以抛出异常vShaderFile.exceptions(ifstream::failbit | ifstream::badbit);fShaderFile.exceptions(ifstream::failbit | ifstream::badbit);try {// 打开文件vShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);// 读取文件缓冲区内容到stream中stringstream vShaderStream, fShaderStream;vShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// 关闭文件处理器vShaderFile.close();fShaderFile.close();// 将stream转换为字符串vertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();} catch (ifstream::failure& e) {cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << endl;}const char* vShaderCode = vertexCode.c_str();const char* fShaderCode = fragmentCode.c_str();// 编译着色器unsigned int vertex, fragment;// 顶点着色器vertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);checkCompileErrors(vertex, "VERTEX");// 片段着色器fragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);checkCompileErrors(fragment, "FRAGMENT");// 着色器程序ID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// 删除着色器glDeleteShader(vertex);glDeleteShader(fragment);}// 激活着色器void use() {glUseProgram(ID);}// 实用的uniform工具函数// 用于在着色器程序中设置uniform值// 设置一个布尔类型的uniform变量void setBool(const std::string& name, bool value) const {glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);}// 设置一个整型的uniform变量void setInt(const std::string& name, int value) const {glUniform1i(glGetUniformLocation(ID, name.c_str()), value);}// 设置一个浮点类型的uniform变量void setFloat(const std::string& name, float value) const {glUniform1f(glGetUniformLocation(ID, name.c_str()), value);}// 设置一个vec2类型的uniform变量void setVec2(const std::string& name, const glm::vec2& value) const {glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}// 设置一个vec2类型的uniform变量void setVec2(const std::string& name, float x, float y) const {glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);}// 设置一个vec3类型的uniform变量void setVec3(const std::string& name, const glm::vec3& value) const {glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}// 设置一个vec3类型的uniform变量void setVec3(const std::string& name, float x, float y, float z) const {glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);}// 设置一个vec4类型的uniform变量void setVec4(const std::string& name, const glm::vec4& value) const {glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);}// 设置一个vec4类型的uniform变量void setVec4(const std::string& name, float x, float y, float z, float w) {glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);}// 设置一个mat2类型的uniform变量void setMat2(const std::string& name, const glm::mat2& mat) const {glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}//  设置一个mat3类型的uniform变量void setMat3(const std::string& name, const glm::mat3& mat) const {glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}// 设置一个mat4类型的uniform变量void setMat4(const std::string& name, const glm::mat4& mat) const {glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);}private:// 检查着色器编译/链接错误void checkCompileErrors(GLuint shader, string type) {GLint success;GLchar infoLog[1024];if (type != "PROGRAM") {glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {glGetShaderInfoLog(shader, 1024, NULL, infoLog);cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << endl;}}else {glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shader, 1024, NULL, infoLog);cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << endl;}}}
};
#endif

RectangleModel.h

#pragma once
#include <glad/glad.h>
#include "shader.h"
#include <vector>
#include <chrono>
#include <cmath>class RectangleModel {
public:// 构造函数RectangleModel(const Shader& shader);// 析构函数~RectangleModel();// 绘制矩形void draw();private:unsigned int VAO;unsigned int VBO;unsigned int EBO;Shader shader;// 着色器程序unsigned int shaderProgram;// 纹理std::vector<unsigned int>texture;// 编译着色器void compileShaders();// 设置缓冲区void setElements();// 加载纹理void loadTexture();// 绑定纹理void bindTexture(GLuint& textured, const char* path);
};

RectangleModel.cpp

#include "RectangleModel.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"using std::cout;
using std::endl;// 构造函数
RectangleModel::RectangleModel(const Shader& shader) : shader(shader) {// 加载纹理loadTexture();// 设置缓冲区setElements();
}// 析构函数
RectangleModel::~RectangleModel() {// 删除VAOglDeleteVertexArrays(1, &this->VAO);// 删除VBOglDeleteBuffers(1, &this->VBO);// 删除EBOglDeleteBuffers(1, &this->EBO);
}/// public
// 绘制矩形
void RectangleModel::draw() {// 使用着色器程序this->shader.use();// 绑定VAOglBindVertexArray(VAO);// 获取当前时间auto now = std::chrono::system_clock::now();// 计算毫秒数auto duration = now.time_since_epoch();double milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();// 计算混合比例float blendRatio = 0.5f * (std::sin(milliseconds / 1000.0) + 1.0f);// 设置uniform变量shader.setFloat("blendRatio", blendRatio);// 绘制矩形,即绘制两个三角形,GL_UNSIGNED_INT表示索引数组中的每个元素都是一个无符号整数// 六个矩形glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}/// private
void RectangleModel::setElements() {float vertices[] = {// 矩形1-0.9f, -0.9f, 0.0f, 0.0f,-0.5f, -0.9f, 1.0f, 0.0f,-0.9f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 1.0f, 1.0f,// 矩形2-0.4f, -0.9f, 0.0f, 0.0f,0.0f, -0.9f, 1.0f, 0.0f,-0.4f, -0.5f, 0.0f, 1.0f,0.0f, -0.5f, 1.0f, 1.0f,// 矩形30.1f, -0.9f, 0.0f, 0.0f,0.5f, -0.9f, 1.0f, 0.0f,0.1f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, 1.0f, 1.0f,// 矩形4-0.9f, 0.1f, 0.0f, 0.0f,-0.5f, 0.1f, 1.0f, 0.0f,-0.9f, 0.5f, 0.0f, 1.0f,-0.5f, 0.5f, 1.0f, 1.0f,// 矩形5-0.4f, 0.1f, 0.0f, 0.0f,0.0f, 0.1f, 1.0f, 0.0f,-0.4f, 0.5f, 0.0f, 1.0f,0.0f, 0.5f, 1.0f, 1.0f,// 矩形60.1f, 0.1f, 0.0f, 0.0f,0.5f, 0.1f, 1.0f, 0.0f,0.1f, 0.5f, 0.0f, 1.0f,0.5f, 0.5f, 1.0f, 1.0f};// 索引数据int indices[] = {// 矩形 10, 1, 2,1, 2, 3,// 矩形 24, 5, 6,5, 6, 7,// 矩形 38, 9, 10,9, 10, 11,// 矩形 412, 13, 14,13, 14, 15,// 矩形516, 17, 18,17, 18, 19,// 矩形620, 21, 22,21, 22, 23};// 生成一个VAOglGenVertexArrays(1, &this->VAO);// 绑定VAO,使其成为当前操作的VAOglBindVertexArray(this->VAO);// 生成一个VBOglGenBuffers(1, &this->VBO);// 绑定VBO, 使其成为当前操作的VBO,GL_ARRAY_BUFFER表示顶点缓冲区glBindBuffer(GL_ARRAY_BUFFER, this->VBO);// 为当前绑定的VBO创建并初始化数据存储,GL_STATIC_DRAW表示数据将一次性提供给缓冲区,并且在之后的绘制过程中不会频繁更改glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 生成一个EBOglGenBuffers(1, &this->EBO);// 绑定EBO,使其成为当前操作的EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);// 传递索引数据glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// 定义顶点属性的布局// - index:顶点属性的索引// - size:每个顶点属性的数量// - type:数据类型// - normalized:是否将非浮点数值归一化// - stride:连续顶点属性之间的间隔// - pointer:数据在缓冲区中的偏移量// 设置顶点属性指针,位置属性glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);// 启用顶点属性glEnableVertexAttribArray(0);// 设置顶点属性指针,颜色属性glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));// 启用顶点属性glEnableVertexAttribArray(1);
}// 加载纹理
void RectangleModel::loadTexture() {std::vector<std::string> path = { "teenager.png", "tex.png" };texture.resize(path.size());for (int i = 0; i < path.size(); i++) {bindTexture(texture[i], path[i].c_str());}// 参数传入指定要激活的纹理单元,例如GL_TEXTURE0/1/*glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texture[0]);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, texture[1]);// 使用着色器程序shader.use();// 设置uniform变量shader.setInt("texture0", 0);shader.setInt("texture1", 1);
}// 绑定纹理
void RectangleModel::bindTexture(GLuint& textureId, const char* path) {glGenTextures(1, &textureId);glBindTexture(GL_TEXTURE_2D, textureId);// 设置纹理环绕和过滤方式glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// 加载并生成纹理stbi_set_flip_vertically_on_load(true);int width, height, nrChannels;unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0);if (data) {GLenum format;if (nrChannels == 4)format = GL_RGBA;else if (nrChannels == 3)format = GL_RGB;elseformat = GL_RED;glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);}else {std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);
}

main.cpp

#include "utils/RectangleModel.h"
#include "utils/windowFactory.h"int main() {// 创建一个窗口Factory对象GLFWWindowFactory myWindow(800, 600, "This is Title");// 创建一个着色器对象Shader shader("shader.vs", "shader.fs");// 创建一个矩形模型对象RectangleModel rectangle(shader);// 运行窗口,传入一个lambda表达式,用于自定义渲染逻辑myWindow.run([&]() {// 绘制矩形rectangle.draw();});return 0;
}

CMakeLists.txt

# 设置CMake的最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(Shader)# vcpkg集成, 这里要换成你自己的vcpkg工具链文件和共享库路径
set(VCPKG_ROOT D:/software6/vcpkg/)
set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(CMAKE_PREFIX_PATH ${VCPKG_ROOT}/installed/x64-mingw-static/share)# 查找所需的包
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)# 搜索并收集utils文件夹下的所有源文件
file(GLOB UTILS "utils/*.cpp", "utils/*.h")# 添加可执行文件(还要加入utils文件夹下的源文件)
add_executable(Shader main.cpp ${UTILS})# 链接所需的库
target_link_libraries(Shader PRIVATE glad::glad glfw glm::glm)# 检查项目是否有dependeicies目录,如果存在,则在使用add_custom_command命令在构建后将dependencies目录中的文件复制到项目的输出目录
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies")
if(EXISTS ${SOURCE_DIR})add_custom_command(TARGET Shader POST_BUILDCOMMAND ${CMAKE_COMMAND} -E copy_directory${SOURCE_DIR} $<TARGET_FILE_DIR:Shader>)
endif()

最终效果

在这里插入图片描述

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

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

相关文章

【9695】基于springboot+vue的学生就业管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取免费源码 项目描述 本学生就业管理系统以springboot作为框架&#xff…

C语言的进制表示【八进制,十六进制】

文章目录 C语言的进制表示【八进制&#xff0c;十六进制】题目介绍C语言的进制表示1. 十进制2. 八进制3. 十六进制4. 二进制 进制表示总结 C语言的进制表示【八进制&#xff0c;十六进制】 题目介绍 故事的起因是今天在群里看到有人发来的问题 //原题目 int main() {int a 0…

高通Quick板上安装编译Ros1 noetic,LeGO_LOAM,FAR_Planner和rslidar_sdk

环境要求&#xff1a; 这里quick板上安装的是Ubuntu20.04版本 Ros Noeti安装&#xff1a; 1.设置软件源&#xff1a; 官方提供的软件源&#xff1a; sudo sh -c echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.…

一招解决Mac没有剪切板历史记录的问题

使用Mac的朋友肯定都为Mac的剪切功能苦恼过&#xff0c;旧内容覆盖新内容&#xff0c;导致如果有内容需要重复输入的话&#xff0c;就需要一次一次的重复复制粘贴&#xff0c;非常麻烦 但其实Mac也能够有剪切板历史记录功能&#xff0c;iCopy&#xff0c;让你的Mac也能拥有剪切…

nginx 搭建网站

1.查看防火墙状态systemctl status firewalld 2.getenforce 3.安装nginx yum install nginx -y 4.网站信息 echo "welcome to yinchuankejixuanyuan" > /usr/share/nginx/html/index.html 5.查看命令状态 nginx -t 6.重启 systemctl restart nginx

gin入门

Gin入门笔记 1. 初始gin 1.1. 依赖安装 go get github.com/gin-gonic/gin写gin程序都有一套固定的格式 初始化写路由监听运行 1.2. hello world package mainimport ("github.com/gin-gonic/gin""net/http" )func main() {router : gin.Default()rou…

CentOS 7 安装 ntp,自动校准系统时间

1、安装 ntp yum install ntp 安装好后&#xff0c;ntp 会自动注册成为服务&#xff0c;服务名称为 ntpd 2、查看当前 ntpd 服务的状态 systemctl status ntpd 3、启动 ntpd 服务、查看 ntpd 服务的状态 systemctl start ntpdsystemctl status ntpd 4、设置 ntpd 服务开机启…

Unity3D UI 拖拽

Unity3D 实现 UI 元素拖拽功能。 UI 拖拽 通常画布上的 UI 元素都是固定位置的&#xff0c;我们可以通过实现拖拽接口&#xff0c;让 UI 元素可以被拖拽到其他位置。 拖拽接口 创建一个脚本 UIDrag.cs&#xff0c;在默认继承的 MonoBehaviour 后面&#xff0c;再继承三个接…

基于 SSM(Spring + Spring MVC + MyBatis)框架构建电器网上订购系统

基于 SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架构建电器网上订购系统可以为用户提供一个方便快捷的购物平台。以下将详细介绍该系统的开发流程&#xff0c;包括需求分析、技术选型、数据库设计、项目结构搭建、主要功能实现以及前端页面设计。 需求分析 …

esp32学习:利用虫洞ESP32开发板,快速实现无线图传

我们的虫洞开发板&#xff0c;能够完美运行esp who AI代码&#xff0c;所以实现无线图传那是非常容易的&#xff0c;我们先看看examples目录&#xff1a; 里面有比较多的web例程&#xff0c;在这些例程下&#xff0c;稍作修改&#xff0c;就可以快速实现我的图传无线功能&#…

mac m1 docker本地部署canal 监听mysql的binglog日志

mac m1 docker本地部署canal监听mysql的binglog日志(虚拟机同理) 根据黑马视频部署 1.docker 部署mysql 1.docker拉取mysql 镜像 因为m1是arm架构.需要多加一条信息 正常拉取 docker pull mysql:tagm1拉取 5.7的版本. tag需要自己指定版本 docker pull --platform linux/x…

还在为慢速数据传输苦恼?Linux 零拷贝技术来帮你!

前言 程序员的终极追求是什么&#xff1f;当系统流量大增&#xff0c;用户体验却丝滑依旧&#xff1f;没错&#xff01;然而&#xff0c;在大量文件传输、数据传递的场景中&#xff0c;传统的“数据搬运”却拖慢了性能。为了解决这一痛点&#xff0c;Linux 推出了 零拷贝 技术&…

基于java+SpringBoot+Vue的微服务在线教育系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

【无标题】西安交通大学提出少锚点的端到端车道线检测算法Polar R-CNN

Abstract 车道线检测在自动驾驶中是一个关键且充满挑战的任务&#xff0c;特别是在实际场景中&#xff0c;由于车道线可能因其他车辆而被遮挡、形状纤细且长度较长&#xff0c;检测难度增大。现有基于锚点的检测方法通常依赖于预设的锚点来提取特征&#xff0c;并随后对车道线…

【手撕排序3】归并排序

&#x1f343; 本系列包括常见的各种排序算法&#xff0c;如果感兴趣&#xff0c;欢迎订阅&#x1f6a9; &#x1f38a;个人主页:小编的个人主页 &#x1f380; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 ✌️ &#x1f91e; &#x1f91f; &#x1f918; &#x1f91…

Vue中使用Antd中a-table实现表格数据列合并展示

原数据 根据需求实现当前两列数据中有相同数据时,合并列单元格 实现 源码 数据 const dataSource = ref([{id: 1,pl: "冰箱",zznd: "P1",sm: "说明说明说明1",dw: "台",gs: "1",dj: "100"},{id: 1,pl: "冰…

数据结构-数组(稀疏矩阵转置)和广义表

目录 1、数组定义 1&#xff09;数组存储地址计算示例①行优先②列优先 2&#xff09;稀疏矩阵的转置三元组顺序表结构定义 ①普通矩阵转置②三元组顺序表转置稀疏矩阵③稀疏矩阵的快速转置 3&#xff09;十字链表结构定义 2、广义表定义 1&#xff09;基本操作①GetHead②GetT…

【Spring】Spring Web MVC基础入门~(含大量例子)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;什么是Spring Web MVC 1&#xff1a;Servlet 2&#xff1a;总结 二&#xff1a;MVC …

有向图的完全可达性(有向图搜索全路径的问题) C#DFs

在考察输入输出方面我觉得是道难题了 第一次遇见邻接表的数据结构该怎么声明 卡码网105 在力扣没找见完全相同的题 感觉需要多练习多复习这种类型的题 105. 有向图的完全可达性 题目描述 给定一个有向图&#xff0c;包含 N 个节点&#xff0c;节点编号分别为 1&…

登陆页面渗透测试常见的20种思路与总结

【渗透测试】16个实用谷歌浏览器插件分享 飞雪网络安全人才培养计划&#xff0c;绝对零区&#xff0c;公益教学&#xff01; 思路总结 1、之前是否已经留过后门&#xff0c;是&#xff0c;直接getshell&#xff0c;否&#xff0c;进行测试 2、SQL注入&万能密码&#xf…