threeJs着色器ShaderMaterial以及统一着色语言GLSL语法基本介绍

news2024/11/19 5:27:30

一、着色器材质ShaderMaterial的基本使用

废话不多讲先来看案例

console.log('着色器入门')

// 引入three.js
import * as THREE from 'three'
// 引入OrbitControls控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 初始化场景
const scene = new THREE.Scene()
//创建透视相机
const camera = new THREE.PerspectiveCamera(
  90,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
// 设置相机位置
camera.position.set(0, 0, 2)
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix()
// 添加相机到场景
scene.add(camera)


// 创建辅助轴
const axesHelper = new THREE.AxesHelper(5)
// 添加辅助轴到场景
scene.add(axesHelper)


// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: `
        void main(){
            gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
        }
    `,
  fragmentShader: `
        void main(){
            gl_FragColor = vec4(1.0,0.0,0.0,1.0);
        }
    `,
})
// 利用着色器材质创建一个平面
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1, 1, 64, 64),
  shaderMaterial
)
// 添加平面到场景
scene.add(plane)


// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新摄像机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 设置渲染器的像素比例
  renderer.setPixelRatio(window.devicePixelRatio);
});
// 添加渲染器到dom
document.body.appendChild(renderer.domElement)


// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 设置控制器阻尼
controls.enableDamping = true

// 渲染函数
function render() {
  // 更新控制器
  controls.update()
  // 渲染
  renderer.render(scene, camera)
  // 动画
  requestAnimationFrame(render)
}
// 调用渲染函数
render()

运行效果如下图所示:
在这里插入图片描述
这个案例就是使用着色器材质创建一个红色的平面。

核心其实就是这一段代码

// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: `
        void main(){
            gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
        }
    `,
  fragmentShader: `
        void main(){
            gl_FragColor = vec4(1.0,0.0,0.0,1.0);
        }
    `,
})
// 利用着色器材质创建一个平面
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1, 1, 64, 64),
  shaderMaterial
)
// 添加平面到场景
scene.add(plane)

这里来详细讲讲这一段代码的作用

上面的代码创建了一个着色器材质(ShaderMaterial),这是three.js中用于创建自定义着色的高级功能。着色器是直接在GPU上运行的小程序,这可以极大地提高渲染效率。这个例子使用了两种类型的着色器:顶点着色器片元着色器

vertexShader : 这是顶点着色器的代码。顶点着色器的主要任务计算顶点的最终位置3D坐标转变为屏幕的2D坐标)。在这个例子中,顶点着色器仅仅是执行了一个标准的模型-视图-投影变换。函数void main()是着色器的入口点。

`gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0 );`

这行代码是进行了一个坐标变换,将模型空间中的顶点位置变换到裁剪空间中

  • position是原始的顶点位置
  • modelMatrix将其变换到世界空间
  • viewMatrix将世界空间变换到视图空间
  • projectionMatrix将视图空间变换到裁剪空间

fragmentShader : 这是片元着色器的代码。片元着色器的任务计算每个像素的最终颜色。在这个例子中,片元着色器直接设置了所有的像素为红色。

`gl_FragColor = vec4(1.0,0.0,0.0,1.0);`

这行代码将片元的颜色设置为红色vec4代表一个四维的向量,前三个元素分别代表了红色,绿色和蓝色的分量,每个值的范围为0.0~1.0,最后一个元素代表了透明度

总的来说,该代码创建了一个红色效果的自定义着色器材质。每个物体在转移到屏幕坐标后都会被染成红色

二、顶点着色器(vertex shader)和片元着色器(fragment shader)的数据交互

Three.js中,顶点着色器(vertex shader)和片元着色器(fragment shader)是通过统一着色语言(GLSL)进行数据交互的。

在着色器程序中,顶点着色器首先处理每个顶点数据,这些数据可以是顶点的位置、颜色、纹素坐标等。计算结果会存储在特殊的变量中,例如 gl_Positiongl_PointSize。这些结果可以通过"varying"变量被传递到片元着色器中。

"varying"变量是顶点着色器和片元着色器之间唯一的通信方式。你可以将它视为从顶点着色器传递给片元着色器的一种“桥梁”。"varying"变量在顶点着色器中被写入,在片元着色器中被读取

顶点着色器计算的结果被插值 (undersampling) 到每个片元上。其过程称为光栅化 (rasterization)。在光栅化过程中,"varying"变量的数据会在每个像素上都被插值(这被称为传递)。之后,片元着色器会使用这些数据来计算每个像素的最终颜色值。

注意,"varying"变量的数量和大小受到硬件限制,过度使用可能会导致性能下降。在需要传递大量数据时,可以尝试使用纹理或缓冲区对象

可能介绍了概念大家还是云里雾里,我们通过一个案例来分析说明。

还是开始那个基本的案例,为了能更明显的感觉到varying传递数据的作用,我们将着色器材质中设置顶点着色器材质和片元着色器的代码抽离出来,新建两个文件

  • vertex.glsl
void main(){
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
  • fragment.glsl
void main(){
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

然后在基础案例中引入这两个glsl,使用到着色器材质的顶点着色器和片元着色器上

完整代码如下

// 引入three.js
import * as THREE from 'three'
// 引入OrbitControls控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 引入顶点着色器
import vertexShader from '../shader/myshader/vertex.glsl'
// 引入片元着色器
import fragmentShader from '../shader/myshader/fragment.glsl'

// 初始化场景
const scene = new THREE.Scene()
//创建透视相机
const camera = new THREE.PerspectiveCamera(
  90,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
// 设置相机位置
camera.position.set(0, 0, 2)
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix()
// 添加相机到场景
scene.add(camera)


// 创建辅助轴
const axesHelper = new THREE.AxesHelper(5)
// 添加辅助轴到场景
scene.add(axesHelper)


// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
})
// 利用着色器材质创建一个平面
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1, 1, 64, 64),
  shaderMaterial
)
// 添加平面到场景
scene.add(plane)


// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新摄像机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 设置渲染器的像素比例
  renderer.setPixelRatio(window.devicePixelRatio);
});
// 添加渲染器到dom
document.body.appendChild(renderer.domElement)


// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 设置控制器阻尼
controls.enableDamping = true

// 渲染函数
function render() {
  // 更新控制器
  controls.update()
  // 渲染
  renderer.render(scene, camera)
  // 动画
  requestAnimationFrame(render)
}
// 调用渲染函数
render()

改动其实也就这一点:

// 引入
import vertexShader from '../shader/myshader/vertex.glsl'
import fragmentShader from '../shader/myshader/fragment.glsl'
// 使用
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
})

当然效果还是一样的,如图:
在这里插入图片描述
接下来我们尝试在顶点着色器中传入一个变量到片元着色器中来修改平面的颜色。

在顶点着色器中

  • vertex.glsl

varying float vColor;

void main(){
    vColor = 1.0;
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
  • fragment.glsl
varying float vColor;
void main(){
    gl_FragColor = vec4(1.0, vColor, 0.0, 1.0);
}

我们在顶点着色器中定义了一个floatvColor变量,并赋值为1.0,然后通过varying传递出去,
片元着色器中同样通过varying接收这个vColor变量并使用

此时我们就成功将平面的颜色改为了黄色,效果如图:
在这里插入图片描述

三、统一着色语言(GLSL)

看了上面的两个案例,可能不了解glsl的小伙伴会有疑问,vertex.glslfragment.glsl两个文件中的float是什么?vec4是什么?gl_Positiongl_FragColor又是什么?好家伙还有个void main()???
跟我这搞c语言呢?

那么接下来就介绍一下glsl语言的基础语法。

1. 数据类型

GLSL语言有多种数据类型,包括:

数据类型描述示例
bool布尔类型,只能是 true 或 falsebool a = true;
int整型int a = 10;
float浮点型float a = 1.0;
double双浮点型,比 float 更高精度double a = 1.0;
vec2二维向量,包含两个 float 组件vec2 a = vec2(1.0, 2.0);
vec3三维向量,包含三个 float 组件vec3 a = vec3 (1.0, 2.0, 3.0);
vec4四维向量,包含四个 float 组件vec4 a = vec4 (1.0, 2.0, 3.0, 4.0);
ivec2二维整型向量ivec2 a = ivec2(1, 2);
ivec3三维整型向量ivec3 a = ivec3(1, 2, 3);
ivec4四维整型向量ivec4 a = ivec4(1, 2, 3, 4);
mat2二维矩阵mat2 a = mat2(1.0, 2.0, 3.0, 4.0);
mat3三维矩阵mat3 a = mat3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
mat4四维矩阵mat4 a = mat4(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0);
sampler2D用于访问纹理uniform sampler2D texture;
samplerCube用于存储和查询立方体纹理uniform samplerCube texture;

请注意,GLSL中的变量必须在开始位置声明,这和许多其他编程语言不同。

2. 控制语句

GLSL中的控制语句具有和C语言相似的语法。以下是一些示例:

  1. if-else 语句:
if (condition) {
    // Code to execute if condition is true
} else {
   // Code to execute if condition is false
}

例如:

if (color.r > 0.5) {
    color.r = 1.0;
} else {
    color.r = 0.0;
}
  1. for 语句:
for (initialization; condition; post-loop expression) {
  // code to execute for each loop iteration
}

例如:

for (int i = 0; i < 3; i++) {
    color.rgb[i] = 1.0;
}
  1. while 语句:
while (condition) {
   // code to execute while condition is true
}

例如:

int i = 0;
while (i < 3) {
    color.rgb[i++] = 1.0;
}
  1. do-while 语句:
do {
   // Code to execute
} while (condition)

例如:

int i = 0;
do {
    color.rgb[i++] = 1.0;
} while (i < 3);
  1. switch-case 语句:
switch (expression) {
    case constant1:
        // code to execute if expression equals constant1
        break;
    case constant2:
        // code to execute if expression equals constant2
        break;
    default: 
        // code to execute if none of the above conditions are met
}

例如:

int i = getValue();
switch (i) {
    case 0:
        color.r = 1.0;
        break;
    case 1:
        color.g = 1.0;
        break;
    default:
        color.b = 1.0;
}

3. 函数

GLSLOpenGL着色语言)中,函数的声明和使用方式与C语言相似。简单来说,首先需要声明函数的返回类型,接着声明函数名和括号中的参数列表,然后在大括号中定义函数的具体操作。下面举例说明。

例如,我们声明一个将向量颜色分量都乘以2的函数:

vec3 doubleColor(vec3 color) {
  return 2.0 * color;
}

这里的vec3是函数返回类型,表示一个三维向量。函数名是doubleColor,参数是一个三维向量color。函数体内部的2.0 * color表示将颜色的每一个分量都乘以2。

然后这个函数可以在着色器程序的任何地方被使用,例如:

void main() {
  vec3 color = vec3(1.0, 0.5, 0.3);
  vec3 newColor = doubleColor(color);
  gl_FragColor = vec4(newColor, 1.0);
}

在这个main函数中,首先定义了一个原始颜色color,然后用我们定义的doubleColor函数将原始颜色的每个分量都乘以2得到新的颜色newColor。最后将包含newColor的颜色设置为片元颜色。

需要注意的是,所有的GLSL函数必须在调用之前就已经声明了,这与JavaScript等其他语言不同

4. 内置attributes 和 uniforms

WebGLGLSL中,attributes uniforms是两种预定义的类型,都是用来在着色器中存储和传递数据的。它们的主要区别在于,attributes存储的是每个顶点独有的数据,如位置,颜色,纹理坐标等;而uniforms用于存储在一个渲染调用中对所有顶点都相同的数据,比如变换矩阵,光照参数等。

threejs中,这些attributesuniforms都是通过JavaScriptCPU端设置,然后在GPU端的着色器中获取其值进行计算。

下面是一些常见的attributesuniforms

  • attributes

    • position:顶点的位置。
    • normal:顶点的法向量。
    • uv:顶点的纹理坐标。
  • uniforms

    • modelViewMatrix:模型视图矩阵。
    • projectionMatrix:投影矩阵。
    • normalMatrix:法向量矩阵。
    • time:用于动画等需要时间参数的场景。

例如,下面是一个顶点着色器的例子,其中使用了position attribute和modelViewMatrix、projectionMatrix两个uniform:

attribute vec3 position; // 顶点位置
uniform mat4 modelViewMatrix; // 模型视图矩阵
uniform mat4 projectionMatrix; // 投影矩阵

void main() {
  vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); // 计算顶点在视图空间中的位置
  gl_Position = projectionMatrix * mvPosition; // 投影到屏幕坐标
}

然后在JavaScript中设定这些值:

var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array([
  -1.0, -1.0,  1.0,
   1.0, -1.0,  1.0,
   1.0,  1.0,  1.0,

   1.0,  1.0,  1.0,
  -1.0,  1.0,  1.0,
  -1.0, -1.0,  1.0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

var material = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 1.0 },
    resolution: { value: new THREE.Vector2() }
  },
  vertexShader: document.getElementById('vertexShader').textContent,
  fragmentShader: document.getElementById('fragmentShader').textContent
});

var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

这样,我们就可以在着色器中使用这些attributesuniforms进行渲染计算了。

四、通过uv渲染彩色平面

通过上面的介绍,我们知道uv是内置的属性,他刚好是二维的,那么如果我们将他从顶点着色器传递到片元着色器,进行渲染平面会有什么结果呢?
一起看看吧

  • vertex.glsl
varying vec2 vUv;
void main(){
    vUv = uv;
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
  • fragment.glsl
varying float vColor;
varying vec2 vUv;
void main(){
    gl_FragColor = vec4(vUv, 0.0, 1.0);
}

完整示例代码

// 引入three.js
import * as THREE from 'three'
// 引入OrbitControls控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 引入顶点着色器
import vertexShader from '../shader/myshader/vertex.glsl'
// 引入片元着色器
import fragmentShader from '../shader/myshader/fragment.glsl'

// 初始化场景
const scene = new THREE.Scene()
//创建透视相机
const camera = new THREE.PerspectiveCamera(
  90,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
// 设置相机位置
camera.position.set(0, 0, 2)
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix()
// 添加相机到场景
scene.add(camera)


// 创建辅助轴
const axesHelper = new THREE.AxesHelper(5)
// 添加辅助轴到场景
scene.add(axesHelper)


// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
})
// 利用着色器材质创建一个平面
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1, 1, 64, 64),
  shaderMaterial
)
// 添加平面到场景
scene.add(plane)


// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight)
// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新摄像机的投影矩阵
  camera.updateProjectionMatrix();
  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);
  // 设置渲染器的像素比例
  renderer.setPixelRatio(window.devicePixelRatio);
});
// 添加渲染器到dom
document.body.appendChild(renderer.domElement)


// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 设置控制器阻尼
controls.enableDamping = true

// 渲染函数
function render() {
  // 更新控制器
  controls.update()
  // 渲染
  renderer.render(scene, camera)
  // 动画
  requestAnimationFrame(render)
}
// 调用渲染函数
render()

效果如下:
在这里插入图片描述
吼吼!利用uv渲染的平面成彩色了是不是很神奇。

为什么呢?

这是因为vUv参数对应的是一个二维向量(vec2),这个向量用于确定像素对应的纹理坐标。它通常是在顶点着色器(Vertex Shader)中计算出来的,然后传递到片元着色器(Fragment Shader)进行使用。

gl_FragColor = vec4(vUv, 0.0, 1.0);这行代码中,我们创建了一个颜色向量,它的前两个分量(R和G)是提供的vUv值,第三个分量(B)是0.0,第四个分量(alpha,表示透明度)是1.0。

简单来说,也就是将纹理位置的坐标值直接转化为了颜色值,因此会得到彩色的效果。
例如

  • 纹理坐标(0, 0)对应的是黑色(R=0, G=0, B=0)
  • 纹理坐标(1, 0)或者(0, 1)对应的是红色或者绿色(R=1, G=0, B=0或者R=0, G=1, B=0)
  • 纹理坐标(1, 1)对应的是黄色(R=1, G=1, B=0)

五、总结

好啦,到这里我们详细介绍了three.js库中的着色器ShaderMaterial和统一着色语言GLSL的基本语法。

首先介绍了ShaderMaterial在three.js中的地位以及功用。它是一种特殊的材质书写方式,让你可以编写自定义着色器。ShaderMaterial给予了我们底层的、未过滤的访问权,让我们可以编写自己的顶点(vertex)和片段(fragment)着色器程序。

接着深入解析了统一着色语言GLSL(OpenGL Shading Language)的语法基础。GLSL是C语言的一种方言,是Open GL的一部分,用于编写短程图形渲染框架。文章讲解了其基本语言构架、数据类型和运算符、流程控制以及函数和过程等核心知识点。

最后给出了如何将这两者结合在一起,如何在three.js中使用GLSL着色器进行渲染的示例,包括如何创建着色器、如何编写着色器代码、以及如何将其应用到three.js场景中。

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

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

相关文章

git 提交换行符问题:LF would be replaced by CRLF

提交git 记录时&#xff0c;遇到问题 fatal: LF would be replaced by CRLF 原因: 是因为git换行符的导致Unix/Linux使用的是LF&#xff0c;Mac后期也采用了LF&#xff0c;但Windows一直使用CRLF 解决方案&#xff1a;禁止转换文件格式&#xff0c;其次允许提交换行符的文件 …

钉钉返回:访问ip不在白名单之中,请参考FAQ

新版钉钉 在开发管理-服务器出口IP-配置返回错误信息返回给你的requestIp

“国产大会”,世界人工智能大会有何影响力?

之前我们讲过&#xff0c;这个世界人工智能大会是我们中国人自己创办的世界级的大会&#xff0c;是全球范围内的人工智能领域盛会&#xff0c;它对人工智能的发展和应用有着超重要的影响力&#xff01;首先&#xff0c;它吸引全球各个领域的专家、学者、企业家和政府官员关注和…

Windows上安装PostgreSQL

下载地址&#xff1a;PostgreSQL下载网址 因为某些问题我自己安装的是postgresql-11.2-1-windows-x64 根据下图顺序安装即可&#xff0c;不同版本可能顺序有点区别但每部分目的都是一样的。 首先右键以管理员身份运行&#xff0c;可以弹出安装界面

在 3ds Max 和 After Effects 中创建逼真的蜘蛛网模型

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建蜘蛛网 步骤 1 打开 3ds Max。 打开 3ds Max 步骤 2 转到创建>标准基元>平面并创建一个平面 在前视图中。 创建平面 步骤 3 保持其长度和宽度 segs 为 80。 段 步骤 4 打开修改器列表…

算法leetcode|64. 最小路径和(rust重拳出击)

文章目录 64. 最小路径和&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 64. 最小路径和&#xff1a; 给定一个包含非负整数的 m x n 网…

32 QFile文件读写

案例&#xff1a;使用LineEdit和TextEdit实现读取文件并显示文件内容 展示&#xff1a; 代码&#xff1a; #include "widget.h" #include "ui_widget.h" #include "QFileDialog" #include "QMessageBox" #include "QFile" …

2、nacos 2.1.0注册中心原理及源码分析

一、为什么有这课程 Spring Cloud Alibaba 新版本中Seata 1.5.2和Nacos 2.1.0 在性能和使用方面都有很大提升&#xff0c;这节课将从使用和源码的角度详细讲解这两个框架。 二、设计注册中心 1、分布式框架的注意点&#xff1a;三高架构 高可用 高可用性&#xff08;High Av…

智慧园区智能照明控制系统解决方案

1、概述 园区照明比较复杂&#xff0c;办公建筑、生产车间和园区道路、景观照明等类型比较多&#xff0c;而且对照明控制方式要求不一样。所以合理使用照明控制系统&#xff0c;针对不同建筑不同场景使用不同的控制策略&#xff0c;大程度使用自然光照明达到节省照明用电&#…

科技资讯|苹果开放Vision Pro头显开发套件申请,此前曝光三款电池

苹果今天宣布面向开发人员&#xff0c;正式接受 Vision Pro 头显开发套件申请&#xff0c;从而帮助其开发和测试应用程序。 苹果官方页面介绍&#xff0c;开发人员在获得 Vision Pro 头显开发套件之外&#xff0c;还可以获得设备设置和入门方面的帮助&#xff0c;与 Apple 专…

谷粒商城篇章5 ---- P173-P192 ---- 检索服务【分布式高级篇二】

目录 1 检索服务 1.1 搭建页面环境 1.1.1 引入依赖 1.1.2 将检索页面放到gulimall-search的src/main/resources/templates/目录下 1.1.3 调整搜索页面 1.1.4 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/ search/ 1.1.5 SwitchHosts配置域…

F5产品队列再添“猛将”,稳健守护云原生基础设施

提到F5产品队列&#xff0c;无论是负载均衡&#xff0c;还是独立部署且一次性购买的硬件产品&#xff0c;或者是F5 分布式云服务组合产品&#xff0c;都体现出F5的能力与能量。前不久&#xff0c;F5宣布推出云工作负载防护解决方案——F5 分布式云应用基础设施防护&#xff08;…

Python基础语法第六章之字典

一、字典 1.1字典是什么 字典是一种存储 键值对 的结构. 啥是键值对? 这是计算机/生活中一个非常广泛使用的概念. 把 键(key) 和 值(value) 进行一个一对一的映射, 然后就可以根据键, 快速找到值. 1.2创建字典 创建一个空的字典. 使用 { } 表示字典. a { } b dict() pr…

Win10从安全模式命令行退出安全模式正常启动

前两天听信他人的一则妖言&#xff0c;将windows设置为安全模式卸载软件&#xff0c;结果初始帐户密码忘记&#xff0c;登陆不了了&#xff0c;琢磨半天才正常启动用pin登录了系统。 微软可以使用本地帐户或在线帐户管理计算机&#xff0c;在线帐户的好处就是可以云同步密钥 设…

uniapp 微信小程序 预览pdf方法

效果图&#xff1a; 1、在小程序中 // #ifdef MP */ 是区分运行的环境&#xff0c;在小程序中可使用如下方法uni.downloadFile({url: item.link,//文件地址success: function (res) {var filePath res.tempFilePath;uni.openDocument({filePath: filePath,showMenu: false…

Hadoop学习日记-MapReduce思想及执行流程

MapReduce思想 Map负责“拆分”&#xff1a;即将复杂问题拆分成可以并行计算的小问题&#xff0c;彼此之间几乎没有依赖联系。 Reduce负责对Map阶段的结果进行合并汇总 Map和Reduce的抽象接口如下&#xff1a; map:(k1; v1) — (k2; v2) reduce:(k2; [v2]) — (k3; v3) 一…

行为型模式 - 解释器模式

概述 如上图&#xff0c;设计一个软件用来进行加减计算。我们第一想法就是使用工具类&#xff0c;提供对应的加法和减法的工具方法。 //用于两个整数相加 public static int add(int a,int b){return a b; }//用于两个整数相加 public static int add(int a,int b,int c){r…

玩转代码|JS实现中文字符串对utf-8的Base64编码的方法

目录 UTF-8 字符串编解码 解决方法 解析 utf8_to_b64 b64_to_utf8 弃用 unescape 和 escape 方法 原由 解决方法 Node.js 下的 Base64 编解码 Base64 编解码 Base64是一种使用64基的位置计数法。它使用2的最大次方来代表仅可打印的ASCII 字符。这使它可用来作为电子邮…

YOLOv2论文对比总结

1、高分辨率图片效果提升 2、Anchor 3、Loss函数 4、小目标友好

【文献分享】比目前最先进的模型轻30%!高效多机器人SLAM蒸馏描述符!

论文题目&#xff1a;Descriptor Distillation for Efficient Multi-Robot SLAM 中文题目&#xff1a;高效多机器人SLAM蒸馏描述符 作者&#xff1a;Xiyue Guo, Junjie Hu, Hujun Bao and Guofeng Zhang 作者机构&#xff1a;浙江大学CAD&CG国家重点实验室 香港中文大学…