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

【Unity3D】法线贴图和凹凸映射

1 法线贴图原理

        表面着色器中介绍了使用表面着色器进行法线贴图,实现简单快捷。本文将介绍使用顶点和片元着色器实现法线贴图和凹凸映射,实现更灵活。 

        本文完整代码资源见→法线贴图和凹凸映射。

        1)光照原理

        Phong 光照模型Blinn Phong 光照模型是应用比较广泛的光照模型,两者区别在与镜面反射光的计算,Phong 光照模型根据反向量和观察向量计算镜面反射光,Blinn Phong 光照模型根据半向量和法向量计算镜面反射光。 

        光照计算如下: 

// 模型自身颜色
fixed4 albedo = tex2D(_MainTex, i.uv) * _ModelColor;
// 环境光
fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo;
// 漫反射光
fixed4 diffuse = _LightColor0 * albedo * max(0, dot(normal, lightDir)); 
// 镜面反射光(Phong光照模型)
// fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(reflectDir, viewDir)), _Gloss); 
// 镜面反射光(Blinn Phong光照模型)
fixed4 specular = _LightColor0 * _Specular * pow(max(0, dot(normal, halfDir)), _Gloss); 
// 合成颜色
fixed4 finalColor = fixed4(ambient + diffuse + specular, 1.0);

         其中,_ModelColor、_LightColor0 分别表示模型颜色、灯光颜色,UNITY_LIGHTMODEL_AMBIENT 表示环境光强度,normal、lightDir、viewDir、halfDir 分别表示法向量、灯光向量、观察向量、半向量(见上图,它们都已归一化)。

        2)法线纹理

        如下,左侧是纹理图,右侧是其对应的法线纹理图。

         法线向量归一化后,每个分量的值域是 [-1, 1],为了使用 RGB 颜色显示法线向量,需要将法线向量映射到区间 [0, 1],映射函数是:y = (x + 1) / 2;由法线纹理还原到法线向量,映射函数是:y = x * 2 - 1。由于法线向量都是归一化的,并且方向始终是由内侧指向外侧,因此可以省去 z 维的存储空间,通过 z = sqrt(x * x + y * y) 推出 z 值。

        由法线纹理获取法线向量的方法如下:

fixed4 packedNormal = tex2D(_NormalTex, i.uv);
fixed3 tangentNormal = UnpackNormal(packedNormal); // 切线空间法线向量
// 如果法线纹理未被标记为"Normal map", 可以使用以下方式求出法线纹理中片元的法线值
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

        说明:法线纹理需要设置 Texture Type 为 Normal map,如下,否则 UnpackNormal 函数会失效,需要使用注释中的代码还原法线向量。其中,_BumpScale 反映了物体表面的凹凸程度。

        3)切线空间

        从法线纹理图中采样并通过 UnpackNormal 还原后得到的法线向量,是切线空间中的向量,切线空间坐标系的定义如下。注意:切线空间坐标系是右手坐标系。

        切线空间坐标轴正方向单位向量对应的世界坐标系中的向量如下:

struct a2v {float4 vertex : POSITION; // 模型空间顶点坐标float3 normal : NORMAL; // 模型空间法线坐标(几何法线, 非纹理法线)float4 tangent : TANGENT; // 模型空间切线坐标(几何切线, 非纹理切线)
};v2f vert(a2v v) {v2f o;fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 法线(z轴)fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); // 切线(x轴)fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 副切线(y轴)return o;
}

        说明: worldTangent、worldBinormal、worldNormal 分别为切向空间坐标系 x、y、z 轴正方向单位向量(世界坐标系下的坐标)。

        4)切线空间与世界空间的变换

        漫反射和镜面反射光照计算使用了法线向量、灯光向量、观察向量,但是它们所处的坐标系不相同,为方便计算光照,需要统一坐标系,即将纹理法线向量由切线坐标系转换到世界坐标系,或者将灯光向量和观察向量由世界坐标系转换到切线坐标系。注意,之所以不在模型空间计算光照(即将法线向量、灯光向量、观察向量转换到模型空间,再进行光照计算),因为如果模型变换中存在非统一缩放,会导致世界空间下的法线与切线不垂直,详见空间和变换 中 2.5 节法线变换。

        假设切线空间 x、y、z 坐标轴正方向单位向量对应的世界坐标系中的向量分别为 r、u、f(即上文中的 worldTangent、worldBinormal、worldNormal),世界坐标系下 x、y、z 轴正方向对应的方向向量分别为 e1、e2、e3,因此存在以下关系:

         由于 r、u、f 两两正交,并且都是单位向量,因此它们组成的的矩阵是正交矩阵,即 A-1 = A',因此 e1、e2、e3 在一组基向量 r、u、f 下的表示如下:

        切线坐标系下的任意向量 v 对应的世界坐标系下的向量如下:

        因此,切线空间→世界空间的变换矩阵如下:

        世界坐标系下的任意向量 v 对应的切线坐标系下的向量如下:

        因此,世界空间→切线空间的变换矩阵如下:

         5)切线坐标系下法线向量对应的世界坐标系坐标

// 1. 计算[切线空间->世界空间]的变换矩阵
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 法线(z轴)
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); // 切线(x轴)
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 副切线(y轴)
float3x3 tangentToWorld = transpose(float3x3(worldTangent, worldBinormal, worldNormal)); // 切线空间->世界空间
// 2. 计算切线空间的法线向量
fixed4 packedNormal = tex2D(_NormalTex, i.uv.zw);
fixed3 tangentNormal = UnpackNormal(packedNormal); // 切线空间法线向量
// 3. 计算世界空间的法线向量
float3 normal = mul(tangentToWorld, tangentNormal);

        6)世界坐标系下灯光向量、观察向量对应的切线坐标系坐标

// 1. 计算[世界空间->切线空间]的变换矩阵
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 法线(z轴)
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); // 切线(x轴)
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 副切线(y轴)
float3x3 worldToTangent = float3x3(worldTangent, worldBinormal, worldNormal); // 世界空间->切线空间
// 2. 计算切线空间的灯光向量、观察向量
float3 lightDir = mul(worldToTangent, UnityWorldSpaceLightDir(v.vertex));
float3 viewDir = mul(worldToTangent, UnityWorldSpaceViewDir(v.vertex));

        7)凹凸映射

        为了使物体表面呈现动态凹凸变化,可以通过以下方式动态调整法线。

fixed4 packedNormal = tex2D(_NormalTex, i.uv); // 法线纹理采样
fixed3 tangentNormal = UnpackNormal(packedNormal); // 切线空间法线向量
tangentNormal.xy *= _BumpScale; // 法线凹凸映射
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); // 重新调整法线z值

2 切线空间中计算光照

        NormalMapInTangentSpace.shader

Shader "MyShader/NormalMapInTangentSpace" {Properties {_Color ("Color", Color) = (1, 1, 1, 1) // 贴图颜色_MainTex ("MainTex", 2D) = "white" {} // 主纹理_NormalTex ("NormalTex", 2D) = "bump" {} // 法线纹理_BumpScale ("Bump Scale", Range(-2, 2)) = -2 // 法线纹理凹凸比例_Specular ("Specular", Color) = (1, 1, 1, 1) // 镜面反射颜色_Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度}SubShader {Pass { Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color; // 贴图颜色sampler2D _MainTex; // 主纹理float4 _MainTex_ST; // 主纹理缩放和偏移sampler2D _NormalTex; // 法线纹理float4 _NormalTex_ST; // 法线纹理缩放和偏移float _BumpScale; // 法线纹理凹凸比例fixed4 _Specular; // 镜面反射光颜色float _Gloss; // 镜面反射光泽度struct a2v {float4 vertex : POSITION; // 模型空间顶点坐标float3 normal : NORMAL; // 模型空间法线坐标(仅用于构造世界空间到切线空间的变换矩阵)float4 tangent : TANGENT; // 模型空间切线坐标(仅用于构造世界空间到切线空间的变换矩阵)float2 texcoord : TEXCOORD0; // 纹理坐标};struct v2f {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float4 uv : TEXCOORD0; // xy存储主纹理坐标, zw存储法线纹理坐标float3 lightDir: TEXCOORD1; // 切线空间光线向量(顶点指向光源)float3 viewDir : TEXCOORD2; // 切线空间观察向量(顶点指向相机)};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间顶点坐标, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; // 主纹理缩放和偏移o.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw; // 法线纹理缩放和偏移// 世界空间法线、切线、副切线fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 世界空间坐标变换到切线空间坐标的变换矩阵float3x3 worldToTangent = float3x3(worldTangent, worldBinormal, worldNormal);// 将光线向量、观察向量由世界空间变换到切线空间o.lightDir = mul(worldToTangent, UnityWorldSpaceLightDir(v.vertex));o.viewDir = mul(worldToTangent, UnityWorldSpaceViewDir(v.vertex));// 如果模型变换中不包含非统一缩放, 可以将光线向量和观察向量直接由模型空间变换到切线空间, 而不必先转换到世界空间// float3 objectBinormal = cross(normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;// float3x3 objectToTangent = float3x3(v.tangent.xyz, objectBinormal, v.normal);// o.lightDir = mul(objectToTangent, ObjSpaceLightDir(v.vertex));// o.viewDir = mul(objectToTangent, ObjSpaceViewDir(v.vertex));return o;}fixed4 frag(v2f i) : SV_Target {				fixed3 tangentLightDir = normalize(i.lightDir); // 切线空间光线向量归一化fixed3 tangentViewDir = normalize(i.viewDir); // 切线空间观察向量归一化fixed4 packedNormal = tex2D(_NormalTex, i.uv.zw); // 法线纹理采样fixed3 tangentNormal = UnpackNormal(packedNormal); // 切线空间法线向量// 如果法线纹理未被标记为"Normal map", 可以使用以下方式求出法线纹理中片元的法线值//tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;//tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));tangentNormal.xy *= _BumpScale; // 法线凹凸映射tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); // 重新调整法线z值fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; // 物体自身颜色fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; // 环境光fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir)); // 漫反射光fixed3 halfDir = normalize(tangentLightDir + tangentViewDir); // 半向量fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss); // 镜面反射光return fixed4(ambient + diffuse + specular, 1.0);}ENDCG}}FallBack "Specular"
}

        通过调节 _BumpScale 值在区间 [-2, 2] 之间变化,使得胶囊体呈现动态凹凸变化,效果如下:

3 世界空间中计算光照

        NormalMapInWorldSpace.shader

Shader "MyShader/NormalMapInWorldSpace" {Properties {_Color ("Color", Color) = (1, 1, 1, 1) // 贴图颜色_MainTex ("MainTex", 2D) = "white" {} // 主纹理_NormalTex ("NormalTex", 2D) = "bump" {} // 法线纹理_BumpScale ("Bump Scale", Range(-2, 2)) = -2 // 法线纹理凹凸比例_Specular ("Specular", Color) = (1, 1, 1, 1) // 镜面反射颜色_Gloss ("Gloss", Range(8.0, 256)) = 20 // 镜面反射光泽度}SubShader {Pass { Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color; // 贴图颜色sampler2D _MainTex; // 主纹理float4 _MainTex_ST; // 主纹理缩放和偏移sampler2D _NormalTex; // 法线纹理float4 _NormalTex_ST; // 法线纹理缩放和偏移float _BumpScale; // 法线纹理凹凸比例fixed4 _Specular; // 镜面反射光颜色float _Gloss; // 镜面反射光泽度struct a2v {float4 vertex : POSITION; // 模型空间顶点坐标float3 normal : NORMAL; // 模型空间法线坐标(仅用于构造世界空间到切线空间的变换矩阵)float4 tangent : TANGENT; // 模型空间切线坐标(仅用于构造世界空间到切线空间的变换矩阵)float2 texcoord : TEXCOORD0; // 纹理坐标};struct v2f {float4 pos : SV_POSITION; // 裁剪空间顶点坐标float4 uv : TEXCOORD0; // xy存储主纹理坐标, zw存储法线纹理坐标float4 T2W0 : TEXCOORD1; // 切线空间到世界空间的变换矩阵的第一行float4 T2W1 : TEXCOORD2; // 切线空间到世界空间的变换矩阵的第二行float4 T2W2 : TEXCOORD3; // 切线空间到世界空间的变换矩阵的第三行};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间顶点坐标, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; // 主纹理缩放和偏移o.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw; // 法线纹理缩放和偏移float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 世界空间顶点坐标// 世界空间法线、切线、副切线fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; // 切线空间到世界空间的变换矩阵(为充分利用GPU插值寄存器, 将顶点坐标保存到w维中)o.T2W0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.T2W1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.T2W2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);return o;}fixed4 frag(v2f i) : SV_Target {				float3 worldPos = float3(i.T2W0.w, i.T2W1.w, i.T2W2.w); // 世界空间顶点坐标fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos)); // 世界空间光线向量fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // 世界空间观察向量fixed4 packedNormal = tex2D(_NormalTex, i.uv.zw); // 法线纹理采样fixed3 tangentNormal = UnpackNormal(packedNormal); // 切线空间法线向量// 如果法线纹理未被标记为"Normal map", 可以使用以下方式求出法线纹理中片元的法线值//tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;//tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));tangentNormal.xy *= _BumpScale; // 法线凹凸映射tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy))); // 重新调整法线z值// 世界空间法线坐标fixed3 worldNormal = normalize(half3(dot(i.T2W0.xyz, tangentNormal), dot(i.T2W1.xyz, tangentNormal), dot(i.T2W2.xyz, tangentNormal)));fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; // 物体自身颜色fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; // 环境光fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir)); // 漫反射光fixed3 halfDir = normalize(worldLightDir + worldViewDir); // 半向量fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); // 镜面反射光return fixed4(ambient + diffuse + specular, 1.0);}ENDCG}}FallBack "Specular"
}

        运行效果同第 2 节。 

相关文章:

【Unity3D】法线贴图和凹凸映射

1 法线贴图原理 表面着色器中介绍了使用表面着色器进行法线贴图,实现简单快捷。本文将介绍使用顶点和片元着色器实现法线贴图和凹凸映射,实现更灵活。 本文完整代码资源见→法线贴图和凹凸映射。 1)光照原理 Phong 光照模型和 Blinn Phong 光…...

代码误写到master分支(或其他分支),此时代码还未提交,如何转移到新建分支?

问题背景 有时候,我们拿到需求,没仔细看当前分支是什么,就开始撸代码了。完成了需求或者写到一半发现开发错分支了。 比如此时新需求代码都在master分支上,提交必然是不可能的,所有修改还是要在新建分支上进行&#x…...

java多线程之线程安全(重点,难点)

线程安全1. 线程不安全的原因:1.1 抢占式执行1.2 多个线程修改同一个变量1.3 修改操作不是原子的锁(synchronized)1.一个锁对应一个锁对象.2.多个锁对应一个锁对象.2.多个锁对应多个锁对象.4. 找出代码错误5. 锁的另一种用法1.4 内存可见性解决内存可见性引发的线程安全问题(vo…...

如何免费使用chatGPT4?无需注册!

Poe体验真滴爽首先提大家问一个大家最关心的问题如何在一年内赚到一百万?用个插件给他翻译一下体验地址效果是非常炸裂的,那么我就将网址分分享给大家https://poe.com/前提:要有魔法,能够科学shangwangChatGPT-3 随便问GPT-4 模型…...

Android Flutter在点击事件上添加动画效果

在Android App的开发项目中,我们需要在点击事件上实现一个动画效果来提高用户的体验度。比如闲鱼底部中间按钮的那种。该怎么实现呢? 一起来看看吧 实现效果如图: ​实现思路 根据UI的设计图,对每个模块设计好动画效果&#xff0…...

VSCode嵌入式开发环境搭建

Vscode开发环境搭建 看这个链接就可以了,后面下载调试有点问题看下3.3。 在VSCode上部署STM32F1的开发环境 1. MXCube配置工程生成Makefile文件 借助正确的编译工具链进行编译, 2. 编译工具链搭建 编译工具链使用GCC的ARM版本 arm-none-eabi-gcc &am…...

数据结构之栈的使用

栈是计算机科学中一个重要的数据结构。它是一种特殊的线性表,只允许在一端进行进出操作。这一端被称为栈顶,另外一端被称为栈底。栈的特点是后进先出,即最后进入栈的元素会先被弹出栈。栈的应用广泛,例如在编译器中,栈…...

QMessageBox手动添加按钮并绑定按钮的信号

视频展示效果(结合代码看效果更佳哦,代码在最下面): QMessageBox手动添加有重试效果的按钮效果图: 点击详细文本之后展开如下图: 图标可选: QMessageBox::Critical错误图标QMessageBox::NoIco…...

【C++进阶】位图和布隆过滤器

文章目录位图位图概念位图使用场景位图的结构构造setresettest完整代码布隆过滤器布隆过滤器概念布隆过滤器结构构造setresettest完整版代码位图 位图概念 所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用…...

Android开发-Android UI与布局

01 Android UI 1.1 UI 用户界面(User Interface,简称 UI,亦称使用者界面)是系统和用户之间进行交互和信息交换的媒介,它实现信息的内部形式与人类可以接受形式之间的转换。软件设计可分为两个部分:编码设计与UI设计。 1.2 Andr…...

在不丢失数据的情况下解锁锁定的 Android 手机的 4 种方法

尽管您可以使用指纹解锁手机,但大多数智能手机都需要 PIN 码、图案或字母数字代码作为主密码。如果您有一段时间没有输入手机密码,很容易忘记。正是由于这个原因,即使您打开了指纹解锁,大多数智能手机也会让您每天至少输入一次 PI…...

【11】核心易中期刊推荐——人工智能 | 图形图像处理

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…...

Spring 中的事件发布与监听

主要代码在org.springframework.context,org.springframework.context.event包中 事件发布与监听主要包含以下角色: 事件:ApplicationEvent事件监听器:ApplicationListener SmartApplicationListener GenericApplicationListene…...

c++ 一些常识 2

前言 今天主要讲类相关概念。 构造和析构函数是否可以抛出异常 在构造函数中抛出异常,控制权会转出构造函数之外,对象的析构函数不会被调用,造成内存泄漏。 如果析构函数中抛出异常,而且没有在当地捕捉,析构函数便执…...

用嘴写代码?继ChatGPT和NewBing之后,微软又开始整活了,Github Copilot X!

用嘴写代码?继ChatGPT和NewBing之后,微软又开始整活了,Github Copilot X! AI盛行的时代来临了,在这段时间,除了爆火的GPT3.5后,OpenAI发布了GPT4版本,同时微软也在Bing上开始加入了A…...

3分钟阐述这些年我的 接口自动化测试 职业生涯经验分享

接口自动化测试学习教程地址:https://www.bilibili.com/video/BV1914y1F7Bv/ 你好,我是凡哥。 很高兴能够分享我的接口自动化测试经验和心得体会。在我目前的职业生涯中,接口自动化测试是我经常进行的一项任务。通过不断地学习和实践&#xf…...

十大Python可视化工具,太强了

今天介绍Python当中十大可视化工具,每一个都独具特色,惊艳一方。 Matplotlib Matplotlib 是 Python 的一个绘图库,可以绘制出高质量的折线图、散点图、柱状图、条形图等等。它也是许多其他可视化库的基础。 import matplotlib.pyplot as p…...

五.ElasticSearch的基础+实战

五.ElasticSearch的基础+实战 1.Elasticsearch的是什么? 2.Elasticsearch的作用是什么? 3.Elasticsearch的核心思想? 4.Elasticsearch启动与简单使用 5.kibana结合elasticsearch实现简单的增删改查 6.elasticsearch安装中文分词器 7.elasticsearch结合springboot开发…...

Oracle的学习心得和知识总结(十三)|Oracle数据库Real Application Testing之Database Reply实操(一)

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《Oracle Database SQL Language Reference》 2、参考书籍:《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Guid…...

CAD外部参照如何重新定位?CAD外部参照重定位步骤

CAD外部参照如何重新定位?这个问题并不算是一个常见的问题,但偶尔也会遇到,今天小编就来给大家简单介绍一下浩辰CAD软件中CAD外部参照重定位的操作步骤,一起来看看吧! CAD外部参照重定位步骤: 浩辰CAD软件…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...

安卓基础(aar)

重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

node.js的初步学习

那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架

1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...

[特殊字符] Spring Boot底层原理深度解析与高级面试题精析

一、Spring Boot底层原理详解 Spring Boot的核心设计哲学是约定优于配置和自动装配&#xff0c;通过简化传统Spring应用的初始化和配置流程&#xff0c;显著提升开发效率。其底层原理可拆解为以下核心机制&#xff1a; 自动装配&#xff08;Auto-Configuration&#xff09; 核…...

开疆智能Ethernet/IP转Modbus网关连接鸣志步进电机驱动器配置案例

在工业自动化控制系统中&#xff0c;常常会遇到不同品牌和通信协议的设备需要协同工作的情况。本案例中&#xff0c;客户现场采用了 罗克韦尔PLC&#xff0c;但需要控制的变频器仅支持 ModbusRTU 协议。为了实现PLC 对变频器的有效控制与监控&#xff0c;引入了开疆智能Etherne…...