WebGL系列教程十一(光照原理及Blinn Phong着色模型)

news2024/10/9 13:38:19

快速导航(持续更新中)

WebGL系列教程一(开篇)
WebGL系列教程二(环境搭建及着色器初始化)
WebGL系列教程三(使用缓冲区绘制三角形)
WebGL系列教程四(绘制彩色三角形)
WebGL系列教程五(使用索引绘制彩色立方体)
WebGL系列教程六(纹理映射与立方体贴图)
WebGL系列教程七(二维及三维旋转、平移、缩放)
WebGL系列教程八(GLSL着色器基础语法)
WebGL系列教程九(动画)
WebGL系列教程十(模型Model、视图View、投影Projection变换)
WebGL系列教程十一(光照原理及Blinn Phong着色模型)

目录

  • 快速导航(持续更新中)
  • 1 前言
  • 2 光源的分类
  • 3 光照的渲染
    • 3.1 高光
    • 3.2 漫反射
    • 3.3 环境光
  • 4 代码实现
    • 4.1 计算逆转置矩阵
    • 4.2 对法向量进行变换
    • 4.3 计算法向量和光线方向的点积
    • 4.4 计算漫反射分量
    • 4.5 计算环境光分量
    • 4.6 计算视线方向单位向量
    • 4.7 计算反射向量
    • 4.8 计算视线方向和反射方向的点积
    • 4.9 计算高光
    • 4.10 组合漫反射、环境光和高光
    • 4.11 完整代码
    • 4.12 效果
  • 5 总结

1 前言

  什么是光照?光照就是模拟出物体被光照射时的效果,使得渲染场景看起来更真实。那么WebGL在干什么?WebGL其实就是在计算继而还原每个像素的颜色和亮度。这就是我们这一节所要讲的内容,对一个立方体进行光照的渲染。

2 光源的分类

  我们常用的光源有点光源、面光源以及环境光。面光源是平行光,因为太阳离地球非常非常远,所以我们处理自然光时将太阳光也认为是面光源。常见的点光源有灯泡、火焰等,点光源照射在物体表面的角度是不一样的,因为会对着色有较大影响。环境光是指被墙壁等物体经过多次反射之后的光,环境光会从各个角度去照射物体,目前我们认为他们的强度都是一样的,且强度很小,因此通过一个微小的的值来代替(想要精确还原的同学可以自行学习光线追踪来进行模拟)。
在这里插入图片描述

3 光照的渲染

  在进行光照的渲染时,可以采用不同的方式,比如对面进行着色(Flat Shading),对顶点进行着色(Ground Shading),对像素进行着色(Phong Shading)。
三种着色方式对比
  本文中采用的是逐像素进行着色,即 Blinn Phong Shading,是在Phong Shading的基础上对高光项进行了改进。Blinn Phong Shading 对光照进行渲染时分成了三个部分,即高光、漫反射、环境光。
Blinn Phong着色模型

3.1 高光

在这里插入图片描述
  如图所示,v为观察方向,R为镜面反射方向,当Rv足够接近时,就会产生高光。这两向量的夹角可以通过单位向量点乘得到。因为这个角度不好计算,因此取入射方向加上出射方向结果的一半,即半程向量h和法线n的夹角来计算这个角度。在实际应用中,会对这个夹角的余弦值取m次方来满足需要。
在这里插入图片描述
在这里插入图片描述
  可以看出,次方m取值越大,高光的范围越小。

3.2 漫反射

  漫反射的反射光在各个方向上是均匀分布的,现实中的很多材质,比如纸张、岩石、塑料,表面都是粗糙的,在这种情况下,反射光将会以不固定的角度反射出去,漫反射正是在此基础上建立的反射模型。
在这里插入图片描述

3.3 环境光

  环境光下的反射称为环境反射,环境反射光的方向可以认为就是入射光的方向,由于环境光照射物体的方式是各方向均匀的、强度相等的,所以反射光也是各项均匀的。

在这里插入图片描述

4 代码实现

  好了,讲完了理论,我们来进行一下实操。现在我们要对每个像素进行操作,因此先在片元着色器中进行声明:

// u_LightColor: 光源的颜色
uniform vec3 u_LightColor;

// u_LightPosition: 光源的位置(世界坐标系中)
uniform vec3 u_LightPosition;

// u_AmbientLight: 环境光的颜色
uniform vec3 u_AmbientLight;

// u_ViewPosition: 观察者的位置(世界坐标系中)
uniform vec3 u_ViewPosition;

// u_Shininess: 高光反射的光泽度系数
uniform float u_Shininess;

// v_Normal: 从顶点着色器传递过来的法向量(世界坐标系中)
varying vec3 v_Normal;

// v_Position: 从顶点着色器传递过来的顶点位置(世界坐标系中)
varying vec3 v_Position;

// v_Color: 从顶点着色器传递过来的顶点颜色
varying vec4 v_Color;

4.1 计算逆转置矩阵

  首先我们要搞明白什么是逆转置矩阵?对一个矩阵先求逆矩阵,然后再求转置,就得到了逆转置矩阵。那么逆转置矩阵是干什么用的?我们之前讲过模型矩阵是对顶点进行变换的,逆转置矩阵是对法线进行变换的。当我们对模型进行了旋转、平移、缩放后,模型的法线也要跟随着变化,这时用逆转置矩阵就可以很方便的求出新的法线了。因此逆转置矩阵针对的是模型矩阵。

var modelMatrix = new Matrix4();  // 模型矩阵
// 计算用于变换法向量的矩阵,即逆转置矩阵
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();

4.2 对法向量进行变换

// u_NormalMatrix: 法向量变换矩阵,用于正确变换法向量
//glsl
uniform mat4 u_NormalMatrix;
// 对法向量进行变换并归一化
v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));

var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');  
// 将法向量变换矩阵传递给 u_NormalMatrix
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

4.3 计算法向量和光线方向的点积

// 计算从光源到片段的方向向量并进行归一化
vec3 lightDirection = normalize(u_LightPosition - v_Position);

// 计算法向量和光线方向的点积(用于漫反射计算)
float nDotL = max(dot(lightDirection, normal), 0.0);

4.4 计算漫反射分量

// 计算漫反射分量
vec3 diffuse = u_LightColor * nDotL;

4.5 计算环境光分量

// 计算环境光分量
vec3 ambient = u_AmbientLight * v_Color.rgb;

4.6 计算视线方向单位向量

// 计算视线方向向量(从观察者到片段的位置)并进行归一化
vec3 viewDirection = normalize(u_ViewPosition - v_Position);

4.7 计算反射向量

// 计算反射方向向量

vec3 reflectDirection = reflect(-lightDirection, normal);

4.8 计算视线方向和反射方向的点积

// 计算视线方向和反射方向的点积,并根据光泽度系数计算高光反射分量
float spec = pow(max(dot(viewDirection, reflectDirection), 0.0), u_Shininess);

4.9 计算高光

// 计算高光分量,使其接近白色
vec3 specular = vec3(1.0, 1.0, 1.0) * spec;

4.10 组合漫反射、环境光和高光

// 最终颜色由漫反射、环境光和高光三部分组成
gl_FragColor = vec4(diffuse + ambient + specular, v_Color.a);

4.11 完整代码

// 顶点着色器
<script id="vertex-shader" type="x-shader/x-vertex">
  // a_Position: 顶点位置
  // a_Color: 顶点颜色
  // a_Normal: 顶点法向量
  attribute vec4 a_Position;
  attribute vec4 a_Color;
  attribute vec4 a_Normal;
  
  // u_MvpMatrix: 模型视图投影矩阵,用于将顶点从模型坐标转换为裁剪坐标
  uniform mat4 u_MvpMatrix;
  
  // u_ModelMatrix: 模型矩阵,用于将顶点从局部坐标变换到世界坐标
  uniform mat4 u_ModelMatrix;
  
  // u_NormalMatrix: 法向量变换矩阵,用于正确变换法向量
  uniform mat4 u_NormalMatrix;
  
  // v_Color: 传递给片段着色器的顶点颜色
  varying vec4 v_Color;
  
  // v_Normal: 传递给片段着色器的法向量(经过变换后的世界坐标系中的法向量)
  varying vec3 v_Normal;
  
  // v_Position: 传递给片段着色器的顶点位置(世界坐标系中的位置)
  varying vec3 v_Position;
  
  void main() {
    // 计算顶点位置在裁剪坐标系中的位置
    gl_Position = u_MvpMatrix * a_Position;
    
    // 计算顶点在世界坐标系中的位置
    v_Position = vec3(u_ModelMatrix * a_Position);
    
    // 对法向量进行变换并归一化
    v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
    
    // 将顶点颜色传递给片段着色器
    v_Color = a_Color;
  }
</script>
// 片段着色器
<script id="vertex-shader" type="x-shader/x-vertex">
#ifdef GL_ES
  precision mediump float;  // 设置浮点数精度
  #endif

  // u_LightColor: 光源的颜色
  uniform vec3 u_LightColor;
  
  // u_LightPosition: 光源的位置(世界坐标系中)
  uniform vec3 u_LightPosition;
  
  // u_AmbientLight: 环境光的颜色
  uniform vec3 u_AmbientLight;
  
  // u_ViewPosition: 观察者的位置(世界坐标系中)
  uniform vec3 u_ViewPosition;
  
  // u_Shininess: 高光反射的光泽度系数
  uniform float u_Shininess;
  
  // v_Normal: 从顶点着色器传递过来的法向量(世界坐标系中)
  varying vec3 v_Normal;
  
  // v_Position: 从顶点着色器传递过来的顶点位置(世界坐标系中)
  varying vec3 v_Position;
  
  // v_Color: 从顶点着色器传递过来的顶点颜色
  varying vec4 v_Color;

  void main() {
    // 对法向量进行归一化处理
    vec3 normal = normalize(v_Normal);

    // 计算从光源到片段的方向向量并进行归一化
    vec3 lightDirection = normalize(u_LightPosition - v_Position);

    // 计算法向量和光线方向的点积(用于漫反射计算)
    float nDotL = max(dot(lightDirection, normal), 0.0);

    // 计算漫反射分量
    vec3 diffuse = u_LightColor * nDotL;

    // 计算环境光分量
    vec3 ambient = u_AmbientLight * v_Color.rgb;

    // 计算视线方向向量(从观察者到片段的位置)并进行归一化
    vec3 viewDirection = normalize(u_ViewPosition - v_Position);

    // 计算反射方向向量
    vec3 reflectDirection = reflect(-lightDirection, normal);

    // 计算视线方向和反射方向的点积,并根据光泽度系数计算高光反射分量
    float spec = pow(max(dot(viewDirection, reflectDirection), 0.0), u_Shininess);

    // 计算高光分量,使其接近白色
    vec3 specular = vec3(1.0, 1.0, 1.0) * spec;

    // 最终颜色由漫反射、环境光和高光三部分组成
    gl_FragColor = vec4(diffuse + ambient + specular, v_Color.a);
  }
</script>
function main() {
  // 获取 <canvas> 元素
  var canvas = document.getElementById('webgl');

  // 获取 WebGL 的渲染上下文
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('无法获取 WebGL 的渲染上下文');
    return;
  }

  // 初始化着色器
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('无法初始化着色器');
    return;
  }

  // 初始化顶点缓冲区
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('无法设置顶点信息');
    return;
  }

  // 设置清除颜色并启用深度测试
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);

  // 获取 uniform 变量的存储位置
  var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
  var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
  var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
  var u_ViewPosition = gl.getUniformLocation(gl.program, 'u_ViewPosition');
  var u_Shininess = gl.getUniformLocation(gl.program, 'u_Shininess');
  if (!u_ModelMatrix || !u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight || !u_ViewPosition || !u_Shininess) { 
    console.log('无法获取存储位置');
    return;
  }

  // 设置光的颜色(白色)
  gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
  // 设置光源的位置(世界坐标系中)
  gl.uniform3f(u_LightPosition, 8.5, 4.0, 3.5);
  // 设置环境光
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
  // 设置观察者的位置
  gl.uniform3f(u_ViewPosition, 6.0, 6.0, 14.0);
  // 设置光泽度系数
  gl.uniform1f(u_Shininess, 128.0);

  var modelMatrix = new Matrix4();  // 模型矩阵
  var mvpMatrix = new Matrix4();    // 模型视图投影矩阵
  var normalMatrix = new Matrix4(); // 法向量变换矩阵
  var currentAngle = 0.0;  // Current rotation angle
  function tick(){
    currentAngle = animate(currentAngle);  // Update the rotation angle
    // 计算模型矩阵
    modelMatrix.setRotate(currentAngle, 0, 1, 0); // 绕 y 轴旋转
    // 计算视图投影矩阵
    mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
    mvpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);
    mvpMatrix.multiply(modelMatrix);
    // 计算用于变换法向量的矩阵,即逆转置矩阵
    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();

    // 将模型矩阵传递给 u_ModelMatrix
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

    // 将模型视图投影矩阵传递给 u_MvpMatrix
    gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

      // 将法向量变换矩阵传递给 u_NormalMatrix
      gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

    // 清除颜色和深度缓冲区
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // 绘制立方体
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
    requestAnimationFrame(tick, canvas); // 开启动画,每一帧都去调用
  }
  tick();
}

function initVertexBuffers(gl) {
  // 创建立方体
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  // 顶点坐标
  var vertices = new Float32Array([
     2.0, 2.0, 2.0,  -2.0, 2.0, 2.0,  -2.0,-2.0, 2.0,   2.0,-2.0, 2.0, // v0-v1-v2-v3 前面
     2.0, 2.0, 2.0,   2.0,-2.0, 2.0,   2.0,-2.0,-2.0,   2.0, 2.0,-2.0, // v0-v3-v4-v5 右面
     2.0, 2.0, 2.0,   2.0, 2.0,-2.0,  -2.0, 2.0,-2.0,  -2.0, 2.0, 2.0, // v0-v5-v6-v1 上面
    -2.0, 2.0, 2.0,  -2.0, 2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0,-2.0, 2.0, // v1-v6-v7-v2 左面
    -2.0,-2.0,-2.0,   2.0,-2.0,-2.0,   2.0,-2.0, 2.0,  -2.0,-2.0, 2.0, // v7-v4-v3-v2 下面
     2.0,-2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0, 2.0,-2.0,   2.0, 2.0,-2.0  // v4-v7-v6-v5 后面
  ]);

  // 颜色
  var colors = new Float32Array([
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,     // v0-v1-v2-v3 前面
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,     // v0-v3-v4-v5 右面
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,     // v0-v5-v6-v1 上面
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,     // v1-v6-v7-v2 左面
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,     // v7-v4-v3-v2 下面
    0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0      // v4-v7-v6-v5 后面
  ]);

  // 法向量
  var normals = new Float32Array([
    0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 前面
    1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 右面
    0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 上面
   -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 左面
    0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 下面
    0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 后面
  ]);

  // 顶点的索引
  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // 前面
     4, 5, 6,   4, 6, 7,    // 右面
     8, 9,10,   8,10,11,    // 上面
    12,13,14,  12,14,15,    // 左面
    16,17,18,  16,18,19,    // 下面
    20,21,22,  20,22,23     // 后面
 ]);

  // 将顶点属性写入缓冲区(坐标、颜色和法向量)
  if (!initArrayBuffer(gl, 'a_Position', vertices, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Color', colors, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', normals, 3)) return -1;

  // 解除绑定缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // 将顶点索引写入缓冲区对象
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log('无法创建缓冲区对象');
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBuffer(gl, attribute, data, num) {
  // 创建缓冲区对象
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('无法创建缓冲区对象');
    return false;
  }
  // 将数据写入缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // 将缓冲区对象分配给 attribute 变量
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('无法获取 ' + attribute + ' 的存储位置');
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, gl.FLOAT, false, 0, 0);
  // 启用缓冲区对象分配
  gl.enableVertexAttribArray(a_attribute);

  return true;
}
// Rotation angle (degrees/second)
var ANGLE_STEP = 30.0;
// Last time that this function was called
var g_last = Date.now();
function animate(angle) {
  // Calculate the elapsed time
  var now = Date.now();
  var elapsed = now - g_last;
  g_last = now;
  // Update the current rotation angle (adjusted by the elapsed time)
  var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
  return newAngle %= 360;
}

4.12 效果

在这里插入图片描述
看下动画效果
在这里插入图片描述

5 总结

  本文介绍了光照原理的基础,并在此基础上讲解了法线计算、入射光计算、反射光计算、逆转置矩阵等等,最后我们使用Blinn Phong 着色模型,通过组合高光、漫反射和环境光,实现了对一个立方体的动态光照效果渲染。本文在理解上有一定的难度,希望读者仔细体会,回见~

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

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

相关文章

【Sceneform-EQR】(手势优化)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放

在上一篇【Sceneform-EQR】&#xff08;手势控制器实现&#xff09;通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放 我们实现了通过手势控制模型节点的旋转、缩放和平移。本文将介绍如何对上一篇做的手势控制器作优化&#xff0c;从而适用于场景相机发生改变…

长芯微LSPGD1系列带气嘴DIP8封装集成表压传感器完全替代松下ADP51B62替代ADP51B62,成本更低!

描述 LSPGD1是长芯微针对家电医疗等市场推出的经过校准的表压传感器系列产品。该系列产品采用高性能信号调理芯片对MEMS压阻芯体输出进行温度和压力的校准和补偿&#xff0c;保证性能和可靠性的同时对封装进行了集成&#xff0c;易于使用。LSPGD1系列集成压力传感器可选量程为…

QT<27> Qt中编写串口通讯,例如读RFID、EKS等等

一、添加文件 ①在项目中添加C文件&#xff0c;继承自QObject ②添加必要模块以及头文件 项目pro文件添加 serialport模块 项目.h文件中添加头文件 #include <QSerialPort> #include <QSerialPortInfo> 二、具体代码 ①在.h文件中声明一个类对象 QSerialPor…

计算机网络 tcp和udp

目录 一、TCP 建立连接-TCP 三次握手 1&#xff09; 什么是半连接队列和全连接队列&#xff1f; 2&#xff09; 为什么要三次握手? 3&#xff09; 三次握手过程中可以携带数据吗&#xff1f; 断开连接-TCP 四次挥手 1&#xff09; 为什么要四次挥手&#xff1f; 2&…

PointNet++网络详解

数据集转换 数据集转换的意义在于将原本的 txt 点云文件转换为更方便运算的npy点云文件&#xff0c;同时&#xff0c;将原本的xyzrgb这 6 个维度转换为xyzrgbc&#xff0c;最后一个c维度代表该点云所属的类别。 for anno_path in anno_paths:print(anno_path)try:elements a…

opencv学习:图像拼接及完整代码实现

概念 图像拼接是计算机视觉领域中的一项技术&#xff0c;它涉及将多个图像合并成一个连续的、无缝的全景图像。在OpenCV中&#xff0c;图像拼接通常包括以下几个关键步骤&#xff1a; 1. 编写代码 导入必要的库&#xff1a;导入sys、cv2和numpy库。定义显示图像的函数&#x…

大文件-分片上传 vue3+java

0.需求背景 遇到大文件上传时&#xff0c;会存在文件过大&#xff0c;后端无法一次性接受上传过程中&#xff0c;异常失败后&#xff0c;需要重新上传&#xff0c;耗时单次请求时间过长&#xff0c;请求受限 分片上传&#xff0c;相比于普通的单线程上传&#xff0c;速度更快&…

利士策分享,婚姻为何被称为大事?

利士策分享&#xff0c;婚姻为何被称为大事&#xff1f; 在历史的长河中&#xff0c;婚姻一直被视为人生中的头等大事&#xff0c;这一观念跨越时空&#xff0c;深深植根于各种文化和社会结构中。 古人为何将婚姻称为“大事”&#xff0c;这背后蕴含着丰富的社会、文化和心理寓…

JUC高并发编程6:Callable接口

1 创建线程的方式 在 Java 中&#xff0c;创建线程的方式主要有以下几种&#xff1a; 继承 Thread 类&#xff1a; 通过继承 Thread 类并重写 run() 方法来创建线程。示例代码&#xff1a;class MyThread extends Thread {Overridepublic void run() {// 线程执行的代码} }pub…

LeetCode题练习与总结:生命游戏--289

一、题目描述 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即…

如何运行服务器上的web页面,打开Outlook 365的全球离线通讯簿功能?

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

Linux入门攻坚——35、Linux防火墙-iptables-1

Firewall&#xff1a;防火墙&#xff0c;就是一个隔离工具。工作于主机或网络的边缘&#xff0c;对于进出本主机或网络的报文根据事先定义好的检查规则做匹配检测&#xff0c;对于能够被规则所匹配到的报文做出相应处理的组件&#xff1a;这个组件可以是硬件&#xff0c;也可以…

WPS的JS宏实现删除某级标题下的所有内容

想要删除Word文档中&#xff0c;包含特定描述的标题下所有内容&#xff08;包含各级子标题以及正文描述&#xff09;。 例如下图中&#xff0c;想删除1.2.1.19.1业务场景下所有内容&#xff1a; 简单版&#xff1a; 删除光标停留位置的大纲级别下所有的内容。实现的JS代码如下…

机器学习笔记-2

文章目录 一、Linear model二、How to represent this function三、Function with unknown parameter四、ReLU总结、A fancy name 一、Linear model 线性模型过于简单&#xff0c;有很大限制&#xff0c;我们需要更多复杂模式 蓝色是线性模型&#xff0c;线性模型无法去表示…

ubuntu 开放 8080 端口快捷命令

文章目录 查看防火墙状态开放 80 端口开放 8080 端口开放 22端口开启防火墙重启防火墙**使用 xhell登录**&#xff1a; 查看防火墙状态 sudo ufw status [sudo] password for crf: Status: inactivesudo ufw enable Firewall is active and enabled on system startup sudo…

Flutter 3.24 发布:GPU模块及多视图嵌入功能

Flutter 3.24 发布&#xff1a;GPU模块及多视图嵌入功能 Flutter 3.24 带来了许多新功能和改进&#xff0c;让开发应用程序变得更加容易和有趣。这个版本重点展示了 Flutter GPU 的预览功能&#xff0c;让应用程序可以直接使用高级图形和 3D 场景功能。 此外&#xff0c;网页…

传智杯 第六届—B

题目&#xff1a; 擂台赛要开始了&#xff0c;现在有 n 名战士&#xff0c;其中第 i 名战士的战斗力为 ai​。现在准备从这些战士中挑两名战士进入擂台赛进行对战&#xff0c;由于观众们更喜欢看势均力敌的比赛&#xff0c;所以我们也要挑选两个战斗力尽可能相近的战士进行参赛…

Linux-分析 IO 瓶颈手册

分析IO瓶颈 此文主要内容&#xff1a;I/O性能重要指标、主要排查工具、主要排查手段、工具图示 磁盘 I/O 性能指标 四个核心的磁盘 I/O 指标 使用率&#xff1a;是指磁盘忙处理 I/O 请求的百分比。过高的使用率&#xff08;比如超过 60%&#xff09;通常意味着磁盘 I/O 存在…

Spring系列 Bean的生命周期

文章目录 初始化时机单例初始化流程getBeandoGetBeangetSingleton(String) 获取单例getSingleton(String, ObjectFactory) 创建单例beforeSingletonCreationcreateBeanafterSingletonCreation createBean 创建对象doCreateBeanaddSingletonFactory createBeanInstance 创建 Bea…

基于springboot vue 学生就业信息管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm springcloud等开发框架&#xff09; vue .net php phython node.js uniapp小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆…