当前位置: 首页 > news >正文

WebGL 理论基础 01 WebGL 基础概念

WebGL 理论基础

  • 基础概念

WebGL 基础概念

顶点着色器的作用是计算顶点的位置。根据计算出的一系列顶点位置,WebGL可以对点, 线和三角形在内的一些图元进行光栅化处理。当对这些图元进行光栅化处理时需要使用片段着色器方法。

片段着色器的作用是计算出当前绘制图元中每个像素的颜色值。

着色器获取数据的4种方法。

  1. 属性(Attributes)和缓冲

    缓冲是发送到GPU的一些二进制数据序列,通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等。 你可以存储任何数据。

    属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。 例如你可能在缓冲中用三个32位的浮点型数据存储一个位置值。 对于一个确切的属性你需要告诉它从哪个缓冲中获取数据,获取什么类型的数据(三个32位的浮点数据), 起始偏移值是多少,到下一个位置的字节数是多少。

    缓冲不是随意读取的。事实上顶点着色器运行的次数是一个指定的确切数字, 每一次运行属性会从指定的缓冲中按照指定规则依次获取下一个值。

  2. 全局变量(Uniforms)

    全局变量在着色程序运行前赋值,在运行过程中全局有效。

  3. 纹理(Textures)

    纹理是一个数据序列,可以在着色程序运行中随意读取其中的数据。 大多数情况存放的是图像数据,但是纹理仅仅是数据序列, 你也可以随意存放除了颜色数据以外的其它数据。

  4. 可变量(Varyings)

    可变量是一种顶点着色器给片段着色器传值的方式,依照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片段着色器运行中获取不同的插值。

WebGL Hello World

​ WebGL只关心两件事:裁剪空间中的坐标值和颜色值。使用WebGL只需要给它提供这两个东西。 你需要提供两个着色器来做这两件事,一个顶点着色器提供裁剪空间坐标值,一个片段着色器提供颜色值。

​ 无论你的画布有多大,裁剪空间的坐标范围永远是 -1 到 1 。 这里有一个简单的WebGL例子展示WebGL的简单用法。

让我们从顶点着色器开始

// 一个属性值,将会从缓冲中获取数据
attribute vec4 a_position;// 所有着色器都有一个main方法
void main() {// gl_Position 是一个顶点着色器主要设置的变量gl_Position = a_position;
}

接下来我们需要一个片段着色器

// 片段着色器没有默认精度,所以我们需要设置一个精度
// mediump是一个不错的默认值,代表“medium precision”(中等精度)
precision mediump float; 
void main() {  // gl_FragColor是一个片段着色器主要设置的变量  gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“红紫色”
}

上方我们设置 gl_FragColor1, 0, 0.5, 1,其中1代表红色值,0代表绿色值, 0.5代表蓝色值,最后一个1表示阿尔法通道值。WebGL中的颜色值范围从 0 到 1 。

现在我们有了两个着色器方法,让我们开始使用WebGL吧

首先我们需要一个HTML中的canvas(画布)对象

 <canvas id="c"></canvas>

然后可以用JavaScript获取它

 var canvas = document.querySelector("#c");

现在我们创建一个WebGL渲染上下文(WebGLRenderingContext)

var gl = canvas.getContext("webgl");if (!gl) {// 你不能使用WebGL!...

现在我们需要编译着色器对然后提交到GPU,先让我们通过字符串获取它们。 你可以利用JavaScript中创建字符串的方式创建GLSL字符串:用串联的方式(concatenating), 用AJAX下载,用多行模板数据。或者在这个例子里,将它们放在非JavaScript类型的标签中。

<script id="vertex-shader-2d" type="notjs">// 一个属性变量,将会从缓冲中获取数据attribute vec4 a_position;// 所有着色器都有一个main方法void main() {// gl_Position 是一个顶点着色器主要设置的变量gl_Position = a_position;}</script><script id="fragment-shader-2d" type="notjs">// 片段着色器没有默认精度,所以我们需要设置一个精度// mediump是一个不错的默认值,代表“medium precision”(中等精度)precision mediump float;void main() {// gl_FragColor是一个片段着色器主要设置的变量gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“瑞迪施紫色”}</script>

事实上,大多数三维引擎在运行时利用模板,串联等方式创建GLSL。 对于这个网站上的例子来说,没有复杂到要在运行时创建GLSL的程度。

接下来我们使用的方法将会创建一个着色器,只需要上传GLSL数据,然后编译成着色器。 你可能注意到这段代码没有任何注释,因为可以从方法名很清楚的了解方法的作用 (这里作为翻译版本我还是稍微注释一下)。

// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源
function createShader(gl, type, source) {var shader = gl.createShader(type); // 创建着色器对象gl.shaderSource(shader, source); // 提供数据源gl.compileShader(shader); // 编译 -> 生成着色器var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);if (success) {return shader;}console.log(gl.getShaderInfoLog(shader));gl.deleteShader(shader);
}

现在我们可以使用以上方法创建两个着色器

var vertexShaderSource = document.querySelector("#vertex-shader-2d").text;
var fragmentShaderSource = document.querySelector("#fragment-shader-2d").text;var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

然后我们将这两个着色器 link(链接)到一个 program(着色程序)

function createProgram(gl, vertexShader, fragmentShader) {var program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);var success = gl.getProgramParameter(program, gl.LINK_STATUS);if (success) {return program;}console.log(gl.getProgramInfoLog(program));gl.deleteProgram(program);
}

然后调用它

var program = createProgram(gl, vertexShader, fragmentShader);

现在我们已经在GPU上创建了一个GLSL着色程序,我们还需要给它提供数据。 WebGL的主要任务就是设置好状态并为GLSL着色程序提供数据。 在这个例子中GLSL着色程序的唯一输入是一个属性值a_position。 我们要做的第一件事就是从刚才创建的GLSL着色程序中找到这个属性值所在的位置。

var positionAttributeLocation = gl.getAttribLocation(program, "a_position");

寻找属性值位置(和全局属性位置)应该在初始化的时候完成,而不是在渲染循环中。

属性值从缓冲中获取数据,所以我们创建一个缓冲

var positionBuffer = gl.createBuffer();

WebGL可以通过绑定点操控全局范围内的许多数据,你可以把绑定点想象成一个WebGL内部的全局变量。 首先绑定一个数据源到绑定点,然后可以引用绑定点指向该数据源。 所以让我们来绑定位置信息缓冲(下面的绑定点就是ARRAY_BUFFER)。

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

现在我们需要通过绑定点向缓冲中存放数据

// 三个二维点坐标
var positions = [0, 0,0, 0.5,0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

这里完成了一系列事情,第一件事是我们有了一个JavaScript序列positions 。 然而WebGL需要强类型数据,所以new Float32Array(positions)创建了32位浮点型数据序列, 并从positions中复制数据到序列中,然后gl.bufferData复制这些数据到GPU的positionBuffer对象上。 它最终传递到positionBuffer上是因为在前一步中我们我们将它绑定到了ARRAY_BUFFER(也就是绑定点)上。

最后一个参数gl.STATIC_DRAW是提示WebGL我们将怎么使用这些数据。WebGL会根据提示做出一些优化。 gl.STATIC_DRAW提示WebGL我们不会经常改变这些数据。

在此之上的代码是 初始化代码。这些代码在页面加载时只会运行一次。 接下来的代码是渲染代码,而这些代码将在我们每次要渲染或者绘制时执行。

渲染

在绘制之前我们应该调整画布(canvas)的尺寸以匹配它的显示尺寸。画布就像图片一样有两个尺寸。 一个是它拥有的实际像素个数,另一个是它显示的大小。CSS决定画布显示的大小。 你应该尽可能用CSS设置所需画布大小 ,因为它比其它方式灵活的多。

为了使画布的像素数和显示大小匹配, 我这里使用了一个辅助方法,你可以在这里获取更多相关信息。

这里的例子中,有独立窗口显示的示例大多使用400x300像素大小的画布。 但是如果像稍后展示的示例那样嵌在页面中,它就会被拉伸以填满可用空间 (你也可以点击示例下方的“点此在新窗口中浏览”在独立窗口中查看示例)。 通过使用CSS调整画布尺寸可以轻松处理这些情况。

webglUtils.resizeCanvasToDisplaySize(gl.canvas);

我们需要告诉WebGL怎样把提供的gl_Position裁剪空间坐标对应到画布像素坐标, 通常我们也把画布像素坐标叫做屏幕空间。为了实现这个目的,我们只需要调用gl.viewport 方法并传递画布的当前尺寸。

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

这样就告诉WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height

我们用0, 0, 0, 0清空画布,分别对应 r, g, b, alpha (红,绿,蓝,阿尔法)值, 所以在这个例子中我们让画布变透明了。

// 清空画布
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);

我们需要告诉WebGL运行哪个着色程序

// 告诉它用我们之前写好的着色程序(一个着色器对)
gl.useProgram(program);

接下来我们需要告诉WebGL怎么从我们之前准备的缓冲中获取数据给着色器中的属性。 首先我们需要启用对应属性

gl.enableVertexAttribArray(positionAttributeLocation);

然后指定从缓冲中读取数据的方式

// 将绑定点绑定到缓冲数据(positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);// 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
var size = 2;          // 每次迭代运行提取两个单位数据
var type = gl.FLOAT;   // 每个单位的数据类型是32位浮点型
var normalize = false; // 不需要归一化数据
var stride = 0;        // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))// 每次迭代运行运动多少内存到下一个数据开始点
var offset = 0;        // 从缓冲起始位置开始读取
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)

一个隐藏信息是gl.vertexAttribPointer是将属性绑定到当前的ARRAY_BUFFER。 换句话说就是属性绑定到了positionBuffer上。这也意味着现在利用绑定点随意将 ARRAY_BUFFER绑定到其它数据上后,该属性依然从positionBuffer上读取数据。

从GLSL的顶点着色器中注意到a_position属性的数据类型是vec4

attribute vec4 a_position;

vec4是一个有四个浮点数据的数据类型。在JavaScript中你可以把它想象成 a_position = {x: 0, y: 0, z: 0, w: 0}。之前我们设置的size = 2, 属性默认值是0, 0, 0, 1,然后属性将会从缓冲中获取前两个值( x 和 y )。 z和w还是默认值 0 和 1 。

我们终于可以让WebGL运行我们的GLSL着色程序了。

var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);

因为count = 3,所以顶点着色器将运行三次。 第一次运行将会从位置缓冲中读取前两个值赋给属性值a_position.xa_position.y。 第二次运行a_position.xy将会被赋予后两个值,最后一次运行将被赋予最后两个值。

因为我们设置primitiveType(图元类型)为 gl.TRIANGLES(三角形), 顶点着色器每运行三次WebGL将会根据三个gl_Position值绘制一个三角形, 不论我们的画布大小是多少,在裁剪空间中每个方向的坐标范围都是 -1 到 1 。

由于我们的顶点着色器仅仅是传递位置缓冲中的值给gl_Position, 所以三角形在裁剪空间中的坐标如下

  0, 0,  0, 0.5, 0.7, 0,

WebGL将会把它们从裁剪空间转换到屏幕空间并在屏幕空间绘制一个三角形, 如果画布大小是400×300我们会得到类似以下的转换

 裁剪空间          屏幕空间   0, 0       ->   200, 150   0, 0.5     ->   200, 225 0.7, 0     ->   340, 150

现在WebGL将渲染出这个三角形。绘制每个像素时WebGL都将调用我们的片段着色器。 我们的片段着色器只是简单设置gl_FragColor1, 0, 0.5, 1, 由于画布的每个通道宽度为8位,这表示WebGL最终在画布上绘制[255, 0, 127, 255]

整个运行程序如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL_Hello_world</title><style>body {margin: 0;}canvas {width: 100vw;height: 100vh;display: block;}</style>
</head><body><canvas id="c"></canvas><script id="vertex-shader-2d" type="notjs">attribute vec4 a_position;void main() {gl_Position = a_position;}</script><script id="fragment-shader-2d" type="notjs">precision mediump float;void main() {gl_FragColor = vec4(1, 0, 0.5, 1);}</script><script>function createShader(gl, type, source) {let shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);if (success) {return shader;}console.log(gl.getShaderInfoLog(shader));gl.deleteShader(shader);}function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let success = gl.getProgramParameter(program, gl.LINK_STATUS);if (success) {return program;}console.log(gl.getProgramInfoLog(program));gl.deleteProgram(program);}function main() {// Get A WebGL contextlet canvas = document.querySelector("#c");let gl = canvas.getContext("webgl");if (!gl) {return;}// Get the strings for our GLSL shaderslet vertexShaderSource = document.querySelector("#vertex-shader-2d").text;let fragmentShaderSource = document.querySelector("#fragment-shader-2d").text;// create GLSL shaders, upload the GLSL source, compile the shaderslet vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);// Link the two shaders into a programlet program = createProgram(gl, vertexShader, fragmentShader);// look up where the vertex data needs to go.let positionAttributeLocation = gl.getAttribLocation(program, "a_position");// Create a buffer and put three 2d clip space points in itlet positionBuffer = gl.createBuffer();// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let positions = [0, 0,0, 0.5,0.7, 0,];gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);// code above this line is initialization code.// code below this line is rendering code.// webglUtils.resizeCanvasToDisplaySize(gl.canvas);// Tell WebGL how to convert from clip space to pixelsgl.viewport(0, 0, gl.canvas.width, gl.canvas.height);// Clear the canvasgl.clearColor(0, 0, 0, 0);gl.clear(gl.COLOR_BUFFER_BIT);// Tell it to use our program (pair of shaders)gl.useProgram(program);// Turn on the attributegl.enableVertexAttribArray(positionAttributeLocation);// Bind the position buffer.gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)let size = 2;          // 2 components per iterationlet type = gl.FLOAT;   // the data is 32bit floatslet normalize = false; // don't normalize the datalet stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next positionlet offset = 0;        // start at the beginning of the buffergl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);// drawlet primitiveType = gl.TRIANGLES;let count = 3;gl.drawArrays(primitiveType, offset, count);}main();</script>
</body></html>

结果如下图所示:

image-20240321195334832

让我们来定义一个可以生成矩形的方法,这样我们就可以调用它定义形状不一的多个矩形。 同时我们需要矩形的颜色是可设置的。

首先我们定义一个片段着色器,可以通过全局变量接收自定义颜色。

<script id="fragment-shader-2d" type="notjs">precision mediump float;uniform vec4 u_color;void main() {gl_FragColor = u_color;}
</script>

这里是一段新代码,可以随机绘制50个随机位置,随机大小,随机颜色的矩形。

  var colorUniformLocation = gl.getUniformLocation(program, "u_color");...// 绘制50个随机颜色矩形for (var ii = 0; ii < 50; ++ii) {// 创建一个随机矩形// 并将写入位置缓冲// 因为位置缓冲是我们绑定在// `ARRAY_BUFFER`绑定点上的最后一个缓冲setRectangle(gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));// 设置一个随机颜色gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);// 绘制矩形gl.drawArrays(gl.TRIANGLES, 0, 6);}
}// 返回 0 到 range 范围内的随机整数
function randomInt(range) {return Math.floor(Math.random() * range);
}// 用参数生成矩形顶点并写进缓冲function setRectangle(gl, x, y, width, height) {var x1 = x;var x2 = x + width;var y1 = y;var y2 = y + height;// 注意: gl.bufferData(gl.ARRAY_BUFFER, ...) 将会影响到// 当前绑定点`ARRAY_BUFFER`的绑定缓冲// 目前我们只有一个缓冲,如果我们有多个缓冲// 我们需要先将所需缓冲绑定到`ARRAY_BUFFER`gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2]), gl.STATIC_DRAW);
}

下面为生成50个矩形的代码。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL_Hello_World-fragmentShader</title><style>body {margin: 0;}canvas {width: 100vw;height: 100vh;display: block;}</style>
</head><body><canvas id="c"></canvas><script id="vertex-shader-2d" type="notjs">//attribute vec4 a_position;attribute vec2 a_position;uniform vec2 u_resolution;void main() {vec2 zeroToOne = a_position / u_resolution;vec2 zeroToTwo = zeroToOne * 2.0;vec2 clipSpace = zeroToTwo - 1.0;gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);//gl_Position = a_position;}</script><script id="fragment-shader-2d" type="notjs">precision mediump float;uniform vec4 u_color;void main() {gl_FragColor = u_color;}</script><script>function createShader(gl, type, source) {let shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);if (success) {return shader;}console.log(gl.getShaderInfoLog(shader));gl.deleteShader(shader);}function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let success = gl.getProgramParameter(program, gl.LINK_STATUS);if (success) {return program;}console.log(gl.getProgramInfoLog(program));gl.deleteProgram(program);}function main() {// Get A WebGL contextlet canvas = document.querySelector("#c");let gl = canvas.getContext("webgl");if (!gl) {return;}// Get the strings for our GLSL shaderslet vertexShaderSource = document.querySelector("#vertex-shader-2d").text;let fragmentShaderSource = document.querySelector("#fragment-shader-2d").text;// create GLSL shaders, upload the GLSL source, compile the shaderslet vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);// Link the two shaders into a programlet program = createProgram(gl, vertexShader, fragmentShader);// look up where the vertex data needs to go.let positionAttributeLocation = gl.getAttribLocation(program, "a_position");let resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");let colorUniformLocation = gl.getUniformLocation(program, "u_color");// Create a buffer and put three 2d clip space points in itlet positionBuffer = gl.createBuffer();// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);let positions = [10, 20,80, 20,10, 30,10, 30,80, 20,80, 30,];gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);// code above this line is initialization code.// code below this line is rendering code.// webglUtils.resizeCanvasToDisplaySize(gl.canvas);// Tell WebGL how to convert from clip space to pixelsgl.viewport(0, 0, gl.canvas.width, gl.canvas.height);// Clear the canvasgl.clearColor(0, 0, 0, 0);gl.clear(gl.COLOR_BUFFER_BIT);// Tell it to use our program (pair of shaders)gl.useProgram(program);// Turn on the attributegl.enableVertexAttribArray(positionAttributeLocation);// Bind the position buffer.gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)let size = 2;          // 2 components per iterationlet type = gl.FLOAT;   // the data is 32bit floatslet normalize = false; // don't normalize the datalet stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next positionlet offset = 0;        // start at the beginning of the buffergl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);// set the resolutiongl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);// draw 50 random rectangles in random colorsfor (var ii = 0; ii < 50; ++ii) {// Setup a random rectangle// This will write to positionBuffer because// its the last thing we bound on the ARRAY_BUFFER// bind pointsetRectangle(gl, randomInt(300), randomInt(300), randomInt(300), randomInt(300));// Set a random color.gl.uniform4f(colorUniformLocation, Math.random(), Math.random(), Math.random(), 1);// Draw the rectangle.let primitiveType = gl.TRIANGLES;let count = 6;gl.drawArrays(primitiveType, offset, count);}}// Returns a random integer from 0 to range - 1.function randomInt(range) {return Math.floor(Math.random() * range);}// Fill the buffer with the values that define a rectangle.function setRectangle(gl, x, y, width, height) {var x1 = x;var x2 = x + width;var y1 = y;var y2 = y + height;gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x1, y1,x2, y1,x1, y2,x1, y2,x2, y1,x2, y2,]), gl.STATIC_DRAW);}main();</script>
</body></html>

结果如下:
image-20240321201252852

相关文章:

WebGL 理论基础 01 WebGL 基础概念

WebGL 理论基础 基础概念 WebGL 基础概念 顶点着色器的作用是计算顶点的位置。根据计算出的一系列顶点位置&#xff0c;WebGL可以对点&#xff0c; 线和三角形在内的一些图元进行光栅化处理。当对这些图元进行光栅化处理时需要使用片段着色器方法。 片段着色器的作用是计算…...

Leetcode 28:找出字符串中第一个匹配项的下标

给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示例 1&#xff1a; 输入&#xff1a;haystack &q…...

docker opensearch arm64 运行失败解决方案

opensearch版本 2.1.0 docker日志错误信息&#xff1a; Disabling execution of install_demo_configuration.sh for OpenSearch Security Plugin Enabling OpenSearch Security Plugin Killing opensearch process 10 OpenSearch exited with code 143 Performance analyze…...

C#、ASP、ASP.NET、.NET、ASP.NET CORE区别、ASP.NET Core其概念和特点、ASP.NET Core个人心得体会

C#是一种面向对象的编程语言&#xff0c;主要用于开发跨平台的应用程序。它是.NET框架的一部分&#xff0c;并且可以在.NET平台上运行。 ASP&#xff08;Active Server Pages&#xff09;是一种用于构建动态Web页面的技术&#xff0c;使用VBScript或JScript作为服务器端脚本语…...

SpringMVC 简介及入门级的快速搭建详细步骤

MVC 回顾 MVC&#xff0c;即Model-View-Controller&#xff08;模型-视图-控制器&#xff09;设计模式&#xff0c;是一种广泛应用于软件工程中&#xff0c;特别是Web应用开发中的架构模式。它将应用程序分为三个核心组件&#xff1a; Model&#xff08;模型&#xff09;&#…...

Flutter编译卡在Running Gradle task ‘assembleDebug

1、翻墙 2、修改国内镜像源&#xff08;以下以Flutter 3.19.3版本为例&#xff09; 找到Flutter SDK目录下的Flutter配置文件resolve_dependencies.gradle 路径&#xff1a;flutter/packages/flutter_tools/gradle/resolve_dependencies.gradle 1)、第一处修改&#xff1a; g…...

基于springboot的牙科就诊管理系统

技术&#xff1a;springbootmysqlvue 一、系统背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样…...

C语言 指针练习

一、 a、b是两个浮点型变量&#xff0c;给a、b赋值&#xff0c;建立两个指针分别指向a的地址和b的地址&#xff0c;输出两个指针的值。 #include<stdio.h> int main() {float a,b,*p1,*p2;a10.2;b2.3;p1&a;p2&b;printf("a%f,b%f\n",a,b);printf("…...

【力扣 TOP100】 无重复字符的最长子串

题目描述&#xff1a; 思路&#xff1a; 使用left和right表示子串的端点。每次判断新的right是否在之前的子串里&#xff0c;如果在&#xff0c;则将left更新为新字符在子串里的位置&#xff08;因为在此之间&#xff0c;没有更长的子串了&#xff09;。如果不在则right1&…...

K8S node磁盘清理

K8S磁盘清理 K8S的部署形式相比传统非容器部署&#xff0c;会消耗更多的磁盘&#xff0c;在运行时可能会把磁盘占满。 这里以使用containerd运行时的K8S node为例&#xff0c;说明磁盘会用到那里了和如何清理磁盘 通用处理 磁盘清理: du -h --max-depth6 / 2>/dev/nul…...

2024年上半年软考,现在开始学真的来得及吗?

24上软考报名进行时&#xff0c;如果从现在开始学习来得及吗&#xff1f;只为拿证&#xff0c;还没报名的选哪科通过率高一点呢&#xff1f; 01、现在开始学来得及吗&#xff1f; 还没开始备考的考生&#xff0c;现在开始抓紧时间学还来得及&#xff0c;但是要正视软考的试题…...

SfM——八点法计算F矩阵(基础矩阵)与三角测量

1 八点法计算F矩阵&#xff08;基础矩阵&#xff09; 基础矩阵用于描述两个视图之间的几何关系 基础矩阵&#xff1a;基础矩阵 F F F 是描述两个视图之间相机投影关系的矩阵。对于两个对应的图像坐标点 ( x , y , 1 ) (x, y, 1) (x,y,1) 和 ( u , v , 1 ) (u, v, 1) (u,v,1…...

分布式事务的解决方案--Seata架构

一、Seata的XA模式 二、AT模式原理 三、TCC模式原理 四、MQ分布式事务 异步&#xff0c;非实时&#xff0c;实现最终的一致性。 四、分布式事务的解决方案...

【 React 】React JSX 转换成真实DOM的过程?

1. 是什么 react通过将组件编写的JSX映射到屏幕&#xff0c;以及组件中的状态发生了变化之后React会将这些「变化」更新到屏幕上 在前面文章了解中&#xff0c;JSX通过babel最终转化成React.createElement这种形式&#xff0c;例如&#xff1a; <div>< img src"…...

[Open3d]: 知识记录

python api 官方手册&#xff1a;http://www.open3d.org/docs/release/ 可视化&#xff1a;http://www.open3d.org/docs/release/tutorial/visualization/visualization.html python-vis 参考代码&#xff1a;https://github.com/isl-org/Open3D/tree/master/examples/python/v…...

css面试题

1、css盒模型 a、标准盒模型---在标准盒模型中&#xff0c;width的宽度指的是content的宽度 b、怪异盒模型---在怪异盒模型中&#xff0c;width的宽度等于contentborderpadding 切换盒子模型的话&#xff0c;使用box-sizing。 2、link和import的区别 a、link是html标签&#x…...

vscode调试launch.json常用格式

1、简单的模版 定义一个简单的模版如下&#xff1a; {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0","configuration…...

巨细!Python爬虫详解

爬虫&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在 FOAF 社区中间&#xff0c;更经常的称为网页追逐者&#xff09;&#xff1b;它是一种按照一定的规则&#xff0c;自动地抓取网络信息的程序或者脚本。 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那…...

项目中如何进行限流(限流的算法、实现方法详解)

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 限流的算法漏…...

https在win7的环境下如何配置

https在win7的环境下如何配置&#xff1f;在Windows7环境下配置https&#xff0c;需要完成以下步骤&#xff1a; 1&#xff09;安装Web服务器软件 可以选择安装常用的Web服务器软件&#xff0c;如Apache、Nginx或IIS&#xff0c;这些服务器软件都支持https。 2&#xff09;获…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...