QT with OpenGL(IBL-镜面反射)

news2024/11/25 11:01:23

文章目录

  • 预滤波
    • generate Mipmap
    • 获取每一层级的预滤波图
      • prefilterMap Shader
        • 重要性采样
        • 效果展示
    • 预过滤卷积的亮点
      • 解决方法
      • 代码解析
        • 首先得确保我们被采样的环境贴图有mipmap贴图
        • 通过计算决定使用那一层mipmap值
      • 效果
  • 预计算BRFD
    • 生成LUT图
  • IBL Shading
  • 渲染结果
  • 与教材的不同
  • 最终结果展示

预滤波

generate Mipmap

Cubemap增加是否生成mipmap选项

if(!mipmap)
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
else
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

if(mipmap) glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

获取每一层级的预滤波图

void CubeMap::getIBLprefilterMapFromEnvCubeMap(unsigned int CubeMap,unsigned int maxMipLevels)
{
    //第一步:编译链接预滤波Shader
    QOpenGLShaderProgram prefilterShader;
    prefilterShader.addShaderFromSourceFile(QOpenGLShader::Vertex,":/cubemap.vert");
    prefilterShader.addShaderFromSourceFile(QOpenGLShader::Fragment,":/prefilterMap.frag");
    prefilterShader.link();

    //第二步:FBO 创建帧缓存、绑定深度缓存和模板缓存
    unsigned int captureFBO, captureRBO;
    glGenFramebuffers(1, &captureFBO);
    glGenRenderbuffers(1, &captureRBO);
    glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
    glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, CubeSize, CubeSize);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);

    //第三步:输入Uniform参数,并渲染到当前Cubemap
    prefilterShader.bind();
    prefilterShader.setUniformValue("projection", captureProjection);   //vert
    prefilterShader.setUniformValue("environmentMap", 0);               //frag
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, CubeMap);//此处输入CubeMap
    glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);

    for (unsigned int mip = 0; mip < maxMipLevels; ++mip)
    {
        // 1.计算第i层的mipmap大小
        unsigned int mipi  = CubeSize >> mip;
            //mipmap第i层的长宽为 第0层大小 * 0.5^mip
            // == CubeSize * pow(0.5, mip)
            // == CubeSize >> mip

        // 2.设置该mip层的渲染窗体大小
        glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipi, mipi);
        glViewport(0, 0, mipi, mipi);

        // 3.根据mipmap的层级选择预滤波的滤波模糊度
        float roughness = (float)mip / (float)(maxMipLevels - 1);
        prefilterShader.setUniformValue("roughness", roughness);        //frag

        // 4.渲染得到该层级的预滤波立方体贴图
        for (unsigned int i = 0; i < 6; ++i)
        {
            prefilterShader.setUniformValue("view", lookatMatrix[i]);   //vert
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                   GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, mip);

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            renderCube();
        }
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

}

prefilterMap Shader

#version 450 core

//输入:预滤波方向
in vec3 WorldPos;
out vec4 FragColor;
//主要参数:
uniform samplerCube environmentMap;//环境立方体贴图
uniform float roughness;//控制预滤波的模糊程度

//辅助参数:
uniform uint sample_count;//每方向采样数
uniform bool enMapHasMipmap;//是否有mipmap
uniform int environmentMapSize;//环境立方体贴图大小

const float PI = 3.1415926535;
void main(void)
{
    vec3 N = normalize(WorldPos);

    //采样数可设为Uniform
    //const uint SAMPLE_COUNT = 1024u;
    const uint SAMPLE_COUNT = sample_count;
    //采样结果保存
    vec3 prefilteredColor = vec3(0.0);
    float totalWeight = 0.0;

    //重要性采样
    for(uint i = 0;i<SAMPLE_COUNT;++i){
        //得到采样方向
        vec3 L = N;

        //采样(roughness 0-1 属于 mipmap 0 - 7)
        prefilteredColor += texture(environmentMap, L).rgb;
        totalWeight += 1.0f;
    }

    prefilteredColor = prefilteredColor / totalWeight;
    FragColor = vec4(prefilteredColor, 1.0);
}

其中获取采样方向,以及确定采样层级是较为关键的部分。

重要性采样

#version 450 core

//输入:预滤波方向
in vec3 WorldPos;
out vec4 FragColor;
//主要参数:
uniform samplerCube environmentMap;//环境立方体贴图
uniform float roughness;//控制预滤波的模糊程度

//辅助参数:
uniform uint sample_count;//每方向采样数
//uniform bool enMapHasMipmap;//是否有mipmap
//uniform int environmentMapSize;//环境立方体贴图大小

//辅助函数
float RadicalInverse_VdC(uint bits);
vec2 Hammersley(uint i, uint N);
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
float DistributionGGX(vec3 N, vec3 H, float roughness);

//const
const float PI = 3.1415926535;


void main(void)
{
    vec3 N = normalize(WorldPos);

    //采样数可设为Uniform
    //const uint SAMPLE_COUNT = 1024u;
    const uint SAMPLE_COUNT = 1024u;
    //采样结果保存
    vec3 prefilteredColor = vec3(0.0);
    float totalWeight = 0.0;

    //重要性采样
    for(uint i = 0;i<SAMPLE_COUNT;++i){
        //得到采样方向
        vec2 randomVec2 = Hammersley(i,SAMPLE_COUNT);
        vec3 H = ImportanceSampleGGX(randomVec2,N,roughness);
        vec3 L = normalize(2.0 * dot(N, H) * H - N);

        //问题:该反射方向可能会向物体背部反射,所以要去除背向光线
        //那为何不直接使用H作为反射方向,这样可以避免光线的失效,增加采样数,减少L的计算时间
        float NdotL = max(dot(N,L),0.0f);
        if(NdotL > 0.0f){
            //采样(roughness 0-1)
            //float D = DistributionGGX(N,H,roughness);
            prefilteredColor += texture(environmentMap, L).rgb;
            totalWeight += 1.0f;

        }
    }

    prefilteredColor = prefilteredColor / totalWeight;
    FragColor = vec4(prefilteredColor, 1.0);
}

效果展示

mipmap 0
在这里插入图片描述
mipmap 1
在这里插入图片描述
在这里插入图片描述

mipmap 2
在这里插入图片描述
mipmap 3
在这里插入图片描述
在这里插入图片描述
mipmap 4

在这里插入图片描述
在这里插入图片描述
mipmap 5 --全黑(就不截图了)
mipmap 4.9
在这里插入图片描述
可以看出没有被渲染的mipmap层级存在(不会报错),但值为纯黑。
因此如果渲染中间层,会将前一层与该黑色层混合,而不报错。

另外可以看到立方体贴图的贴图之间并未进行滤波。
OpenGL 可以启用 GL_TEXTURE_CUBE_MAP_SEAMLESS,以为我们提供在立方体贴图的面之间进行正确过滤的选项:

glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);  

如下:可以看到边角位置不再有明显边界
mipmap 3.6
在这里插入图片描述
mipmap 4.9
在这里插入图片描述

注意:开启该选项后,并不是对纹理做模糊,而是当指向像素边界时,会根据边界临近的纹理像素做插值。
因此,如果只是在预滤波中开启GL_TEXTURE_CUBE_MAP_SEAMLESS,而在显示预滤波图时不开启GL_TEXTURE_CUBE_MAP_SEAMLESS,我们看到的边界结果才是纹理中真正存储的像素值。

纹理真正记录的数据如下,GL_TEXTURE_CUBE_MAP_SEAMLESS相当于边界处的 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR)函数。
在这里插入图片描述

预过滤卷积的亮点

解决方法

较高采样率区域使用较低mipmap(更清晰)级别的纹理进行采样。
较低采样率区域使用较高mipmap(更模糊)级别的纹理进行采样。

代码解析

首先得确保我们被采样的环境贴图有mipmap贴图

glBindTexture(GL_TEXTURE_CUBE_MAP,envCubemap);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

环境贴图mipmap
mipmap 1
在这里插入图片描述
mipmap 2
在这里插入图片描述
mipmap 3
在这里插入图片描述
mipmap 4
在这里插入图片描述
mipmap 7
在这里插入图片描述

通过计算决定使用那一层mipmap值

mipmap每加1层,纹理大小就缩小一半,每四个像素合成一个像素。
因此,
如果一个采样点覆盖率为4个像素,则应该在第1层mipmap上采样;
如果一个采样点覆盖率为16个像素,则应该在第2层mipmap上采样;
如果一个采样点覆盖率为8个像素,则应该在 l o g 2 ( 8 1 2 ) = 0.5 ∗ l o g 2 ( 8 ) log_2(8^\frac{1}{2}) = 0.5 * log_2(8) log2(821)=0.5log2(8)层mipmap采样。

现在问题转移到了一个采样点覆盖的像素数为多少

一个采样点覆盖的像素数 = 该方向像素数 该方向采样数 一个采样点覆盖的像素数 = \frac{该方向像素数}{该方向采样数} 一个采样点覆盖的像素数=该方向采样数该方向像素数
已知,球面坐标上的WorldPos指向立方体贴图,每个单位立体角向量指向的贴图像素数也是不同的。
向量指向立方体贴图边角位置,该方向的像素数会偏多。
而指向立方体一面中心位置,像素数就会偏少。

这里做平均处理,将该方向像素数平均。
该方向像素数 = 立方体像素数 球面积分 = 6 ∗ 分辨 率 2 4 π 该方向像素数 = \frac{立方体像素数}{球面积分} = \frac{6 * 分辨率^2}{4\pi} 该方向像素数=球面积分立方体像素数=4π6分辨2

该方向采样数 = 总采样数 ∗ 该方向采样概率 = S A M P L E _ C O U N T ∗ p d f 该方向采样数 = 总采样数 * 该方向采样概率 \\= SAMPLE\_COUNT * pdf 该方向采样数=总采样数该方向采样概率=SAMPLE_COUNTpdf

综上:
一个采样点覆盖的像素数 = 6 ∗ r e s o l u t i o n 2 4 π ∗ 总采样数 ∗ p d f 一个采样点覆盖的像素数 = \frac{6 * resolution^ 2}{4\pi * 总采样数 * pdf } 一个采样点覆盖的像素数=4π总采样数pdf6resolution2

问题又来了:pdf怎么计算?
p d f pdf pdfImportanceSampleGGX函数生成的H向量计算得到L 的分布
H的向量分布为均匀分布的伪随机数。

这里 Chetan Jags 做了近似,将 p d f pdf pdf 近似为法线分布函数计算得到的值。

p d f = D i s t r i b u t i o n G G X ( N ⋅ H , r o u g h n e s s ) ∗ ( N ⋅ H ) 4 ∗ ( H ⋅ V ) pdf = \frac{DistributionGGX(N\cdot H, roughness) * (N \cdot H) }{4 * (H \cdot V)} pdf=4(HV)DistributionGGX(NH,roughness)(NH)
因为 N = = V N==V N==V,所以简化为
p d f = D i s t r i b u t i o n G G X ( N ⋅ H , r o u g h n e s s ) 4 pdf = \frac{DistributionGGX(N\cdot H, roughness) }{4 } pdf=4DistributionGGX(NH,roughness)

综上,代码为:

    //重要性采样
    for(uint i = 0;i<SAMPLE_COUNT;++i){
        //得到采样方向
        vec2 randomVec2 = Hammersley(i,SAMPLE_COUNT);
        vec3 H = ImportanceSampleGGX(randomVec2,N,roughness);
        vec3 L = normalize(2.0 * dot(N, H) * H - N);

        //问题:该反射方向可能会向物体背部反射,所以要去除背向光线
        //那为何不直接使用H作为反射方向,这样可以避免光线的失效,增加采样数,减少L的计算时间
        float NdotL = max(dot(N,L),0.0f);
        if(NdotL > 0.0f){
            //计算采样mipmap
            float D = DistributionGGX(N,H,roughness);
            float pdf = D / 4.0 + 0.0001;

            //一个采样点对应四个采样像素,mipmap=1;
            //mipmap级别 = 0.5 * log_2(一个采样点采样的像素数) ;
            //一个采样点采样的像素数 = 每方向像素数 / (采样数量 * 该方向采样概率)
            // = 6 * res * res / (4 * PI * SAMPLE_COUNT * pdf);
            //每像素平均立体角,当前采样方向的概率
            float resolution = 512.0; // 原空间盒清晰度 (per face)
            float TexPerSample = 4 * resolution * resolution / (6 * PI * SAMPLE_COUNT * pdf);

            float mipLevel = ( roughness == 0.0 ? 0.0 : 0.5 * log2(TexPerSample) );

            //加权
            prefilteredColor += textureLod(environmentMap, L, mipLevel).rgb * NdotL;
            totalWeight      += NdotL;
        }
    }

最后加权NdotL是为了减少较大倾角对像素点的权值。

平均

prefilteredColor = prefilteredColor / totalWeight;
FragColor = vec4(prefilteredColor, 1.0);

效果

上方为解决两点的显示,下方为之前的显示。
mipmap 3
使用预过滤环境贴图LINEAR,使用加权NdotL
在这里插入图片描述
使用预过滤环境贴图NEAREST,使用加权NdotL
在这里插入图片描述
未使用预过滤环境贴图LINEAR未使用加权 NdotL

在这里插入图片描述
未使用预过滤环境贴图LINEAR未使用加权 NdotL
在这里插入图片描述
未使用预过滤环境贴图NEAREST,使用加权 NdotL

在这里插入图片描述

mipmap 4

在这里插入图片描述

在这里插入图片描述
综上:在一般情况下使用预过滤环境贴图,效果不大,但使用加权 NdotL可以很大程度上改变效果,将更多的采样权值放在中心采样区。

预计算BRFD

生成LUT图

已知视口方向与法线的夹角 N ⋅ V N \cdot V NV),粗糙度 r o u g h n e s s roughness roughness
得到与 F 0 F_0 F0 无关的两个参数。

LUT图纹理的坐标:
横坐标:视口方向与法线的夹角( N ⋅ V N \cdot V NV
纵坐标:粗糙度( r o u g h n e s s roughness roughness

纹理存储的值:
R F 0 F_0 F0 的比例
G F 0 F_0 F0 的偏差

:LUT图与材质无关(粗糙度,金属度),与环境贴图无关,与视口法线无关。因此所有的IBL镜面反射只需要一个LUT图(所有材质的BRDF都是基于金属度粗糙度给定的 F 0 F_0 F0,而且反射方式相同的情况下)。

在这里插入图片描述

IBL Shading

输入Uniform增加如下参数

// IBL
uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform int maxMipmapLevels;

uniform sampler2D LUT;

环境光反射计算

//环境光镜面反射 specular
vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
vec3 prefilterColor = textureCubeLod(prefilterMap,R,roughness * maxMipmapLevels).rgb;
vec2 brdf = texture2D( LUT, vec2( max(dot(N,V),0.0) , roughness )).rg;
vec3 specular = prefilterColor * (kS * brdf.x + brdf.y);

合入渲染结果

//合计最终值
vec3 color = ambient + specular + Lo;

渲染结果

在这里插入图片描述

与教材的不同

教材中 k S k_S kS 的计算使用了如下方程

vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);

//调用如下方程计算kS
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
{
    return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}

渲染效果如下
在这里插入图片描述
差别,使用fresnelSchlickRoughness(教材)函数计算的 F 0 F_0 F0 相比于fresnelSchlick(个人)函数会更小一点。也就是说反射量会小一点。

但比较两图,肉眼上并无差别。
在这里插入图片描述
在这里插入图片描述

最终结果展示

在这里插入图片描述

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

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

相关文章

(C语言版)力扣(LeetCode)189. 轮转数组官方3种解法分析

轮转数组 题目第一种解法&#xff1a;额外数组第二种解法&#xff1a;环状替换第三种解法&#xff1a;翻转数组结语 题目 题目链接&#xff1a;轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: num…

实战项目:手把手带你实现一个高并发内存池

项目介绍 1.这个项目做的是什么&#xff1f; 当前项目是实现一个高并发的内存池&#xff0c;他的原型是google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存的malloc&#xff0c;实现了高效的多线程内存管理&#xff0c;用于替…

Java——字符串的排列

题目链接 牛客网在线oj题——字符串的排列 题目描述 输入一个长度为 n 字符串&#xff0c;打印出该字符串中字符的所有排列&#xff0c;你可以以任意顺序返回这个字符串数组。 例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。 数…

【SpringMVC】| SpringMVC 入门

目录 一&#xff1a;SpringMVC 入门 1. SpringMVC简介 2. SpringMVC的优点 3. SpringMVC的优化 4. SpringMVC执行的流程 5. 基于注解的SpringMVC程序 图书推荐 一&#xff1a;《Spring Boot进阶&#xff1a;原理、实战与面试题分析》 二&#xff1a;《深入理解Java虚拟…

19.网络爬虫—照片管道

网络爬虫—照片管道 Scrapy基础Scrapy运行流程原理Scrapy的工作流程 scrapy照片管道实战演示设置图片路径配置爬虫解析数据运行爬虫查看文件 后记 前言&#xff1a; &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域…

C++ Primer笔记——排列算法(next_permutation、prev_permutation、is_permutation)

目录 概述 ①next_permutation ②prev_permutation ③is_permutation 概述 页数&#xff1a;P778 &#xff08;A.2.7 排列算法&#xff09; 头文件&#xff1a;<algorithm> 函数名&#xff1a;next_permutation & prev_permutation & is_permutation C为…

信息安全复习七:报文鉴别与哈希函数

一、章节梗概 1.安全服务与安全需求 2.报文鉴别的安全需求 3.对报文加密来实现报文鉴别 4.报文鉴别码 5.哈希函数 6.生日攻击 二、安全服务与安全需求 2.1 引入 通信保密可以概况所有的安全需求吗? 不能&#xff0c;信息安全需求有很多种&#xff0c;通信保密只是一种安全…

2023移动云大会 | “六大”服务承诺 全力做优“心级服务”

4月25日&#xff0c;以“云擎未来 智信天下”为主题的2023移动云大会在苏州金鸡湖国际会议中心举办&#xff0c;众多政府领导、院士专家、知名企业客户与合作伙伴高层等数千名嘉宾齐聚一堂。 大会期间&#xff0c;移动云深入践行“为国建云”的使命&#xff0c;推出“六大”服…

vdo磁盘管理

在 storagesrv 上新加一块 10G 磁盘;  创建 vdo 磁盘,并开启 vdo 磁盘的重删和压缩;  名字为 vdodisk,大小为 150G,文件系统为 ext4;  并设置开机自动挂载。挂载到/vdodata。 1.lsblk 查看自己添加的硬盘名称 2.安装vdo软件包 yum -y install vdo…

卡尔曼滤波原理及代码

目录 一.简介 二.原理 1.先验估计原理 2.后验估计原理 3.总结 三.示例 一.简介 卡尔曼滤波&#xff08;Kalman filtering&#xff09;是一种利用线性系统状态方程&#xff0c;通过系统输入输出观测数据&#xff0c;对系统状态进行最优估计的算法&#xff0c;它可以在任意…

Vue-全局过滤器以及进阶操作

前言 上篇文件讲述了&#xff0c;Vue全局过滤器的基本使用&#xff1a;Vue过滤器的基本使用 本篇将延续上文&#xff0c;讲述vue中过滤器的进阶操作 过滤器传参 如果有一天&#xff0c;多个地方使用过滤器&#xff0c;而且需要传递参数&#xff0c;那么可以这么写 多个过滤…

《Netty》从零开始学netty源码(四十三)之PoolChunk.allocate

allocate PoolChunk分配内存空间时可调用allocate方法来分配&#xff0c;具体的源码过程如下&#xff1a; 从代码中可以看出会根据分配的内存大小决定分配的是subpage还是normal的page&#xff0c;接下来具体分析以下方法&#xff1a; allocateSubpageallocateRuninitBuf …

Unity|| 如何把生存类游戏设计得更优秀

你是否曾经玩过这样的生存类游戏&#xff1a; 1、通过最初阶段后&#xff0c;你觉得游戏变得越来越简单 2、游戏的重点从生存转移到了基地建设或其他方面 诸如此类&#xff0c;很大程度上是由于糟糕的难度曲线所致。包括很多&#xff08;非常受欢迎的&#xff09;生存游戏都…

Redis——缓存更新策略

业务场景&#xff1a; 低一致性需求&#xff1a;使用内存淘汰机制。例如店铺类型的查询缓存&#xff0c;很少修改 高一致性需求&#xff1a;主动更新&#xff0c;并以超时剔除作为兜底方案。例如店铺详情查询的缓存&#xff0c;经常修改 主动更新策略 实际开发中最常用的还是…

51单片机(三)独立按键控制LED

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

【SpringMVC源码三千问】DispatcherServlet源码解析

DispatcherServlet#doDispatch() 是 SpringMVC 处理请求分发的方法&#xff0c;只要是 spring mvc 处理的 http 请求&#xff0c;都会经过 DispatcherServlet 的请求分发处理&#xff0c;从而调用相应的 handler method。 DispatcherServlet#doDispatch() 源码分析&#xff1a…

PCL点云库(3) — common模块

目录 3.1 common模块中的头文件 3.2 common模块中的基本函数 &#xff08;1&#xff09;angle角度转换 &#xff08;2&#xff09;distance距离计算 &#xff08;3&#xff09;random随机数生成 &#xff08;4&#xff09;sping扩展模块 &#xff08;5&#xff09;time获…

请问你见过吐代码的泡泡吗(冒泡排序)

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《算法详解》&#xff0c;笔者用重金(时间和精力)打造&#xff0c;将算法知识一网打尽&#xff0c;希望可以…

现场工程师救火-UEFI(BIOS)节能设置导致金牌服务器只跑出龟速

近期协助出现场&#xff0c;解决了一个非常典型的UEFI 启动参数配置不当导致的服务器降效案例。错误的节能参数配置&#xff0c;导致价值几十万的服务器变成龟速服务器&#xff0c;并造成严重的生产事故。 1. 现象 朋友公司近期准备升级2010年就部署的服务器组&#xff0c;新…

vue移动端项目通用技巧

目录 一、配置文件 1.1、取消eslint校验 1.2、基础文件引入 1.3、iconfont引入svg使用 1.4、css的简化应用 1.5、内容溢出用省略号替代 1.6、非组件库的底部导航跳转 1.7、基础版轮播图 一、配置文件 1.1、取消eslint校验 在vue.config.js文件里&#xff1a; const …