【OpenGL ES】GLSL基础语法
1 前言
本文将介绍 GLSL 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符(in、out、inout)、函数参数限定符等内容,另外提供了一个 include 工具,方便多文件管理 glsl 代码,实现代码的精简、复用。
Unity 中 Shader 介绍详见 → 【Unity3D】Shader常量、变量、结构体、函数,渲染管线介绍详见 → 【OpenGL ES】渲染管线。
2 数据类型
2.1 基本数据类型
2.1.1 基本数据类型
| 类型 | 说明 | 案例 |
|---|---|---|
| void | 空类型,即不返回任何值 | void fun() { } |
| bool | 布尔类型 true、false | bool a = true; |
| int | 带符号的整数 | int a = 0; |
| float | 带符号的浮点数 | float a = 1.0; float b = 2.; |
| vec2、vec3、vec4 | 2 维、3 维、4 维浮点数向量 | vec2 a = vec2(1., 2.); vec2 b = vec2(1.); // ⇔ vec2(1., 1.) vec3 c = vec3(a, 3.); // ⇔ vec3(1., 2., 3.) |
| bvec2、bvec3、bvec4 | 2 维、3 维、4 维布尔向量 | bvec2 a = bvec2(true, false); bvec2 b = bvec2(true); // ⇔ bvec2(true, true) bec3 c = bvec3(a, true); // ⇔ bvec3(true, false, true) |
| ivec2、ivec3、ivec4 | 2 维、3 维、4 维整数向量 | ivec2 a = ivec2(1, 2); ivec2 b = ivec2(1); // ⇔ ivec2(1, 1) ivec3 c = ivec3(a, 3); // ⇔ ivec3(1, 2, 3) |
| mat2、mat3、mat4 | 2x2、3x3、4x4 浮点数矩阵 (列向量) | mat2 a = mat2(1., 2., 3., 4.); mat2 b = mat2(1.); // ⇔ mat2(1., 1., 1., 1.) |
| sampler2D | 2D 纹理 | sampler2D sampler; |
| samplerCube | 盒纹理 | samplerCube sampler; |
说明:mat2、mat3、mat4 中的元素都是按照列向量的顺序排列的,即 mat2 m = mat2(m11, m21, m12, m22) 对应的公式如下。

2.1.2 向量分量访问
GLSL 中的向量(vec2、vec3、vec4)可以表示一个空间坐标 (x, y, z, w),也可以表示一个颜色 (r, g, b, a),还可以表示一个纹理坐标 (s, t ,p, q),所以 GLSL 提供了多样的分量访问方式。
vec4 v = vec4(1.0, 2.0, 3.0, 4.0);
float x1 = v.x; // 1.0
float x2 = v.r; // 1.0
float x3 = v.s; // 1.0
float x4 = v[0]; // 1.0vec3 xyz = v.xyz; // vec3(1.0, 2.0, 3.0)
vec3 stq = v.stq; // vec3(1.0, 2.0, 3.0)
vec3 rgb = v.rgb; // vec3(1.0, 2.0, 3.0)
vec3 abc = vec3(v[0], v[1], v[2]); // vec3(1.0, 2.0, 3.0)
2.1.3 数据类型转换
GLSL 可以使用构造函数进行显式类型转换。
// 0或0.0转换为false, 非0转换为true
bool a1 = bool(1.0); // true
bool a2 = bool(0); // false// true转换为1或1.0, false转换为0或0.0
int a3 = int(true); // 1
float a4 = float(false); // 0.0int a5 = int(2.0); // 2
float a6 = float(1); // 1.0
2.2 数组
GLSL 只支持一维数组。
// float 数组
float[3] a = float[] (1.0, 2.0, 3.0);
float b[3] = float[] (1.0, 2.0, 3.0);
float c[3] = float[3] (1.0, 2.0, 3.0);// vec 数组
vec2[2] d = vec2[] (vec2(0.0), vec2(1.0));
2.3 结构体
struct light {vec4 color;vec3 pos;
};
const light lgt = light(vec4(1.0), vec3(0.0));
说明:结构体中的字段不可用 const 修饰。
2.4 内置变量
| 范围 | 变量 | 说明 |
| 顶点着色器的 Output 变量 | highp vec4 gl_Position; | 顶点坐标信息 |
| mediump float gl_PointSize; | 顶点大小 (只在 GL_POINTS 图元模式下有效) | |
| 片元着色器的 Input 变量 | mediump vec4 gl_FragCoord; | 片元在屏幕空间的坐标,假设屏幕宽高分别为 width、height x: 片元的x坐标,值域 [0, width - 1] y: 片元的x坐标,值域 [0, height - 1] z: 片元的深度坐标,值域 [0, 1] w: 总是 1,通常用于透视除法 |
| bool gl_FrontFacing; | 标志当前图元是否是正面图元的一部分 | |
| 片元着色器的 Output 变量 | mediump vec4 gl_FragColor; | 设置当前片点的颜色 |
2.5 宏
与 C 语言一样,GLSL 中也可以通过 #define 定义宏,如下。
#define PI 3.14159265359
另外,GLSL 也提供了一些内置宏。
__LINE__ // 当前源码中的行号
__VERSION__ // 当前glsl版本号, 如: 300
GL_ES // 当前运行环境是否是 OPGL ES, 1 表示是
GL_FRAGMENT_PRECISION_HIGH // 当前系统的片元着色器是否支持高浮点精度, 1表示支持
3 运算符
3.1 基础运算符
| 优先级 (越小越高) | 运算符 | 说明 | 结合性 |
|---|---|---|---|
| 1 | () | 聚组: a * (b + c) | N/A |
| 2 | [] () . ++ -- | 数组下标 方法参数: fun(arg1, arg2) 属性访问 自增 (a++) / 自减 (a--) | L - R |
| 3 | ++ -- + - ! | 自增 (++a) / 自减 (--a) 正 (+a) 负 (-a) 号 取反 (!a) | R - L |
| 4 | * / % | 乘法 / 除法运算 整数求余运算 (浮点数求余用 mod 函数) | L - R |
| 5 | + - | 加法 / 减法运算 | L - R |
| 7 | < > <= >= | 关系运算符 | L - R |
| 8 | == != | 相等性运算符 | L - R |
| 12 | && | 逻辑与 | L - R |
| 13 | ^^ | 逻辑排他或 (用处基本等于 !=) | L - R |
| 14 | || | 逻辑或 | L - R |
| 15 | ? : | 三目运算符 | L - R |
| 16 | = += -= *= /= | 赋值和复合赋值 | L - R |
| 17 | , | 顺序分配运算 | L - R |
说明:GLSL 中没有隐式类型转换,因此任何表达式左右两侧的类型必须一致,以下表达式都是错误的。
// 以下代码运行时会报错
int a = 2.;
int b = 1. + 2;
float c = 2;
float d = 2. + 1;
bool e = 0;
vec2 f = vec2(1., 2.) * 2;
3.2 向量运算符
// 标量与向量运算
vec2 a = vec2(1., 2.) + 3.; // vec2(4., 5.)
vec2 b = 3. + vec2(1., 2.); // vec2(4., 5.)
vec2 c = vec2(1., 2.) * 3.; // vec2(3., 6.)
vec2 d = 3. * vec2(1., 2.); // vec2(3., 6.)// 向量与向量运算
vec2 e = vec2(1., 2.) + vec2(3., 4.); // vec2(4., 6.)
vec2 f = vec2(1., 2.) * vec2(3., 4.); // vec2(3., 8.)
3.3 矩阵运算符
// 标量与矩阵运算
mat2 a = mat2(1.) + 2.; // mat2(3.)
mat2 b = 2. + mat2(1.); // mat2(3.)
mat2 c = mat2(1.) * 2.; // mat2(2.)
mat2 d = 2. * mat2(1.); // mat2(2.)// 向量与矩阵运算
vec2 e = vec2(1., 2.) * mat2(1., 2., 3., 4.); // vec2(5., 11.)
vec2 f = mat2(1., 2., 3., 4.) * vec2(1., 2.); // vec2(7., 10.)// 矩阵与矩阵运算(矩阵对应元素运算)
mat2 g = mat2(1.) + mat2(2.); // mat2(3.)
mat2 h = matrixCompMult(mat2(1.), mat2(2.)); // mat2(2.)// 矩阵与矩阵运算(矩阵乘法)
mat2 i = mat2(1., 2., 3., 4.) * mat2(5., 6., 7., 8.); // mat2(23., 34., 31., 46.)
mat2 j = mat2(5., 6., 7., 8.) * mat2(1., 2., 3., 4.); // mat2(19., 22., 43., 50.)

4 函数
4.1 自定义函数
GLSL 允许在程序的最外部声明函数,函数不能嵌套、不能递归调用,且必须声明返回值类型(无返回值时声明为 void)在其他方面 GLSL 函数与 C 语言函数非常类似。
vec4 getPosition() { vec4 pos = vec4(0.,0.,0.,1.);return pos;
}void doubleSize(inout float size) {size = size * 2.0;
}
4.2 内置函数
1)数值运算
sign(x)、abs(x) // 符号、绝对值
min(a, b)、max(a, b) // 最值函数
ceil(x)、floor(x)、round(x) // 取整函数
fract(x) // 取小数部分
mod(x, y) // 取余数
sqrt(x)、pow(x)、inversesqrt(x) // 幂函数, inversesqrt(x)=1/sqrt(x)
exp(x)、exp2(x) // 指数函数(e^x、2^x)
log(x)、log2(x) // 对数函数
degrees(x)、radians(x) // 角度转换函数
sin(x)、cos(x)、tan(x)、asin(x)、acos(x)、atan(x) // 三角函数
sinh(x)、cosh(x)、tanh(x) // 双曲线函数
clamp(x, min, max) // 将x约束在min和max之间, 超过边界就取边界值
smoothstep(min, max, x) // 平滑比例, 公式: k=saturate((x-min)/(max-min)), y=k*k*(3-2*k)
mix(a, b, f) // 混合, 公式: y=(1-f)*a+f*b
step(a, b) // 如果a>b, 返回0; 如果a<=b, 返回1; 当a、b是向量时, 每个分量独立判断, 如: step(fixed2(1,1),fixed(0,2))=(0,1)
说明:以上函数输入的可以是:float、vec2、vec3、vec4,且可以逐分量操作;对于整数求余运算,只能用 %;对于浮点数求余运算,只能用 mod。
2)逻辑运算
bvec z = lessThan(x, y) // 逐分量比较x < y, 将结果写入z的对应位置
bvec z = lessThanEqual(x, y) // 逐分量比较x <= y, 将结果写入z的对应位置
bvec z = greaterThan(x, y) // 逐分量比较x > y, 将结果写入z的对应位置
bvec z = greaterThanEqual(x, y) // 逐分量比较x >= y, 将结果写入z的对应位置
bvec z = equal(x, y) // 逐分量比较x == y, 将结果写入z的对应位置
bvec z = notEqual(x, y) // 逐分量比较x != y, 将结果写入z的对应位置
bvec y = not(x) // bool矢量的逐分量取反
bool y = any(x) // 如果x的任意一个分量是true, 则结果为true
bool y = all(x) // 如果x的所有分量是true, 则结果为true
3)向量运算
distance(pos1, pos2) // 计算pos1与pos2之间的距离
length(vec) // 计算向量的模长
normalize(vec) // 计算向量的单位向量
dot(v1, v2) // 向量点乘
cross(v1, v2) // 向量叉乘
reflect(i, n) // 根据入射向量和法线向量, 计算反射向量(i和n不需要归一化)
refract(i, n, ratio); // 根据入射向量、法线向量、折射率比值, 计算折射向量(i和n需要归一化, ratio为入射介质折射率/折射介质折射率, 或sin(折射角)/sin(入射角))
4)矩阵运算
// 矩阵与矩阵运算(矩阵对应元素运算)
mat2 a = mat2(1.) + mat2(2.); // mat2(3.)
mat2 b = matrixCompMult(mat2(1.), mat2(2.)); // mat2(2.)// 矩阵与矩阵运算(矩阵乘法)
mat2 c = mat2(1., 2., 3., 4.) * mat2(5., 6., 7., 8.); // mat2(23., 34., 31., 46.)
mat2 d = mat2(5., 6., 7., 8.) * mat2(1., 2., 3., 4.); // mat2(19., 22., 43., 50.)

5)纹理查询函数
vec4 texture(sampler2D sampler, vec2 coord);
vec4 texture2D(sampler2D sampler, vec2 coord);
vec4 texture2DProj(sampler2D sampler, vec3 coord);
vec4 texture2DProj(sampler2D sampler, vec4 coord);
vec4 textureCube(samplerCube sampler, vec3 coord);
5 流程控制
GLSL 的流控制与 C 语言非常相似,主要有 if、for、while、continue、break,不同的是 GLSL 中多了 discard。使用 discard 会退出片元着色器,不会执行后面的操作,片元也不会写入帧缓冲区。
for (int i = 0; i < 10; i++) {sum += a[i];if (sum > 5.0)break;
}while (i < 10) {sum += a[i];if (i % 3 == 1)continue;i++;
}do {sum += a[i];if (sum > 5.0)discard;i++;
} while (i < 10)
6 限定符
6.1 精度限定符
片元着色器中,对于浮点数 GLSL 有 highp(高)、mediump(中)、lowp(低) 三种精度。
lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;
在片元着色器的第一行加上 precision highp float,表示设定了默认的精度,所有没有显式表明精度的变量都会按照默认精度处理。
precision highp float;
通过判断系统环境,来选择合适的精度。
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
6.2 变量限定符
| 限定符 | 说明 |
|---|---|
| none | 默认的变量限定符,可省略,可读写。 |
| const | 修饰的变量为只读类型,变量在定义时必须初始化。 |
| attribute | 只能在顶点着色器中使用,修饰全局只读变量,一般用于修饰顶点属性,如:顶点的位置、法线、纹理坐标、颜色等。 |
| uniform | 修饰全局只读变量,一般用于修饰程序传递给 shader 的变量,如:屏幕宽高比、光源位置等。 |
| varying | 修饰需要进行光栅化插值的变量,在片元着色器中是只读的,如:纹理坐标等。 |
// 顶点着色器
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord0;void main() {gl_Position = vec4(a_position, 1.0);v_texCoord0 = a_texCoord0;
}// 片元着色器
precision highp float;
uniform sampler2D u_texture;
varying vec2 v_texCoord0;void main() {gl_FragColor = texture(u_texture, v_texCoord0);
}
6.3 函数参数限定符
函数的参数默认以拷贝的形式传递,即值传递,我们可以为参数添加限定符实现引用传递,GLSL 中提供的参数限定符如下。
| 限定符 | 说明 |
|---|---|
| in | 默认的限定符,参数是值传递,在函数中可读写。 |
| out | 参数是引用传递,在函数中只能写(write-only)。 |
| inout | 参数是引用传递,在函数中可读写(read-write)。 |
除了函数的参数可以用 in、out、inout 修饰,全局变量也可以用这些限定符修饰,如下。
// 顶点着色器
in vec3 a_position;
in vec2 a_texCoord0;
out vec2 v_texCoord0;void main() {gl_Position = vec4(a_position, 1.0);v_texCoord0 = a_texCoord0;
}// 片元着色器
precision highp float;
uniform sampler2D u_texture;
in vec2 v_texCoord0;
out vec4 o_fragColor;void main() {o_fragColor = texture(u_texture, v_texCoord0);
}
7 include
GLSL 中没有提供 #include 功能,如以下代码会运行报错。
#include <shaders/utils/constant.glsl>
要想实现一个 glsl 文件依赖另一个 glsl 文件,可以使用以下工具加载 glsl 字符串。
ShaderUtils.java
import android.content.Context;import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** Shader工具类* @author little fat sheep*/
public class ShaderUtils {// 匹配: #include <xxx> 或 #include “xxx”, 并且不以"//"开头private static final String INCLUDE_REGEX = "(?m)^(?!//\\s*)#include\\s+(<([^>]+)>|\"([^\"]+)\")";/*** 通过asset加载shader* @param vertexShader 顶点着色器路径, 如: "shaders/origin_vert.glsl"* @param fragmentShader 片元着色器路径, 如: "shaders/origin_frag.glsl"*/public static String[] loadShader(Context context, String vertexShader, String fragmentShader) {String vertex = StringUtils.loadString(context, vertexShader);String fragment = StringUtils.loadString(context, fragmentShader);String vertex1 = replaceIncludeFiles(context, vertex);String fragment1 = replaceIncludeFiles(context, fragment);return new String[] { vertex1, fragment1 };}/*** 通过资源id加载shader* @param vertexShader 顶点着色器资源id, 如: R.raw.origin_vertex* @param fragmentShader 片元着色器资源id, 如: R.raw.origin_fragment*/public static String[] loadShader(Context context, int vertexShader, int fragmentShader) {String vertex = StringUtils.loadString(context, vertexShader);String fragment = StringUtils.loadString(context, fragmentShader);String vertex1 = replaceIncludeFiles(context, vertex);String fragment1 = replaceIncludeFiles(context, fragment);return new String[] { vertex1, fragment1 };}/*** 将shader字符串中#include的文件替换为文件内容*/public static String replaceIncludeFiles(Context context, String shaderContent) {Pattern pattern = Pattern.compile(INCLUDE_REGEX);HashSet<String> set = new HashSet<>(); // 用于去掉重复的includereturn replaceIncludeFiles(context, shaderContent, pattern, set);}/*** 将shader字符串中#include的文件替换为文件内容(include的文件中可能也有include, 需要递归调用)*/private static String replaceIncludeFiles(Context context, String shaderContent, Pattern pattern, HashSet<String> set) {Matcher matcher = pattern.matcher(shaderContent);StringBuffer sb = new StringBuffer(shaderContent.length());while (matcher.find()) {String angleBrackets = matcher.group(2); // 尖括号内的路径String quotationMarks = matcher.group(3); // 引号内的路径String file = angleBrackets != null ? angleBrackets : quotationMarks;if (set.contains(file)) {matcher.appendReplacement(sb, ""); // 删除重复的include} else {set.add(file);String includeShader = StringUtils.loadString(context, file); // 加载include文件中的字符串String quoteShader = Matcher.quoteReplacement(includeShader); // 替换字符串中的转义字符String wholeShader = replaceIncludeFiles(context, quoteShader, pattern, set); // 递归替换字串中的includematcher.appendReplacement(sb, wholeShader); // 将字符串添加到sb中}}matcher.appendTail(sb);return sb.toString();}
}
StringUtils.java
import android.content.Context;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;/*** 字符串工具类* @author little fat sheep*/
public class StringUtils {/*** 根据资源路径读取字符串* @param assetPath 资源路径, 如: "shaders/origin_vert.glsl"*/public static String loadString(Context context, String assetPath) {String str = "";try (InputStream inputStream = context.getAssets().open(assetPath)) {str = loadString(inputStream);} catch (IOException e) {e.printStackTrace();}return str;}/*** 根据资源id读取字符串* @param rawId 资源id, 如: R.raw.origin_vertex*/public static String loadString(Context context, int rawId) {String str = "";try (InputStream inputStream = context.getResources().openRawResource(rawId)) {str = loadString(inputStream);} catch (IOException e) {e.printStackTrace();}return str;}private static String loadString(InputStream inputStream) {StringBuilder sb = new StringBuilder();try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = br.readLine()) != null) {sb.append(line).append("\n");}} catch (IOException e) {e.printStackTrace();}return sb.toString();}
}相关文章:
【OpenGL ES】GLSL基础语法
1 前言 本文将介绍 GLSL 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符(in、out、inout)、函数参数限定符等内容,另外提供了一个 include 工具,方便多文件管理 glsl 代码&a…...
如何使用交叉编译器调试C语言程序在安卓设备中运行
一、前言 随着移动设备的普及与技术的飞速发展,越来越多的开发者面临着在Android设备上运行和调试C语言等程序的需求。然而,在软件开发的世界里,不同硬件架构对程序运行的要求千差万别,这无疑增加了开发的复杂性。特别是在移动计…...
Java全栈项目 - 智能考勤管理系统
项目介绍 智能考勤管理系统是一个基于 Java 全栈技术开发的现代化企业考勤解决方案。该系统采用前后端分离架构,实现了员工考勤、请假管理、统计分析等核心功能,旨在帮助企业提高人力资源管理效率。 技术栈 后端技术 Spring Boot 2.6.xSpring Securi…...
Linux Shell : Process Substitution
注:本文为 “Process Substitution” 相关文章合辑。 英文引文机翻,未校。 Process Substitution. 进程替换允许使用文件名引用进程的输入或输出。它采取以下形式 <(list)or >(list)进程 list 异步运行,其输入或输出显示为文件名。…...
JOGL 从入门到精通:开启 Java 3D 图形编程之旅
一、引言 Java 作为一门广泛应用的编程语言,在图形编程领域也有着强大的工具和库。JOGL(Java OpenGL)便是其中之一,它为 Java 开发者提供了访问 OpenGL(Open Graphics Library)功能的接口,使得…...
汽车网络安全基线安全研究报告
一、引言 随着汽车行业朝着智能网联方向飞速发展,汽车网络安全已成为保障用户安全和行业健康发展的关键要素。本报告将深入探讨汽车网络安全相关内容,以及国际、国内重要的汽车网络安全标准基线和相应防护措施等内容。 二、汽车网络安全的重要性 &…...
Eclipse 修改项目栏字体大小
1、菜单栏选择window->preference,然后选择General->Appearance->Colors and Fonts,在搜索栏输入"tree",点击"Edit"修改字体。 2、修改字体,选择"四号字体",点击"确定&qu…...
【PCIe 总线及设备入门学习专栏 5.1 -- PCIe 引脚 PRSNT 与热插拔】
文章目录 OverviewPRSNT 与热插拔PRSNT 硬件设计 Overview Spec 定义的热插拔是把一个PCIe卡(设备)从一个正在运行的背板或者系统中插入/或者移除。这个过程需要不影响系统的其他功能。插入的新的设备可以正确工作。 显然,这里面需要考虑的问…...
【YOLO】YOLOv5原理
概述 YOLOv5的主要架构 Backbone(主干网络):负责提取输入图像的多层次特征 Neck(颈部网络):进行特征融合和多尺度特征处理,通常包含FPN(特征金字塔网络)和PAN࿰…...
uniapp中wx.getFuzzyLocation报错如何解决
一、用wx.getLocation接口审核不通过 用uniapp开发小程序时难免需要获取当前地理位置。 代码如下: uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度: res.longitude);console.log(当前位置的纬度: r…...
opencv图像直方图
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 1、基本直方图计算 // 灰度图直方图 cv::Mat calculateGrayscaleHistogram(const cv::Mat& image) {cv::Mat histogram;int histSize 256; // 灰度级别float range[] {0, 256};const float* histRange …...
OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)
上一篇文章:OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理 目录 添加椒盐噪声 图像平滑常见处理方式 均值滤波 (blur) 方框滤波 (boxFilter) 高斯滤波 (GaussianBlur) 中值滤波 (medianBlur) 添加椒盐噪声 def add_peppersalt_noise(image, n…...
【嵌入式C语言】内存分布
内存分布 内存分布图内存的属性:只读空间只读空间的特点编程注意事项 栈空间栈的工作原理栈的特点栈溢出与堆的区别 堆空间堆的特点内存分配函数内存泄漏总结 内存分布图 内存的属性: 在C语言中,内存的属性主要取决于它是如何分配的以及它在…...
【brainpan靶场渗透】
文章目录 一、基础信息 二、信息收集 三、反弹shell 四、提权 一、基础信息 Kali IP:192.168.20.146 靶机 IP:192.168.20.155 二、信息收集 似乎开放了9999,10000端口,访问页面没有太多内容,扫描一下目录 dirs…...
Java实现观察者模式
一、前言 观察者模式,又称为发布订阅模式,是一种行为设置模式,允许对象之间建立一对多的依赖关系,这样当一个对象状态改变时,它的所有依赖者(观察者)都会收到通知并自动更新。 二、具体实现 …...
通过百度api处理交通数据
通过百度api处理交通数据 1、读取excel获取道路数据 //道路名称Data EqualsAndHashCode public class RoadName {ExcelProperty("Name")private String name; }/*** 获取excel中的道路名称*/private static List<String> getRoadName() {// 定义文件路径&…...
探索CSDN博客数据:使用Python爬虫技术
探索CSDN博客数据:使用Python爬虫技术 在数字化的浪潮中,数据的获取与分析变得日益关键。CSDN作为中国领先的IT社区和服务平台,汇聚了海量的技术博客与文章,成为一座蕴藏丰富的数据宝库。本文将引领您穿梭于Python的requests和py…...
b站ip属地评论和主页不一样怎么回事
在浏览B站时,细心的用户可能会发现一个有趣的现象:某些用户的评论IP属地与主页显示的IP属地并不一致。这种差异引发了用户的好奇和猜测,究竟是什么原因导致了这种情况的发生呢?本文将对此进行深入解析,帮助大家揭开这一…...
如何查看服务器内存占用情况?
如何查看服务器的内存占用情况?你知道内存使用情况对服务器性能的重要性吗?内存是服务器运行的核心资源之一,了解内存的占用情况可以帮助你优化系统性能。 要查看服务器的内存占用情况,首先需要确定你使用的是哪种操作系统。不同…...
流架构的读书笔记(2)
流架构的读书笔记(2) 一、建模工具之一沃德利地图 推测技术的发展,交流和辩论思想的最有力的方法是沃德利地图 沃德利地图的制作步骤 1确定范围和用户需求 2确定满足用户需求所需的组件 3在一条范围从全新到被人们接受的演进轴上评估这些组成 部分的演…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
【QT控件】显示类控件
目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏:QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...
