Cube Map 系列之:手把手教你 使用 立方体贴图

news2024/11/27 19:46:33

什么是Cube Map

在开始立方体贴图之前,我们先简单了解下cube map。

cube map 包含了六个纹理,分别表示了立方体的六个面;
相较二维的纹理使用坐标uv来获取纹理信息,这里我们需要使用三维的方向向量来获取纹理信息(一些地方称为法线 normal,但我认为方向向量更合理)。

Cube Map可以用于:

  • 正方体表面贴图
  • 用于环境贴图(反射贴图),模拟镜面反射结果
  • 天空盒

什么是立方体贴图

故名思意,将cube map的六个纹理分别贴到立方体的六个面上,就算立方体贴图。效果如下:

  • 纹理图
    在这里插入图片描述
  • 立方体贴图
    效果如图

我们下面从两个部分来进行讲解

  • 使用文本生成对应的纹理图
  • 使用cubemap将对应的纹理分别贴到立方体的六个面上

生成立方体纹理

  • 纹理图效果如下
    在这里插入图片描述
  • 关键代码(使用canvas生成对应的图片)
function generateFace(ctx, faceColor, textColor, text){
    const {width, height} = ctx.canvas;
    ctx.fillStyle = faceColor;
    ctx.fillRect(0, 0, width, height);
    ctx.font = `${width * 0.7}px sans-serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = "middle";
    ctx.fillStyle = textColor;
    ctx.fillText(text, width / 2, height / 2);
}
  • 关键代码(组织数据,获得图片,并使用html显示)
const ctx = document.createElement("canvas").getContext("2d");

ctx.canvas.width = 128;
ctx.canvas.height = 128;

const faceInfos = [
    { faceColor: '#F00', textColor: '#0FF', text: '+X' },
    { faceColor: '#FF0', textColor: '#00F', text: '-X' },
    { faceColor: '#0F0', textColor: '#F0F', text: '+Y' },
    { faceColor: '#0FF', textColor: '#F00', text: '-Y' },
    { faceColor: '#00F', textColor: '#FF0', text: '+Z' },
    { faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
];

faceInfos.forEach((faceInfo) => {
    const { faceColor, textColor, text } = faceInfo;
    generateFace(ctx, faceColor, textColor, text);

    ctx.canvas.toBlob((blob => {
        const img = new Image();
        img.src = URL.createObjectURL(blob);
        document.body.appendChild(img);
    }))
})
}

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CubeMap</title>
</head>
<body>
<script>

    function main(){
        const ctx = document.createElement("canvas").getContext("2d");

        ctx.canvas.width = 128;
        ctx.canvas.height = 128;

        const faceInfos = [
            { faceColor: '#F00', textColor: '#0FF', text: '+X' },
            { faceColor: '#FF0', textColor: '#00F', text: '-X' },
            { faceColor: '#0F0', textColor: '#F0F', text: '+Y' },
            { faceColor: '#0FF', textColor: '#F00', text: '-Y' },
            { faceColor: '#00F', textColor: '#FF0', text: '+Z' },
            { faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
        ];

        faceInfos.forEach((faceInfo) => {
            const { faceColor, textColor, text } = faceInfo;
            generateFace(ctx, faceColor, textColor, text);

            ctx.canvas.toBlob((blob => {
                const img = new Image();
                img.src = URL.createObjectURL(blob);
                document.body.appendChild(img);
            }))
        })
    }

    function generateFace(ctx, faceColor, textColor, text){
        const {width, height} = ctx.canvas;
        ctx.fillStyle = faceColor;
        ctx.fillRect(0, 0, width, height);
        ctx.font = `${width * 0.7}px sans-serif`;
        ctx.textAlign = 'center';
        ctx.textBaseline = "middle";
        ctx.fillStyle = textColor;
        ctx.fillText(text, width / 2, height / 2);
    }

    main()

</script>
</body>
</html>


将纹理贴到立方体

关键代码说明

1. 创建gl

const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");

2. 初始化片元着色器和顶点着色器

const V_SHADER_SOURCE = '' +
    'attribute vec4 a_position;' +
    'uniform mat4 u_matrix;' +
    'varying vec3 v_normal;' +
    'void main(){' +
    'gl_Position = u_matrix * a_position;' +
    'v_normal = normalize(a_position.xyz);' +
    '}'

const F_SHADER_SOURCE = '' +
    'precision mediump float;' +
    'varying vec3 v_normal;' +
    'uniform samplerCube u_texture;' +
    'void main(){' +
    'gl_FragColor = textureCube(u_texture, normalize(v_normal));' +
    '}'
    
if (!initShaders(gl, V_SHADER_SOURCE, F_SHADER_SOURCE)){
	console.log('Failed to initialize shaders.');
	return;
}

3. 配置attribute信息a_position

// 获取a_position
const positionLocation = gl.getAttribLocation(gl.program, "a_position");

// 创建数据buffer 并绑定pisitions数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

// 开启顶点属性 并设置其对应的属性 从而从数据buffer中获取对应的数据
gl.enableVertexAttribArray(positionLocation);
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);

4. 配置uniform信息u_texture

// 创建纹理对象 并绑定到 cube_map
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);

// 生成纹理图片 并调用 gl.texImage2D进行绑定
const ctx = document.createElement("canvas").getContext("2d");

ctx.canvas.width = 128;
ctx.canvas.height = 128;

const faceInfos = [
    { target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, faceColor: '#F00', textColor: '#0FF', text: '+X' },
    { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, faceColor: '#FF0', textColor: '#00F', text: '-X' },
    { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, faceColor: '#0F0', textColor: '#F0F', text: '+Y' },
    { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, faceColor: '#0FF', textColor: '#F00', text: '-Y' },
    { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, faceColor: '#00F', textColor: '#FF0', text: '+Z' },
    { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
];

faceInfos.forEach((faceInfo) => {
    const { target, faceColor, textColor, text } = faceInfo;
    generateFace(ctx, faceColor, textColor, text);

    // Upload the canvas to the cube map face.
    const level = 0;
    const internalFormat = gl.RGBA;
    const format = gl.RGBA;
    const type = gl.UNSIGNED_BYTE;
    gl.texImage2D(target, level, internalFormat, format, type, ctx.canvas);

})

// 生成cubemap纹理 并进行传递
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
const textureLocation = gl.getUniformLocation(gl.program, "u_texture");
gl.uniform1i(textureLocation, 0);

5. 动态更新uniform信息u_matrix

// 获取每一帧的时间差
time *= 0.001;
const deltaTime = time - then;
then = time;

// 计算沿x y 轴的旋转角度
modelXRotationRadians += -0.7 * deltaTime;
modelYRotationRadians += -0.4 * deltaTime;

// 计算投影矩阵
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const projectionMatrix =
    m4.perspective(fieldOfViewRadians, aspect, 1, 2000);
    
// 计算相机矩阵
const cameraPosition = [0, 0, 2];
const up = [0, 1, 0];
const target = [0, 0, 0];
const cameraMatrix = m4.lookAt(cameraPosition, target, up);

// 获取view矩阵
const viewMatrix = m4.inverse(cameraMatrix);

// 计算得到vp矩阵
const viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

// 返回有旋转角度的 vp矩阵
let matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
return m4.yRotate(matrix, modelYRotationRadians);

6. 开始绘制

const matrixLocation = gl.getUniformLocation(gl.program, "u_matrix");

function drawScene(time){
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.enable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(gl.program);

    // 动态更新矩阵信息
    gl.uniformMatrix4fv(matrixLocation, false, updateMatrix(time));

    // Draw the geometry.
    gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);

    requestAnimationFrame(drawScene);
}

效果

在这里插入图片描述

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CubeMap</title>
</head>
<body>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<canvas id="canvas" style="height: 256px; width: 246px"></canvas>
<script>
    const V_SHADER_SOURCE = '' +
        'attribute vec4 a_position;' +
        'uniform mat4 u_matrix;' +
        'varying vec3 v_normal;' +
        'void main(){' +
        'gl_Position = u_matrix * a_position;' +
        'v_normal = normalize(a_position.xyz);' +
        '}'

    const F_SHADER_SOURCE = '' +
        'precision mediump float;' +
        'varying vec3 v_normal;' +
        'uniform samplerCube u_texture;' +
        'void main(){' +
        'gl_FragColor = textureCube(u_texture, normalize(v_normal));' +
        '}'

    function main(){
        // Get A WebGL context
        /** @type {HTMLCanvasElement} */
        const canvas = document.querySelector("#canvas");
        const gl = canvas.getContext("webgl");
        if (!gl) {
            return;
        }

        if (!initShaders(gl, V_SHADER_SOURCE, F_SHADER_SOURCE)){
            console.log('Failed to initialize shaders.');
            return;
        }

        const matrixLocation = gl.getUniformLocation(gl.program, "u_matrix");

        setGeometry(gl, getGeometry());

        setTexture(gl)


        function radToDeg(r) {
            return r * 180 / Math.PI;
        }

        function degToRad(d) {
            return d * Math.PI / 180;
        }

        const fieldOfViewRadians = degToRad(60);
        let modelXRotationRadians = degToRad(0);
        let modelYRotationRadians = degToRad(0);

        // Get the starting time.
        let then = 0;

        requestAnimationFrame(drawScene);

        function drawScene(time){
            gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
            gl.enable(gl.CULL_FACE);
            gl.enable(gl.DEPTH_TEST);

            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            gl.useProgram(gl.program);


            gl.uniformMatrix4fv(matrixLocation, false, updateMatrix(time));

            // Draw the geometry.
            gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);

            requestAnimationFrame(drawScene);
        }

        function updateMatrix(time){
            time *= 0.001;
            const deltaTime = time - then;
            then = time;

            modelXRotationRadians += -0.7 * deltaTime;
            modelYRotationRadians += -0.4 * deltaTime;
            // Compute the projection matrix
            const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
            const projectionMatrix =
                m4.perspective(fieldOfViewRadians, aspect, 1, 2000);

            const cameraPosition = [0, 0, 2];
            const up = [0, 1, 0];
            const target = [0, 0, 0];

            // Compute the camera's matrix using look at.
            const cameraMatrix = m4.lookAt(cameraPosition, target, up);

            // Make a view matrix from the camera matrix.
            const viewMatrix = m4.inverse(cameraMatrix);

            const viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

            let matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
            return m4.yRotate(matrix, modelYRotationRadians);
        }

    }

    function generateFace(ctx, faceColor, textColor, text){
        const {width, height} = ctx.canvas;
        ctx.fillStyle = faceColor;
        ctx.fillRect(0, 0, width, height);
        ctx.font = `${width * 0.7}px sans-serif`;
        ctx.textAlign = 'center';
        ctx.textBaseline = "middle";
        ctx.fillStyle = textColor;
        ctx.fillText(text, width / 2, height / 2);
    }

    /**
     * create a program object and make current
     * @param gl GL context
     * @param vShader  a vertex shader program (string)
     * @param fShader   a fragment shader program(string)
     */
    function initShaders(gl, vShader, fShader){
        const program = createProgram(gl, vShader, fShader);
        if (!program){
            console.log("Failed to create program");
            return false;
        }

        gl.useProgram(program);
        gl.program = program;

        return true;
    }

    /**
     * create a program object and make current
     * @param gl GL context
     * @param vShader  a vertex shader program (string)
     * @param fShader   a fragment shader program(string)
     */
    function createProgram(gl, vShader, fShader){
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vShader);
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fShader);

        if (!vertexShader || !fragmentShader){
            return null;
        }

        const program = gl.createProgram();
        if (!program){
            return null;
        }

        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);

        gl.linkProgram(program);

        const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (!linked){
            const error = gl.getProgramInfoLog(program);
            console.log('Failed to link program: ' + error);
            gl.deleteProgram(program);
            gl.deleteShader(vertexShader);
            gl.deleteShader(fragmentShader);
        }
        return program;
    }

    /**
     *
     * @param gl GL context
     * @param type  the type of the shader object to be created
     * @param source    shader program (string)
     */
    function loadShader(gl, type, source){
        const shader = gl.createShader(type);
        if (shader == null){
            console.log('unable to create shader');
            return null;
        }

        gl.shaderSource(shader, source);

        gl.compileShader(shader);

        const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (!compiled){
            const error = gl.getShaderInfoLog(shader);
            console.log('Failed to compile shader: ' + error);
            gl.deleteShader(shader);
            return null;
        }

        return shader;
    }


    function getGeometry(){
        return new Float32Array(
            [
                -0.5, -0.5, -0.5,
                -0.5, 0.5, -0.5,
                0.5, -0.5, -0.5,
                -0.5, 0.5, -0.5,
                0.5, 0.5, -0.5,
                0.5, -0.5, -0.5,

                -0.5, -0.5, 0.5,
                0.5, -0.5, 0.5,
                -0.5, 0.5, 0.5,
                -0.5, 0.5, 0.5,
                0.5, -0.5, 0.5,
                0.5, 0.5, 0.5,

                -0.5, 0.5, -0.5,
                -0.5, 0.5, 0.5,
                0.5, 0.5, -0.5,
                -0.5, 0.5, 0.5,
                0.5, 0.5, 0.5,
                0.5, 0.5, -0.5,

                -0.5, -0.5, -0.5,
                0.5, -0.5, -0.5,
                -0.5, -0.5, 0.5,
                -0.5, -0.5, 0.5,
                0.5, -0.5, -0.5,
                0.5, -0.5, 0.5,

                -0.5, -0.5, -0.5,
                -0.5, -0.5, 0.5,
                -0.5, 0.5, -0.5,
                -0.5, -0.5, 0.5,
                -0.5, 0.5, 0.5,
                -0.5, 0.5, -0.5,

                0.5, -0.5, -0.5,
                0.5, 0.5, -0.5,
                0.5, -0.5, 0.5,
                0.5, -0.5, 0.5,
                0.5, 0.5, -0.5,
                0.5, 0.5, 0.5,

            ]);

    }
    // Fill the buffer with the values that define a cube.
    function setGeometry(gl, positions) {
        const positionLocation = gl.getAttribLocation(gl.program, "a_position");

        const positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

        gl.enableVertexAttribArray(positionLocation);
        const size = 3;
        const type = gl.FLOAT;
        const normalize = false;
        const stride = 0;
        const offset = 0;
        gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);

    }


    function setTexture(gl){

        // Create a texture.
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);

        const ctx = document.createElement("canvas").getContext("2d");

        ctx.canvas.width = 128;
        ctx.canvas.height = 128;

        const faceInfos = [
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, faceColor: '#F00', textColor: '#0FF', text: '+X' },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, faceColor: '#FF0', textColor: '#00F', text: '-X' },
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, faceColor: '#0F0', textColor: '#F0F', text: '+Y' },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, faceColor: '#0FF', textColor: '#F00', text: '-Y' },
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, faceColor: '#00F', textColor: '#FF0', text: '+Z' },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
        ];

        faceInfos.forEach((faceInfo) => {
            const { target, faceColor, textColor, text } = faceInfo;
            generateFace(ctx, faceColor, textColor, text);

            // Upload the canvas to the cube map face.
            const level = 0;
            const internalFormat = gl.RGBA;
            const format = gl.RGBA;
            const type = gl.UNSIGNED_BYTE;
            gl.texImage2D(target, level, internalFormat, format, type, ctx.canvas);

        })

        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
        // Tell the shader to use texture unit 0 for u_texture
        const textureLocation = gl.getUniformLocation(gl.program, "u_texture");
        gl.uniform1i(textureLocation, 0);
    }

    main()

</script>
</body>
</html>


参考资料

WebGL Cubemaps

WebGL Environment Maps (reflections)

WebGL SkyBox

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

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

相关文章

[PyTorch][chapter 33][卷积神经网络]

前言 参考&#xff1a; 《数字图像处理与机器视觉》 第五章 空间域图像增强&#xff0c; 图像卷积&#xff1a; 空间域图像增强 图像增强是根据特定需要突出一副图像中的某些信息&#xff0c;同时削弱或去除 某些不需要信息的处理方法,其主要目的是是的处理后的图像对某种特定的…

有人问C# web怎么部署iis上面

C# web怎么部署iis上面 C# web项目怎么来&#xff1f;&#xff1f;&#xff1f;IIS作用 IIS怎么开启步骤一&#xff1a;打开控制面板->程序->启动或关闭windows 功能->找到**Internet Information Services**步骤二&#xff1a;勾选 web管理工具&#xff0c;http错误&…

超好用的工具类网站分享,打工人必备!

今天给大家推荐几个超好用的工具类网站&#xff0c;无论是工作还是生活中堪称打工人必备工具。 Background Maker https://colorkit.co/background-maker/82cedd-b04a0d-56d8a9/ 网站内置了 7 种不同风格的背景&#xff0c;如渐变色、波浪、低三角形等&#xff0c;非常适合用作…

基于梯度提升决策树的组合特征方法,《百面机器学习》学习笔记

《百面机器学习》学习笔记&#xff1a;基于梯度提升决策树的组合特征方法 基于梯度提升决策树的组合特征方法梯度提升决策树这里举一个例子来说明梯度提升决策树的思想方法假设对于某种类型的输入&#xff0c;经过上述介绍的梯度提升决策树构建流程得到的模型如下图所示 基于梯…

开源的表单系统效果怎么样?

当前&#xff0c;开源的表单系统得到了企业的重视和喜爱&#xff0c;因为它的简洁、灵活、便捷等优势特点&#xff0c;助力企业提质增效&#xff0c;各部门之间的协作沟通效率得到提升&#xff0c;数据资源也获得了有效整理和应用。今天&#xff0c;我们就一起来看看开源的表单…

不愧是腾讯 ,问的贼细

腾讯软件测试岗位的面试流程可能会因个人经验和公司而异&#xff0c;但通常情况下&#xff0c;腾讯软件测试的面试分为初试、二面、三面和四面。以下是每一轮面试可能涉及到的问题&#xff1a; 初试&#xff1a; 请介绍一下自己&#xff0c;以及为什么想要加入腾讯软件测试团…

oracle 学习之 unpivot/pivot函数及hive实现该功能

Oracle中pivot函数详解_实泽有之&#xff0c;无泽虚之的博客-CSDN博客pivot函数格式&#xff1a;pivot(聚合函数 for 需要转为列的字段名 in(需要转为列的字段值))&#xff1b;pivot函数说明&#xff1a;实现将指定字段的值转换为列的效果。https://blog.csdn.net/qq_40018576/…

最新DNDC模型在土地利用变化、未来气候变化下的建模方法及温室气体时空动态模拟实践技术应用

由于全球变暖、大气中温室气体浓度逐年增加等问题的出现&#xff0c;“双碳”行动特别是碳中和已经在世界范围形成广泛影响。。DNDC&#xff08;Denitrification-Decomposition&#xff0c;反硝化-分解模型&#xff09;是目前国际上最为成功的模拟生物地球化学循环的模型之一&a…

Flutter报错Building with plugins requires symlink support的解决方法

错误 Building with plugins requires symlink support. Please enable Developer Mode in your system settings. Runstart ms-settings:developers to open settings. 原因 这个错误表示你的系统尚未启用开发者模式,所以无法使用Flutter的插件功能。 Flutter插件会通过符号…

OJ刷题 第十六篇(递推较多,难)

31012 - 贴瓷砖 时间限制 : 1 秒 内存限制 : 32 MB 有一块大小是 2 * n 的墙面&#xff0c;现在需要用2种规格的瓷砖铺满&#xff0c;瓷砖规格分别是 2 * 1 和 2 * 2&#xff0c;请计算一共有多少种铺设的方法。 输入 输入的第一行包含一个正整数T&#xff08;T<20&…

第十三章 使用DHCP动态管理主机地址

文章目录 第十三章 使用DHCP动态管理主机地址一、动态主机地址管理协议1、DHCP简介2、DHCP常见术语 二、部署DHCP服务程序1、安装DHCP服务程序2、配置文件参考模板3、dhcpd服务程序配置文件中常见参数及作用 三、自动管理IP地址1、机房所用的网络地址以及参数信息2、关闭虚拟网…

Agisoft Metashape 红外影像处理

系列文章目录 文章目录 系列文章目录前言一、加载红外影像二、对齐照片三、构建 DEM四、生成 DOM五、温度值可视化前言 Agisoft Metashape 专业版支持处理来自 AscTec(ARA 格式)、WIRIS(TIFF 格式)热成像仪和以 R-JPEG(FLIR 数据)格式保存数据的热成像数据。 在本文中,…

如何编写自动化测试用例,一篇带你解决

自动化测试脚本 什么是自动化测试&#xff1f; 自动化测试是验证和验证软件是否满足所有用户需求&#xff0c;并使用自动化工具按预期运行。它检查在产品开发阶段期间和之后出现的错误、问题和其他类型的缺陷。这种类型的软件测试运行在由测试工具处理的编程脚本上。有多种测…

linux基础(IO)

目录&#xff1a; 1.复习C文件IO相关操作 2.介绍C程序会默认打开3个输入输出流 3.学习文件的系统调用接口 ---------------------------------------------------------------------------------------------------------------------------- 1.复习C文件IO相关操作 fopen函数…

HTTP协议格式及 fiddler 的使用

HTTP 协议格式 简述 HTTP 协议fiddler(抓包工具) 的使用协议格式总结 : 简述 HTTP 协议 HTTP 协议是个使用非常广泛的应用层协议, 应用层协议通常是需要 “自定义协议” 的, 自定义协议可以基于现有协议进行定制, HTTP 之所以应用广泛就是因为其可定制性非常强. 日常生活中遇到…

网络安全工程师的岗位需求,破237万了!

3月底凌晨&#xff0c;腾讯QQ和微信的相关业务都出现了功能异常。 包括微信语音对话、朋友圈、微信支付&#xff0c;以及QQ文件传输、QQ空间和QQ邮箱在内的多个功能无法使用。 这事儿都听说了吧&#xff1f; 据报道&#xff0c;这次的事故是因为电信机房冷却系统故障导致。 …

Linux安装Tomcat搭建Jenkins

前提条件 由于Tomcat服务器主要用于部署java web项目。所以先安装java jdk。安装过程大家应该很轻车熟练了。 一、安装Tomcat 创建/usr/tomcat文件夹 mkdir -p /usr/tomcat下载tomcat安装包 wget https://archove.apache.org/dist/tomcat/tomcat-8/v8.0.23/bin/apache-tomcat…

umi4 实现msal aad 登录loginRedirect方式并获取令牌

在 Umi 4 中使用 Ant Design Pro Layout 以及 MSAL (Microsoft Authentication Library) 实现登录并获取令牌进入到首页&#xff0c;你需要按照以下步骤进行操作&#xff1a; 安装所需依赖&#xff1a; 使用 npm 或 yarn 安装所需的包&#xff1a; npm install azure/msal-bro…

从零开始Vue3+Element Plus后台管理系统(十)——自定义水印指令与全局注册

在实际项目开发中&#xff0c;自定义指令用得还是比较多的&#xff0c;比如&#xff1a;复制粘贴、输入框防抖、输入框禁止特殊字符、权限校验、背景水印、拖拽等等… 指令确实是个优雅的存在。 Vue3中定义一个普通的自定义指令的详细说明参见官网&#xff1a;https://cn.vue…

sentinel配置持久化到apollo

背景 sentinel-dashborad的配置默认是存储到内存中的&#xff0c;生产环境肯定不能这样使用&#xff0c;官网支持zookeeper、nacos、apollo的配置&#xff0c;本文就来介绍apollo的持久化 apollo sentinel-dashboard 整合 apollo 进行规则的持久化配置&#xff0c;主要方式是…