高级着色语言(HLSL)

High-Level Shading Language,简称为HLSL,可以使用HLSL编写顶点着色器和像素着色器程序,简要地说,顶点着色器像素着色器就是我们自行编写的一些规模较小的定制程序,这些定制程序可取代固定功能流水线中某一功能模块,并可在图形卡的GPU(Graphics Processing Unit,.图形处理单元)中执行。通过这种功能替换,我们便在实现各种图形效果时获得了巨大的灵活性。也就是说,我们不再受制于那些预定义的“固定”运算。

还有一点要注意,如果您的图形卡不支持项点着色器和像素着色器,在运行着色器例程时,请务必切换至REF设备。使用REF设备也就意味着该着色器例程的运行速度将会很慢,但显示的结果一定是正确的,这样我们仍可验证代码的正确性。顶点着色器可用软件顶点运算方式来模拟,即在创建设备时,将设备行为标记设定为D3DCREATE_SOFTWARE_VERTEXPROCESSING

HLSL着色器程序的编制

HLSL着色器程序可以一个长字符串的形式出现在应用程序的源文件中。但更方便也更模块化的做法是将着色器代码与应用程序代码分离。基于上述考虑,我们可在记事本中编写着色器代码并将其保存为常规的ASCll文本文件。接下来就可以使用函数D3DXCompileShaderFromFile对着色器文件进行编译了。

下面向您介绍一个用HLSL编写的顶点着色器程序,该程序是在记事本中编辑的,并保存在文本文件“Transform.txt”中。该顶点着色器程序对顶点实施了取景变换(view transformation)和投影变换(projection transformation),并将顶点的漫反射颜色分量设为蓝色。

全局变量

matrix ViewProjMatrix;
vector Blue={0.0f,0.0f,1.0f,1.0f};

matrix是HLSL中内置类型,表示维数4x4的矩阵,该变量存储了取景变换矩阵和投影变换矩阵的乘积,这样它就同时描述了俩种变换,vector表示一个4D向量,我们只是将其视为RGBA颜色向量,并将其初始化为蓝色。

输入和输出结构

struct VS_INPUT
{vector position : POSITION;
};struct VS_OUTPUT
{vector position : POSITION;vector diffuse : COLOR;
};

对于顶点着色器,输入和输出结构分别定义了该着色器的输入和输出的顶点数据,冒号语法用来指定变量的用途,:POSITION的意思指position用于描述输入顶点的位置信息,:COLOR意思是指diffuse用于描述输出顶点的颜色信息。

从底层观点看,语义语法建立了着色器中的变量与硬件寄存器之间的联系。即输入变量总是与输入寄存器相联系的,而输出变量总是与输出寄存器相联系的。例如,VS_INPUT结构的成员position将被连接到一个特定的项点输入位置寄存器。类似地,VS_OUTPUT结构中的diffuse成员将被连接到一个特定的顶点输出颜色寄存器。

入口函数

像C++程序一样,每个HLSL程序也应该有一个入口点。在上面的着色器例程中,入口函数为Main。但是,该名称并非强制性的。在遵循函数命名规则的前提下,着色器的入口函数的命名可自由选择。入口函数必须有一个可接收输入结构的参数,该参数将用于把输入顶点传给着色器,而且入口函数必须返回一个输出结构的实例,用来将经过处理的顶点自着色器输出。mul是HLSL的内置函数,它既可进行向量-矩阵乘法,也可以进行矩阵-矩阵乘法。

VS_OUTPUT Main(VS_INPUT input)
{//对该结构示例化,并将各成员设为0VS_OUTPUT output = (VS_OUTPUT)0;output.position = mul(input.position,ViewProjMatrix);output.diffuse = Blue;return output;
}

输入和输出结构的使用并非强制性,例如有时会遇到类似下面的用法,该用法在像素着色器程序中尤为常见,我们输入3个纹理坐标,着色器返回一个单个颜色作为输出,这是通过在函数签名后加上":COLOR":从语义层面表示的。

float4 Main(in float2 base : TEXCOORD0, in float2 spot : TEXCOORD1, in float2 text : TEXCOORD2) : COLOR
{
}

上面定义等价于

struct INPUT
{float2 base : TEXCOORD0;float2 spot : TEXCOORD1;float2 text : TEXCOORD2;
};sturct OUTPUT
{float4 c : COLOR;
};OUTPUT Main(INPUT input)
{...
}

常量表

每个着色器都用常量表来存储其变量。为了使应用程序能够访问着色器的常量表,D3DX库提供了接口ID3DXConstantTable。借助该接口,我们可在应用程序源代码中对着色器源代码中的变量进行设置

获取常量的句柄

为了从应用程序的源代码中对着色器程序中的某个变量进行设置,我们需要用某种方式来引用该变量。D3DXHANDLE类型的句柄恰合此意。当给定着色器中我们期望引用的那个变量的名称时,下面的函数将返回个引用了该变量的D3DXHANDLE类型的句柄(Handle)。

D3DXHANDLE ID3DXConstantTable::GetConstantByName(D3DXHANDLE hConstant,LPCSTR pName);D3DXHANDLE h0;
h0 = ConstTable->GetConstantByName(0,"ViewProjMatrix");

hConstant:一个D3DXHANDLE类型的句柄,标识了那个包含了我们希望获取其句柄的变量的父结构。例如,如果我们希望得到某个特定结构实例的一个单个数据成员的句柄,可为该参数传入该结构实例的句柄。如果想要获取指向顶级变量的句柄,应将该参数指定为0
pName:我们希望获取其句柄的那个着色器源代码中的变量的名称。

常量的设置

一旦应用程序获取了着色器代码中希望被引用的那个变量的D3DXHANDLE类型的句柄,我们就可在应用程序中使用方法ID3DXConstantTable:SetXXX对该变量进行设置,其中XXX表示被设置变量的类型名称,实际调用时只要用类型名将其替换即可。例如,如果我们希望设置的变量为一个vector类型的数组,该方法将对应Set VectorArray。该方法的通用签名如下

HRESULT ID3DXConstantTable::SetXXX(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant, 
XXX value
);

pDevice:设备指针
hConstant:想要设置那个变量句柄
value:指定了我们引用的那个着色器中的变量应被赋为何值,XXX应替换为该额类型名,对于某些类型(bool,it,float),我们传入的是该值的一个副本,而对另外一些类型(向量、矩阵、结构体),我们传入的是指向该值(value)的指针。如果要对数组进行设置,SetXXX方法还应增加一个参数,以接收该数组的维数

HRESULT ID3DXConstantTable::SetVectorArray(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant, 
CONST D3DXVECTOR4* pVector,
UINT Count
);SetBool 用于设置一个布尔值
bool b = true;
CostTable->SetBool(Device,handle,b);SetBoolArray 用于设置布尔数组
bool b[3] = {true,false,true};
CostTable->SetBoolArray(Device,handle,b,3);SetFloat 
float f = 3.14f;
CostTable->SetFloat(Device,handle,f);SetFloatArray 
float f[2] = {1.0f,2.0f};
CostTable->SetFloatArray(Device,handle,f,2);SetInt 
int x = 4;
CostTable->SetInt(Device,handle,x);SetIntArray
int x[4] = {1,2,3,4};
CostTable->SetIntArray(Device,handle,x,4);SetMatrix 用于设置一个4x4矩阵
D3DMATRIX M(...);
CostTable->SetMatrix(Device,handle,&M);SetMatrixArray 用于设置一个4x4的矩阵数组
D3DMATRIX M[4];
//初始化矩阵...
CostTable->SetMatrixArray(Device,handle,M,4);SetMatrixPointerArray用于设置一个4×4矩阵的指针数组
D3DXMATRIX*M(4];
//...Allocate and initialize matrix pointers
ConstTable->SetMatrixPointerArray(Device,handle,M,4);SetMatrixTranspose 用于设置一个4×4的转置矩阵
D3DXMATRIX M(...)
D3DXMatrixTranspose (&M,&M);
ConstTable->SetMatrixTranspose(Device,handle,&M);SetMatrixTransposeArray 用于设置一个4x4的转置矩阵数组
D3DXMATRIX M[4];
//..Initialize matrices and transpose them.
ConstTable->SetMatrixTransposeArray(Device,handle,M,4);SetMatrixTransposePointerArray 用于设置4×4的转置矩阵的指针数组。调用实例:
D3DXMATRIX*M[4];
//...Allocate,initialize matrix pointers and transpose them.
ConstTable->SetMatrixTransposePointerArray(Device,handle,M,4)SetVector 用于设置一个D3DXVECTOR4类型的变量
D3DXVECTOR4 v(1.0f,2.0f,3.0f,4.0f);
ConstTable->SetVector(Device,handle,&v);SetVectorArray 用于设置向量数组类型的变量
D3DXVECTOR4 v[3];
//...Initialize vectors
ConstTable->SetVectorArray(Device,handle,v,3);SetValue   用于设置大小任意的类型,例如结构体。在下面的调用实例中,我们用该函数对
一个D3DXMATRIX类型的变量进行了设置:
D3DXMATRIX M(...);
ConstTable->SetValue(Device,handle,(void*)&M,sizeof(M));

设置常量的默认值

下面的方法仪是将常量设为其默认值,即那些在变量声明时被赋予的初值。该方法在应用程序的设置过程中应调用一次。

HRESULT ID3DXConstantTable::SetDefaults(
LPDIRECT3DDEVICE9 pDevice
);

HLSL着色器程序的编译

HRESULT D3DXCompileShaderFromFile(
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
LPCSTR pFunctionName,
LPCSTR pTarget,
DWORD Flags,
LPD3DXBUFFER* ppShader,
LPD3DXBUFFER* ppErrorMsgs,
LPD3DXCONSTANTTABLE* ppConstantTable
);

pSrcFile:我们想要进行编译的、保存了着色器源代码的那个文本文件的名称。
pDefines:该参数可选,本书中我们将该参数设为NULL。
pInclude:指向ID3DXInterface接口的指针。应用程序应实现该接凵,以重载默认的include行为(include behavior)。通常,默认的include行为已足够满足要求,我们将该参数指定为NULL而将其忽略。
pFunctionName:一个指定了着色器入口函数名称的字符串。例如,如果着色器的入口函数为Main,该参数应赋为“Main”。
pTarget:指定了要将HLSL源代码编译成的着色器版本,该参数为一字符串。合法的顶点着色器版本有:vs1_1、vs2_0、vs_2_sw。合法的像素着色器版本有:ps_1_1、ps_1_2、ps_1_3、ps_1_4、ps_2_0、ps_2_sw。例如,如果我们想将顶点着色器编译为2.0版本,则需要将该参
数指定为vs_2_0。这种能够编译为不同着色器版本的能力是HLSL与汇编语言相比的一个主要优势。借助HLSL我们几乎可以即时地将一个着色器移植到不同版本中,而您所要做的仅仅是指定编译目标后重新执行编译。而如果编写着色器程序时使用了汇编语言,您只能手工进行移植。
Flags:可选的编译选项。若该参数设为0,表示不使用任何选项。合法的选项包括:

  • D3DXSHADER_DEBUG指示编译器写入调试信息
  • D3DXSHADER_SKIPVALIDATION指示编译器不要进行任何代码验证。仅当您正在使用一个已确定可用的着色器时,该参数才被使用。
  • D3DXSHADER_SKIPOPTIMIZATION指示编译器不要对代码做任何优化。实际上,仅在调试时该选项有用,因为调试时您不希望编译器对代码做任何改动。

ppShader:返回一个指向接口ID3DXBuffer的指针,该接口包含了编译后的着色器代码。然后编译后的着色器代码就可作为另个函数的参数来创建实际的顶点着色器或像素着色器。
ppErrorMsgs:返回个指向接口ID3DXBuffer的指针,该接口包含了一个存储了错误代码和消息的字符串。
ppConstantTable:返回一个指向接口ID3DXConstantTable的指针,该接口包含了该着色器的常量表数据。

ID3DXConstantTable* TransformConstantTable = 0;
ID3DXBuffer* shader = 0:
ID3DXBuffer* errorBuffer = 0;
hr = D3DXCompileShaderFromFile(
"transform.txt",
0,
0,
"Main",
"vs_2_0",
D3DXSHADER DEBUG,
&shader,
&errorBuffer,
&TransformConstantTable);if(errorBuffer)
{::MessageBox(0,(char*)errorBuffer->GetBufferPointer(),0,0);d3d::Release<ID3DXBuffer*>(errorBuffer);
}if (FAILED(hr))
{::MessageBox(0,"D3DXCreateEffectFromFile()-FAILED",0,0);return false;
}

HLSL支持的变量类型

标量类型
  • bool  布尔值  注意,HLSL提供了关键词true和false。
  • int  32位的有符号整数。
  • half  16位的浮点数。
  • float  32位的浮点数。
  • double  64位的浮点数。

有些平台可能不支持int、half和double。如果遇到这种情况,这些类型将用float来模拟。

向量类型
  • vector  一个4D向量,其中每个元素的类型都是float。
  • vector<T,n>  一个n维向量,其中每个元素的类型均为标量类型T。维数n必须介于1~4之间。下面是一个二维double型向量的例子。

我们可通过数组下标语法来访问向量的每个元素。此外,借助一些已定义的分量名x,y,z,w,r,g,b,a,我们还可像访问结构体中成员那样访问向量vec中的分量。这些名称r、g、b、a与x、y、z、w一样,分别精确地指向了同一分量。当用向量表示颜色时,我们更倾向于使用RGBA表示法,因为这将有助于强调该向量代表了某种颜色的事实。我们还可使用其他预定义类型来分别表示2D向量、3D向量和4D向量。

vec[i] = 2.0f;vec.x = vec.r = 1.0f;
vec.y = vec.g = 2.0f;
vec.z = vec.b = 3.0f;
vec.w = vec.a = 4.0f;float2 vec2;
float3 vec3;
float4 vec4;

替换调配:给定向量u=(u_{x},u_{y},u_{z},u_{w}),假定我们要将向量u的各分量复制到向量v中,使得v=(u_{x},u_{y},u_{z},u_{w})。最直接的做法是将u的各元素逐个复制到v对应的元素中。但是,HLSL提供了一种特殊的语法一“替换调配(swizzles)”来专门用来完成这类不关心顺序的复制操作。对向量进行复制操作时,我们不一定非要对每个分量进行复制(可有选择地复制)。例如,我们可仅复制x和y分量。

vector u = {1.0f,2.0f,3.0f,4.0f};
vector v = {0.0f,0.0f,5.0f,6.0f}:
v = y.xyyw; //v={1.0f,2.0f,2.0f,4.0f}vector u = {1.0f,2.0f,3.0f,4.0f};
vector v = {0.0f,0.0f,5.0f,6.0f};
v.xy=u;  //v=1.0f,2.0f,5.0f,6.0f}
矩阵类型
  • matrix表示一个4×4矩阵,该矩阵中每个元素的类型均为float。
  • matrix<T,m,n>表示一个m×n矩阵,其中的每个元素都为标量类型T。该矩阵的维数m和n必须介于1~4之间。例如,要表示一个2×2的整型矩阵,可写作:matrix<int,2,2> m2x2;
  • 我们还可用如下语法来定义一个m×n矩阵,其中m和n必须介于1~4之间。floatmxn matmxn

矩阵类型可以不是float类型,我们也可使用其他类型

float2x2 mat2x2;
float3x3 mat3x3;
float4x4 mat4x4;
f1oat2x4 mat2×4;int2x2 i2×2;

我们可用数组的双下标语法来访问矩阵的各项(enty,元素)。要对矩阵M中第i行、第j列的项进行设置,可以这样以下这样,此外,我们还可以像访问结构体中的成员那样访问矩阵M中的项。HLSL己定义了下列项名称。

M[i][j] = value;//下标从1开始
M._11 = M._12 =M._13 = M._14 = 0.0f:
M._21 = M._22 =M._23 = M._24 = 0.0f:
M._31 = M._32 =M._33 = M._34 = 0.0f:
M._41 = M._42 =M._43 = M._44 = 0.0f://下标从0开始
M._m00 = M._m01 = M._m02 = M._m03 = 0.0f;
M._m10 = M._m11 = M._m12 = M._m13 = 0.0f;
M._m20 = M._m21 = M._m22 = M._m23 = 0.0f;
M._m30 = M._m31 = M._m32 = M._m33 = 0.0f;

有时,我们想要引用矩阵中某一特定行。我们可通过数组单下标语法来实现。例如,要想引用矩阵M的第i行,可以这样做:

vector ithRow = M[i];

初始化方式可以直接初始化也可以通过构造函数进行初始化

vector u={0.6f,0.3f,1.0f,1.0f};
vector v={1.0f,5.0f,0.2f,1.0f}:vector u vector(0.6f,0.3f,1.0f,1.0f);
vector v vector(1.0f,5.0f,0.2f,1.0f);float2x2 f2x2 = float2x2(1.0f,2.0f,3.0f,4.0f);
int2x2 m={1,2,3,4};
int n = int(5);
int a = {5};
float3 x = float3(0,0,0);
数组
f1oat M[4][4];
half p[4];
vector v[12];
结构体

HLSL中的结构体定义方法与C++完全相同。但是,HLSL中的结构体不允许有成员函数。下面是一个HLSL中结构体的例子。

struct MyStruct
{Matrix T;vector n;float f;int   x;bool  b;
};
MyStruct s;
s.f = 5.0f;

变量的前缀

static:如果全局变量在声明时使用了关键字static,就意味着这个变量在该着色器程序外不可见。这个全局变量是该着色器程序的局部变量。与C++中的局部变量具有完全相同的行为。即,包含该局部变量的函数在首次被调用时,该变量仅初始化一次,而且在该函数的所有调用过程中,该变量都对自身当前值进行维护(每次该函数调用结束时,该变量仍然保留了调用过程中的状态,直至主
程序的生命期结束)。如果在函数中没有对该变量进行初始化,该变量将自动初始化为0。

 uniform:如果变量声明时使用了关键字uniform,表明该变量将在该着色器之外进行初始化。例如,在C++应用程序中对该变量进行初始化,然后再作为输入传给该着色器。

extern:如果变量声明时使用了关键字extern,表明该变量可在该着色器程序之外进行访问,例如可由C++程序对其进行访问。只有全局变量可以使用关键字extern。非静态的全局变量在默认状态下都是extern类型的。

shared:如果变量声明时使用了关键字shared,则提示效果框架该变量可在多个效果之间共享。只有全局变量方可使用该关键字进行声明

volatile:如果变量声明时使用了关键字volatile,则提示效果框架该变量将经常被修改。只有全局变量方可使用该关键字进行声明。

const:HLSL中的const关键字与C++中的含义完全相同。

类型转换

HLSL支持一种灵活的类型转换机制。HLSL中的类型转换语法与C语言的完全相同。例如,如果要
将float类型转换为matrix类型,可以这样做

float f 5.0f;
matrix m = (matrix)f;

运算符

运算符的行为与C++中的非常相似,但仍有一些差别。首先,取模运算符%适用于整型和浮点型数据。使用取模运算符时,左操作数和右操作数必须同号(即左右操作数必须同为正或同为负)。其次需要注意,许多HLSL运算都是在变量的分量级(component basis)上进行的。这是由于向量和矩阵都是HLSL的内置类型,而这些类型都由若干分量组成。由于有了能够在分量级上进行运算的运算符,如向量/知阵加法、向量/矩阵减法、向量/矩阵的相等测试等运算就可使用与标量类型相同的运算符来进行了。下面给出了几个例子。

vector u = {1.0f,0.0f,-3.0f,1.0f}:
vector v = {-4.0f,2.0f,1.0f,0.0f};
vector sum = u + v; //sum (-3.0f,2.0f,-2.0f,1.0f)向量自增也就是每个分量进行自增:
sum++; //after increment:sum (-2.0f,3.0f,-1.0f,2.0f)向量的逐分量(component-.wise)相乘:
vector u={1.0f,0.0f,-3.0f,1.0f}:
vector v={-4.0f,2.0f,1.0f,0.0f}:
vector sum =  u * v;  //product (-4.0f,0.0f,-3.0f,0.0f)比较运算符也是在分量上操作的,并将返回一个bool型的向量或矩阵(其每个分量的类型均为bool
型)。返回的“bool”向量包含了两个分量的比较结果。例如:
vector u = {1.0f,0.0f,-3.0f,1.0f}
vector v = {-4.0f,0.0f,1.0f,1.0f};
vector b = (u==v);  //b = (false,true,false,true)

双目运算中的变量类型提升:

  • 对于双目运算,如果左操作数与右操作数的维数不同,则维数较小的操作数得到提升(类型转换)使得其维数与原先维数较大的操作数相同。例如,如果x的类型为float,y的类型为float3,则在表达式(x+y)中,x将被提升为float3类型,所以该表达式的值也是float3类型的。这种提升是按照已定义的类型转换规则进行的。在这个例子中,我们是将标量转换为向量:所以,x被提升为float3类型后,x=(x,x,x),标量到向量的转换就是这样定义。注意,如果某种类型转换没有定义,则相应的类型提升也就找不到依据而无法进行。例如,由于float2类型到float3类型的转换没有定义,所以我们无法将float2类型提升为float3类型。
  • 对于双目运算,如果左右操作数类型不同,则具有较低类型的操作数得到提升(类型转换),使得其类型精度与原先具有较高类型精度的操作数相同。例如,如果x是int类型变量,而y是half类型变量,则在表达式(x+y)中,变量x被提升为half类型,所以该表达式的结果也将是-个half类型的值。

用户自定义函数

HLSL中的函数具有以下性质

  • 函数使用与C++类似的语法
  • 参数总是按值传递的
  • 不支持递归
  • 函数总是内联的
关键字in、out、inout
bool foo(in const bool b,out int r1,inout float r2)
{if(b)r1=5;elser1=1;r2=r2*r2*r2;return true;
}

in:指定在该函数执行之前,必须对该形参传入实参的副本。函数声明中形参可以不显式指定in,因为默认状态下每个形参都是in类型的。

out:指定当函数返回时,形参的值将复制给实参。这样我们就可将形参作为返回值。关键字out是很必要的,因为HLSL不支持引用或指针。注意,如果一个形参是out类型的,则在函数开始执行时,实参值将不被复制到形参中。即out类型的参数仅用于输出数据,不可用作输入。

inout:表示一个参数同时兼有i和out类型参数的特点。即如果您希望一个参数即可作为输入又可作为输出,可指定该关键字。

俩者等价
float square(in float x)
{return x * x;
}不显式指定in:
float square(float x)
{return x * x;
}

内置函数

大多数函数都经过了重载,以适用于所有内置类型。例如,取绝对值对任意标量类型都是有意义的,所以函数abs就对各种内置标量类型做了重载。另外·一个例子是,叉积仅于3D向量有定义,所以cross函数仅对所有类型的3D向量(例如int类型的、float类型的、double类型的3D向量)做了重载。而线性插值对标量、2D向量、3D向量和4D向量均有意义,所以函数lerp对所有的内置类型都进行了重载。

如果您为一个“标量”函数(即一般只对标量进行运算的函数,如cos(x)传入一个非标量类
型的参数,该函数将针对该传入参数的每个分量进行计算。例如,如果有如下代码:

float3 v = float3(0.0f,0.0f,0.0f);
v cos(v);
则该函数将会分别对v的每个分量进行计算:v=(cos(x),cos(y),cos(z))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/176634.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“技能兴鲁”职业技能大赛-网络安全赛项-学生组初赛 WP

Crypto BabyRSA 共模攻击 题目附件&#xff1a; from gmpy2 import * from Crypto.Util.number import *flag flag{I\m not gonna tell you the FLAG} # 这个肯定不是FLAG了&#xff0c;不要交这个咯p getPrime(2048) q getPrime(2048) m1 bytes_to_long(bytes(flag.e…

数据结构:树的基本概念(二叉树,定义性质,存储结构)

目录 1.树1.基本概念1.空树2.非空树 2.基本术语1.结点之间的关系描述2.结点、树的属性描述3.有序树、无序树4.森林 3.树的常考性质 2.二叉树1.基本概念2.特殊二叉树1.满二叉树2.完全二叉树3.二叉排序树4.平衡二叉树 3.常考性质4.二叉树的存储结构1.顺序存储2.链式存储 1.树 1.…

应用在唱放一体音响麦克风中的投影K歌模组

从十年前智能手机和移动互联网开始兴起&#xff0c;手机K歌&#xff08;全民K歌、唱吧等&#xff09;娱乐潮流成为年轻人的新宠&#xff0c;衍生出全新的手机K歌麦克风。到如今家庭智能电视、智能娱乐影院设备普及&#xff0c;家庭、聚会、户外K歌新娱乐潮流兴起&#xff0c;拓…

智能电网线路阻抗模拟的工作原理

智能电网线路阻抗模拟是一种通过模拟电网线路的阻抗特性来实现电网故障检测和定位的技术。智能电网系统通过安装在电网线路上的传感器&#xff0c;实时采集线路上的电流、电压等参数&#xff0c;并将这些数据传输到监控中心。监控中心接收到传感器采集的数据后&#xff0c;对数…

基于JAVA SpringBoot和HTML美食网站博客程序设计

摘要 美食网站是一个提供各种美食信息和食谱的网站&#xff0c;旨在帮助用户发现、学习和分享美食。旨在探讨美食网站在现代社会中的重要性和影响。随着互联网的普及&#xff0c;越来越多的人开始使用美食网站来获取各种美食信息和食谱。这些网站不仅提供了方便快捷的搜索功能&…

小型洗衣机哪个牌子质量好?性价比高的迷你洗衣机推荐

这两年内衣洗衣机可以称得上较火的小电器&#xff0c;小小的身躯却有大大的能力&#xff0c;一键可以同时启动洗、漂、脱三种全自动为一体化功能&#xff0c;在多功能和性能的提升上&#xff0c;还可以解放我们双手的同时将衣物给清洗干净&#xff0c;让越来越多小伙伴选择一款…

计算机网络期末复习-Part5

1、CRC计算 看例题&#xff1a;待发送序列为101110&#xff0c;生成多项式为X31&#xff0c;计算CRC校验码 先在待发送序列末尾添加与生成多项式次数相同的零&#xff0c;在上述例子中&#xff0c;生成多项式是X^3 1&#xff0c;所以需要添加3个零&#xff0c;待发送序列变成…

Redis最新2023年面试题高级面试题及附答案解析(1)【Redis最新2023年面试题高级面试题及附答案解析-第三十八刊】

文章目录 Redis最新2023年面试题高级面试题及附答案解析(1)01、为什么 Redis 需要把所有数据放到内存中&#xff1f;02、查看 Redis 使用情况及状态信息用什么命令&#xff1f;03、MySQL里有2000w数据&#xff0c;Redis 中只存20w的数据&#xff0c;如何保证 Redis 中的数据都是…

如何选择正确的SSL证书?

SSL证书已经成为网站安全管理的重要部分&#xff0c;但是市场上SSL证书种类繁多&#xff0c;很多新手在初次购买时都会感到困惑。下面我们就一起来看看如何快速地选择正确的SSL证书。 第一步&#xff1a;明确SSL证书的主要分类 SSL证书主要有三种类型&#xff1a;单域名证书、…

openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程

文章目录 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程122.1 创建并执行涉及加密列的函数/存储过程 openGauss学习笔记-122 openGauss 数据库管理-设置密态等值查询-密态支持函数/存储过程 密态支持函数/存储过程当前版本只支持sql和P…

系列八、Mybatis一对多查询,只查询出了一条记录

一、Mybatis一对多查询&#xff0c;只查询出了一条记录 1.1、问题说明 典型的权限管理框架的数据库表中&#xff0c;一般会存在这样3种角色的表&#xff0c;即用户表、角色表、用户角色关联表&#xff0c;表设计好之后&#xff0c;往这三张表中初始化了一些测试数据&#xff0…