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

【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、falsebool a = true;
int带符号的整数int a = 0;
float带符号的浮点数

float a = 1.0;

float b = 2.;

vec2、vec3、vec42 维、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、bvec42 维、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、ivec42 维、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、mat42x2、3x3、4x4 浮点数矩阵 (列向量)

mat2 a = mat2(1., 2., 3., 4.);

mat2 b = mat2(1.); // ⇔ mat2(1., 1., 1., 1.)

sampler2D2D 纹理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 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符&#xff08;in、out、inout&#xff09;、函数参数限定符等内容&#xff0c;另外提供了一个 include 工具&#xff0c;方便多文件管理 glsl 代码&a…...

如何使用交叉编译器调试C语言程序在安卓设备中运行

一、前言 随着移动设备的普及与技术的飞速发展&#xff0c;越来越多的开发者面临着在Android设备上运行和调试C语言等程序的需求。然而&#xff0c;在软件开发的世界里&#xff0c;不同硬件架构对程序运行的要求千差万别&#xff0c;这无疑增加了开发的复杂性。特别是在移动计…...

Java全栈项目 - 智能考勤管理系统

项目介绍 智能考勤管理系统是一个基于 Java 全栈技术开发的现代化企业考勤解决方案。该系统采用前后端分离架构&#xff0c;实现了员工考勤、请假管理、统计分析等核心功能&#xff0c;旨在帮助企业提高人力资源管理效率。 技术栈 后端技术 Spring Boot 2.6.xSpring Securi…...

Linux Shell : Process Substitution

注&#xff1a;本文为 “Process Substitution” 相关文章合辑。 英文引文机翻&#xff0c;未校。 Process Substitution. 进程替换允许使用文件名引用进程的输入或输出。它采取以下形式 <(list)or >(list)进程 list 异步运行&#xff0c;其输入或输出显示为文件名。…...

JOGL 从入门到精通:开启 Java 3D 图形编程之旅

一、引言 Java 作为一门广泛应用的编程语言&#xff0c;在图形编程领域也有着强大的工具和库。JOGL&#xff08;Java OpenGL&#xff09;便是其中之一&#xff0c;它为 Java 开发者提供了访问 OpenGL&#xff08;Open Graphics Library&#xff09;功能的接口&#xff0c;使得…...

汽车网络安全基线安全研究报告

一、引言 随着汽车行业朝着智能网联方向飞速发展&#xff0c;汽车网络安全已成为保障用户安全和行业健康发展的关键要素。本报告将深入探讨汽车网络安全相关内容&#xff0c;以及国际、国内重要的汽车网络安全标准基线和相应防护措施等内容。 二、汽车网络安全的重要性 &…...

Eclipse 修改项目栏字体大小

1、菜单栏选择window->preference&#xff0c;然后选择General->Appearance->Colors and Fonts&#xff0c;在搜索栏输入"tree"&#xff0c;点击"Edit"修改字体。 2、修改字体&#xff0c;选择"四号字体"&#xff0c;点击"确定&qu…...

【PCIe 总线及设备入门学习专栏 5.1 -- PCIe 引脚 PRSNT 与热插拔】

文章目录 OverviewPRSNT 与热插拔PRSNT 硬件设计 Overview Spec 定义的热插拔是把一个PCIe卡&#xff08;设备&#xff09;从一个正在运行的背板或者系统中插入/或者移除。这个过程需要不影响系统的其他功能。插入的新的设备可以正确工作。 显然&#xff0c;这里面需要考虑的问…...

【YOLO】YOLOv5原理

概述 YOLOv5的主要架构 Backbone&#xff08;主干网络&#xff09;&#xff1a;负责提取输入图像的多层次特征 Neck&#xff08;颈部网络&#xff09;&#xff1a;进行特征融合和多尺度特征处理&#xff0c;通常包含FPN&#xff08;特征金字塔网络&#xff09;和PAN&#xff0…...

uniapp中wx.getFuzzyLocation报错如何解决

一、用wx.getLocation接口审核不通过 用uniapp开发小程序时难免需要获取当前地理位置。 代码如下&#xff1a; uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度&#xff1a; res.longitude);console.log(当前位置的纬度&#xff1a; r…...

opencv图像直方图

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 1、基本直方图计算 // 灰度图直方图 cv::Mat calculateGrayscaleHistogram(const cv::Mat& image) {cv::Mat histogram;int histSize 256; // 灰度级别float range[] {0, 256};const float* histRange …...

OpenCV计算机视觉 03 椒盐噪声的添加与常见的平滑处理方式(均值、方框、高斯、中值)

上一篇文章&#xff1a;OpenCV计算机视觉 02 图片修改 图像运算 边缘填充 阈值处理 目录 添加椒盐噪声 图像平滑常见处理方式 均值滤波 (blur) 方框滤波 (boxFilter) ​高斯滤波 (GaussianBlur) 中值滤波 (medianBlur) 添加椒盐噪声 def add_peppersalt_noise(image, n…...

【嵌入式C语言】内存分布

内存分布 内存分布图内存的属性&#xff1a;只读空间只读空间的特点编程注意事项 栈空间栈的工作原理栈的特点栈溢出与堆的区别 堆空间堆的特点内存分配函数内存泄漏总结 内存分布图 内存的属性&#xff1a; 在C语言中&#xff0c;内存的属性主要取决于它是如何分配的以及它在…...

【brainpan靶场渗透】

文章目录 一、基础信息 二、信息收集 三、反弹shell 四、提权 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机 IP&#xff1a;192.168.20.155 二、信息收集 似乎开放了9999&#xff0c;10000端口&#xff0c;访问页面没有太多内容&#xff0c;扫描一下目录 dirs…...

Java实现观察者模式

一、前言 观察者模式&#xff0c;又称为发布订阅模式&#xff0c;是一种行为设置模式&#xff0c;允许对象之间建立一对多的依赖关系&#xff0c;这样当一个对象状态改变时&#xff0c;它的所有依赖者&#xff08;观察者&#xff09;都会收到通知并自动更新。 二、具体实现 …...

通过百度api处理交通数据

通过百度api处理交通数据 1、读取excel获取道路数据 //道路名称Data EqualsAndHashCode public class RoadName {ExcelProperty("Name")private String name; }/*** 获取excel中的道路名称*/private static List<String> getRoadName() {// 定义文件路径&…...

探索CSDN博客数据:使用Python爬虫技术

探索CSDN博客数据&#xff1a;使用Python爬虫技术 在数字化的浪潮中&#xff0c;数据的获取与分析变得日益关键。CSDN作为中国领先的IT社区和服务平台&#xff0c;汇聚了海量的技术博客与文章&#xff0c;成为一座蕴藏丰富的数据宝库。本文将引领您穿梭于Python的requests和py…...

b站ip属地评论和主页不一样怎么回事

在浏览B站时&#xff0c;细心的用户可能会发现一个有趣的现象&#xff1a;某些用户的评论IP属地与主页显示的IP属地并不一致。这种差异引发了用户的好奇和猜测&#xff0c;究竟是什么原因导致了这种情况的发生呢&#xff1f;本文将对此进行深入解析&#xff0c;帮助大家揭开这一…...

如何查看服务器内存占用情况?

如何查看服务器的内存占用情况&#xff1f;你知道内存使用情况对服务器性能的重要性吗&#xff1f;内存是服务器运行的核心资源之一&#xff0c;了解内存的占用情况可以帮助你优化系统性能。 要查看服务器的内存占用情况&#xff0c;首先需要确定你使用的是哪种操作系统。不同…...

流架构的读书笔记(2)

流架构的读书笔记&#xff08;2&#xff09; 一、建模工具之一沃德利地图 推测技术的发展,交流和辩论思想的最有力的方法是沃德利地图 沃德利地图的制作步骤 1确定范围和用户需求 2确定满足用户需求所需的组件 3在一条范围从全新到被人们接受的演进轴上评估这些组成 部分的演…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能

vxe-table vue 表格复选框多选数据&#xff0c;实现快捷键 Shift 批量选择功能 查看官网&#xff1a;https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...

NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地

NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配&#xff0c;成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景&#xff0c;通过标准化 SQL 工作台与细粒度权限管控两大能力&#xff0c;助力企业安全高效…...

Async-profiler 内存采样机制解析:从原理到实现

引言 在 Java 性能调优的工具箱中&#xff0c;async-profiler 是一款备受青睐的低开销采样分析器。它不仅能分析 CPU 热点&#xff0c;还能精确追踪内存分配情况。本文将深入探讨 async-profiler 实现内存采样的多种机制&#xff0c;结合代码示例解析其工作原理。 为什么需要内…...