LearnOpenGL-高级OpenGL-10.实例化

news2024/12/23 14:10:14

本人初学者,文中定有代码、术语等错误,欢迎指正

文章目录

  • 实例化
    • 例子1.1:100个2D四边形使用Uniform
  • 实例化数组
    • 例子1.2:100个2D四边形使用实例化数组
    • 例子2.1:行星带不使用实例化数组
    • 例子2.2:行星带使用实例化数组

实例化

  • 引出

    • 假设有一个绘制了很多模型的场景,而大部分的模型包含的是同一组顶点数据,只不过进行的是不同的世界空间变换,比如:草

    • 渲染上千上万个草,渲染函数调用会极大地影响性能

      for(unsigned int i = 0; i < amount_of_models_to_draw; i++)
      {
          DoSomePreparations(); // 绑定VAO,绑定纹理,设置uniform等
          glDrawArrays(GL_TRIANGLES, 0, amount_of_vertices);
      }
      
    • 性能消耗的地方

      OpenGL在绘制顶点数据之前需要做很多准备工作(比如告诉GPU该从哪个缓冲读取数据,从哪寻找顶点属性,而且这些都是在相对缓慢的CPU到GPU总线(CPU to GPU Bus)上进行的)。

      即便渲染顶点非常快,命令GPU去渲染却未必。

  • 什么是实例化

    • 解决上述的性能消耗的地方

      我们能够将数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制个物体。这就是实例化

    • 进一步解释

      实例化这项技术能够让我们使用一个渲染调用来绘制多个物体,来节省每次绘制物体时CPU -> GPU的通信,它只需要一次即可。

    • 使用什么函数

      将glDrawArrays和glDrawElements的渲染调用分别改为glDrawArraysInstanced和glDrawElementsInstanced

    • GLSL有内建变量:gl_InstanceID

      渲染同一个物体一千次对我们并没有什么用处,每个物体都是完全相同的,而且还在同一个位置。

      利用gl_InstanceID可以标识每个实例,可以用此gl_InstanceID对应专属的uniform变换矩阵,从而变换当前渲染的物体(改变位置、大小等)。

例子1.1:100个2D四边形使用Uniform

  • 思路

    • glsl的顶点着色器

      定义uniform数组,每个渲染的实例quad:根据gl_InstanceID当做uniform数组的下标得到当前渲染的实例的变换位置

    • cpp

      • 定义quad的顶点输入数据
      • 定义顶点缓冲数组、绑定顶点缓冲对象、指定好顶点属性布局
      • 生成偏移位置数组,并用uniform上传给glsl
      • 绘制时使用glDrawArraysInstanced,将同一个quad数据渲染100次
  • 代码

    #version 330 core
    layout (location = 0) in vec2 aPos;
    layout (location = 1) in vec3 aColor;
    
    out vec3 fColor;
    
    uniform vec2 offsets[100];
    
    void main()
    {
    	// gl_InstanceID当前绘制实例的ID,作为offsets的下标
    	vec2 offset = offsets[gl_InstanceID];
    	gl_Position = vec4(aPos + offset, 0.0, 1.0);
    	fColor = aColor;
    }
    
    #version 330 core
    out vec4 FragColor;
    
    in vec3 fColor;
    
    void main(){ 
    	FragColor = vec4(fColor, 1.0);
    }
    

    cpp

    float quadVertices[] = {
        // 位置,二维          // 颜色
        -0.05f,  0.05f,  1.0f, 0.0f, 0.0f,
        0.05f, -0.05f,  0.0f, 1.0f, 0.0f,
        -0.05f, -0.05f,  0.0f, 0.0f, 1.0f,
    
        -0.05f,  0.05f,  1.0f, 0.0f, 0.0f,
        0.05f, -0.05f,  0.0f, 1.0f, 0.0f,
        0.05f,  0.05f,  0.0f, 1.0f, 1.0f
    };
    // quad VAO
    unsigned int quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float)));
    
    // 生成偏移位置数组
    glm::vec2 translations[100];
    int index = 0;
    float offset = 0.1f;
    for (int y = -10; y < 10; y += 2)
    {
        for (int x = -10; x < 10; x += 2)
        {
            glm::vec2 translation;
            translation.x = (float)x / 10.0f + offset;
            translation.y = (float)y / 10.0f + offset;
            translations[index++] = translation;
        }
    }
    // 注意这里:上传位置给glsl,只能一个一个传///
    shader.use();
    for (unsigned int i = 0; i < 100; i++)
    {
        shader.setVec2(("offsets[" + to_string(i) + "]").c_str(), translations[i]);
    }
    // render loop
    while (!glfwWindowShouldClose(window))
    {
        // quad
        glBindVertexArray(quadVAO);
        // 将数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制多个物体
        // 注意:第三个参数,设置需要绘制的实例数量//
        glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);
    
    • 重点

      glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);,将quad实例渲染100次

  • 效果

    1.1效果实例化quad

实例化数组

  • 引出

    由上的实例化例子,使用uniform传递变换数据,但是我们最终会超过最大能够发送至着色器的uniform数据大小上限。

    从而需要使用实例化数组,即不使用uniform。

  • 什么是实例化数组

    • 实例化数组它被定义为一个顶点属性(可以用缓冲存储),仅在顶点着色器渲染一个新的实例(可设置为两个或三个等)时才会更新。

      既然是顶点属性,则有对应的顶点缓冲对象,附加到顶点数组时需指定顶点属性布局

    • 重点

      • 原本的顶点属性,比如:位置、法线、颜色,都是在顶点着色器的每次运行都会让GLSL获取新一组适用于当前顶点的属性。

        • 详细说明

          注意:是当前顶点,比如:一个quad有位置和颜色信息,它有6个顶点(两个三角形组成),需运行6次顶点着色器,每一次运行顶点着色器渲染当前顶点,都需更新当前顶点的属性。

          一个实例6个顶点运行6次顶点着色器、次都得更新顶点的属性。

      • 当我们将顶点属性定义为一个实例化数组时,顶点着色器就只需要对每个实例更新顶点属性的内容。

        • 详细说明

          注意:是当前实例,比如:一个quad有一个变换信息,即使它有6个顶点,需运行6次顶点着色器,但当每一次运行顶点着色器渲染当前顶点的变换信息是同一个,只需渲染完6个顶点即为一个实例时,才需要更新顶点属性的内容。

          一个实例6个顶点运行6次顶点着色器、6次后才更新顶点的属性。

例子1.2:100个2D四边形使用实例化数组

  • 思路

    • glsl的顶点着色器

      获取每个实例的实例化数组的顶点属性

    • cpp

      • 实例化数组(位置变换数据)当做顶点属性
      • 定义顶点数组,指定quad的顶点属性布局
      • 指定实例化数组(位置变换数据)的顶点属性布局,并指定对应顶点属性(顶点位置、颜色什么的)渲染几个实例时更新一次。
      • 绘制时使用glDrawArraysInstanced,将同一个quad数据渲染100次
  • 代码

    glsl

    #version 330 core
    layout (location = 0) in vec2 aPos;
    layout (location = 1) in vec3 aColor;
    layout (location = 2) in vec2 aOffset;// 实例化数组(位置变换数据)它被定义为一个顶点属性
    
    out vec3 fColor;
    
    void main()
    {
    	// quad的大小逐渐变大,从0.01到1
    	vec2 pos = aPos * (gl_InstanceID / 100.0);
    	gl_Position = vec4(pos + aOffset, 0.0, 1.0);
    	fColor = aColor;
    }
    

    cpp

    // uniform数组,偏移位置
    glm::vec2 translations[100];
    int index = 0;
    float offset = 0.1f;
    for (int y = -10; y < 10; y += 2) {
        for (int x = -10; x < 10; x += 2) {
            glm::vec2 translation;
            // 控制在 -1,1之间
            translation.x = (float)x / 10.0f + offset;
            translation.y = (float)y / 10.0f + offset;
            translations[index++] = translation;
        }
    }
    // 用顶点缓冲对象存储
    unsigned int instanceVBO;
    glGenBuffers(1, &instanceVBO);
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &translations[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    float quadVertices[] = {
        // 位置          // 颜色
        -0.05f,  0.05f,  1.0f, 0.0f, 0.0f,
        0.05f, -0.05f,  0.0f, 1.0f, 0.0f,
        -0.05f, -0.05f,  0.0f, 0.0f, 1.0f,
    
        -0.05f,  0.05f,  1.0f, 0.0f, 0.0f,
        0.05f, -0.05f,  0.0f, 1.0f, 0.0f,
        0.05f,  0.05f,  0.0f, 1.0f, 1.0f
    };
    
    // quad VAO
    unsigned int quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float)));
    
    // 重点:设置layout=2的属性,aOffset,实例化数组//
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    // 设置顶点layout=2布局的属性是,每1个实例更新一次属性//
    glVertexAttribDivisor(2, 1); 
    // 解绑
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    // 记得绑定shader,即使没有数据上传给uniform
    shader.use();
    
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // quad
        glBindVertexArray(quadVAO);
        // 将数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制多个物体
        // 注意第三个参数,设置需要绘制的实例数量
        glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);
        glBindVertexArray(0);
    

    可以看到没有使用uniform,而是用顶点属性

    • glVertexAttribDivisor(2,1)

      • 第一个参数:对应glsl的layout=2,指向的aOffset

      • 第二个参数:

        0:在顶点着色器的每次迭代时更新顶点属性,默认

        1:渲染一个新实例的时候更新顶点属性

        2:每2个实例更新一次属性

  • 效果

    请添加图片描述

例子2.1:行星带不使用实例化数组

  • 说明

    行星周围的石头都有自己的变换矩阵model,每渲染一个石头时,上传自己的变换矩阵model给glsl的uniform,所以实际上是使用uniform来变换每个石头的位置,而没有用glDrawArraysInstanced或者glDrawElementsInstanced函数来用上述的实例化。

    很大的原因是因为石头是obj模型,之前声明的mesh与model类封装了渲染函数,不好变动。

  • 代码

    glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 2) in vec2 aTexCoords;
    
    out vec2 TexCoords;  
    
    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;
    
    void main()
    {
        TexCoords = aTexCoords;
        gl_Position = projection * view * model * vec4(aPos, 1.0);
    }
    
    #version 330 core
    out vec4 FragColor;
    
    in vec2 TexCoords;
    
    uniform sampler2D texture_diffuse1;
    
    void main(){
    	FragColor = texture(texture_diffuse1, TexCoords);
    }
    

    cpp

    // 加载模型
    Model rock(FileSystem::getPath("assest/model/rock/rock.obj"));
    Model planet(FileSystem::getPath("assest/model/planet/planet.obj"));
    
    // model数组,石头的偏移位置
    unsigned int amount = 1000;
    glm::mat4* modelMatrices;
    modelMatrices = new glm::mat4[amount];
    srand(glfwGetTime());// 初始化随机种子
    float radius = 50.0f;
    float offset = 2.5f;
    for (unsigned int i = 0; i < amount; i++) {
        glm::mat4 model = glm::mat4(1.0f);
        // 角度,0-360度
        float angle = (float)i / (float)amount * 360.0f;
        // 1. 位移:分布在半径为 radius 的圆形上,偏移范围是[-0ffset, offset]
        // rand()范围为0~RAND_MAX, 700 % 500 = 200 / 100 = 2 - 2.5 = -0.5
        float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float x = sin(angle) * radius + displacement;
    
        displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float y = displacement * 0.4f;
    
        displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float z = cos(angle) * radius + displacement;
        model = glm::translate(model, glm::vec3(x, y, z));
    
        // 2.缩放:在0.05和0.25f之间缩放
        float scale = (rand() % 20) / 100.0f + 0.05;
        model = glm::scale(model, glm::vec3(scale));
    
        // 3.旋转:绕着一个(半)随机选择的旋转轴向量进行随机的旋转
        float rotAngle = (rand() % 360);
        model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
    
        // 4. 添加到矩阵的数组中
        modelMatrices[i] = model;
    }
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    { 
        // 摄像机
        glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 1000.0f);
        glm::mat4 view = camera.GetViewMatrix();
        shader.use();
        shader.setMat4("projection", projection);
        shader.setMat4("view", view);
    
        // 绘画行星
        glm::mat4 model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
        model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
        shader.setMat4("model", model);
        planet.Draw(shader);
    
        // 绘画石头
        for (unsigned int i = 0; i < amount; i++) {
            // 设置偏移
            shader.setMat4("model", modelMatrices[i]);
            rock.Draw(shader);
        }
    
  • 效果

    请添加图片描述

  • 缺点

    由代码可见,渲染石头是用for循环+上传uniform,当要渲染的石头数量增加,即for循环的次数增加,调用uniform的次数会变多,而调用uniform的次数会影响性能,

    当amount=10000时,可以感到明显的卡顿(根据自己的机器配置,amount太大会感到卡顿)

例子2.2:行星带使用实例化数组

  • 说明

    不使用原本model类封装的draw函数,而是获取mesh的顶点缓冲数组再调用实例化函数glDrawElementsInstanced。

    这样我们就可以将每个实例的变换矩阵(实例化数组)当做顶点属性

  • 代码

    glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 2) in vec2 aTexCoords;
    layout (location = 3) in mat4 instanceMatrix;// 实例化数组(位置变换数据)它被定义为一个顶点属性
    
    out vec2 TexCoords;  
    
    uniform mat4 view;
    uniform mat4 projection;
    
    void main()
    {
        TexCoords = aTexCoords;
        gl_Position = projection * view  * instanceMatrix*  vec4(aPos, 1.0);
    }
    
    
    #version 330 core
    out vec4 FragColor;
    
    in vec2 TexCoords;
    
    uniform sampler2D texture_diffuse1;
    
    void main(){
    	FragColor = texture(texture_diffuse1, TexCoords);
    }
    

    cpp

    Shader planetshader("assest/shader/4高级OpenGL/6.10.3.渲染大量物体-行星带-无实例化.vs", "assest/shader/4高级OpenGL/6.10.3.渲染大量物体-行星带-无实例化.fs");
    Shader rockshader("assest/shader/4高级OpenGL/6.10.4.渲染大量物体-行星带-实例化数组.vs", "assest/shader/4高级OpenGL/6.10.4.渲染大量物体-行星带-实例化数组.fs");
    
    // 加载模型
    Model planet(FileSystem::getPath("assest/model/planet/planet.obj"));
    Model rock(FileSystem::getPath("assest/model/rock/rock.obj"));
    
    // model数组,石头的偏移位置
    unsigned int amount = 100000;
    glm::mat4* modelMatrices;
    modelMatrices = new glm::mat4[amount];
    srand(static_cast<unsigned int>(glfwGetTime()));// 初始化随机种子
    float radius = 150.0f;
    float offset = 25.0f;
    for (unsigned int i = 0; i < amount; i++) {
        glm::mat4 model = glm::mat4(1.0f);
        // 角度,0-360度
        float angle = (float)i / (float)amount * 360.0f;
        // 1. 位移:分布在半径为 radius 的圆形上,偏移范围是[-0ffset, offset]
        // rand()范围为0~RAND_MAX, 700 % 500 = 200 / 100 = 2 - 2.5 = -0.5
        float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float x = sin(angle) * radius + displacement;
    
        displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float y = displacement * 0.4f;
    
        displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
        float z = cos(angle) * radius + displacement;
        model = glm::translate(model, glm::vec3(x, y, z));
    
        // 2.缩放:在0.05和0.25f之间缩放
        float scale = (rand() % 20) / 100.0f + 0.05;
        model = glm::scale(model, glm::vec3(scale));
    
        // 3.旋转:绕着一个(半)随机选择的旋转轴向量进行随机的旋转
        float rotAngle = (rand() % 360);
        model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
    
        // 4. 添加到矩阵的数组中
        modelMatrices[i] = model;
    }
    // 关键代码-开始///
    // 设置给rock的model,实例化数组当做顶点属性,需要指定顶点属性布局
    unsigned int buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    // 注意这里将数组绑定到缓冲中
    glBufferData(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), &modelMatrices[0], GL_STATIC_DRAW);
    for (unsigned int i = 0; i < rock.meshes.size(); i++) {// rock.meshes.size() = 1
        unsigned int VAO = rock.meshes[i].VAO;
        glBindVertexArray(VAO);
        // 设置mat4的顶点属性指针
        glEnableVertexAttribArray(3);
        glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)0);
        glEnableVertexAttribArray(4);
        glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)));
        glEnableVertexAttribArray(5);
        glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)));
        glEnableVertexAttribArray(6);
        glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)));
        // layout=3矩阵的instanceMatrix顶点属性,每1个实例更新一次属性
        glVertexAttribDivisor(3, 1);
        glVertexAttribDivisor(4, 1);
        glVertexAttribDivisor(5, 1);
        glVertexAttribDivisor(6, 1);
    
        glBindVertexArray(0);
    }
    // 关键代码-结束///
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;
    
        // input
        // -----
        processInput(window);
    
        // render
        // ------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // 摄像机
        glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 1000.0f);
        glm::mat4 view = camera.GetViewMatrix();
        rockshader.use();
        rockshader.setMat4("projection", projection);
        rockshader.setMat4("view", view);
        planetshader.use();
        planetshader.setMat4("projection", projection);
        planetshader.setMat4("view", view);
        // 绘画行星
        planetshader.use();
        glm::mat4 model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
        model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
        planetshader.setMat4("model", model);
        planet.Draw(planetshader);
    
        // 绘画石头
        rockshader.use();
        // 绑定纹理单元
        rockshader.setInt("texture_diffuse1", 0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, rock.textures_loaded[0].id);;
        for (unsigned int i = 0; i < rock.meshes.size(); i++) {
            glBindVertexArray(rock.meshes[i].VAO);
            // 注意第5个参数,设置需要绘制的实例数量
            glDrawElementsInstanced(GL_TRIANGLES, rock.meshes[i].indices.size(), GL_UNSIGNED_INT, 0, amount);
            glBindVertexArray(0);
        }
    
    • 当矩阵当做顶点属性时

      由于顶点属性的类型只能是小于等于vec4大小,而mat4本质上是4个vec4,所以我们需要为这个矩阵预留4个顶点属性。

      因为我们将它的位置值设置为3,矩阵每一列的顶点属性位置值就是3、4、5和6。

  • 效果

    100000个石头

    请添加图片描述

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

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

相关文章

【转码】nohup后台执行ffmpeg转码后推流srs

【ffmpeg】flv1转码h264且降低分辨率 【ffmpeg】filter_complex 转码视频保留音频推直播flv流 拉流播放剪辑到2M mp4 转flv 同时转码,拉流地址:http://1.1.1.5:8078/live/streamLow.flv 过了三个月,忘了。通过查询后台程序,判断当时自己是用的哪个脚本:看下上次操作的时间…

MySQL简单教程

MySQL的数据类型 类型用途int整型&#xff0c;相当于java的intbigint整型&#xff0c;相当于java的longfloat浮点型double浮点型datetime日期类型timestamp日期类型(可存储时间戳)char定长字符varchar不定长字符text大文本&#xff0c;用于存储很长的…

皮卡丘xss之盲打、xss之过滤

1.xss之盲打 我们先按照题目输入 提交后&#xff0c;很平常 再试试插入payload&#xff1a;<script>alert(1)</script>和<script>alert(2)</script> 提交后还是不变 此时我们看提示 我们尝试访问该地址&#xff1a;127.0.0.1/pk/vul/xss/xssblind/ad…

聊聊Scrum价值观与测试启发

这是鼎叔的第六十二篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 敏捷理论博大精深&#xff0c;相关实践方法论和工具层出不穷&#xff0c;各大公司都有特…

从零开始学习JVM(六)-直接内存和执行引擎

1 直接内存介绍 直接内存不是虚拟机运行时数据区的一部分&#xff0c;也不是《Java虚拟机规范》中定义的内存区域。直接内存是在Java堆外的、直接向系统申请的内存空间。直接内存来源于NIO&#xff0c;通过存在堆中的DirectByteBuffer操作Native内存。通常访问直接内存的速度会…

Stable Diffusion教程

什么是Stable Diffusion Stable Diffusion是一种潜在扩散模型&#xff08;Latent Diffusion Model&#xff09;&#xff0c;能够从文本描述中生成详细的图像。它还可以用于图像修复、图像绘制、文本到图像和图像到图像等任务。简单地说&#xff0c;我们只要给出想要的图片的文…

RK3588平台开发系列讲解(网络篇)本地套接字

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、本地套接字是什么二、本地字节流套接字 服务器三、本地字节流套接字 客户端沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本地套接字是 IPC,也就是本地进程间通信的一种实现方式。除了本地套接字以外,…

皮卡丘Unsafe Filedownload

1.不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c;便会向后台发送一个下载请求&#xff0c;一般这个请求会包含一个需要下载的文件名称&#xff0c;后台在收到请求后 会开始执行下载代码&#xff0c;将该文件名对应…

旧改快讯--桑泰南山桃源“工改商住”项目规划修改

南山区桃源街道西丽同富裕工业城城市更新单元原列入《2019年深圳市南山区城市更新单元计划第一批计划》&#xff0c;后进行更新方向调整&#xff0c;列入《2020年深圳市南山区城市更新单元计划第三批计划》&#xff0c;2022年8月发布实施主体公示&#xff0c;实施主体为深圳市桑…

chatgpt赋能python:Python到底是一个SEO友好的选择吗?

Python到底是一个SEO友好的选择吗&#xff1f; 随着Python成为越来越受欢迎的编程语言&#xff0c;人们开始担心它是否适用于SEO。因为SEO是一项关键性能指标&#xff0c;因此希望了解Python是否确实是适用于这一目标。 Python的SEO优缺点 首先&#xff0c;Python确实具有一…

shell编程之循环语句与函数

文章目录 一.shell函数1.作用2.使用函数的优势3.shell函数定义3.1方式1&#xff1a;3.2方式2&#xff1a; 4.调用函数的方法5.函数返回值5.1 return5.2 exit 6.1-100奇偶求和相加 二.函数传参1.含义2.函数传参的方式 三.函数变量的作用范围1.函数变量的作用范围2.调用函数2.1示…

什么蓝牙耳机戴着舒服,介绍几款佩戴舒适的骨传导蓝牙耳机

骨传导耳机是一种新式的耳机&#xff0c;与常规听歌的入耳式耳机相比&#xff0c;不需要将耳机塞住的耳道&#xff0c;在长时间佩戴时不会损伤听觉。能听声音不需要入耳&#xff0c;在户外运动时能及时听到环境音&#xff0c;避免安全隐患。现在市场上有骨传导。以下是一些骨传…

解决message(antd-design组件库)弹窗多次数出现的问题

当我们多次点击按钮的时候&#xff0c;会出现下图这样的情况&#xff1a; 网址&#xff1a; 全局提示 Message - Ant Design 这样看起来&#xff0c;会降低用户的体验。所以&#xff0c;我想要的效果是&#xff0c;点一次出现一次&#xff0c;当我再次点击的时候&#xff0c;会…

二叉搜索树桶排序

1、二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树 *若它的左子树不为空则左子树上所有的节点的值都小于根节点的值 *若它的右子树不为空则右子树上所有的节点的值都大于根节点的值 *它的左右子树也分别是一棵二叉搜索树 *二…

皮卡丘CSRF

1.CSRF&#xff08;get&#xff09; 首先看提示&#xff0c;我们选择用户kobe&#xff0c;密码123456登录 点击修改个人信息&#xff0c;假如用户要把住址改为shanxi 再点击submit&#xff0c;同时用bp抓包&#xff0c;我们可以看到是get请求&#xff0c;数据包含在URL之中 将…

web服务器有哪些

<1>什么是web服务器 “网络服务”&#xff08;Web Service&#xff09;的本质&#xff0c;就是通过网络调用其他网站的资源。 Web Service架构和云 如果一个软件的主要部分采用了”网络服务”&#xff0c;即它把存储或计算环节”外包”给其他网站了&#xff0c;那么我…

【Linux网络编程】HTTPS协议原理

https协议原理 一、HTTPS是什么二、基本概念2.1、什么是加密2.2、为什么要加密 三、常见的加密方式四、数据摘要(指纹)&&数字签名五、HTTPS的工作过程探究方案&#xff08;1&#xff09;&#xff1a;只使用对称加密方案&#xff08;2&#xff09;&#xff1a;只使用非对…

论文笔记--PANGU-α

论文笔记--PANGU-α: LARGE-SCALE AUTOREGRESSIVE PRETRAINED CHINESE LANGUAGE MODELS WITH AUTO-PARALLEL COMPUTATION 1. 文章简介2. 文章概括3 文章重点技术3.1 Transformer架构3.2 数据集3.2.1 数据清洗和过滤3.2.2 数据去重3.2.3 数据质量评估 4. 文章亮点5. 原文传送门6…

旧改快讯--罗湖蔡屋围项目二期子项目D、E启动行政征收

蔡屋围城市更新统筹片区蔡屋围&#xff08;城中村&#xff09;项目二期子项目D&#xff08;南村片区&#xff09;、子项目E&#xff08;南村东片区&#xff09;房屋征收提示 5月9日&#xff0c;深圳市罗湖区城市更新和土地整备局发布关于罗湖区桂园街道蔡屋围城市更新统筹片区…

[架构之路-204]- 常见的需求分析技术:结构化分析与面向对象分析

目录 前言&#xff1a; 1 1 . 3 需求分析概述 导言&#xff1a; 11.3.1需求分析的任务 (1) 绘制系统上下文范围关系图&#xff1a; (2) 创建用户界面原型&#xff1a; (3) 分析需求的可行性&#xff1a; (4) 确定需求的优先级&#xff1a; (5) 为需求建立模型&#xf…