动手实践WebVR 全景

前言

近年来VR概念越来越火,相信大家在网上都有过VR的浏览体验,比如VR全景看房[1]、VR全景看车[2]、VR全景旅游[3]等等,VR全景给了我们视觉上的沉浸式体验。本文将会简单探究Web VR全景的实现原理,同时也会用threejs实现两个小的demo,希望对大家以后在业务上遇到类似的场景能有所帮助。

什么是VR

引用维基百科的定义:

虚拟现实(英语:virtual reality,缩写VR),是利用电脑模拟产生一个三维空间的虚拟世界,提供用户关于视觉等感官的模拟,让用户感觉仿佛身历其境,可以即时、没有限制地观察三维空间内的事物。用户进行位置移动时,电脑可以立即进行复杂的运算,将精确的三维世界影像传回产生临场感。

与基于现实场景进行增强效果的 AR(Augmented Reality)的区别在于,VR 的场景需要完全重建,类似于进入另一个世界。

虚拟现实的实现原理

人眼对世界的感知,是通过将三维世界投射至视网膜上,以二维图像建立的视觉体系。所以一张具备透视关系的图像,在特定的角度,可以使人感受到三维的空间关系,这就是人眼的深度知觉(depth perception)。VR 技术则建立在这个基础之上。

广泛意义上来说,只要符合模拟三维空间这一行为,就可以称为 VR,手机、电脑、大荧幕、VR 眼镜甚至于空气,都可以成为 VR 的载体。

如何在web上模拟三维空间

三维空间是由2D和3D图形构成的,要模拟三维空间,必须绘制2D和3D图形,我们写js有ECMAScript规范,同理的绘制2D和3D图形也有一套规范,这套规范就是OpenGL

OpenGL是什么

OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)

OpenGL规范描述了绘制2D和3D图形的抽象API,常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。

在Web上能用OpenGL吗

可以的,在web上可以使用WebGL,它是Web浏览器中OpenGL的JavaScript绑定。

什么是WebGL

WebGL是一种JavaScript API,用于在不使用外挂程式的情况下在任何相容的网页浏览器中呈现交互式2D和3D图形。WebGL完全整合到浏览器的所有网页标准中,可将影像处理和效果的GPU加速使用方式当做网页Canvas的一部分。WebGL元素可以加入其他HTML元素之中并与网页或网页背景的其他部分混合。

有了WebGL API,我们就可以web网页上构建三维空间啦。我们可以借助canvas标签来获取WebGL实例来进行WebGL API的调用,更多API可以戳WebGL: 2D and 3D graphics for the web - Web APIs | MDN[4]查看,下面是使用示例:

// 创建canvas标签
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);// 获取webgl实例
const gl = canvas.getContext("webgl");
// 调用webgl api绘制图形
// .....

目前业界有很多成熟的WebGL3D引擎,比如three.js[5]、babylon.js[6]等等,这些3D引擎已经对WebGL API做了非常高效的封装,可以帮助我们快速绘制2D和3D图形。

three.js实现vr全景

在实战前先了解下three.js的基本概念

基本概念

在three.js中,渲染一个三维空间的必要因素是场景(scene)、摄像机(camera)、渲染器(renderer)。渲染出一个三维空间后,可以往里面增加各种各样的物体、光源等等。

场景(scene)

一个容器,容纳着除渲染器以外的3d世界里的一切。场景的元素采用右手坐标系,x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外

// three.js创建场景
const scene = new THREE.Scene();

摄像机(camera)

就像人的眼睛,在一个空间里可以看向任意方向,可以通过参数调节可视角度和可视距离。

three.js中常用的camera有两种,透视投影相机(PerspectiveCamera)与正交投影相机(OrthographicCamera)。这里的投影是指将三维空间中的物体坐标投影到二维平面上。

  • 透视 投影是将每个点都投影到三维空间中,近大远小,看起来更符合真实世界看到的物体。

  • 正交 投影是只考虑所有点的XY坐标,每一个二维空间中的点都是与Z轴平行的直线在观察平面上的投影。所看到的物体大小不会受到距离远近的影响。

透视相机(PerspectiveCamera)

使用perspective projection(透视投影)来进行投影。

这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。

PerspectiveCamera(fov, aspect, near, far) 有四个参数:

  • fov - field of view,视野角,下图中绿色英文标注的地方,是距离观测点near长度处,最上端与最下端之间的角度,Fov越大,表示眼睛睁得越大,离得越远,看得更多。

  • aspect - 画面横宽比

  • near - 相机最近范围内可以看到的物体的距离

  • far - 相机最远范围内可以看到的物体的距离

正交相机(OrthographicCamera)

这一摄像机使用orthographic projection(正交投影)来进行投影。

在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变。这对于渲染2D场景或者UI元素是非常有用的。

OrthographicCamera(left, right, top, bottom, near, far)有六个参数

  • left, right, top, bottom - 分别是红色点距离左右上下边框的距离,对应图中XY轴的值;

  • near - 场景开始渲染并可以显示的起点,对应图中Z轴坐标的值,通常为负;

  • far - 场景结束渲染的终点,对应图中Z轴坐标的值,通常为正;

渲染器(renderer)

将camera在scene里看到的内容渲染/绘制到画布上

设置好 dpr、画布宽高,Three.js 就会生成一个 canvas。

const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
const canvas = renderer.domElement;
document.body.appendChild(canvas);
// 将camera在scene里看到的内容渲染/绘制到画布上
renderer.render( scene, camera );

几何体(geometry)

三维空间里的所有物体都是点组成面,面组成几何体。

three.js核心中内置了多种几何体,如下图的几何体从左到右分别为:球缓冲几何体(SphereGeometry)、 圆柱缓冲几何体(CylinderGeometry)立方缓冲几何体(BoxGeometry)圆锥缓冲几何体(ConeGeometry)、 圆环(TorusGeometry)圆环缓冲扭结几何体(TorusKnotGeometry)等等

贴图(texture)

想象一下你手里有一个几何体,你用一张A4纸包裹上几何体,并在上面画画。你画的内容就是贴图。

下面给立方体贴上小黄鸭贴图:

// 立方缓冲几何体(BoxGeometry)
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
// 小黄鸭贴图
const texture = new THREE.TextureLoader().load( "textures/duck.png" );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const cube = new THREE.Mesh( boxGeometry, material );
scene.add( cube );

也可以给立方体贴上视频

const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
const video = document.getElementById( 'video' );
const texture = new THREE.VideoTexture( video );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const cube = new THREE.Mesh( boxGeometry, material );
scene.add( cube );

材质(material)

延续贴图里的想象,你用白卡纸画画,还是用油纸画画,呈现出来的质感是不同的,这就是材质!

下面五个圆环扭结的颜色都是一样的,而材质从左至右分别是:

  • MeshBasicMaterial(基础材质,简单的几何材料,不考虑光照的影响)

  • MeshMatcapMaterial(网帽材质,在场景没有光源的情况下,会模拟出物体被光照的效果)

  • MeshPhongMaterial(高光材质,适用于陶瓷,烤漆类质感)

  • MeshToonMaterial(卡通材质)

  • MeshLambertMaterial (非光泽表面的材质,没有镜面高光,可以很好地模拟一些表面,例如未经处理的木材或石材,但不能模拟具有镜面高光的光泽表面,例如涂漆木材。)

环境光的情况下:

        

有从上往下的平行光:

灯光(light)

在没有手动创建光的情况下会默认有个环境光,不然你什么都看不到。

常见的灯光有以下几种类型:

  • AmbientLight:环境光,没有方向,会均匀的照亮场景中的所有物体,不会产生阴影

  • DirectionalLight:平行光,常常用平行光来模拟太阳光 的效果; 太阳足够远,因此我们可以认为太阳的位置是无限远,所以我们认为从太阳发出的光线也都是平行的。

  • PointLight:点光源,从一个点向各个方向发射的光源。可以模拟一个灯泡发出的光。

  • SpotLight:聚光灯,光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。

VR全景看房的实现

原理

我们可以通过模拟人走在房子里的行为来实现:

  • 用摄像机模拟人,镜头相当于人的眼睛

  • 用立方几何体六个面贴上贴图模拟房子,贴图要求真实还原房子六个面的情况

  • 将摄像机放置在几何体里面,移动摄像机相当于人在房子里走动

素材准备

房子六面贴图:

代码实现

  1. 创建场景和摄像机,并且设置摄像机位置和添加加轨道控制器

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 0.1, 100 );
// 设置摄像机位置
camera.position.set(1, 1, 4);
const controls = new THREE.OrbitControls( camera, renderer.domElement );
  1. 创建立方几何体,并且给几何体贴上准备好的贴图

// 获取加载6个面的贴图
const textures = getTexturesFromAtlasFile( 'textures/house.jpeg', 6 );
const materials = [];
for ( let i = 0; i < 6; i ++ ) {// 将贴图贴在立方几何体上materials.push( new THREE.MeshBasicMaterial( { map: textures[ i ] } ) );
}
// 创建立方几何体
const skyBox = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), materials );
// 把几何体加到场景里
scene.add( skyBox );function getTexturesFromAtlasFile( atlasImgUrl, tilesNum ) {const textures = [];for ( let i = 0; i < tilesNum; i ++ ) {textures[ i ] = new THREE.Texture();}new THREE.ImageLoader().load( atlasImgUrl, ( image ) => {let canvas, context;const tileWidth = image.height;for ( let i = 0; i < textures.length; i ++ ) {canvas = document.createElement( 'canvas' );context = canvas.getContext( '2d' );canvas.height = tileWidth;canvas.width = tileWidth;context.drawImage( image, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth );textures[ i ].image = canvas;textures[ i ].needsUpdate = true;}} );return textures;
}

页面效果:

  1. 将摄像头的位置移动到几何体内部

camera.position.set(0, 0, -0.01);

将摄像机移动到几何体内部后发现视野一片黑暗,这是因为贴图是在几何体外表面,所以需要将贴图翻转至内表面。

  1. 将几何体贴图翻转至内表面,就能看到全景啦

skyBox.geometry.scale( 1, 1, - 1 );
  1. 监听键盘事件移动摄像机

window.addEventListener('keydown', onKeydown);
function onKeydown(e) {switch(e.keyCode) {// 左case 37:if(camera.position.x > -0.5) {camera.position.x -= 0.01;} break;// 上case 38:if(camera.position.z > -0.5) {camera.position.z -= 0.01;} break;// 右case 39:if(camera.position.x < 0.5) {camera.position.x += 0.01;} break;// 下case 40:if(camera.position.z < 0.5) {camera.position.z += 0.01;} break;}
}

最终效果

VR全景看车的实现

原理

VR全景看车的难点是汽车模型的设计,需要设计一个3d汽车模型,然后渲染到页面上。汽车换装则是通过对关键节点的操作来完成。

另外我们一般说的3d模型就是一个或多个几何体,只是有的3d模型文件里除了包含几何体还可以包含一些额外的信息,比如贴图,材质等等需要在读取模型文件时解析出来

3d模型的文件格式有很多,但threejs里常用的基本是:

  • OBJ格式:老牌通用3d模型文件,不包含贴图,材质,动画等信息。

  • GLTF格式(图形语言传输格式):由OpenGL官方维护团队推出的现代3d模型通用格式,可以包含几何体、材质、动画及场景、摄影机等信息,并且文件量还小。有3D模型界的JPEG之称。GLTF格式有.gltf(JSON/ASCII)和.glb(二进制)两种拓展名。

素材准备

官网上找了一个汽车模型,可以用PlayCanvas glTF Viewer[7]查看汽车模型的节点信息。

代码实现

  1. 创建场景和摄像机,并且设置摄像机位置和添加加轨道控制器

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
const camera.position.set( 4.25, 1.4, - 4.5 );
const controls = new THREE.OrbitControls( camera, container );
  1. 加载汽车模型

通过GLTFLoader,我们可以加载一个gltf格式的3d模型文件。需要注意的是,这些Loader都以插件的形式存在,需要引入相应的XXXLoader.js才能使用

const bodyMaterial = new THREE.MeshPhysicalMaterial( {color: 0xff0000, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );const detailsMaterial = new THREE.MeshStandardMaterial( {color: 0xffffff, metalness: 1.0, roughness: 0.5
} );const glassMaterial = new THREE.MeshPhysicalMaterial( {color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( 'draco/' );
// GLTF文件loader
const loader = new THREE.GLTFLoader();
loader.setDRACOLoader( dracoLoader );
// 存储车轮,后面让车轮旋转
const wheels = [];
loader.load( 'glb/car.glb', function ( gltf ) {// 获取模型节点const carModel = gltf.scene.children[ 0 ];// 重定义模型节点的材质carModel.getObjectByName( 'body' ).material = bodyMaterial;carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;carModel.getObjectByName( 'trim' ).material = detailsMaterial;carModel.getObjectByName( 'glass' ).material = glassMaterial;// 存储车轮wheels.push(carModel.getObjectByName( 'wheel_fl' ),carModel.getObjectByName( 'wheel_fr' ),carModel.getObjectByName( 'wheel_rl' ),carModel.getObjectByName( 'wheel_rr' ));// 车底阴影const mesh = new THREE.Mesh(new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),new THREE.MeshBasicMaterial( {map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true} ));mesh.rotation.x = - Math.PI / 2;mesh.renderOrder = 2;carModel.add( mesh );scene.add( carModel );
});
  1. 车轮滚动

function render() {controls.update();const time = - performance.now() / 1000;// 让车轮旋转,模拟行驶效果for ( let i = 0; i < wheels.length; i ++ ) {wheels[ i ].rotation.x = time * Math.PI * 2;}grid.position.z = - ( time ) % 1;renderer.render( scene, camera );stats.update();
}
  1. 创建场景和摄像机,并且设置摄像机位置和添加加轨道控制器

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
const camera.position.set( 4.25, 1.4, - 4.5 );
const controls = new THREE.OrbitControls( camera, container );
  1. 加载汽车模型

通过GLTFLoader,我们可以加载一个gltf格式的3d模型文件。需要注意的是,这些Loader都以插件的形式存在,需要引入相应的XXXLoader.js才能使用

const bodyMaterial = new THREE.MeshPhysicalMaterial( {color: 0xff0000, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );const detailsMaterial = new THREE.MeshStandardMaterial( {color: 0xffffff, metalness: 1.0, roughness: 0.5
} );const glassMaterial = new THREE.MeshPhysicalMaterial( {color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( 'draco/' );
// GLTF文件loader
const loader = new THREE.GLTFLoader();
loader.setDRACOLoader( dracoLoader );
// 存储车轮,后面让车轮旋转
const wheels = [];
loader.load( 'glb/car.glb', function ( gltf ) {// 获取模型节点const carModel = gltf.scene.children[ 0 ];// 重定义模型节点的材质carModel.getObjectByName( 'body' ).material = bodyMaterial;carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;carModel.getObjectByName( 'trim' ).material = detailsMaterial;carModel.getObjectByName( 'glass' ).material = glassMaterial;// 存储车轮wheels.push(carModel.getObjectByName( 'wheel_fl' ),carModel.getObjectByName( 'wheel_fr' ),carModel.getObjectByName( 'wheel_rl' ),carModel.getObjectByName( 'wheel_rr' ));// 车底阴影const mesh = new THREE.Mesh(new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),new THREE.MeshBasicMaterial( {map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true} ));mesh.rotation.x = - Math.PI / 2;mesh.renderOrder = 2;carModel.add( mesh );scene.add( carModel );
});
  1. 车轮滚动

function render() {controls.update();const time = - performance.now() / 1000;// 让车轮旋转,模拟行驶效果for ( let i = 0; i < wheels.length; i ++ ) {wheels[ i ].rotation.x = time * Math.PI * 2;}grid.position.z = - ( time ) % 1;renderer.render( scene, camera );stats.update();
}
  1. 提供拾色器,改变车身(body)、细节(detail)和玻璃(glass)的颜色

<div id="info"><span class="colorPicker"><input id="body-color" type="color" value="#ff0000"></input><br/>Body</span><span class="colorPicker"><input id="details-color" type="color" value="#ffffff"></input><br/>Details</span><span class="colorPicker"><input id="glass-color" type="color" value="#ffffff"></input><br/>Glass</span>
</div>
// 车身颜色
const bodyColorInput = document.getElementById( 'body-color' );
bodyColorInput.addEventListener( 'input', function () {bodyMaterial.color.set( this.value );
} );
// 车细节颜色
const detailsColorInput = document.getElementById( 'details-color' );
detailsColorInput.addEventListener( 'input', function () {detailsMaterial.color.set( this.value );
} );
// 车玻璃颜色
const glassColorInput = document.getElementById( 'glass-color' );
glassColorInput.addEventListener( 'input', function () {glassMaterial.color.set( this.value );
} );

最终效果

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

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

相关文章

Linux文本三剑客-sed

一、sed介绍&#xff1a; sed&#xff08;Stream Editor&#xff09;是一种流编辑器&#xff0c;用于对文本进行处理和转换。它可以从输入流中读取文本&#xff0c;并根据指定的规则进行编辑和替换。sed通常用于在命令行中进行文本处理&#xff0c;可以实现搜索、替换、删除、…

Vmware 无法开启虚拟化解决方法

最近遇到了Vmware无法开启虚拟化的问题,已经解决,记录一下解决经过。 我遇到的情况是BIOS已经开启虚拟化,HV服务也停用了,但是Vmware仍然提示模块“VPMC”启动失败。网上的解决方案千篇一律,基本都是排查BIOS、停用Windows的虚拟化功能、停用HV主机服务、Vmware配置中关闭…

【开源】SpringBoot框架开发天然气工程运维系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统角色分类2.2 核心功能2.2.1 流程 12.2.2 流程 22.3 各角色功能2.3.1 系统管理员功能2.3.2 用户服务部功能2.3.3 分公司&#xff08;施工单位&#xff09;功能2.3.3.1 技术员角色功能2.3.3.2 材料员角色功能 2.3.4 安…

如何快速将 HTML 文件转换为 PDF

HTML表格是一种在网页上显示数据的通用且强大的方式。它们可用于创建简单的表&#xff08;例如日历&#xff09;或更复杂的表&#xff08;例如数据网格&#xff09;。在这篇博文中&#xff0c;我们将逐步学习如何在 C# 中创建 HTML 表格。本指南将为您提供在 C# 中有效创建 HTM…

阿里云服务器一键部署幻兽帕鲁专属服务器,图形化可视化修改游戏参数是如此简单!

幻兽帕鲁这款游戏最近很火&#xff0c;在线玩家太多了&#xff0c;官方服务器也是承压巨大&#xff0c;很多玩家现在都是搭建自己的专属幻兽帕鲁服务器来玩。搭建教程现在也是越来越简单了&#xff0c;可以说是真正的零基础&#xff0c;一看就会&#xff0c;并且开箱即玩&#…

【python】在python中使用单元测试unittest

在python中使用单元测试unittest 大家好&#xff0c;欢迎来到我的技术乐园&#xff01;今天&#xff0c;我们将一起踏入Python单元测试的奇妙旅程&#xff0c;探索这个让我们的代码更可靠、更强壮的令人愉快的世界。 前言&#xff1a;为什么单元测试如此重要&#xff1f; 在我…

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上&#xff0c;阿里云数据库产品事业部负责人李飞飞在主题演讲中提到&#xff0c;瑶池数据库推出“DB存储”一体化能力&#xff0c;结合人工智能、机器学习、存储等方法和创新能力&#xff0c;实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…

01神经网络的理论及实现

感知机的缺点就是需要设置合适的权重&#xff0c;而权重的设置都是人工操作的。 1、从感知机到神经网络 重新画出感知机的模型&#xff0c;在图上加上偏置&#xff0c;由于偏置始终为1&#xff0c;所以颜色加深。 图1-1 感知机模型 引入新函数(激活函数&#xff09;&#xff…

2024前端面试题汇总

2024前端面试题汇总 1、全局变量与局部变量2、Vue3 区分ref 与 reactive 的原因3、关于服务端渲染&#xff08;SSR&#xff09;与客户端渲染&#xff08;CSR&#xff09;的理解4、单页面应用&#xff08;SPA&#xff09;与多页面应用&#xff08;MPA&#xff09;的优劣5、Vue2 …

计算机二级Python选择题考点——Python语言程序设计Ⅱ

代码1&#xff1a; def fibRate(n):if n < 0:return -1elif n 1:return -1elif n 2:return 1else:L [1,5]for i in range(2,n):L.append(L[-1] L[-2])return L[-2] % L[-1] print(fibRate(7))n 7 L [1,5] i 2 , 3 , 4 , 5 , 6 i 2 -> L[-1] L[-2] 5 1 6 -&g…

Linux逻辑卷(LV)扩容

Linux逻辑卷&#xff08;LV&#xff09;扩容 1、准备物理磁盘&#xff08;分区和不分区都行&#xff09;&#xff0c;可以使用lsblk命令查看新增的磁盘&#xff0c;如下图sde就是我们新增磁盘&#xff0c;容量为600G。 2、将新磁盘变成物理卷&#xff08;PV&#xff09; pvcr…

缓存的概念

文章目录 一、系统缓存buffer与cachecache 的保存位置cache 的特性 二、用户层缓存DNS缓存 三、浏览器缓存过期机制最后修改时间Etag标记过期时间 expires混合使用和缓存刷新缓存刷新 cookie和session 四、CDN缓存什么是CDN用户请求CDN流程利用 302 实现转发请求重定向至最优服…

RabbitMQ 死信队列应用

1. 概念 死信队列&#xff08;Dead Letter Queue&#xff09;是在消息队列系统中的一种特殊队列&#xff0c;用于存储无法被消费的消息。消息可能会因为多种原因变成“死信”&#xff0c;例如消息过期、消息被拒绝、消息队列长度超过限制等。当消息变成“死信”时&#xff0c;…

【数据分享】1929-2023年全球站点的逐月最高气温数据(Shp\Excel\无需转发)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2023年全球气象站…

Leetcode1109. 航班预订统计

Every day a Leetcode 题目来源&#xff1a;1109. 航班预订统计 解法1&#xff1a;差分数组 注意到一个预订记录实际上代表了一个区间的增量。我们的任务是将这些增量叠加得到答案。因此&#xff0c;我们可以使用差分解决本题。 代码&#xff1a; /** lc appleetcode.cn i…

asp.net core监听本地ip地址

开发asp.net core的时候遇到一个问题我想提供访问供其他同事测试&#xff0c;但是默认都是localhost或者127.0.0.1。我想换成我的Ip地址访问但是不行&#xff0c;百度搜索需要更换监听的地址即修改launchSettings.json&#xff0c;修改为0.0.0.0:5248&#xff0c;这样不管local…

力扣hot100 跳跃游戏 II 贪心 思维

Problem: 45. 跳跃游戏 II 思路 &#x1f468;‍&#x1f3eb; 参考 每次在上次能跳到的范围&#xff08;end&#xff09;内选择一个能跳的最远的位置&#xff08;也就是能跳到max_far位置的点&#xff09;作为下次的起跳点 &#xff01; Code ⏰ 时间复杂度: O ( n ) O(n…

DVI接口主机连接VGA显示器解决方案:DVI转VGA转换器DV

DVI转VGA转换器概述 DVI转VGA转换器能够将DVI数字信号转换成VGA模拟信号&#xff0c;通过VGA线缆传输给VGA显示设备使用&#xff0c;这样就能实现DVI接口主机连接VGA接口的显示器。 DVI转VGA转换器DV DVI转VGA转换器DV接口说明 DVI转VGA转换器DV接口介绍 DVI转VGA转换器连接示…

Spring Boot集成RocketMQ

本文目的是&#xff1a;教会你使用Spring Boot集成RocketMQ。 pom.xml文件引入rocketMQ依赖 <!-- rocketmq 依赖--><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId>&…

XCTF:warmup[WriteUP]

CtrlU查看页面源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible&q…