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

WebGL 计算点光源下的漫反射光颜色

目录

点光源光

逐顶点光照(插值)

示例程序(PointLightedCube.js)  

代码详解

示例效果

逐顶点处理点光源光照效果时出现的不自然现象

更逼真:逐片元光照

示例程序(PointLightedCube_perFragment.js)

代码详解


点光源光

与平行光相比,点光源光发出的光,在三维空间的不同位置上其方向也不同,如下图所示。所以,在对点光源光下的物体进行着色时,需要在每个入射点计算点光源光在该处的方向。

点光源光的方向随位置变化

平行光漫反射https://blog.csdn.net/dabaooooq/article/details/132942385?spm=1001.2014.3001.5502WebGL 计算平行光、环境光下的漫反射光颜色_山楂树の的博客-CSDN博客https://blog.csdn.net/dabaooooq/article/details/132942385?spm=1001.2014.3001.5502根据每个顶点的法向量和平行光入射方向来计算反射光的颜色。这一节还是采用该方法,只不过点光源光的方向不再是恒定不变的,而要根据每个顶点的位置逐一计算。着色器需要知道点光源光自身的所在位置,而不是光的方向。 

示例程序PointLightedCube是WebGL 计算平行光、环境光下的漫反射光颜色_山楂树の的博客-CSDN博客LightedCube_ambient示例程序的点光源光版本,显示了一个点光源下的红色立方体。立方体表面仍然是漫反射,环境光保持不变,程序的效果如下图所示。

 

逐顶点光照(插值)

示例程序(PointLightedCube.js)  

如下显示了示例程序的代码,与WebGL 计算平行光、环境光下的漫反射光颜色_山楂树の的博客-CSDN博客LightedCube_ambient相比,顶点着色器中新增加了u_ModelMatrix变量和u_LightPosition变量,前者表示模型矩阵,后者表示点光源的位置。本例中的光是点光源光而非平行光,所以我们需要用到点光源光的位置,而不是光线方向。为了让你看得更清楚,本例将立方体稍做放大。

var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'attribute vec4 a_Color;\n' +'attribute vec4 a_Normal;\n' +'uniform mat4 u_MvpMatrix;\n' +'uniform mat4 u_ModelMatrix;\n' +   // 模型矩阵'uniform mat4 u_NormalMatrix;\n' +  // 用来变换法向量的矩阵'uniform vec3 u_LightColor;\n' +    // 光的颜色'uniform vec3 u_LightPosition;\n' + // 光源位置(世界坐标系)'uniform vec3 u_AmbientLight;\n' +  // 环境光颜色'varying vec4 v_Color;\n' +'void main() {\n' +'  gl_Position = u_MvpMatrix * a_Position;\n' +//计算变换后的法向量并归一化'  vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +// 计算顶点的世界坐标(模型矩阵变换后的世界坐标)'  vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +// 计算点光源方向并归一化(用点光源的世界坐标 - 物体顶点坐标)'  vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +// 计算光线方向和法向量的点积 cosθ'  float nDotL = max(dot(lightDirection, normal), 0.0);\n' +// 计算点光源漫反射光的颜色'  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +// 计算环境光产生的反射光的颜色'  vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +// 将以上两者相加作为最终的颜色(物体表面的反射光颜色 = 漫反射光颜色(这里是点光源) + 环境反射光颜色)'  v_Color = vec4(diffuse + ambient, a_Color.a);\n' + '}\n';var FSHADER_SOURCE ='#ifdef GL_ES\n' +'precision mediump float;\n' +'#endif\n' +'varying vec4 v_Color;\n' +'void main() {\n' +'  gl_FragColor = v_Color;\n' +'}\n';function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n = initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.enable(gl.DEPTH_TEST);// 获取统一变量的存储位置等等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');// 设置点光源的颜色为白色gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);// 设置点光源的位置(世界坐标下)gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);// 设置环境光的颜色gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);var modelMatrix = new Matrix4();  // 模型矩阵var mvpMatrix = new Matrix4();    // 模型视图投影矩阵var normalMatrix = new Matrix4(); // 用于计算变换后的法向量的矩阵// 旋转90度,计算模型矩阵modelMatrix.setRotate(90, 0, 1, 0);// 将模型矩阵传递给u_ModelMatrixgl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);// 计算模型视图投影矩阵mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); mvpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);mvpMatrix.multiply(modelMatrix); // 模型 视图投影 相乘得到最终矩阵gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);  // 将模型视图投影矩阵传给u_MvpMatrix变量/* 根据模型矩阵计算逆转置矩阵以变换法线 */ normalMatrix.setInverseOf(modelMatrix); // 求原矩阵的逆矩阵normalMatrix.transpose(); // 将上一步求得的逆矩阵进行转置,并将自己设为转置后的结果gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);   // 将用来变换法向量的矩阵传给u_NormalMatrix变量gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}function initVertexBuffers(gl) {// Create a cube//    v6----- v5//   /|      /|//  v1------v0|//  | |     | |//  | |v7---|-|v4//  |/      |///  v2------v3// Coordinatesvar 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 front2.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 right2.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 up-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 left-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 down2.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 back]);// Colorsvar colors = new Float32Array([1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0     // v4-v7-v6-v5 back]);// Normalvar 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 front1.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 right0.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 up-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 left0.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 down0.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 back]);// Indices of the verticesvar indices = new Uint8Array([0, 1, 2,   0, 2, 3,    // front4, 5, 6,   4, 6, 7,    // right8, 9,10,   8,10,11,    // up12,13,14,  12,14,15,    // left16,17,18,  16,18,19,    // down20,21,22,  20,22,23     // back]);// 将顶点属性写入缓冲区(坐标、颜色和法线)if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;gl.bindBuffer(gl.ARRAY_BUFFER, null);var indexBuffer = gl.createBuffer();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, type) {var buffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);var a_attribute = gl.getAttribLocation(gl.program, attribute);gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);gl.enableVertexAttribArray(a_attribute);return true;
}

代码详解

最关键的变化发生在顶点着色器中。首先使用模型矩阵变换顶点坐标,获得顶点在世界坐标系中的坐标(即变换后的坐标),以便计算点光源光在顶点处的方向。点光源向四周放射光线,所以顶点处的光线方向是由点光源光坐标减去顶点坐标而得到的矢量。点光源在世界坐标系中的坐标已经传给了着色器中的u_LightPosition(第9行),而前面也已经算出了顶点在世界坐标系中的坐标,这样就计算出了光线方向矢量lightDirection(第19行)。注意,需要使用normalize()函数进行归一化,以保证光线方向矢量的长度为1.0。最后,计算光线方向矢量与法向量的点积(第21行),从而算出每个顶点的颜色。

运行程序,你会发现效果更加逼真了,如下图所示。但是,如果仔细观察还是能发现一个问题:立方体表面上有不自然的线条

示例效果

逐顶点处理点光源光照效果时出现的不自然现象

出现该现象的原因:你应该还记得,WebGL系统会根据顶点的颜色,内插出表面上每个片元的颜色。实际上,点光源光照射到一个表面上,所产生的效果(即每个片元获得的颜色)与简单使用4个顶点颜色(虽然这4个顶点的颜色也是由点光源产生)内插出的效果并不完全相同(在某些极端情况下甚至很不一样),所以为了使效果更加逼真,我们需要对表面的每一点(而不仅仅是4个顶点)计算光照效果。如果使用一个球体,二者的差异可能会更明显,如下图所示。

点光源下的球体

如你所见,左图中球体暗部与亮部的分界不是很自然,而右侧的就自然多了。

更逼真:逐片元光照

乍一听,要在表面的每一点上计算光照产生的颜色,似乎是个不可能完成的任务。但实际上,我们只需要逐片元地进行计算。片元着色器总算要派上用场了。

示例程序(PointLightedCube_perFragment.js)

如下显示了示例程序的代码,与PointLightedCube.js相比,只有着色器部分被修改了,计算光照效果的逻辑从顶点着色器移到了片元着色器中。

var VSHADER_SOURCE = // 309'attribute vec4 a_Position;\n' +'attribute vec4 a_Color;\n' +'attribute vec4 a_Normal;\n' +'uniform mat4 u_MvpMatrix;\n' +'uniform mat4 u_ModelMatrix;\n' +    // 模型矩阵'uniform mat4 u_NormalMatrix;\n' +   // 用来变换法向量的矩阵'varying vec4 v_Color;\n' +'varying vec3 v_Normal;\n' +'varying vec3 v_Position;\n' +'void main() {\n' +'  gl_Position = u_MvpMatrix * a_Position;\n' +// 计算顶点的世界坐标'  v_Position = vec3(u_ModelMatrix * a_Position);\n' +'  v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +'  v_Color = a_Color;\n' + '}\n';var FSHADER_SOURCE ='#ifdef GL_ES\n' +'precision mediump float;\n' +'#endif\n' +'uniform vec3 u_LightColor;\n' +     // 点光源颜色'uniform vec3 u_LightPosition;\n' +  // 点光源位置'uniform vec3 u_AmbientLight;\n' +   // 环境光颜色'varying vec3 v_Normal;\n' +'varying vec3 v_Position;\n' +'varying vec4 v_Color;\n' +'void main() {\n' +// 对法线进行归一化,因为其内插之后长度不一定是1.0'  vec3 normal = normalize(v_Normal);\n' +// 计算点光源光线方向并归一化'  vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +// 计算光线方向和法向量的点积(余弦角度值)'  float nDotL = max(dot(lightDirection, normal), 0.0);\n' +// 计算diffuse,ambient以及最终的颜色'  vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;\n' +'  vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +'  gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' +'}\n';function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n = initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.enable(gl.DEPTH_TEST);// Get the storage locations of uniform variablesvar 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');// Set the light color (white)gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);// Set the light direction (in the world coordinate)gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);// Set the ambient lightgl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);var modelMatrix = new Matrix4();  // Model matrixvar mvpMatrix = new Matrix4();    // Model view projection matrixvar normalMatrix = new Matrix4(); // Transformation matrix for normals// Calculate the model matrixmodelMatrix.setRotate(90, 0, 1, 0); // Rotate around the y-axis// Calculate the view projection matrixmvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);mvpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);mvpMatrix.multiply(modelMatrix);// Calculate the matrix to transform the normal based on the model matrixnormalMatrix.setInverseOf(modelMatrix);normalMatrix.transpose();// Pass the model matrix to u_ModelMatrixgl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);// Pass the model view projection matrix to u_mvpMatrixgl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);// Pass the transformation matrix for normals to u_NormalMatrixgl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);// Clear color and depth buffergl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);// Draw the cubegl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}function initVertexBuffers(gl) {// Create a cube//    v6----- v5//   /|      /|//  v1------v0|//  | |     | |//  | |v7---|-|v4//  |/      |///  v2------v3// Coordinatesvar 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 front2.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 right2.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 up-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 left-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 down2.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 back]);// Colorsvar colors = new Float32Array([1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0     // v4-v7-v6-v5 back]);// Normalvar 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 front1.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 right0.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 up-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 left0.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 down0.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 back]);// Indices of the verticesvar indices = new Uint8Array([0, 1, 2,   0, 2, 3,    // front4, 5, 6,   4, 6, 7,    // right8, 9,10,   8,10,11,    // up12,13,14,  12,14,15,    // left16,17,18,  16,18,19,    // down20,21,22,  20,22,23     // back]);// Write the vertex property to buffers (coordinates, colors and normals)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();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();gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);var a_attribute = gl.getAttribLocation(gl.program, attribute);gl.vertexAttribPointer(a_attribute, num, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(a_attribute);return true;
}

代码详解

为了逐片元地计算光照,你需要知道:(1)片元在世界坐标系下的坐标,(2)片元处表面的法向量。可以在顶点着色器中,将顶点的世界坐标和法向量以varying变量的形式传入片元着色器,片元着色器中的同名变量就已经是内插后的逐片元值了。

顶点着色器使用模型矩阵乘以顶点坐标计算出顶点的世界坐标(第14行),将其赋值给v_Position变量。经过内插过程后,片元着色器就获得了逐片元的v_Position变量,也就是片元的世界坐标。类似地,顶点着色器将顶点的法向量赋值给v_Normal变量(第15行),经过内插,片元着色器就获得了逐片元的v_Normal变量,即片元的法向量。

片元着色器计算光照效果的方法与PointLightedCube.js相同。首先对法向量v_Normal进行归一化(第31行),因为内插之后法向量可能不再是1.0了;然后,计算片元处的光线方向并对其归一化(第33行);接着计算法向量与光线方向的点积(第35行);最后分别计算点光源光和环境光产生的反射光颜色,并将两个结果加起来,赋值给gl_FragColor,片元就会显示为这个颜色。

如果场景中有超过一个点光源,那么就需要在片元着色器中计算每一个点光源(当然还有环境光)对片元的颜色贡献,并将它们全部加起来。换句话说,有几个点光源,就得按照表面反射光颜色(漫反射+环境反射)公式计算几次。

相关文章:

WebGL 计算点光源下的漫反射光颜色

目录 点光源光 逐顶点光照(插值) 示例程序(PointLightedCube.js) 代码详解 示例效果 逐顶点处理点光源光照效果时出现的不自然现象 更逼真:逐片元光照 示例程序(PointLightedCube_perFragment.js…...

Java精品项目源码第61期垃圾分类科普平台(代号V061)

Java精品项目源码第61期垃圾分类科普平台(代号V061) 大家好,小辰今天给大家介绍一个垃圾分类科普平台,演示视频公众号(小辰哥的Java)对号查询观看即可 文章目录 Java精品项目源码第61期垃圾分类科普平台(代号V061)难度指数&…...

【Unity3D】资源管理

1 前言 Unity 中资源管理方案主要有 Resources、TextAsset、ScriptableObject 、AssetDatabase、PlayerPrefs、Addressables、AssetBundle、SQLite,本文将介绍其中大部分方案。 2 Resources Resources 主要用于加载资源,被加载的资源需要放在 Resources…...

数据结构-----队列

目录 前言 队列 定义 队列的定义和操作方法 队列节点的定义 操作方式 顺序表实现队列(C/C代码) 链表实现队列(C/C代码) Python语言实现队列 前言 排队是我们日常生活中必不可少的一件事,去饭堂打饭的时候排队&a…...

postgresql教程

postgreSQL教程目录 postgreSQL 创建数据库的方式:postgreSQL删除数据库的方式:PostgreSQL 创建表格postgre删除表格:postgreSQL INSERT INTO 语句postgreSQL SELECT 语句:postgresql索引:什么情况下要避免使用索引? p…...

1万6千多最好的背单词SQLITE\ACCESS数据库

本来是实在不想再整英语类的数据了,因为实在是太多了,奈何今天弄到的这份数据库实在很精彩,因此还是希望能够有人喜欢。 搞一个“accept”字段的样例: 【explain】 vi. 承认;同意;承兑; vt. 接受;承认;承担;承兑; 【etyma】 ac…...

springboot aop Aspectj 切面

常用: Aspect、Component、Pointcut、Before、AfterReturning SpringBoot的AOP(aspect注解)的简单使用 - 知乎 springboot项目中引入Aspectj并使用_springboot引入aspectj_山鬼谣me的博客-CSDN博客...

Leetcode 2862. Maximum Element-Sum of a Complete Subset of Indices

Leetcode 2862. Maximum Element-Sum of a Complete Subset of Indices 1. 解题思路2. 代码实现 题目链接:2862. Maximum Element-Sum of a Complete Subset of Indices 1. 解题思路 这一题的核心在于想明白一点: 要使得子序列当中任意两个数之积均为…...

第一百四十七回 自定义组件一

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了跟手指移动的小球相关的内容,本章回中将介绍 自定义组件.闲话休提,让我们一起Talk Flutter吧。 概念介绍 在项目中有些内容无法通过现有的组件来实现,因此需要自定义组件&#xf…...

MySQL 重复数据的处理

文章目录 MySQL 重复数据的处理一,常用处理方法二,统计重复数据三,过滤重复数据四,删除重复数据拓展:MySQL预防SQL注入(一)SQL注入 概述(二)预防措施 MySQL 重复数据的处…...

Java文字描边效果实现

效果: FontUtil工具类的完整代码如下: 其中实现描边效果的函数为:generateAdaptiveStrokeFontImage() package com.ncarzone.data.contentcenter.biz.img.util;import org.springframework.core.io.ClassPathResource; import org.springfr…...

【Web_环境搭建_Python3_pip】pip的升级、安装、更新、卸载,以及pipupgrade和pip-review的基础使用

** 官方说明 ** pip(Python Package Index)是一个以 Python 语言写成的软件包管理系統,使用 pip 可以非常方便的安装和管理 python 软件包PIP ** 查看信息 ** 查看版本 : pip --version查看已有 : pip list、pip freeze查看帮助 : pip help查看库信息 : pip show -f package_…...

农民朋友有福利啦!建行江门市分行“裕农通+农资结算”平台正式上线

随着广东广圣农业发展有限公司办公室内的裕农通“智慧眼”结算机“叮”的一声到账提醒,标志着全国首个“裕农通农资结算“平台的成功上线,也标志着建行广东省江门市分行的裕农通业务又迈上了一个新的台阶。 广东广圣农业发展有限公司(以下简…...

super详解

父类 package com.mypackage.oop.demo06;public class Person06{public Person06() {System.out.println("Person06无参执行了");}protected String name "hexioahei";public void print(){System.out.println("Person");} }子类 package com…...

GMS地下水数值模拟丨GMS各模块、三维地质模型构建及与MODFLOW耦合、地下水流动数值模拟及报告编制、地下水溶质运移模型、反应性溶质运移等

目录 第一部分 地下水数值模拟理论模块 第二部分 地下水数值模拟数据收集、准备及预处理 第三部分 GMS各模块实践 第四部分 三维地质模型构建及与MODFLOW耦合 第五部分 地下水流动数值模拟及报告编制 第六部分 地下水溶质运移模型 第七部分 反应性溶质运移 更多应用 以…...

Redis 配置文件详解 - 持久化(RDB、AOF)

目录 Redis 配置文件详解 单位 包含 INCLUDES 网络 NETWORK 通用 GENERAL 快照 SNAPSHOTTING (持久化) 复制 REPLICATION(主从复制) 安全 SECURITY(账号密码设置) ​编辑 限制 CLIENTS 追加模…...

在线Excel转JSON工具

在线Excel转JSON工具 上传excel将数据转换成json格式...

Spring编程常见错误50例-Spring Bean依赖注入常见错误(下)

Value没有注入预期的值 问题 对于Value可以装配多种类型的数据: 装配对象: Value("#{student}") private Student student;Bean public Student student(){Student student createStudent(1, "xie");return student; }装配字符…...

SpringBoot整合Canal实现MySQL与ES数据同步

文章目录 SpringBoot项目引入Canal依赖配置文件项目结构设置监听类其余类、接口内容启动类实体类Controller类Mapper接口Serice接口 运行测试 开始之前请确认docker中已运行mysql与canal容器,并完成了监听binlog配置 未完成可移步: Docker部署Canal监听…...

Zookeeper 源码分析流程

文章目录 前言Zookeeper启动加载磁盘数据与客户端的通信交互Leader选举准备节点状态处理总结 前言 Zookeeper 作为分布式协调服务为分布式系统提供了一些基础服务,如:命名服务、配置管理、同步等,使得开发者可以更加轻松地处理分布式问题。 …...

计数排序与基数排序

计数排序与基数排序 计数排序 计数排序:使用一个数组记录序列中每一个数字出现的次数,将该数组的下标作为实际数据,元素的值作为数据出现的次数。例如对于序列[3,0,1,1,3,3,0,2],统计的结果为: 0出现的次数&#xf…...

Mysql—表操作

目录 1、linux中数据库表名区分大小写,windows不区分2、创建数据库表3、外键4、查看数据表结构5、修改表5.1、修改表名5.2、添加字段5.3、指定位置添加字段5.4、修改字段名称5.5、修改字段类型5.6、修改字段位置5.7、删除字段5.8、修改表存储引擎5.9、删除外键 1、l…...

SpringCloud——微服务

微服务技术栈 在之前的开发过程中,我们将所有的服务都部署在一台服务器中,当我们的服务开始越来越多,业务越来越复杂,当一台服务器不能承担我们的业务的时候,就需要将不同的业务分开部署在不同的服务器上,…...

深入理解Java单例模式和优化多线程任务处理

目录 饿汉模式懒汉模式单线程版多线程版双重检查锁定 阻塞队列 单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例,并提供一个全局访问点。 饿汉模式 类加载的同时,创建实例。 class Singleton {private static final Singlet…...

已解决 Kotlin Error: Type mismatch: inferred type is String but Int was expected

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页: 🐅🐾猫头虎的博客🎐《面试题大全专栏》 🦕 文章图文并茂&#x1f996…...

Web应用系统的小安全漏洞及相应的攻击方式

写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为。 主要目的如下: 了解什么叫安全漏洞 知道什么是api 了解一些获取api的工具 通过对API的认识了解白盒接口测试基本概念和技术 免责声明: 本文主要是以学习交流为目的&a…...

git工具下载和安装

(1)从git官网下载安装包 然后安装 https://git-scm.com/downloads (2)git 学习参考官方的资料 https://git-scm.com/book/en/v2...

腾讯mini项目-【指标监控服务重构】2023-08-04

今日已办 关于 span-references 的调研 https://github.com/DataDog/dd-trace-js/issues/1761 https://github.com/open-telemetry/opentelemetry-specification/blob/874a451e7f6ac7fc54423ee3f03e5394197be35b/specification/compatibility/opentracing.md#span-references h…...

怎么推广自己抖店的商品?最适合0经验新手操作的办法,来看看

我是王路飞。 抖店开通后,想要把自己店铺的商品卖出去,就需要进行推广了。 但是怎么推广呢? 要么利用抖音的搜索和推荐流量,获取曝光,实现点击和转化。 不过这种玩法有个弊端,就是需要你有一定的电商经…...

线性代数的本质(三)——线性方程组

文章目录 线性方程组高斯消元法初等行变换线性方程组的解向量方程齐次线性方程组的解非齐次线性方程组的解 线性方程组 高斯消元法 客观世界最简单的数量关系是均匀变化的关系。在均匀变化问题中,列出的方程组是一次方程组,我们称之为线性方程组(Linea…...

微信开放平台是什么/常熟seo网站优化软件

1.请列举出在 JDK 中几个常用的设计模式? 单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.value…...

做进口货的电商网站/游戏推广员是做什么的

Nuxt.js 和 Vue 一样,支持插件,可以分为三种类型:自定义插件、Vue 插件和外部包和模块。 虽然 Nuxt 文档 详细讨论了最后两个,但它们仅简要说明了如何在 Nuxt 应用程序中构建和使用自定义插件。 全局自定义插件可以在几种情况下派…...

炉火建站/整站优化seo

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者前言在上一篇文章中,壹哥给大家讲解了Java中的数据类型,从此大家就知道了基本类型和引用类型,尤…...

wordpress第一张缩略图/处理事件seo软件

我正在使用docker-compose.yaml构建我的应用程序以进行本地开发,使用两个Dockerfiles - 一个用于app(WordPress),另一个用于nginx。由于这是一个使用Jenkins管道构建的特定应用程序,我无法更改Dockerfiles,但我希望能够在本地测试…...

wordpress下滑显示导航/输入关键词自动生成文章

一、进阶算法 1、贪心算法 买卖股票的最佳时机柠檬水找零 2、动态规划 不同路径K站中转内最便宜的航班二、算法的设计思想 1、分而治之 2、动态规划 3、贪心 4、回溯 三、算法在 js 中的应用场景...

充值网站制作/最新中高风险地区名单

转载请注明出处:http://blog.csdn.net/u011733020前言:在Android开发中,对于图片的加载可以说是个老生常谈的问题了,图片加载是一个比较坑的地方,处理不好,会有各种奇怪的问题,比如 加载导致界面…...