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

WebGL之灯光使用解析

在使用灯光之前,首先我们需要了解,与定义更广泛的 OpenGL 不同,WebGL 并没有继承 OpenGL 中灯光的支持。所以你只能由自己完全得控制灯光。幸运得是,这也并不是很难,本文接下来就会介绍完成灯光的基础。

在 3D 空间中模拟现实灯光

在 3D 空间中模拟现实世界的灯光的具体原理和细节绝非本篇文章能够描述清楚的,但是对灯光模型有一定的了解对我们的学习还是很有帮助的。虽然这里没办法深入讲解,但是维基百科中的Phong 着色法给出了一个不错的概要介绍,其中包含了最常用的几种光照模型。

光源类型可以概括成如下三种:

环境光 是一种可以渗透到场景的每一个角落的光。它是非方向光并且会均匀地照射物体的每一个面,无论这个面是朝向哪个方向的。

方向光 是一束从一个固定的方向照射过来的光。这种光的特点可以理解为好像是从一个很遥远的地方照射过来的,然后光线中的每一个光子与其他光子都是平行运动的。举个例子来说,阳光就可以认为是方向光。

点光源光 是指光线是从一个点发射出来的,是向着四面八方发射的。这种光在我们的现实生活中是最常被用到的。举个例子来说,电灯泡就是向各个方向发射光线的。

以我们的需要来看,我们会简化光照模型,只考虑简单的方向光和环境光,不会考虑任何镜面反射和点光源。这样的话,我们只需要在我们使用的环境光上加上照射到旋转立方体的方向光就可以了。在这里可以看到之前的旋转立方体的例子。

虽然可以抛开了点光源和镜面反射,但是关于方向光还是有两点需要注意一下:

需要在每个顶点信息中加入面的朝向法线。这个法线是一个垂直于这个顶点所在平面的向量。
需要明确方向光的传播方向,可以使用一个方向向量来定义。
接着,我们会更新顶点着色器,考虑到环境光,再考虑到方向光(方向光的作用会因为光线方向与面的夹角关系而不同),计算每一个顶点的颜色。实现这一目标的代码如下。

建立顶点法线

首先我们需要做的是建立一个数组来存放立方体所有顶点的法线。由于立方体是一个很简单的物体,所以很容易实现;显然如果是对复杂物体,则法线的计算方法需要更深入的研究。(注:译者调试后发现此处 new WebGLFloatArray(...) 可能应该使用 new Float32Array())

cubeVerticesNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);var vertexNormals = [// Front0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,// Back0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,// Top0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,// Bottom0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0,// Right1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,// Left-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
];gl.bufferData(gl.ARRAY_BUFFER,new WebGLFloatArray(vertexNormals),gl.STATIC_DRAW,
);

现在我们应该对此非常熟悉了;创建新的 buffer,将它和 gl.ARRAR_BUFFER 绑定在一起,然后通过调用 bufferData() 把我们的顶点法线数组一起传入。

然后我们在 drawScene() 中添加代码,将法线数组和着色器的 attribute 绑定起来以便着色器能够获取到法线数组的信息。

(此处变量 vertexNormalAttribute 应该在 initShader() 函数中声明,并赋值:vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); gl.enableVertexAttribArray(vertexNormalAttribute);)

gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);

最后,我们(此处代码应该在 setMatrixUniforms() 函数中添加)需要更新下代码,在着色器中建立和传递法线向量矩阵,用这个矩阵来处理当前立方体相对于光源位置法线向量的转换 (注:译者调试后发现此处 new WebGLFloatArray(...) 应该使用 new Float32Array()):

var normalMatrix = mvMatrix.inverse();
normalMatrix = normalMatrix.transpose();
var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
gl.uniformMatrix4fv(nUniform,false,new WebGLFloatArray(normalMatrix.flatten()),
);

更新着色器

现在着色器需要的所有数据已经全部可以获取到了(或者说全部准备好了),我们需要更新下着色器本身的代码。

顶点着色器

首先更新顶点着色器,让它给每一个基于环境光和方向光的顶点一个着色器值。让我们看下代码:

<script id="shader-vs" type="x-shader/x-vertex">// 定义了一个顶点属性aVertexNormal,用于存储顶点的法向量信息。attribute highp vec3 aVertexNormal;// 定义了一个顶点属性aVertexPosition,用于存储顶点的位置信息。attribute highp vec3 aVertexPosition;// 定义了一个顶点属性aTextureCoord,用于存储顶点的纹理坐标信息attribute highp vec2 aTextureCoord;// 定义了一个uniform变量uNormalMatrix,用于传递法向量矩阵。uniform highp mat4 uNormalMatrix;// 定义了一个uniform变量uMVMatrix,用于传递模型视图矩阵。uniform highp mat4 uMVMatrix;// 定义了一个uniform变量uPMatrix,用于传递投影矩阵uniform highp mat4 uPMatrix;// 在顶点着色器和片元着色器之间传递纹理坐标信息varying highp vec2 vTextureCoord;// 用于在顶点着色器和片元着色器之间传递光照信息varying highp vec3 vLighting;void main(void) {gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);vTextureCoord = aTextureCoord;// 定义了环境光、定向光的颜色和方向。highp vec3 ambientLight = vec3(0.6, 0.6, 0.6);highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);// 将法向量乘以法向量矩阵uNormalMatrix得到变换后的法向量transformedNormalhighp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);// 这里计算了法向量与光线方向向量之间的点积,点积结果表示了法向量和光线的夹角关系,值域为[-1, 1]// 使用max函数确保夹角余弦值不会为负数,如果夹角余弦值小于0,则将其设为0,表示光线不会照射到背对光源的表面highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);// 最终的光照效果通过将环境光和定向光的效果相加得到,乘以定向光的强度directional,从而得到最终的光照效果vLighting = ambientLight + (directionalLightColor * directional);}
</script>

一旦顶点位置计算完毕,我们就可以获得纹理对应于顶点的坐标,从而计算出顶点的阴影。

我们先根据立方体位置和朝向,通过顶点法线乘以法线矩阵来转换法线。接着我们可以通过计算转换过后的法线与方向向量(即,光来自的方向)的点积来计算得出顶点反射方向光的量。如果计算出的这个值小于 0,则我们把值固定设为 0,因为你不会有小于 0 的光。

当方向光的量计算完,我们可以通过获取环境光并且添加方向光的颜色和要提供的定向光的量来生成光照值(lighting value)。最终结果我们会得到一个 RGB 值,用于片段着色器调整我们渲染的每一个像素的颜色。

片段着色器

片段着色器现在需要根据顶点着色器计算出的光照值来更新:

<script id="shader-fs" type="x-shader/x-fragment">varying highp vec2 vTextureCoord;varying highp vec3 vLighting;uniform sampler2D uSampler;// 从纹理中获取的颜色与光照效果相乘,得到受光照影响的最终颜色,并将其作为片元的最终颜色输出void main(void) {// 这里从纹理uSampler中获取纹理坐标为(vTextureCoord.s, vTextureCoord.t)处的颜色mediump vec4 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));// 将从纹理中获取的颜色与之前计算的光照效果vLighting相乘,以获得最终的受光照影响的颜色gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);}
</script>

源码如下:

// init-buffers.js
function initBuffers(gl) {const positionBuffer = initPositionBuffer(gl);const textureCoordBuffer = initTextureBuffer(gl);const indexBuffer = initIndexBuffer(gl);const normalBuffer = initNormalBuffer(gl);return {position: positionBuffer,normal: normalBuffer,textureCoord: textureCoordBuffer,indices: indexBuffer,};
}function initPositionBuffer(gl) {// Create a buffer for the square's positions.const positionBuffer = gl.createBuffer();// Select the positionBuffer as the one to apply buffer// operations to from here out.gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);const positions = [// Front face-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,// Back face-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,// Top face-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,// Bottom face-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,// Right face1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,// Left face-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,];// Now pass the list of positions into WebGL to build the// shape. We do this by creating a Float32Array from the// JavaScript array, then use it to fill the current buffer.gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);return positionBuffer;
}function initColorBuffer(gl) {const faceColors = [[1.0, 1.0, 1.0, 1.0], // Front face: white[1.0, 0.0, 0.0, 1.0], // Back face: red[0.0, 1.0, 0.0, 1.0], // Top face: green[0.0, 0.0, 1.0, 1.0], // Bottom face: blue[1.0, 1.0, 0.0, 1.0], // Right face: yellow[1.0, 0.0, 1.0, 1.0], // Left face: purple];// Convert the array of colors into a table for all the vertices.var colors = [];for (var j = 0; j < faceColors.length; ++j) {const c = faceColors[j];// Repeat each color four times for the four vertices of the facecolors = colors.concat(c, c, c, c);}const colorBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);return colorBuffer;
}function initIndexBuffer(gl) {const indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);// This array defines each face as two triangles, using the// indices into the vertex array to specify each triangle's// position.const indices = [0,1,2,0,2,3, // front4,5,6,4,6,7, // back8,9,10,8,10,11, // top12,13,14,12,14,15, // bottom16,17,18,16,18,19, // right20,21,22,20,22,23, // left];// Now send the element array to GLgl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Uint16Array(indices),gl.STATIC_DRAW);return indexBuffer;
}function initTextureBuffer(gl) {const textureCoordBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);const textureCoordinates = [// Front0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,// Back0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,// Top0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,// Bottom0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,// Right0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,// Left0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,];gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(textureCoordinates),gl.STATIC_DRAW);return textureCoordBuffer;
}
// 设置法线数据
function initNormalBuffer(gl) {const normalBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);const vertexNormals = [// Front0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,// Back0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,// Top0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,// Bottom0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0,// Right1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,// Left-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,];gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertexNormals),gl.STATIC_DRAW);return normalBuffer;
}export { initBuffers };
// draw-scene.js
function drawScene(gl, programInfo, buffers, texture, cubeRotation) {gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaquegl.clearDepth(1.0); // Clear everythinggl.enable(gl.DEPTH_TEST); // Enable depth testinggl.depthFunc(gl.LEQUAL); // Near things obscure far things// Clear the canvas before we start drawing on it.gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);// Create a perspective matrix, a special matrix that is// used to simulate the distortion of perspective in a camera.// Our field of view is 45 degrees, with a width/height// ratio that matches the display size of the canvas// and we only want to see objects between 0.1 units// and 100 units away from the camera.const fieldOfView = (45 * Math.PI) / 180; // in radiansconst aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;const zNear = 0.1;const zFar = 100.0;const projectionMatrix = mat4.create();// note: glmatrix.js always has the first argument// as the destination to receive the result.mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);// Set the drawing position to the "identity" point, which is// the center of the scene.const modelViewMatrix = mat4.create();// Now move the drawing position a bit to where we want to// start drawing the square.mat4.translate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to translate[-0.0, 0.0, -6.0]); // amount to translatemat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation, // amount to rotate in radians[0, 0, 1]); // axis to rotate around (Z)mat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation * 0.7, // amount to rotate in radians[0, 1, 0]); // axis to rotate around (Y)mat4.rotate(modelViewMatrix, // destination matrixmodelViewMatrix, // matrix to rotatecubeRotation * 0.3, // amount to rotate in radians[1, 0, 0]); // axis to rotate around (X)// 创建了一个新的4x4矩阵normalMatrix,用于存储法向量变换所需的数据const normalMatrix = mat4.create();// 计算了modelViewMatrix的逆矩阵,并将结果存储在normalMatrix中。这一步是为了得到法向量矩阵的基础,确保在对象发生变换时法向量能够正确地跟随变换mat4.invert(normalMatrix, modelViewMatrix);// 对normalMatrix进行转置操作。在一些情况下,转置操作可以用来修正因为缩放操作而导致的法向量失真问题。然而,在通常情况下,由于法向量矩阵通常是模型视图矩阵的逆转置,因此这一步可能并不是必要的,因为mat4.invert()函数已经正确生成了法向量矩阵mat4.transpose(normalMatrix, normalMatrix);// Tell WebGL how to pull out the positions from the position// buffer into the vertexPosition attribute.setPositionAttribute(gl, buffers, programInfo);setTextureAttribute(gl, buffers, programInfo);// Tell WebGL which indices to use to index the verticesgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);setNormalAttribute(gl, buffers, programInfo);// Tell WebGL to use our program when drawinggl.useProgram(programInfo.program);// Set the shader uniformsgl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix,false,projectionMatrix);gl.uniformMatrix4fv(programInfo.uniformLocations.modelViewMatrix,false,modelViewMatrix);gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix,false,normalMatrix);// Tell WebGL we want to affect texture unit 0gl.activeTexture(gl.TEXTURE0);// Bind the texture to texture unit 0gl.bindTexture(gl.TEXTURE_2D, texture);// Tell the shader we bound the texture to texture unit 0gl.uniform1i(programInfo.uniformLocations.uSampler, 0);{const vertexCount = 36;const type = gl.UNSIGNED_SHORT;const offset = 0;gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);}
}// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
function setPositionAttribute(gl, buffers, programInfo) {const numComponents = 3;const type = gl.FLOAT; // the data in the buffer is 32bit floatsconst normalize = false; // don't normalizeconst stride = 0; // how many bytes to get from one set of values to the next// 0 = use type and numComponents aboveconst offset = 0; // how many bytes inside the buffer to start fromgl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);gl.vertexAttribPointer(programInfo.attribLocations.vertexPosition,numComponents,type,normalize,stride,offset);gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
}// Tell WebGL how to pull out the colors from the color buffer
// into the vertexColor attribute.
function setColorAttribute(gl, buffers, programInfo) {const numComponents = 4;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);gl.vertexAttribPointer(programInfo.attribLocations.vertexColor,numComponents,type,normalize,stride,offset);gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
}// tell webgl how to pull out the texture coordinates from buffer
function setTextureAttribute(gl, buffers, programInfo) {const num = 2; // every coordinate composed of 2 valuesconst type = gl.FLOAT; // the data in the buffer is 32-bit floatconst normalize = false; // don't normalizeconst stride = 0; // how many bytes to get from one set to the nextconst offset = 0; // how many bytes inside the buffer to start fromgl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);gl.vertexAttribPointer(programInfo.attribLocations.textureCoord,num,type,normalize,stride,offset);gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);
}// 设置法线数据读取步长并将法线数据传递给着色器
function setNormalAttribute(gl, buffers, programInfo) {const numComponents = 3;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);gl.vertexAttribPointer(programInfo.attribLocations.vertexNormal,numComponents,type,normalize,stride,offset);gl.enableVertexAttribArray(programInfo.attribLocations.vertexNormal);
}export { drawScene };

相关文章:

WebGL之灯光使用解析

在使用灯光之前&#xff0c;首先我们需要了解&#xff0c;与定义更广泛的 OpenGL 不同&#xff0c;WebGL 并没有继承 OpenGL 中灯光的支持。所以你只能由自己完全得控制灯光。幸运得是&#xff0c;这也并不是很难&#xff0c;本文接下来就会介绍完成灯光的基础。 在 3D 空间中…...

【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…...

PostgreSQL索引篇 | TSearch2 全文搜索

PostgreSQL版本为8.4.1 &#xff08;本文为《PostgreSQL数据库内核分析》一书的总结笔记&#xff0c;需要电子版的可私信我&#xff09; 索引篇&#xff1a; PostgreSQL索引篇 | BTreePostgreSQL索引篇 | GiST索引PostgreSQL索引篇 | Hash索引PostgreSQL索引篇 | GIN索引 (倒排…...

SpringMVC 中的常用注解和用法

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;JavaEE &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 注解 1. MVC定义2. 注解2.1 RequestMappin…...

智慧城市中的数据力量:大数据与AI的应用

目录 一、引言 二、大数据与AI技术的融合 三、大数据与AI在智慧城市中的应用 1、智慧交通 2、智慧环保 3、智慧公共安全 4、智慧公共服务 四、大数据与AI在智慧城市中的价值 1、提高城市管理的效率和水平 2、优化城市资源的配置和利用 3、提升市民的生活质量和幸福感…...

德人合科技|天锐绿盾加密软件——数据防泄漏系统

德人合科技是一家专注于提供企业级信息安全解决方案的服务商&#xff0c;提供的天锐绿盾加密软件是一款专为企业设计的数据安全防护产品&#xff0c;主要用于解决企事业单位内部敏感数据的防泄密问题。 www.drhchina.com PC端&#xff1a; https://isite.baidu.com/site/wjz012…...

C语言---单身狗问题

1.单身狗初阶 这个题目就是数组里面有一串数字&#xff0c;都是成对存在的&#xff0c;只有一个数字只出现了一次&#xff0c;请你找出来 &#xff08;1&#xff09;异或是满足交换律的&#xff0c;两个相同的数字异或之后是0&#xff1b; &#xff08;2&#xff09;让0和每个…...

一次gitlab 502故障解决过程

通过top,发现prometheus进程占用CPU接近100%&#xff0c;这肯定有点异常。gitlab-ctl tail prometheus 发现有报错的情况&#xff0c;提示空间不足。暂时不管空间的问题。 2024-03-07_05:48:09.01515 ts2024-03-07T05:48:09.014Z callermain.go:1116 levelerror err"open…...

Xilinx 7系列 FPGA硬件知识系列(一)——FPGA选型参考

目录 1.1 Xilinx-7系列产品的工艺级别 ​编辑1.2 Xilinx-7系列产品的特点 1.2.1 Spartan-7系列 1.2.2 Artix-7系列 1.2.3 Kintex-7系列 1.2.4 Virtex-7系列 1.3 Xilinx-7系列FPGA对比 1.3.1 DSP资源柱状图 ​1.3.2 Block RAM资源柱状图 ​1.3.3 高速串行收…...

【C++从练气到飞升】02---初识类与对象

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 ⛳️推荐 一、面向过程和面向对象初步认识 二、类的引用 1. C语言版 2. C版 三、类的定义 类的两种定义方式&#xff…...

探秘分布式神器RMI:原理、应用与前景分析(一)

本系列文章简介&#xff1a; 本系列文章将深入探究RMI远程调用的原理、应用及未来的发展趋势。首先&#xff0c;我们会详细介绍RMI的工作原理和基本流程&#xff0c;解析其在分布式系统中的核心技术。随后&#xff0c;我们将探讨RMI在各个领域的应用&#xff0c;包括分布式计算…...

JVM(Java虚拟机)概述

1. JVM的定义和作用 JVM&#xff08;Java Virtual Machine&#xff09;是一个能够运行Java字节码的虚拟计算机。它是Java平台的核心组成部分&#xff0c;负责执行编译后的Java程序&#xff0c;提供跨平台运行的能力。JVM使得Java程序可以在任何安装了JVM的操作系统上运行&#…...

C#,数值计算,用割线法(Secant Method)求方程根的算法与源代码

1 割线法 割线法用于求方程 f(x) 0 的根。它是从根的两个不同估计 x1 和 x2 开始的。这是一个迭代过程&#xff0c;包括对根的线性插值。如果两个中间值之间的差值小于收敛因子&#xff0c;则迭代停止。 亦称弦截法&#xff0c;又称线性插值法.一种迭代法.指用割线近似曲线求…...

HTML静态网页成品作业(HTML+CSS)——花主题介绍网页设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…...

Keepalive 解决nginx 的高可用问题

一 说明 keepalived利用 VRRP Script 技术&#xff0c;可以调用外部的辅助脚本进行资源监控&#xff0c;并根据监控的结果实现优先动态调整&#xff0c;从而实现其它应用的高可用性功能 参考配置文件&#xff1a; /usr/share/doc/keepalived/keepalived.conf.vrrp.localche…...

DPN网络

DPN DPN&#xff08;Dual Path Networks&#xff09;是一种网络结构&#xff0c;它结合了DensNet和ResNetXt两种思想的优点。这种结构的目的是通过不同的路径来利用神经网络的不同特性&#xff0c;从而提高模型的效率和性能。 DenseNet 的特点是其稠密连接路径&#xff0c;使…...

循序渐进丨MogDB 数据库新特性之SQL PATCH绑定执行计划

1 SQL PATCH 熟悉 Oracle 的DBA都知道&#xff0c;生产系统出现性能问题时&#xff0c;往往是SQL走错了执行计划&#xff0c;紧急情况下&#xff0c;无法及时修改应用代码&#xff0c;DBA可以采用多种方式针对于某类SQL进行执行计划绑定&#xff0c;比如SQL Profile、SPM、SQL …...

【论文阅读随笔】RoPE/旋转编码:ROFORMER: ENHANCED TRANSFORMER WITH ROTARY POSITION EMBEDDING

文章目录 1.目的&#xff1a;通过绝对位置编码的方式实现相对位置编码2.理解RoPE&#xff0c;在我看来有几个需要注意的点&#xff1a;3.本文相关复数概念&#xff1a;3.1.复数乘法的几何意义3.2.复数内积 VS. 复数乘法 4.REF&#xff1a; 1.目的&#xff1a;通过绝对位置编码的…...

数据挖掘

一.数据仓库概述&#xff1a; 1.1数据仓库概述 1.1.1数据仓库定义 数据仓库是一个用于支持管理决策的、面向主题、集成、相对稳定且反映历史变化的数据集合。 1.1.2数据仓库四大特征 集成性&#xff08;Integration&#xff09;&#xff1a; 数据仓库集成了来自多个不同来源…...

java SSM旅游景点与公交线路查询系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM旅游景点与公交线路查询系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...