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

news2024/12/23 17:17:00

一、简介

本文介绍了如何使用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-混合

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

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

相关文章

ABAP关于PS模块CJ20N中项目物料的屏幕和字段增强CI_RSADD

网上搜关于CJ20N的屏幕增强,基本都是关于项目定义(CI_PROJ)、项目WBS(CI_PRPS)、项目网络活动工序(CI_AFVU)的字段与屏幕增强,几乎没有关于项目物料(CI_RSADD)的字段屏幕增强,我在这里做一个分享。 主要逻辑:实现badi增强,并自建一个函数组后创建屏幕,在badi里面调用…

Android setTheme设置透明主题无效

【问题现象】 1、首先&#xff0c;你在AndroidManifest.xml中声明一个activity&#xff0c;不给application或者activity设置android:theme, 例如这样&#xff1a; <applicationandroid:allowBackup"true"android:icon"mipmap/ic_launcher"android:lab…

windows下git和TortoiseGit(小乌龟)和putty安装配置对github进行操作

本次安装版本如下&#xff1a; 1&#xff0c;先下载安装tortoiseGit一路下载安装即可一直到在桌面上右键可以看到有git的选项出现为止&#xff0c;注意在第一步的时候选择使用putty还是ssh建立网络连接决定后面的步骤&#xff0c;本次以选择putty为例。 2&#xff0c;安装git&a…

Flutter:input输入框

输入框&#xff1a; // 是否显示关闭按钮 bool _showClear false; // 文字编辑控制器&#xff0c;监听搜索框的变化。 final TextEditingController _controller TextEditingController(); // 输入框发生变化事件 void _onChange(String value){if(value.length > 0){setS…

Ubuntu相关指令

1、查看 Ubuntu 系统的版本信息&#xff0c;在终端输入&#xff1a; lsb_release -a该命令会输出类似如下的信息&#xff1a; No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.4 LTS Release: 22.04 Codename: jammy 在 Re…

Unity教程(十八)战斗系统 攻击逻辑

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

【软件测试】设计测试用例的方法(正交法、判定表法、错误猜测法),测试文档的写法

文章目录 正交法正交表设计正交表 判定表法判定表 设计测试用例的步骤 错误猜测法测试文档 正交法 正交试验设计(Orthogonal experimentaldesign)是研究多因素多⽔平的⼀种设计⽅法&#xff0c;它是根据正交性&#xff0c;由试验因素的全部⽔平组合中挑选出部分有代表性的点进…

MySQL技巧之跨服务器数据查询:进阶篇-从A数据库复制到B数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;进阶篇-从A数据库复制到B数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的连接名: MY_ODBC_MYSQ…

网络安全练习之 ctfshow_web

文章目录 VIP题目限免&#xff08;即&#xff1a;信息泄露题&#xff09;源码泄露前台JS绕过协议头信息泄露robots后台泄露phps源码泄露源码压缩包泄露版本控制泄露源码(git)版本控制泄露源码2(svn)vim临时文件泄露cookie泄露域名txt记录泄露敏感信息公布内部技术文档泄露编辑器…

Elasticsearch retrievers 通常与 Elasticsearch 8.16.0 一起正式发布!

作者&#xff1a;来自 Elastic Panagiotis Bailis Elasticsearch 检索器经过了重大改进&#xff0c;现在可供所有人使用。了解其架构和用例。 在这篇博文中&#xff0c;我们将再次深入探讨检索器&#xff08;retrievers&#xff09;。我们已经在之前的博文中讨论过它们&#xf…

debug笔记:gpustat显示没有进程运行,但是GPU显存被占用

1 问题描述 使用gpustat之后&#xff0c;虽然显示除了gpu5之外别的都没有进程&#xff0c;但是GPU显存却被占用了 2 解决方法 原因是存在僵尸进程&#xff0c;已经运行完&#xff0c;但是内存并没有释放 fuser -v /dev/nvidia* 这个指令查看僵尸进程 然后kill掉即可&#…

【Chapter 3】Machine Learning Classification Case_Prediction of diabetes-XGBoost

文章目录 1、XGBoost Algorithm2、Comparison of algorithm implementation between Python code and Sentosa_DSML community edition(1) Data reading and statistical analysis(2)Data preprocessing(3)Model Training and Evaluation(4)Model visualization 3、summarize 1…

Android OpenGL ES详解——立方体贴图

目录 一、概念 二、如何使用 1、创建立方体贴图 2、生成纹理 3、设置纹理环绕和过滤方式 4、激活和绑定立方体贴图 三、应用举例——天空盒 1、概念 2、加载天空盒 3、显示天空盒 4、优化 四、应用举例——环境映射:反射 五、应用举例——环境映射:折射 六、应用…

聊天服务器(9)一对一聊天功能

目录 一对一聊天离线消息服务器异常处理 一对一聊天 先新添一个消息码 在业务层增加该业务 没有绑定事件处理器的话消息会派发不出去 聊天其实是服务器做一个中转 现在同时登录两个账号 收到了聊天信息 再回复一下 离线消息 声明中提供接口和方法 张三对离线的李…

T6识别好莱坞明星

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 导入基础的包 from tensorflow import keras from tensorflow.keras import layers,models import os, PIL, pathlib import matplotlib.pyplot as pl…

Odoo :一款免费开源的日化行业ERP管理系统

文 / 开源智造Odoo亚太金牌服务 概述 构建以 IPD 体系作为核心的产品创新研发管控体系&#xff0c;增进企业跨部门业务协同的效率&#xff0c;支撑研发管控、智慧供应链、智能制造以及全渠道营销等行业的场景化&#xff0c;构筑行业的研产供销财一体化管理平台。 行业的最新…

48.第二阶段x86游戏实战2-鼠标点击call

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

Vue 学习随笔系列十五 -- 数组遍历方法

数组遍历方法 文章目录 数组遍历方法1. for 循环2. forEach (不会修改数组本身)3. map (不修改数组本身)4. some(不修改数组本身&#xff09;5. every(不修改数组本身&#xff09;6. filter(不修改数组本身)7. find(不修改数组本身)8. findIndex拓展 9. reduce(累加)拓展 1. fo…

FreeRTOS的列表与列表项

目录 1.为什么要学列表&#xff1f; 2.什么是列表和列表项&#xff1f; 2.1 列表 2.2列表项 2.3&#xff0c;迷你列表项 3.列表与列表项的初始化 3.1 列表初始化 3.2列表项初始化 4.列表项的“增删查”&#xff08;插入、删除、遍历&#xff09; 4.1列表项的插入 4.1.1…

数字IC后端教程之Innovus hold violation几大典型问题

今天小编给大家分享下数字IC后端实现Physical Implementation过程中经常遇到的几个hold violation问题。每个问题都是小编自己在公司实际项目中遇到的。 数字后端实现静态时序分析STA Timing Signoff之min period violation Q1: 在Innouvs postCTS时序优化的log中我们经常会看…