当前位置: 首页 > news >正文

opengl 学习着色器

一.GLSL

        着色器是使用一种叫GLSL的类C语言写成的。GLSL着色器编码顺序:声明版本==》定义输入输出==》uniform==》main函数。每个着色器的入口点是main函数,在main函数中我们处理所有的输入变量,并将结果输出到输出变量中。如下图:

#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;int main()
{// 处理输入并进行一些图形操作...// 输出处理过的结果到输出变量out_variable_name = weird_stuff_we_processed;
}

二.顶点着色器(补充) 

        顶点着色器的每个输入变量也叫顶点属性,我们能够声明的定点属性是有上限的,它通常由硬件决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: ";

三.GLSL数据类型

        GLSL包含默认基础数据类型:intfloatdoubleuintbool。GLSL也有两种容器类型,分别是向量(Vector)和矩阵(Matrix)。这里先介绍向量,后面在学矩阵。

四.向量

        GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量):

类型含义
vecn包含n个float分量的默认向量
bvecn包含n个bool分量的向量
ivecn包含n个int分量的向量
uvecn包含n个unsigned int分量的向量
dvecn包含n个double分量的向量

        一个向量的分量可以通过vec.x这种方式获取,这里x是指这个向量的第一个分量。你可以分别使用.x.y.z.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba,或是对纹理坐标使用stpq访问相同的分量。

        向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

        你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

四.输入和输出

        着色器是许多独立的小程序,GLSL定义了inout关键字专门来实现这个目的,每个着色器使用这两个关键字设定输入和输出。只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

        如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。我们用lesson2的示例修改顶点着色器和片段着色器的代码,得到:

 测试 着色器之间的参数传递
// 注意我们如何把一个vec3作为vec4的构造器的参数
// 把输出变量设置为暗红色
const char *vertexShaderSource = "#version 330 core\n""layout (location = 0) in vec3 aPos;\n""out vec4 vertexColor;\n""void main()\n""{\n""   gl_Position = vec4(aPos, 1.0);\n""   vertexColor = vec4(0.5, 0.0, 0.0, 1.0);\n""}\0";const char *fragmentShaderSource = "#version 330 core\n""out vec4 FragColor;\n""in vec4 vertexColor;\n""void main()\n""{\n""FragColor = vertexColor;\n""}\n\0";测试 着色器之间的参数传递 end

五.Uniform关键字

        Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

下面演示如何设置uniform的值,我们让六边形的渲染颜色动起来。

 测试 Uniform
const char *fragmentShaderSource = "#version 330 core\n""out vec4 FragColor;\n""uniform vec4 customColor;\n""void main()\n""{\n""FragColor = customColor;\n""}\n\0";///  测试 end
   // 着色器处理float timeValue = glfwGetTime();                            //获取运行的秒数float greenValue = (sin(timeValue) / 2.0f) + 0.5f;          //sin函数让颜色在0.0到1.0之间改变///glGetUniformLocation查询uniform ourColor的位置值。/// 我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。/// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。/// 最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,/// 但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。int vertexColorLocation = glGetUniformLocation(shaderProgram, "customColor");glUseProgram(shaderProgram);glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//glUseProgram(shaderProgram);// 着色器end

        先设置片段着色器的uniform值,我们在main函数中,渲染之前使用着色器,通过获取着色器的位置,然后设置颜色值。效果如下: 

live.csdn.net/v/364740​​​​​​

六.更多属性

        在lesson2里面,我们知道了解了VBO,VAO等关键字,这次我们学习如何设置顶点的颜色以及加入着色器中。

        1.先把顶点数组里面每个坐标后面添加一个颜色值。

    // 顶点输入六边形float vertices[] = {-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f,1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

        2. 顶点着色器,使它能够接收颜色值作为一个顶点属性输入。需要注意的是我们用layout标识符来把aColor属性的位置值设置为1:

/// 测试将定点着色器添加颜色值
const char *vertexShaderSource = "#version 330 core\n""layout (location = 0) in vec3 aPos;\n""layout (location = 1) in vec3 aColor;\n""out vec3 ourColor;\n""void main()\n""{\n""   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n""   ourColor = aColor;\n""}\0";const char *fragmentShaderSource = "#version 330 core\n""in vec3 ourColor;\n""out vec4 FragColor;\n""void main()\n""{\n""FragColor = vec4(ourColor, 1.0f);\n""}\n\0";
/// 测试 end

        3.计算VAO里顶点着色器解析的位置和颜色。

 // 解析顶点数据// 第一个参数: 0, 着色器的location = 0,对应这里的0// 第二个参数: 3, 顶点数据是3个坐标构成,这里是3// 第三个参数: 数据类型GL_FLOAT// 第四个参数: 是否标准化,标准化就会映射到0~1之间// 第五个参数步长: 表示每个顶点的所占空间的大小// 第六个参数: 代表偏移,默认位置为0//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);// 启动顶点属性//glEnableVertexAttribArray(0);// 位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 颜色属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);

         4.得到的效果如下:

 这是原作者给出的图像解释:(原作者是3角形,我们是六边形,下面的颜色数量为6)

这个图片可能不是你所期望的那种,因为我们只提供了3个颜色,而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。
基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。

这正是在这个三角形中发生了什么。我们有3个顶点,和相应的3个颜色,从这个三角形的像素来看它可能包含50000左右的片段,片段着色器为这些像素进行插值颜色。如果你仔细看这些颜色就应该能明白了:红首先变成到紫再变为蓝色。片段插值会被应用到片段着色器的所有输入属性上。

七.编写我们自己的着色器(重点来啦)

         编写、编译、管理着色器是件麻烦事。在着色器主题的最后,我们会写一个类来让我们的生活轻松一点,它可以从硬盘读取着色器,然后编译并链接它们,并对它们进行错误检测,这就变得很好用了。这也会让你了解该如何封装目前所学的知识到一个抽象对象中。

编写定点和片段着色器代码文件:

#version 330 core
layout (location = 0) in vec3 aPos;
out vec4 vertexColor;
void main()
{gl_Position = vec4(aPos, 1.0);vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}
#version 330 core
out vec4 FragColor;
uniform vec4 customColor;
void main()
{FragColor = customColor;
}

 封装着色器类:

shader.h

#pragma once
// 包含glad来获取所有的必须OpenGL头文件
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>class Shader
{
public:// 程序IDunsigned int ID;// 构造器读取并构建着色器Shader(const char* vertexPath, const char* fragmentPath);// 使用/激活程序void use();// uniform工具函数void setFloat(const std::string &name, float value) const;
};

shader.cpp

#include "shader.h"Shader::Shader(const char* vertexPath, const char* fragmentPath)
{// 1. 从文件路径中获取顶点/片段着色器std::string vertexCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream fShaderFile;// 保证ifstream对象可以抛出异常:vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);try{// 打开文件vShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// 读取文件的缓冲内容到数据流中vShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// 关闭文件处理器vShaderFile.close();fShaderFile.close();// 转换数据流到stringvertexCode   = vShaderStream.str();fragmentCode = fShaderStream.str();}catch(std::ifstream::failure e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;}const char* vShaderCode = vertexCode.c_str();const char* fShaderCode = fragmentCode.c_str();// 2. 编译着色器unsigned int vertex, fragment;int success;char infoLog[512];// 顶点着色器vertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);// 打印编译错误(如果有的话)glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);if(!success){glGetShaderInfoLog(vertex, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// 片段着色器fragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);if(!success){glGetShaderInfoLog(fragment, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// 着色器程序ID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);// 打印连接错误(如果有的话)glGetProgramiv(ID, GL_LINK_STATUS, &success);if(!success){glGetProgramInfoLog(ID, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了glDeleteShader(vertex);glDeleteShader(fragment);
}void Shader::use()
{glUseProgram(ID);
}void Shader::setFloat(const std::string &name, float value) const
{glUniform4f(glGetUniformLocation(ID, name.c_str()),  0.0f, value, 0.0f, 1.0f);
}

 在main函数中使用着色器类:

        真正实现的代码就两句:

           Shader ourShader("D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs", "D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs");

          // 测试从文件读取着色器
           ourShader.use();
           ourShader.setFloat("customColor", greenValue);

int main()
{// 告知opengl我们使用的版本和渲染模式glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// mac 下需要这句话glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);// end// 创建opengl窗口GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// 创建上下文glfwMakeContextCurrent(window);// 渲染窗口自适应glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// glad加载if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "failed to init glad!" << std::endl;}// // 构建顶点着色器// // 1.创建着色器对象// unsigned int vertexShader;// vertexShader = glCreateShader(GL_VERTEX_SHADER);// // 2.将着色器代码关联到对象上并编译// glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);// glCompileShader(vertexShader);// // 3.检查编译是否成功// int  success;// char infoLog[512];// glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);// if(!success)// {//     glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);//     std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;// }// // 构建片段着色器// unsigned int fragmentShader;// fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);// glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);// glCompileShader(fragmentShader);// glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);// if(!success)// {//     glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);//     std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;// }// // 编译完成之后链接到程序对象// unsigned int shaderProgram;// shaderProgram = glCreateProgram();// glAttachShader(shaderProgram, vertexShader);// glAttachShader(shaderProgram, fragmentShader);// glLinkProgram(shaderProgram);// glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);// // 判断是否链接程序成功// if(!success) {//     glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);// }// // 使用程序// //glUseProgram(shaderProgram);// // 删除着色器对象,在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们// glDeleteShader(vertexShader);// glDeleteShader(fragmentShader);Shader ourShader("D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs", "D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs");// 顶点输入六边形float vertices[] = {-0.5f, 0.5f, 0.0f,0.5f,  0.5f, 0.0f,1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f,-0.5f, -0.5f, 0.0f,-1.0f, 0.0f, 0.0f};// float vertices[] = {//     -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,//     0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 0.0f,//     1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,//     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,//     -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,//     -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f// };unsigned int indices[] = {0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5};// 建立缓冲对象unsigned int VBO;unsigned int VAO;unsigned int EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);// 绑定到缓冲bufferglBindBuffer(GL_ARRAY_BUFFER, VBO);// 刷入缓冲bufferglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// 解析顶点数据// 第一个参数: 0, 着色器的location = 0,对应这里的0// 第二个参数: 3, 顶点数据是3个坐标构成,这里是3// 第三个参数: 数据类型GL_FLOAT// 第四个参数: 是否标准化,标准化就会映射到0~1之间// 第五个参数步长: 表示每个顶点的所占空间的大小// 第六个参数: 代表偏移,默认位置为0glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);// 启动顶点属性glEnableVertexAttribArray(0);// // 位置属性// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);// glEnableVertexAttribArray(0);// // 颜色属性// glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));// glEnableVertexAttribArray(1);// 解绑VAOglBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);// 等待用户关闭窗口while(!glfwWindowShouldClose(window)){processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 着色器处理uniformfloat timeValue = glfwGetTime();                            //获取运行的秒数float greenValue = (sin(timeValue) / 2.0f) + 0.5f;          //sin函数让颜色在0.0到1.0之间改变// ///glGetUniformLocation查询uniform ourColor的位置值。// /// 我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。// /// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。// /// 最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,// /// 但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。// int vertexColorLocation = glGetUniformLocation(shaderProgram, "customColor");// glUseProgram(shaderProgram);// glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);// 着色器end//glUseProgram(shaderProgram);// 测试从文件读取着色器ourShader.use();ourShader.setFloat("customColor", greenValue);// 3. 绘制物体glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);// 双缓冲交换glfwSwapBuffers(window);// 响应各种交互事件glfwPollEvents();}// 释放资源glfwTerminate();return 0;
}

效果同uniform的动画一样。截图如下:

        到这里基本就简单的学习了着色器。

八.学习地址:(感谢原作者的讲解)

着色器 - LearnOpenGL CN (learnopengl-cn.github.io)

九.demo地址:

learningOpengl: 一起学习opengl

相关文章:

opengl 学习着色器

一.GLSL 着色器是使用一种叫GLSL的类C语言写成的。GLSL着色器编码顺序&#xff1a;声明版本》定义输入输出》uniform》main函数。每个着色器的入口点是main函数&#xff0c;在main函数中我们处理所有的输入变量&#xff0c;并将结果输出到输出变量中。如下图&#xff1a; #ver…...

【数据结构】18 二叉搜索树(查找,插入,删除)

定义 二叉搜索树也叫二叉排序树或者二叉查找树。它是一种对排序和查找都很有用的特殊二叉树。 一个二叉搜索树可以为空&#xff0c;如果它不为空&#xff0c;它将满足以下性质&#xff1a; 非空左子树的所有键值小于其根节点的键值非空右子树的所有键值都大于其根结点的键值左…...

力扣日记2.20-【回溯算法篇】491. 非递减子序列

力扣日记&#xff1a;【回溯算法篇】491. 非递减子序列 日期&#xff1a;2023.2.20 参考&#xff1a;代码随想录、力扣 ps&#xff1a;放了个寒假&#xff0c;日记又搁置了三星期……&#xff08;下跪忏悔&#xff09; 491. 非递减子序列 题目描述 难度&#xff1a;中等 给你一…...

Android 13.0 SystemUI下拉状态栏定制二 锁屏页面横竖屏解锁图标置顶显示功能实现

1.前言 在13.0的系统rom定制化开发中,在关于systemui的锁屏页面功能定制中,由于在平板横屏锁屏功能中,时钟显示的很大,并且是在左旁边居中显示的, 由于需要和竖屏显示一样,所以就需要用到小时钟显示,然后同样需要居中,所以就来分析下相关的源码,来实现具体的功能 如图…...

FPGA_简单工程_拨码开关

一 框图 二 波形图 三 代码 3.1 工程代码 module bomakiaguan (input [15:0] switch, // 输入16路拨码开关output reg [15:0] led // 输出16个LED灯 );always (switch) beginled < switch; // 将拨码开关的值直接赋给LED灯 end // 将拨码开关的值直接赋给LED灯 endmodu…...

LaunchPad 市场的复苏,Penpad 成新兴生力军

以 Fair Launch 为主要启动方式的铭文市场的爆发&#xff0c;推动了 LaunchPad 市场的复苏&#xff0c;绝多数所铭文项目都能通过 Fairr Launch 的方式筹集资金实现启动&#xff0c;该赛道的爆发不仅推动了数百亿美元的热钱开始在链上不断涌动&#xff0c;同时也进一步形成了新…...

知识图谱实战应用30-基于py2neo的天文学中的恒星、行星与卫星之间的关系知识图谱研究与应用

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用30-基于py2neo的天文学中的恒星、行星与卫星之间的关系知识图谱研究与应用。本文将详细介绍如何利用py2neo构建天文学中的恒星、行星与卫星之间的关系知识图谱,并探讨其在天文学研究中的应用。文章将提供多条太阳系中恒…...

笔试题详解(C语言进阶)

前言 欢迎阅读本篇文章&#xff01;本篇文章通过一个笔试题来加强我们对C语言的理解&#xff0c;希望对你有帮助。后续我会写一个栏目&#xff0c;集合我见到的C语言题目&#xff0c;进行分析讲解。 1、题目一 判断下面程序的输出结果&#xff1a;(下面说的地址4/8字节是因为对…...

ClickHouse快速上手

简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS) 官网(https://clickhouse.com/docs/zh)给出的定义&#xff0c;其实没看懂 特性 ClickHouse支持一种基于SQL的声明式查询语言&#xff0c;它在许多情况下与ANSI SQL标准相同。使用时和MySQL有点相似&#…...

蓝桥杯DP算法——背包问题(C++)

目录 一、01背包问题 二、完全背包问题 三、多重背包问题 四、多重背包问题&#xff08;优化版&#xff09; 五、分组背包问题 一、01背包问题 01背包问题就是有N件物品&#xff0c;一个空间大小为V的背包&#xff0c;每个物品只能使用一次&#xff0c;使得背包中所装物品…...

【LeetCode+JavaGuide打卡】Day22|235. 二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

学习目标&#xff1a; 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点 学习内容&#xff1a; 235. 二叉搜索树的最近公共祖先 题目链接&&文章讲解 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最…...

Stable Diffusion WebUI 界面介绍

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 大家好,我是水滴~~ 本文主要对 Stable Diffusion WebUI 的界面进行简单的介绍,让你对该 WebUI 有个大致的了解,为后面的深入学习打下一个基础。主要内容包括:Stable Diffusion 模型(Stable Diffusion checkp…...

Cocos2dx-lua ScrollView[一]基础篇

一.ScrollView概述 cocos游戏中ScrollView控件大量使用,95%以上的项目都会使用ScrollView,个别游戏可能全部使用翻页的滑动效果。如果想要精通Cocos的UI开发,精通ScrollView控件非常关键,因此对ScrollView的使用进行总结很有必要。 下文缩写说明:sv = ScrollView, item代…...

QT应用软件【协议篇】周立功CAN接口卡代码示例

文章目录 USBCAN系列CAN接口卡规格参数资料下载QT引用周立功的库安装sdk代码USBCAN系列CAN接口卡 USBCAN系列CAN接口卡兼容USB2.0全速规范,可支持1/2/4/8路CAN接口。采用该接口卡,PC机可通过USB连入CAN网络,进行CAN总线数据采集和处理,主要具备以下几大优势特点: 支持车载…...

JVM对象的创建流程与内存分配

对象的创建流程与内存分配 创建流程对象内存分配方式内存分配安全问题对象内存分配流程【重要】:对象怎样才会进入老年代?重点 案例演示:对象分配过程大对象直接进入老年代02-对象内存分配的过程: 创建流程 加载 验证 解析 准备 初始化 使用 写在 对象内存分配方式 内存分配…...

docker (六)-进阶篇-数据持久化最佳实践MySQL部署

容器的数据挂载通常指的是将宿主机&#xff08;虚拟机或物理机&#xff09;上的目录或文件挂载到容器内部 MySQL单节点安装 详情参考docker官网文档 1 创建对应的数据目录、日志目录、配置文件目录(参考二进制安装&#xff0c;需自己建立数据存储目录) mkdir -p /data/mysq…...

力扣题目训练(17)

2024年2月10日力扣题目训练 2024年2月10日力扣题目训练551. 学生出勤记录 I557. 反转字符串中的单词 III559. N 叉树的最大深度241. 为运算表达式设计优先级260. 只出现一次的数字 III126. 单词接龙 II 2024年2月10日力扣题目训练 2024年2月10日第十七天编程训练&#xff0c;今…...

【react】react中和vue中的provide/inject、context写法示例

react写法 在 React 中&#xff0c;provide和inject的功能类似于 Vue.js 中的 provide和inject。它们都是用于跨组件层次传递数据的。 在 React 中&#xff0c;没有内置的 provide 和 inject 函数。但是&#xff0c;你可以使用 React 的 Context 来实现类似的功能。 Context…...

MySQL 的存储引擎(基本介绍)

文章目录 前言MySQL 的存储引擎介绍存储引擎是什么&#xff1f;存储引擎的特性? Innodb 与 Mylsam 的区别行级锁与表级锁是否支持事务是否支持恢复数据是否支持外键是否支持 MVCC 总结 前言 好文章不要错过&#xff0c;前两天跟大家分享的文章 1.MySQL的基础架构 2.SQL语句的…...

Unity3D 实现基于物理引擎的绳子关节解析详解

前言 在游戏开发中&#xff0c;有时候我们需要实现绳子关节效果&#xff0c;比如在射击游戏中射击绳子&#xff0c;或者在平衡游戏中使用绳子作为支撑。本文将详细介绍如何使用Unity3D的物理引擎实现绳子关节效果。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...