Cube Map 系列之:手把手教你 实现 环境光贴图

news2025/1/19 8:27:43

什么是环境光贴图

下面先看两个例子:

  • 使用左侧的纹理 渲染茶壶,得到茶壶对真实空间的反射效果
    在这里插入图片描述
  • 同样使用左侧的纹理,得到中心的球对四周物体的反射效果
    在这里插入图片描述
    所以,环境光贴图指的是通过构建物体周围世界的纹理,使用纹理贴图的方式得到该物体对周围世界的反射效果。

环境光贴图的方式

环境光贴图常见的两种方式如下,图中也描述了其各自的优缺点,下面我们尝试使用cube map的方式来实现环境光贴图。

  1. cube map
    在这里插入图片描述

  2. Spherical Environment Map

在这里插入图片描述

实现环境光贴图

首先,我们先看最后的效果。
在这里插入图片描述
下面我们会分为如下几个部分在上一博客:Cube Map 系列之:手把手教你 使用 立方体贴图的代码基础上进行完善

  • 更新纹理材料,使用环境光纹理替换上一博客中自动生成的纹理
  • 通过更新法线信息,使得物体在更新状态的过程中,获取其世界法线方向对应的纹理(而非模型坐标下的法线方向)
  • 通过入射方向和法线方向,获取其反射方向,获取反射状态的纹理

步骤1:更新法线纹理

  • 环境光贴图资源
    在这里插入图片描述

  • 修改setTexture

// 创建纹理
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,
    url: 'resources/pos-x.jpg',
  },
  {
    target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
    url: 'resources/neg-x.jpg',
  },
  {
    target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
    url: 'resources/pos-y.jpg',
  },
  {
    target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
    url: 'resources/neg-y.jpg',
  },
  {
    target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
    url: 'resources/pos-z.jpg',
  },
  {
    target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
    url: 'resources/neg-z.jpg',
  },
];

faceInfos.forEach((faceInfo) => {
  const {target, url} = faceInfo;

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

  const image = new Image();
  image.src = url;
  image.addEventListener("load", function (){
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
    gl.texImage2D(target, level, internalFormat, format, type, image);
    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
  })
})

gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
  • 效果如下
    在这里插入图片描述

步骤2:使用世界法线去获取纹理

修改顶点着色器

  1. 传入法线信息
  2. 传入 m 矩阵
  3. 计算 v_worldNormal,用于后续计算反射方向
  4. 计算v_worldPosition,用于结合相机位置计算入射方向
  const V_SHADER_SOURCE = '' +
          'attribute vec4 a_position;' +
          'attribute vec3 a_normal;' +
          'uniform mat4 u_projection;' +
          'uniform mat4 u_view;' +
          'uniform mat4 u_world;' +
          'varying vec3 v_worldPosition;' +
          'varying vec3 v_worldNormal;' +
          'void main(){' +
          'gl_Position = u_projection * u_view * u_world * a_position;' +
          'v_worldPosition = (u_world * a_position).xyz;' +
          'v_worldNormal = mat3(u_world) * normalize(a_position.xyz);' +
          '}'

传入法线信息

  • 新增函数setNormals()
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normal, gl.STATIC_DRAW);

const normalLocation = gl.getAttribLocation(gl.program, "a_normal");
gl.enableVertexAttribArray(normalLocation);
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(normalLocation, size, type, normalize, stride, offset);

传入 m、v、p等uniform

  • 修改函数updateMatrix(time)
// 获取project的位置
const projectionLocation = gl.getUniformLocation(gl.program, "u_projection");
// 获取view的位置
const viewLocation = gl.getUniformLocation(gl.program, "u_view");
// 获取world(模型变换矩阵)的位置
const worldLocation = gl.getUniformLocation(gl.program, "u_world");
// 获取纹理的位置
const textureLocation = gl.getUniformLocation(gl.program, "u_texture");
// 获取相机坐标的位置
const worldCameraPositionLocation = gl.getUniformLocation(gl.program, "u_worldCameraPosition");

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);

let worldMatrix = m4.xRotation(modelXRotationRadians);
worldMatrix = m4.yRotate(worldMatrix, modelYRotationRadians);


// 分别设置对应的uniforms
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
gl.uniformMatrix4fv(viewLocation, false, viewMatrix);
gl.uniformMatrix4fv(worldLocation, false, worldMatrix);
gl.uniform3fv(worldCameraPositionLocation, cameraPosition);
gl.uniform1i(textureLocation, 0);

修改片元着色器

  • 使用顶点着色器计算得到的世界法线 来获取纹理
gl_FragColor = textureCube(u_texture, normalize(v_worldNormal));

效果

请添加图片描述

  • 可以看见,现在已经初具成效,但是其效果并非是反射,而且以立方体的片元所在位置的法线向量去获取纹理,相当于真实世界的纹理投射到一个旋转的立方体上,因此在拐角处也会出现明显的拉升

步骤3:使用反射方向去获取纹理

如下图所示

  • 通过相机位置和所看的片元世界坐标,可以获取入射角度
  • 结合法线方向 可以获取反射方向
  • 通过反射方向,可以计算得到真实的反射纹理
    在这里插入图片描述

修改片元着色器

    const F_SHADER_SOURCE = '' +
        'precision mediump float;' +
        'varying vec3 v_worldPosition;' +
        'varying vec3 v_worldNormal;' +
        'uniform samplerCube u_texture;' +
        'uniform vec3 u_worldCameraPosition;' +
        'void main(){' +
        'vec3 worldNormal = normalize(v_worldNormal);' +
        'vec3 eyeToSurfaceDir = normalize(v_worldPosition - u_worldCameraPosition);' +
        'vec3 direction = reflect(eyeToSurfaceDir, worldNormal);' +
        ' gl_FragColor = textureCube(u_texture, direction);' +
        '}'

修改顶点着色器

  • 使用传入的a_normal替换normalize(a_position.xyz)
v_worldNormal = mat3(u_world) * a_normal;

效果

在这里插入图片描述

完整代码

<!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;' +
        'attribute vec3 a_normal;' +
        'uniform mat4 u_projection;' +
        'uniform mat4 u_view;' +
        'uniform mat4 u_world;' +
        'varying vec3 v_worldPosition;' +
        'varying vec3 v_worldNormal;' +
        'void main(){' +
        'gl_Position = u_projection * u_view * u_world * a_position;' +
        'v_worldPosition = (u_world * a_position).xyz;' +
        'v_worldNormal = mat3(u_world) * a_normal;' +
        '}'

    const F_SHADER_SOURCE = '' +
        'precision mediump float;' +
        'varying vec3 v_worldPosition;' +
        'varying vec3 v_worldNormal;' +
        'uniform samplerCube u_texture;' +
        'uniform vec3 u_worldCameraPosition;' +
        'void main(){' +
        'vec3 worldNormal = normalize(v_worldNormal);' +
        'vec3 eyeToSurfaceDir = normalize(v_worldPosition - u_worldCameraPosition);' +
        'vec3 direction = reflect(eyeToSurfaceDir, worldNormal);' +
        ' gl_FragColor = textureCube(u_texture, direction);' +
        '}'


    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;
        }

        setGeometry(gl, getGeometry());
        setNormals(gl, getNormals())
        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);


            updateMatrix(gl, time);

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

            requestAnimationFrame(drawScene);
        }

        function updateMatrix(gl, time){
            const projectionLocation = gl.getUniformLocation(gl.program, "u_projection");
            const viewLocation = gl.getUniformLocation(gl.program, "u_view");
            const worldLocation = gl.getUniformLocation(gl.program, "u_world");
            const textureLocation = gl.getUniformLocation(gl.program, "u_texture");
            const worldCameraPositionLocation = gl.getUniformLocation(gl.program, "u_worldCameraPosition");

            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);

            let worldMatrix = m4.xRotation(modelXRotationRadians);
            worldMatrix = m4.yRotate(worldMatrix, modelYRotationRadians);


            // Set the uniforms
            gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
            gl.uniformMatrix4fv(viewLocation, false, viewMatrix);
            gl.uniformMatrix4fv(worldLocation, false, worldMatrix);
            gl.uniform3fv(worldCameraPositionLocation, cameraPosition);
            gl.uniform1i(textureLocation, 0);
        }

    }

    /**
     * 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,
                url: 'resources/pos-x.jpg',
            },
            {
                target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
                url: 'resources/neg-x.jpg',
            },
            {
                target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
                url: 'resources/pos-y.jpg',
            },
            {
                target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
                url: 'resources/neg-y.jpg',
            },
            {
                target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
                url: 'resources/pos-z.jpg',
            },
            {
                target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
                url: 'resources/neg-z.jpg',
            },
        ];

        faceInfos.forEach((faceInfo) => {
            const {target, url} = faceInfo;

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

            const image = new Image();
            image.src = url;
            image.addEventListener("load", function (){
                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
                gl.texImage2D(target, level, internalFormat, format, type, image);
                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
            })


        })

        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

    }
    function setNormals(gl, normal){
        const normalBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, normal, gl.STATIC_DRAW);
        //
        const normalLocation = gl.getAttribLocation(gl.program, "a_normal");
        gl.enableVertexAttribArray(normalLocation);
        const size = 3;
        const type = gl.FLOAT;
        const normalize = false;
        const stride = 0;
        const offset = 0;
        gl.vertexAttribPointer(normalLocation, size, type, normalize, stride, offset);
    }

    function getNormals() {
        return new Float32Array(
            [
                0, 0, -1,
                0, 0, -1,
                0, 0, -1,
                0, 0, -1,
                0, 0, -1,
                0, 0, -1,

                0, 0, 1,
                0, 0, 1,
                0, 0, 1,
                0, 0, 1,
                0, 0, 1,
                0, 0, 1,

                0, 1, 0,
                0, 1, 0,
                0, 1, 0,
                0, 1, 0,
                0, 1, 0,
                0, 1, 0,

                0, -1, 0,
                0, -1, 0,
                0, -1, 0,
                0, -1, 0,
                0, -1, 0,
                0, -1, 0,

                -1, 0, 0,
                -1, 0, 0,
                -1, 0, 0,
                -1, 0, 0,
                -1, 0, 0,
                -1, 0, 0,

                1, 0, 0,
                1, 0, 0,
                1, 0, 0,
                1, 0, 0,
                1, 0, 0,
                1, 0, 0,
            ]);
    }
    main()

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

参考资料

WebGL Cubemaps

WebGL Environment Maps (reflections)

WebGL SkyBox

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

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

相关文章

25的大学生转行学云计算,能拿到10k+的月薪,是真的吗?

25的大学生转行学云计算&#xff0c;能拿到10k的月薪&#xff0c;是真的吗&#xff1f; 对于IT行业来说&#xff0c;月薪上万并不少见&#xff0c;毕竟互联网常年占据行业薪资排行榜首。作为技术行业&#xff0c;由于其发展的前沿性&#xff0c;引导性&#xff0c;也是作为其他…

26-2 vue-router

原始的方式好多东西需要我们自己去写&#xff0c;vue-router是一个集成好了的路由包&#xff0c;vue-router 官网 Vue Router | Vue.js 的官方路由 并非原始的东西就不好&#xff0c;只要是包就可能存在版本兼容问题&#xff0c;如果是简单的需求就建议用原始的方法 目录 1 …

如何进行远程控制电脑

电脑在我们日常生活中的作用是非常大的&#xff0c;尤其是在信息时代地位非常高。 其中&#xff0c;最常见、最具代表性的功能是实现远程控制功能。它可以直接解决一些问题&#xff0c;而不需要去现场&#xff0c;在一定程度上提高了工作效率。但是有很多朋友不知道如何实现远…

边缘计算盒子有哪些?边缘计算应用场景

边缘计算&#xff08;Edge Computing&#xff09;是一种分布式计算模型&#xff0c;旨在将数据处理和计算功能从中心数据中心移到数据源附近的边缘设备上。它的目标是在接近数据生成的地方进行实时数据处理和分析&#xff0c;减少数据传输延迟和网络拥塞&#xff0c;提高应用程…

计算机图形学-GAMES101-2

Vectors向量 一、向量的介绍 表示一个方向。计算向量的方法&#xff1a;AB &#xff08;B-A&#xff09;。向量对应的单位向量 AB / ||AB|| 。向量具有平移性&#xff0c;我们不关心它的开始位置。向量求和&#xff1a;三角形法则和平行四边形法则。在代数上计算直接把向量的…

如何防止网站被黑客攻击?黑客是怎样炼成的?

现在的黑客网站可谓是多如牛毛&#xff0c;不管在哪里只要你愿意学&#xff0c;都可以学到一招半式。看过别人的个性签名:卖菜的王大妈是黑客&#xff0c;烤红薯的李大爷也是黑客&#xff0c;对面成人用品店的老板&#xff0c;挖日&#xff0c;还是黑客-_-~!..黑客还真多啊!!!据…

关于对自动化测试的理解:目的与本质!(新手必看)

其实自动化测试很好理解&#xff0c;由两部分组成&#xff0c;“自动化”和“测试”&#xff0c;所以我们要理解自动化测试&#xff0c;就必须理解“自动化”和“测试”&#xff0c;只有理解了这些概念&#xff0c;才能更轻松的做好的自动化测试。其中“自动化”可以想象成通过…

晶飞FLA5000光谱仪.FlaSpec格式解析批处理导出CSV文件

引言 首先说明下晶飞上位机软件存在的问题&#xff0c;实验所采用的FLA5000型号光谱仪&#xff0c;光谱波段从280-970nm&#xff0c;FWHM值为2.4nm。 1、上位机软件中的光谱数据复制功能基本是废的&#xff0c;最多只能到599.9nm&#xff0c;后面的数据全部消失。 2、上位机软…

2023系统分析师---软件工程、系统规划高频错题

系统规划---成本效益分析 评价信息系统经济效益常用的方法主要有成本效益分析法,投入产出分析法和价值工程方法。盈亏平衡法常用于销售定价; 可行性分析 系统规划是信息系统生命周期的第一个阶段,其任务是对企业的环境、目标以及现有系统的状况进行初步调查,根据企业目标…

示波器的数据处理怎么记录?

示波器的使用 - 记录和保存示波器测试结果 安泰测试为您分享如何记录示波器的数据。 "从您把示波器探头连接到器件的那一刻起&#xff0c;信号就开启了一次瞬间即可完成的重大旅程。它必须 跨过五个不同的“模块”&#xff0c;才能完成从器件到示波器&#xff0c;最后返回…

Vue电商项目--开发floor模块

获取floor组件mock数据 开发floor组件 在开发之前&#xff0c;说一下之前存在的一些小毛病 那就是在开发vue中还需要通过dom这种方式来操作元素吗&#xff1f; 我们用ref来做这个 我们先写api 然后去找仓库 getFloorList这个action在哪里触发&#xff0c;是需要在Home路由组件…

计算机组成原理硬件实验 - 计算机组成实验FAQ作业

一、 Quartus II 9.0 (32-Bit) 使用问题&#xff1a; Q1【示例】: 显示【未授权】或者【编译成功不能生成SOF文件】 A1&#xff1a; 在quartus中点击【TOOLS】→【license setup】,在license.DAT中修改“HOSTID”后的字段&#xff0c;设置为“你的电脑最新显卡ID” Q2【示例】…

怎么学习网络安全?这篇文带你从入门级开始学习网络安全

随着网络安全被列为国家安全战略的一部分&#xff0c;这个曾经细分的领域发展提速了不少&#xff0c;除了一些传统安全厂商以外&#xff0c;一些互联网大厂也都纷纷加码了在这一块的投入&#xff0c;随之而来的吸引了越来越多的新鲜血液不断涌入。 不同于Java、C/C等后端开发岗…

网络安全学什么

由于我之前写了不少网络安全技术相关的故事文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人在微信里问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#xff1f;有哪些方向&#xff1f;怎么选&#xff…

卷麻了,可别再为难软件测试人了

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

最“拼爹妈”的美国大学TOP10,什么是Legacy 录取?

Legacy的直译是遗产的意思&#xff0c;父母和大学的关系&#xff0c;就像家庭的遗产一样&#xff0c;可以传递给孩子一代&#xff08;有时候&#xff0c;亲哥哥&#xff0c;亲姐姐也会被算作是 legacy&#xff09;。有的大学祖父母如果是校友&#xff0c;也会算作 legacy。再远…

Docker部署配置Gitlab

Docker部署配置Gitlab 1 参考文档2 Gitlab相关介绍2.1 Gitlab2.2 Git和SVN的区别2.3 Git、Gitlab、GitHub的简单区别 3 搭建Gitlab仓库3.1 拉取镜像3.2 启动容器 4 修改配置文件并配置邮箱4.1 开放linux端口4.2 设置IP、端口4.3 配置邮箱4.4 让配置生效 5 管理员登录Gitlab6 创…

【输变电线路 JL-8C/12反时限电流继电器 报警信号、切除故障 JOSEF约瑟】

系列型号 JL-8C/11反时限电流继电器&#xff1b; JL-8C/12反时限电流继电器&#xff1b; JL-8C/12X反时限电流继电器&#xff1b; JL-8C/21-1反时限电流继电器&#xff1b; JL-8C/21-2反时限电流继电器&#xff1b; JL-8C/21-3反时限电流继电器&#xff1b; JL-8C/21-4反…

在线域名批量查询工具-在什么网站可以挖到老域名

怎么能挖掘到好域名 挖掘到好域名对于网站的建设和SEO排名是非常重要的&#xff0c;因为好的域名可以提高网站的置信度&#xff0c;增加自然引荐的数量&#xff0c;并且可以在搜索引擎排名中获得优势。下面介绍一些优秀的老域名挖掘方法&#xff0c;以及一种常用、免费的老域名…

verflow属性的常用值详解

什么是overflow 在CSS中&#xff0c;overflow是“溢出”的意思&#xff0c;该属性规定当内容溢出元素框时发生的事情&#xff0c;设置内容是否会被修剪&#xff0c;溢出部分是否会被隐藏&#xff1b;例如当属性值设置为“visible”则内容不会被修剪&#xff0c;为“hidden”则内…