[OpenGL]使用OpenGL实现透明效果

一、简介

本文介绍了如何使用OpenGL实现透明效果(transparent),并在最后给出了全部的代码。
在实现透明效果时,使用OpenGL中的混合(Blend)功能,根据纹理贴图的 alpha 分量将各像素(片段)对应的多个颜色值进行混合。

按照本文代码实现完成后,理论上可以得到如下结果:

渲染结果

二、混合 Blend

0. Blend 是什么

在 OpenGL 中,Blend 功能指的是在渲染片段时,可以根据一定的自定义规则(Blend Function)将
将当前渲染的颜色(源颜色,source color)与目标缓冲区,通常是帧缓冲区(目标颜色,destination color)中已有的颜色进行合并的技术。其计算公式如下:
C r e s u l t = C s o u r c e ∗ F s o u r c e + C d e s t i n a t i o n ∗ F d e s t i n a t i o n C_{result} = C_{source} * F_{source} + C_{destination} * F_{destination} Cresult=CsourceFsource+CdestinationFdestination
其中:
C r e s u l t C_{result} Cresult 是混合后的结果,跟 C d e s t i n a t i o n C_{destination} Cdestination存储在相同的位置;
C s o u r c e C_{source} Csource是源颜色向量,指源自纹理的颜色向量。
C d e s t i n a t i o n C_{destination} Cdestination是目标颜色向量。指当前储存在目标帧缓冲中的颜色向量;
F s o u r c e F_{source} Fsource是源因子值。指定了纹理alpha分量值对源颜色的影响;
F d e s t i n a t i o n F_{destination} Fdestination是目标因子值。指定了当前储存在目标帧缓冲中的alpha值对目标颜色的影响;

1. 启用 GL_BLEND

OpenGL 中默认没有启用 Blend,因此要想渲染有多个透明度级别的图像,我们需要在 OpenGL 中启用混合(Blending)。和OpenGL大多数的功能一样,我们可以启用 GL_BLEND 来启用混合:

glEnable(GL_BLEND);

2. 设置 Blend Function

我们可以使用

glBlendFunc(GLenum sfactor, GLenum dfactor)

函数设置 Blend Function,指定如何根据源自纹理颜色向量中的 alpha 分量混合 C s o u r c e C_{source} Csource C d e s t i n a t i o n C_{destination} Cdestination
本文使用

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

设置混合方程。即, C r e s u l t = C s o u r c e ∗ a l p h a s o u r c e + C d e s t i n a t i o n ∗ ( 1.0 − a l p h a s o u r c e ) C_{result} = C_{source} * alpha_{source} + C_{destination} * (1.0-alpha_{source}) Cresult=Csourcealphasource+Cdestination(1.0alphasource)

三、使用OpenGL实现透明效果

0. 环境需要

  • Linux,或者 windos下使用wsl2。
  • 安装GLFW和GLAD。请参考[OpenGL] wsl2上安装使用cmake+OpenGL教程。
  • 安装glm。glm是个可以只使用头文件的库,因此可以直接下载release的压缩文件,然后解压到include目录下。例如,假设下载的release版本的压缩文件为glm-1.0.1-light.zip。将glm-1.0.1-light.zip复制include目录下,然后执行以下命令即可解压glm源代码:
    unzip glm-1.0.1-light.zip
    
  • 需要使用Assimp库加载obj模型,在 ubuntu 下可以使用以下命令安装 Assimp
    sudo apt-get update
    sudo apt-get install libassimp-dev
    
  • 需要下载 stb_image.h 作为加载.png图像的库。将 stb_image.h 下载后放入include/目录下。

1. 项目目录

项目目录

其中:

  • Mesh.hpp 包含了自定义的 Vertex, Texture, 和 Mesh 类。
  • Model.hpp 包含了自定义的Model类,用于加载obj模型。一个Model可以包含多个Mesh。在加载obj模型时使用Assimp库加载。还包含一个用于加载纹理的函数TextureFromFile()
  • Shader.hpp 用于创建 shader 程序。
  • Blinn-Phong.vertBlinn-Phong.frag是使用Blinn-Phong光照模型 渲染场景 的 顶点着色器 和 片段着色器 代码。
  • transparent.verttransparent.frag是用于 渲染透明物体的 顶点着色器 和 片段着色器 代码。该 shader 只需要将指定的纹理加载到模型上即可,无需根据光照模型对颜色进行处理。

下面介绍各部分主要的代码:

2. CMakeLists.txt代码

cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)

project(OpenGL_Blending)

include_directories(include)

find_package(glfw3 REQUIRED)
find_package(assimp REQUIRED)
file(GLOB project_file main.cpp glad.c)
add_executable(${PROJECT_NAME} ${project_file})
target_link_libraries(${PROJECT_NAME} glfw assimp)

3. Model.hpp和Mesh.hpp 代码

Model.hpp 和 Mesh.hpp 代码与 LearnOpenGL-模型加载-模型 中的代码类似,使用Assimp库,基于递归的方式加载模型和纹理。读者可以参考LearnOpenGL-模型加载-模型。

对 Model.hpp 中加载纹理的函数TextureFromFile()进行了修改,修改后的函数为:

unsigned int TextureFromFile(const char *path, const string &directory, bool gamma = false,
                             GLuint wrap_type = GL_REPEAT);
...
unsigned int TextureFromFile(const char *path, const string &directory, bool gamma, GLuint wrap_type)
{
    string filename = string(path);
    filename = directory + '/' + filename;

    unsigned int textureID;
    glGenTextures(1, &textureID);

    int width, height, nrComponents;
    unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
    if (data)
    {
        GLenum format;
        if (nrComponents == 1)
            format = GL_RED;
        else if (nrComponents == 3)
            format = GL_RGB;
        else if (nrComponents == 4)
            format = GL_RGBA;

        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_type);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_type);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        stbi_image_free(data);
    }
    else
    {
        std::cout << "Texture failed to load at path: " << path << std::endl;
        stbi_image_free(data);
    }

    return textureID;
}

在函数TextureFromFile(...) 中增加了指定纹理wrap方式的参数。这是因为对于普通的纹理我们可以使用GL_REPEAT方式,将纹理坐标为 [0,1] 之外的纹理使用重复的方式填充。但是对于具有透明效果的纹理如果依旧使用 重复 效果填充,可能会出现透明处理错误的结果,因此对于具有 透明效果的纹理,我们选择使用GL_CLAMP_TO_EDGE方式,这样可以将纹理坐标在 [0,1] 之外的采样强制 clamp 到 0 或者 1,保证了透明效果的正确性。
本文最后给出了全部的代码,可以下载运行使用。

4. Blinn-Phong shader 代码

渲染场景的 Blinn-Phong shader 使用Blinn-Phong模型渲染场景。
Blinn-Phong shader的顶点着色器和片段着色器代码:
Blinn-Phong.vert:

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNor;
layout(location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 vertexPos;
out vec3 vertexNor;
out vec2 textureCoord;

void main() {
  textureCoord = aTexCoord;
  // 裁剪空间坐标系 (clip space) 中 点的位置
  gl_Position = projection * view * model * vec4(aPos, 1.0f);
  // 世界坐标系 (world space) 中 点的位置
  vertexPos = (model * vec4(aPos, 1.0f)).xyz;
  // 世界坐标系 (world space) 中 点的法向
  vertexNor = mat3(transpose(inverse(model))) * aNor;
}

Blinn-Phong.frag:

#version 330 core
out vec4 FragColor;

in vec3 vertexPos;
in vec3 vertexNor;
in vec2 textureCoord;

uniform vec3 cameraPos;
uniform vec3 lightPos;
uniform vec3 k;

uniform sampler2D texture0;

void main() {

  vec3 lightColor = vec3(1.0f, 1.0f, 1.0f);

  // Ambient
  // Ia = ka * La
  float ambientStrenth = k[0];
  vec3 ambient = ambientStrenth * lightColor;

  // Diffuse
  // Id = kd * max(0, normal dot light) * Ld
  float diffuseStrenth = k[1];
  vec3 normalDir = normalize(vertexNor);
  vec3 lightDir = normalize(lightPos - vertexPos);
  vec3 diffuse =
      diffuseStrenth * max(dot(normalDir, lightDir), 0.0) * lightColor;

  // Specular (Phong)
  // Is = ks * (view dot reflect)^s * Ls

  // float specularStrenth = k[2];
  // vec3 viewDir = normalize(cameraPos - vertexPos);
  // vec3 reflectDir = reflect(-lightDir, normalDir);
  // vec3 specular = specularStrenth *
  //                 pow(max(dot(viewDir, reflectDir), 0.0f), 2) * lightColor;

  // Specular (Blinn-Phong)
  // Is = ks * (normal dot halfway)^s Ls
  float specularStrenth = k[2];
  vec3 viewDir = normalize(cameraPos - vertexPos);
  vec3 halfwayDir = normalize(lightDir + viewDir);
  vec3 specular = specularStrenth *
                  pow(max(dot(normalDir, halfwayDir), 0.0f), 2) * lightColor;

  // Obejct color
  vec3 objectColor = vec3(0.8, 0.8, 0.8);
  if (textureCoord.x >= 0 && textureCoord.y >= 0) {
    objectColor = texture(texture0, textureCoord).xyz;
  }

  FragColor = vec4((ambient + diffuse + specular) * objectColor, 1.0f);
}

5. transparent shader 代码

transparent shader 用于渲染具有透明纹理的模型。在该 shader 中只需要设置各顶点的 position,以及加载纹理即可。
顶点着色器的代码如下:
transparent.vert

#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNor;
layout(location = 2) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec2 textureCoord;

void main() {
  // 裁剪空间坐标系 (clip space) 中 点的位置
  gl_Position = projection * view * model * vec4(aPos, 1.0f);
  // 纹理坐标
  textureCoord = aTexCoord;
}

片段着色器如下
transparent.frag

#version 330 core
out vec4 FragColor;

in vec2 textureCoord;

uniform sampler2D texture0;

void main() {
  FragColor = texture(texture0, textureCoord);
}

6. main.cpp 代码

6.1). 代码整体流程

  1. 初始化glfw,glad,窗口
  2. 编译 shader 程序
  3. 加载obj模型、纹理图片、透明物体模型
  4. 设置光源和相机位置,Blinn-Phong 模型参数
  5. 开始渲染
    5.1 使用 blinnPhongShader 渲染不透明的物体
    5.2 使用 transShader 渲染透明的物体
  6. 释放资源

6.2). main.cpp代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "Skybox.hpp"
#include "Shader.hpp"
#include "Mesh.hpp"
#include "Model.hpp"

#include "glm/ext.hpp"
#include "glm/mat4x4.hpp"

#include <random>
#include <iostream>
// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);

// 指定窗口默认width和height像素大小
unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

/************************************/

int main()
{
    /****** 1.初始化glfw, glad, 窗口 *******/
    // glfw 初始化 + 配置 glfw 参数
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 在创建窗口之前
    glfwWindowHint(GLFW_SAMPLES, 4); // 设置多重采样级别为4
    // glfw 生成窗口
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        // 检查是否成功生成窗口,如果没有成功打印出错信息并且退出
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    // 设置窗口window的上下文
    glfwMakeContextCurrent(window);
    // 配置window变化时的回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 使用 glad 加载 OpenGL 中的各种函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    // 启用 深度测试
    glEnable(GL_DEPTH_TEST);
    // 启用 多重采样抗锯齿
    glEnable(GL_MULTISAMPLE);

    // 启用 混合
    glEnable(GL_BLEND);
    // 源: 片段着色器中处理的片段; 目标: 颜色缓冲
    // 使用 源颜色alpha 作为 源因子, (1-源颜色alpha) 作为 目标因子
    // 那么颜色缓冲中最终的颜色为 C_color_buffer = S_alpha * C_s + (1.0 - S_alpha) * C_color_buffer
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 使用线框模式,绘制时只绘制 三角形 的轮廓
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // 使用填充模式,绘制时对 三角形 内部进行填充
    /************************************/

    /****** 2.编译 shader 程序 ******/

    // 使用Blinn-Phong模型渲染场景的 shader
    Shader blinnPhongShader("../resources/Blinn-Phong.vert", "../resources/Blinn-Phong.frag");
    // 渲染 透明物体的 shader
    Shader transparentShader("../resources/transparent.vert", "../resources/transparent.frag");

    /************************************/

    /****** 3.加载obj模型、纹理图片、透明物体模型 ******/

    // scene mesh
    Model ourModel("../resources/models/spot/spot.obj");
    // Model ourModel("../resources/models/nanosuit/nanosuit.obj");

    // 透明的窗户 0
    vector<Vertex> transWinVertex0 = {
        {{-0.80, 0.20, -1.0}, {0.0, 0.0, 1.0}, {0.0, 1.0}},  // position, normal, texture_coordinate
        {{-0.80, -0.80, -1.0}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, //
        {{0.20, -0.80, -1.0}, {0.0, 0.0, 1.0}, {1.0, 0.0}},  //
        {{0.20, 0.20, -1.0}, {0.0, 0.0, 1.0}, {1.0, 1.0}}};
    vector<unsigned int> transWinIndex0 = {0, 1, 2, 2, 3, 0};
    vector<Texture> transWinTexture0 = {{TextureFromFile("window.png", "../resources/textures", true, GL_CLAMP_TO_EDGE),
                                         "texture_diffuse",
                                         "../resources/textures/window.png"}}; // textire_id, type, file_path
    Mesh transWinMesh0(transWinVertex0, transWinIndex0, transWinTexture0);
    Model transWinModel0(transWinMesh0);

    // 透明的窗户 1
    vector<Vertex> transWinVertex1 = {
        {{-0.10, 0.90, -1.2}, {0.0, 0.0, 1.0}, {0.0, 1.0}},  //  position, normal, texture_coordinate
        {{-0.10, -0.10, -1.2}, {0.0, 0.0, 1.0}, {0.0, 0.0}}, //
        {{0.90, -0.10, -1.2}, {0.0, 0.0, 1.0}, {1.0, 0.0}},  //
        {{0.90, 0.90, -1.2}, {0.0, 0.0, 1.0}, {1.0, 1.0}}};
    vector<unsigned int> transWinIndex1 = {0, 1, 2, 2, 3, 0};
    vector<Texture> transWinTexture1 = {{TextureFromFile("window.png", "../resources/textures", true, GL_CLAMP_TO_EDGE),
                                         "texture_diffuse",
                                         "../resources/textures/window.png"}}; // textire_id, type, file_path
    Mesh transWinMesh1(transWinVertex1, transWinIndex1, transWinTexture1);
    Model transWinModel1(transWinMesh1);

    /************************************/

    /****** 4.设置光源和相机位置,Blinn-Phong 模型参数 ******/
    // I = Ia + Id + Is
    // Ia = ka * La
    // Id = kd * (normal dot light) * Ld
    // Is = ks * (reflect dot view)^s * Ls
    // 模型参数 ka, kd, ks
    float k[] = {0.1f, 0.7f, 0.2f}; // ka, kd, ks
    // 光源位置
    glm::vec3 light_pos = glm::vec3(-2.0f, 2.0f, 0.0f);
    // 相机位置
    glm::vec3 camera_pos = glm::vec3(0.0f, 0.0f, 1.5f);
    /************************************/

    /****** 5.开始渲染 ******/

    float rotate = 180.0f;
    while (!glfwWindowShouldClose(window))
    {
        // rotate += 0.05f;
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        // 清除颜色缓冲区 并且 清除深度缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 5.1 使用 blinnPhongShader 渲染不透明的物体
        blinnPhongShader.use();

        // 设置 camera_MVP 矩阵, 假设以 camera 为视角,渲染 camera 视角下的场景深度图
        // camera model 矩阵
        glm::mat4 camera_model = glm::mat4(1.0f);
        camera_model = glm::translate(camera_model, glm::vec3(0.0f, 0.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(rotate), glm::vec3(0.0f, 1.0f, 0.0f));
        camera_model = glm::rotate(camera_model, glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
        camera_model = glm::scale(camera_model, glm::vec3(0.5f, 0.5f, 0.5f));

        // camera view 矩阵
        glm::mat4 camera_view = glm::mat4(1.0f);
        camera_view = glm::lookAt(camera_pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        // camera projection 矩阵
        glm::mat4 camera_projection = glm::mat4(1.0f);
        camera_projection = glm::perspective(glm::radians(60.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        blinnPhongShader.setMat4("model", camera_model);
        blinnPhongShader.setMat4("view", camera_view);
        blinnPhongShader.setMat4("projection", camera_projection);
        blinnPhongShader.setVec3("k", k[0], k[1], k[2]);
        blinnPhongShader.setVec3("cameraPos", camera_pos);
        blinnPhongShader.setVec3("lightPos", light_pos);

        ourModel.Draw(blinnPhongShader);

        // 5.2 使用 transShader 渲染透明的物体
        transparentShader.use();
        transparentShader.setMat4("model", camera_model);
        transparentShader.setMat4("view", camera_view);
        transparentShader.setMat4("projection", camera_projection);

        transWinModel0.Draw(transparentShader);
        transWinModel1.Draw(transparentShader);

        glfwSwapBuffers(window); // 在gfw中启用双缓冲,确保绘制的平滑和无缝切换
        glfwPollEvents(); // 用于处理所有挂起的事件,例如键盘输入、鼠标移动、窗口大小变化等事件
    }

    /************************************/
    /****** 6.释放资源 ******/
    // glfw 释放 glfw使用的所有资源
    glfwTerminate();
    /************************************/
    return 0;
}

// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{
    // 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, true);
    }
}

// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    SCR_WIDTH = width;
    SCR_HEIGHT = height;
    glViewport(0, 0, width, height);
}

7. 编译运行及结果

编译运行:

cd ./build
cmake ..
make
./OpenGL_Blending

渲染结果:
渲染结果

四、透明效果中存在的问题

如果调整相机的位置,会发现当渲染多个透明物体,并且透明物体相互重叠时会出现渲染结果错误,如下图所示:
渲染错误结果

这是因为深度测试和混合一起使用的话会产生一些麻烦。当写入深度缓冲时,深度缓冲不会检查片段是否是透明的,所以(左下的窗户)透明的部分会和其它值一样写入到深度缓冲中。结果就是前面窗户的整个四边形不论透明度都会进行深度测试。即使透明的部分应该显示后面的窗户(右上的窗户),深度测试仍然丢弃了它们(右上的窗户)

一个解决该问题的思路要想保证窗户中能够显示它们背后的窗户,我们需要首先绘制背后的这部分窗户。这也就是说在绘制的时候,我们必须先手动将窗户按照最远到最近来排序,再按照顺序渲染

即,当绘制一个有不透明和透明物体的场景的时候,大体的原则如下:

  • 先绘制所有不透明的物体。
  • 对所有透明的物体排序。
  • 按顺序绘制所有透明的物体。

但是这也不能完美的解决所有的问题,毕竟如何确定奇怪形状的物体距离相机的远近也是一个很复杂的问题。

总结来讲:在场景中排序物体是一个很困难的技术,很大程度上由你场景的类型所决定,更别说它额外需要消耗的处理能力了。完整渲染一个包含不透明和透明物体的场景并不是那么容易。更高级的技术还有次序无关透明度(Order Independent Transparency, OIT)。
接下来有机会的话也会介绍与OIT相关的方法介绍以及代码分享。

五、全部代码及模型文件

全部代码以及模型文件可以在使用OpenGL实现透明效果中下载。

六、参考

[1.] LearnOpenGL-CN-高级OpenGL-混合

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【实用技能】ASP.NET Core:在同一个 Razor 视图中使用文档编辑器和查看器

Essential Studio for ASP.NET Core UI控件库是构建应用程序所需的卓越套件&#xff0c;提供支持的 ASP.NET Core 工具包拥有超过 85 个组件&#xff0c;包含构建业务线应用程序所需的一切&#xff0c;包括数据网格、图表、甘特图、图表、电子表格、时间表、数据透视网格等流行…

Android从Drawable资源Id直接生成Bitmap,Kotlin

Android从Drawable资源Id直接生成Bitmap,Kotlin val t1 System.currentTimeMillis()val bmp getBmpFromDrawId(this, R.mipmap.ic_launcher_round)Log.d("fly", "1 ${bmp?.byteCount} h${bmp?.height} w${bmp?.width} cost time${System.currentTimeMillis…

【JavaScript】LeetCode:96-100

文章目录 96 单词拆分97 最长递增子序列98 乘积最大子数组99 分割等和子集100 最长有效括号 96 单词拆分 动态规划完全背包&#xff1a;背包-字符串s&#xff0c;物品-wordDict中的单词&#xff0c;可使用多次。问题转换&#xff1a;s能否被wordDict中的单词组成。dp[i]&#x…

【扩散——BFS】

题目 代码 #include <bits/stdc.h> using namespace std; const int t 2020, off 2020; #define x first #define y second typedef pair<int, int> PII; int dx[] {0, 0, 1, -1}, dy[] {-1, 1, 0, 0}; int dist[6080][6080]; // 0映射到2020&#xff0c;2020…

柯桥生活英语口语学习“面坨了”英语怎么表达?

“面坨了”英语怎么表达&#xff1f; 要想搞清楚这个表达&#xff0c;首先&#xff0c;我们要搞明白“坨”是啥意思&#xff1f; 所谓“坨”就是指&#xff0c;面条在汤里泡太久&#xff0c;从而变涨&#xff0c;黏糊凝固在一起的状态。 有一个词汇&#xff0c;很适合用来表达这…

IOT物联网低代码可视化大屏解决方案汇总

目录 参考来源云服务商阿里云物联网平台产品主页产品文档 开源项目DGIOT | 轻量级工业物联网开源平台项目特点项目地址开源许可 IoTGateway | 基于.NET6的跨平台工业物联网网关项目特点项目地址开源许可 IoTSharp | 基于.Net Core开源的物联网基础平台项目特点项目地址开源许可…

CSP-X2024山东小学组T2:消灭怪兽

题目链接 题目名称 题目描述 怪兽入侵了地球&#xff01; 为了抵抗入侵&#xff0c;人类设计出了按顺序排列好的 n n n 件武器&#xff0c;其中第 i i i 件武器的攻击力为 a i a_i ai​&#xff0c;可以造成 a i a_i ai​ 的伤害。 武器已经排列好了&#xff0c;因此不…

信息收集—JS框架识别泄露提取API接口泄露FUZZ爬虫插件项目

前言 免杀结束了&#xff0c;我们开个新的篇章——信息收集。为什么我一开始先写信息收集的文章呢&#xff0c;是因为现在我才发现我的信息收集能力其实有点弱的&#xff0c;所以呢开始知不足&#xff0c;而后进。 什么是JS JS就是JavaScript的简称&#xff0c;它和Java是没…

性能调优专题(9)之从JDK源码级别解析JVM类加载机制

一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 package com.tuling.jvm;public class Math {public static final int initData 666;public static User user new User();public int compute() {…

Gin 框架入门(GO)-1

解决安装包失败问题&#xff08;*&#xff09; go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct 1 介绍 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快&#xff0c;Gin 最擅长的就是 Api 接口的高并发。 2 Gin 环境搭建…

Python如何从HTML提取img标签下的src属性

目录 前提准备步骤1. 解析HTML内容2. 查找所有的img标签3. 提取src属性 完整代码 前提准备 在处理网页数据时&#xff0c;我们经常需要从HTML中提取特定的信息&#xff0c;比如图片的URL。 这通常通过获取img标签的src属性来实现。 在开始之前&#xff0c;你需要确保已经安装…

web——upload-labs——第五关——大小写绕过绕过

先上传一个 先尝试直接上传一个普通的一句话木马 不行 可以看到&#xff0c;.htaccess文件也被过滤了&#xff0c;我们来查看一下源码 第五关的源码没有把字符强制转换为小写的语句&#xff1a; $file_ext strtolower($file_ext); //转换为小写 直接通过Burpsuite抓包修改文…

C#/WinForm拖拽文件上传

一、首先创建一个上传文件的类&#xff0c;继承Control类&#xff0c;如下&#xff1a; public class UploadControl : Control{private Image _image;public UploadControl(){this.SetStyle(ControlStyles.UserPaint | //控件自行绘制&#xff0c;而不使用操作系统的绘制Cont…

oracle查询字段类型长度等字段信息

1.查询oracle数据库的字符集 SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER NLS_CHARACTERSET; 2.查询字段长度类型 SELECT * FROM user_tab_columns WHERE table_name user AND COLUMN_NAME SNAME 请确保将user替换为您想要查询的表名。sname为字段名 这里的字…

大模型基础BERT——Transformers的双向编码器表示

大模型基础BERT——Transformers的双向编码器表示 整体概况 BERT&#xff1a;用于语言理解的深度双向Transform的预训练 论文题目&#xff1a;BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding Bidirectional Encoder Representations from…

Ceph层次架构分析

Ceph的层次结构可以从逻辑上自下向上分为以下几个层次&#xff1a; 一、基础存储系统RADOS层 功能&#xff1a;RADOS&#xff08;Reliable Autonomic Distributed Object Store&#xff09;是Ceph的底层存储系统&#xff0c;提供了分布式存储的核心功能。它是一个完整的对象存…

实验6记录网络与故障排除

实验6记录网络与故障排除 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握如何利用文档记录网络设备相关信息并完成网络拓扑结构的绘制。能够使用各种技术和工具来找出连通性问题&#xff0c;使用文档来指导故障排除工作&#xff0c;确定具体的网络问题&#xff0c;实施…

【前端】技术演进发展简史

一、前端 1、概述 1990 年&#xff0c;第一个web浏览器诞生&#xff0c;Tim 以超文本语言 HTML 为基础在 NeXT 电脑上发明了最原始的 Web 浏览器。 1991 年&#xff0c;WWW诞生&#xff0c;这标志着前端技术的开始。 前端&#xff08;Front-end&#xff09;和后端&#xff08;…

【笔记】关于git和GitHub和git bash

如何推送更新的代码到github仓库 如何在此项目已经提交在别的远程仓库的基础上更改远程仓库地址&#xff08;也就是换一个远程仓库提交&#xff09; 如何删除github中的一个文件 第二版 删除github上的一个仓库或者仓库里面的某个文件_github仓库删除一个文件好麻烦-CSDN博客 …

Chromium 中sqlite数据库操作演示c++

本文主要演示sqlite数据库 增删改查创建数据库以及数据库表的基本操作&#xff0c;仅供学习参考。 一、sqlite数据库操作类封装&#xff1a; sql\database.h sql\database.cc // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-sty…