#ggfgfgf 2024.7.2 下载着色器的源码和编译工具 [战团]shader工具(fx源码编译工具) https://bbs.mountblade.com.cn/thread-2102979-1-1.html (出处: 骑马与砍杀中文站论坛) (一)骑砍战团着色器概述 战团着色器使用HLSL(High-Level Shader Language)代码,并运行在DirectX,遵守其规范。 结构分三个:顶点着色器(VertexShader)和像素着色器(PixelShader) 而技术(technique)则定义着色器效果名以在brf中调用。 顶点着色器(VertexShader) 顶点着色器作用于每个顶点,执行变换、光照计算、纹理坐标传递等操作,并将处理后的数据传递给渲染管线的下一阶段,即传给像素着色器。 struct VS_OUTPUT_FONT { float4 Pos : POSITION; float Fog : FOG; float4 Color : COLOR0; float2 Tex0 : TEXCOORD0; }; 这里举字体着色器为例,VS_OUTPUT_FONT定义了顶点着色器输出的结构 像素着色器(PixelShader) 像素着色器是在顶点着色器处理完顶点数据后,对渲染过程中的像素进行颜色、光照、纹理等操作的着色器。 struct PS_OUTPUT { float4 RGBColor : COLOR; }; 这是像素着色器的输出的结构定义,可以看到是一个四元组,颜色对应所渲染的单个像素颜色值,分别是R G B A, 即R(红)G(绿)B(蓝)A(Alpha透明度)。 也就是说战团不会有更多的引擎处理,最后的显示在屏幕上的颜色直接由fx源码里的着色器完成(实际上,还要经过postfx.fx的后期处理), 也就是没有内置的额外着色器。这点和现代游戏引擎unity等很不同。 以下是字体着色器例子 顶点着色器(VertexShader) VS_OUTPUT_FONT vs_font(float4 vPosition : POSITION, float4 vColor : COLOR, float2 tc : TEXCOORD0) { VS_OUTPUT_FONT Out; Out.Pos = mul(matWorldViewProj, vPosition); float3 P = mul(matWorldView, vPosition).xyz; //position in view space Out.Tex0 = tc; Out.Color = vColor * vMaterialColor; //apply fog float d = length(P); float4 vWorldPos = (float4)mul(matWorld,vPosition); Out.Fog = get_fog_amount_new(d, vWorldPos.z); return Out; } 可以看到vs_font接受了来自引擎的数据传入变量vPosition及vColor等, VS_OUTPUT_FONT Out;定义输出Out由四个元素Pos,Fog,Color,Tex0组成。 像素着色器(PixelShader) PS_OUTPUT ps_font_uniform_color(VS_OUTPUT_FONT In) { PS_OUTPUT Output; Output.RGBColor = In.Color; Output.RGBColor.a *= tex2D(FontTextureSampler, In.Tex0).a; return Output; } 可以看到ps_font_uniform_color接受了来自vs_font的数据结构体,并命名为IN,IN由四个元素Pos,Fog,Color,Tex0组成。 但是并非因为 ps_font_uniform_color(VS_OUTPUT_FONT In) 中写了VS_OUTPUT_FONT就传入了vs_font的数据, 而是因为下面技术定义了着色器font_uniform_color VertexShader vs_font_compiled_2_0 = compile vs_2_0 vs_font();//_compiled_2_0为ps的版本指定,定义vs_font_compiled_2_0为compile vs_2_0 vs_font() technique font_uniform_color { pass P0 { VertexShader = vs_font_compiled_2_0;//等同于VertexShader compile vs_2_0 vs_font(); PixelShader = compile ps_2_0 ps_font_uniform_color(); } } 同时调用了vs_font(后面的_compiled_2_0为ps的版本指定,由前面定义)顶点着色器和ps_font_uniform_color像素着色器 ps_font_uniform_color必须接受vs_font的数据传入,所以为了规范,将传入数据定义为VS_OUTPUT_FONT类型结构体变量IN 像素着色器ps_font_uniform_color按PS_OUTPUT结构体定义了Output,计算赋值后传回Output供引擎渲染。 可能你注意到VS_OUTPUT_FONT vs_font(float4 vPosition : POSITION, float4 vColor : COLOR, float2 tc : TEXCOORD0) vs_font后面括号内的定义变量,比如vColor : COLOR,其中vColor是变量的名称 ": COLOR" 是语义声明,指定了 vColor 数据的用途。 在DirectX中,COLOR 语义用于传递顶点颜色。它告诉渲染管线这个变量应该被用作颜色数据。 DirectX语义指示告诉渲染管线这个变量应该被用作什么。 DirectX语义指示表部分如下: 顶点着色器输入语义 : POSITION 顶点在对象空间中的位置 NORMAL 顶点的法线向量 TANGENT 顶点的切线向量 BINORMAL 顶点的副法线向量 TEXCOORD 顶点的纹理坐标 TEXCOORD0 顶点第一个纹理通道坐标 COLOR 顶点的颜色信息 COLOR0 顶点第一个颜色通道 顶点着色器输出语义 : SV_POSITION 经过投影变换的顶点位置,用于光栅化阶段 SV_ClipDistance 用于定义剪辑平面的距离值 像素着色器输入语义 : SV_IsFrontFace 指示像素是否为正面。 SV_Coverage 遮罩数据,用于抗锯齿多样本反走样(MSAA) SV_SampleIndex 当前样本的索引,同样用于MSAA 像素着色器输出语义 : SV_Target 像素着色器的输出颜色。 SV_Depth 像素的深度值。 系统值语义 所有系统值都以 SV_ 前缀开头 : SV_VertexID 顶点索引 SV_InstanceID 实例索引 SV_PrimitiveID 基元(如三角形)索引 vColor : COLOR等中vColor等变量由引擎传入,由引擎计算完成后赋值给变量,不同类型的值依赖语义指示赋值给不同变量。 顶点着色器和像素着色器后括号内也可以加入其他一些无语义指示的变量,供技术指定时赋值以灵活写出多个技术,而不必为每个技术编写 一个顶点着色器或像素着色器。比如为是否浑浊或有无景深效果编写不同技术,可以用一个变量判断是否启用代码块,如: PS_OUTPUT ps_parallax_water( /*省略*/, uniform const bool use_high, uniform const bool apply_depth, uniform const bool mud_factor ) /*省略*/ if(!apply_depth) { normal = float3(0,0,1); } /*省略*/ if(mud_factor) { Output.RGBColor *= 0.125f; } /*省略*/ if(use_refraction && use_high) { /*省略*/ } technique parallax_water { pass P0 { /*省略*/ PixelShader = compile PS_2_X ps_parallax_water(false, true, false);//(PCF_NONE); } } 整个着色器具体流程为,引擎由模型的着色器名对应同名技术,调用该技术内的顶点着色器和像素着色器,顶点着色器获取引擎传入值, 并将其中一部分通过语义指示传如一些变量,计算后传入像素着色器,像素着色器计算后返回值到引擎渲染。 骑砍战团着色器开头也定义了一些来自模型的数据如 ... ... ... ... sampler ReflectionTextureSampler : register(fx_ReflectionTextureSampler_RegisterS ) = sampler_state { Texture = env_texture; }; sampler EnvTextureSampler : register(fx_EnvTextureSampler_RegisterS ) = sampler_state { Texture = env_texture; }; sampler Diffuse2Sampler : register(fx_Diffuse2Sampler_RegisterS ) = sampler_state { Texture = diffuse_texture_2; }; sampler NormalTextureSampler : register(fx_NormalTextureSampler_RegisterS ) = sampler_state { Texture = normal_texture; }; sampler SpecularTextureSampler : register(fx_SpecularTextureSampler_RegisterS ) = sampler_state { Texture = specular_texture; }; ... ... ... ... 其中四个重要的是 MeshTextureSampler 模型的第一贴图(一通道) Diffuse2Sampler 模型的第二贴图(二通道) EnvTextureSampler 模型的环境贴图(三通道) SpecularTextureSampler 模型的反射贴图(四通道) 其他可自行翻译意思 ########################################################################################################################################################################## HLSL和C语言很像,结构类似。每句代码都有结束符号," ; "英文符号 HLSL的一些常用函数: 基本数学函数: abs(x) 绝对值 sin(x) 正弦函数 cos(x) 余弦函数 tan(x) 正切函数 asin(x) 反正弦函数 acos(x) 反余弦函数 atan(x) 反正切函数 sqrt(x) 平方根 exp(x) 自然指数(e^x) log(x) 自然对数(lnx) pow(x, y) x的y次幂(x^y) 向量和矩阵函数: dot(a, b) 向量a和b的点积 cross(a, b) 向量a和b的叉积 normalize(a) 向量a的归一化 reflect(a, n) 反射向量a关于法线n refract(a, n, eta) 折射向量a通过法线n,考虑折射率eta matrixCompMult(x, y) 矩阵的逐元素乘法 几何着色器函数: EmitVertex() 发射一个顶点 EndPrimitive() 结束当前的基元 纹理采样函数: tex2D(s, coord) 2D纹理采样 tex2Dlod(s, coord) 2D纹理级联采样,包括LOD(Level of Detail) texCUBE(s, coord) 立方体贴图采样 顶点和像素着色器输入/输出函数: Input 顶点着色器输入结构 Output 顶点着色器输出结构 SV_Position 系统值语义,表示裁剪空间中的顶点位置 动态分支函数: if, else 条件语句(如果,否则)分支函数尽量不要用,着色器运行在GPU上, GPU擅长计算而非推理,这些涉及布尔判断的操作性能不佳 for 循环语句 break, continue 控制循环流程(跳出循环,继续循环) 属性和纹理资源: Texture2D, TextureCube, Texture2DArray 定义纹理资源(二维纹理,立方纹理,二维数组纹理) sampler, sampler_state 定义采样器状态 几何和三角函数: floor(x) 向下取整 ceil(x) 向上取整 frac(x) 取小数部分 fmod(x, y) x除以y的余数 颜色和照明函数: saturate(x) 将值限制在0到1之间 lerp(a, b, t) 线性插值。 位操作函数: countbits(x) 计算整数x中设置的位数 firstbitHi(x) 找到x中最高位的1的位置 firstbitLo(x) 找到x中最低位的1的位置 数学常数和类型: float, int, uint 基本数据类型(浮点,整数,无符号整数) float2, float3, float4 向量类型(二元组,三元组,四元组) float2x2, float3x3, float4x4 矩阵类型(2*2矩阵,3*3矩阵,4*4矩阵) PI, E 数学常数π和e ########################################################################################################################################################################## HLSL的一些基本操作: 变量声明: 声明变量时需要指定类型和名称。 float myFloat; float4 myVector; 向量和矩阵操作: 向量和矩阵是HLSL中的基本数据结构,用于存储和操作空间中的点、颜色等。 float4 v = float4(1.0, 2.0, 3.0, 4.0); float3x3 m = float3x3(1, 0, 0, 0, 1, 0, 0, 0, 1); 算术运算: 支持基本的算术运算,如加、减、乘、除。 float a = 5.0; float b = 3.0; float result = a + b; // 8.0 result = a * b; // 15.0 逻辑运算: 逻辑运算包括AND(&&)、OR(||)、NOT(!)。 bool condition = true; bool result = !condition || (a > b); 控制流: 使用if、else和switch进行条件判断。 使用for、while和do-while进行循环。 if (condition) { // 执行代码 } else { // 执行其他代码 } for (int i = 0; i < count; ++i) { // 循环体 } 函数定义: 定义函数以复用代码和执行特定任务。 float myFunction(float input) { return input * 2.0; } 纹理采样: 从纹理中采样颜色或其他数据。 Texture2D myTexture; sampler mySampler = sampler_state { Texture = myTexture; }; 战团中是这样: sampler MeshTextureSampler : register(fx_MeshTextureSampler_RegisterS) = sampler_state{Texture = diffuse_texture;}; ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- sampler MeshTextureSampler 定义了一个名为 MeshTextureSampler的采样器资源。采样器用于控制如何从纹理资源中获取数据(即采样)。 : register(fx_MeshTextureSampler_RegisterS): 这里的: register 语法语义指示指定了采样器的寄存器绑定。 fx_MeshTextureSampler_RegisterS 是一个字符串,表示这个采样器的注册地址或名称,和引擎有关, = sampler_state: 这初始化了采样器状态,sampler_state 是一个定义采样器行为的结构体,可以包含多种采样选项,如过滤模式、地址模式(纹理坐标超出[0.0, 1.0]范围时的处理方式)等。 { Texture = diffuse_texture; }这指定了采样器状态,即要使用的纹理资源是名为diffuse_texture的贴图数据。diffuse_texture 对应的是贴图第一通道。 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- float4 texColor = tex2D(mySampler, float2(u, v)); 系统值语义: 使用系统值语义来获取GPU提供的值,如顶点位置。 float4 position : SV_Position; 结构体和接口: 使用结构体定义复杂的数据类型,接口用于着色器输入和输出。 struct MyStruct { float value; int index; }; 模板和宏: 使用宏进行条件编译和定义常量,使用模板创建泛型类型。 #define MY_CONSTANT 10 // 条件编译 #ifdef MY_MACRO // 某些代码 #endif 资源绑定: 绑定纹理、缓冲区和其他资源到着色器。 Texture2D myTexture : register(t0); Buffer myBuffer : register(b1); ########################################################################################################################################################################## 以下是一个简单贴图着色器示例: struct PS_OUTPUT { float4 RGBColor : COLOR; }; //你其实不用定义PS_OUTPUT结构体,因为战团着色器源码开头就定义过。 struct VS_OUTPUT_MY { float4 Pos : POSITION; float Fog : FOG; float4 Color : COLOR0; float2 Tex0 : TEXCOORD0; }; VS_OUTPUT_MY vs_my(float4 vPosition : POSITION, float4 vColor : COLOR, float2 tc : TEXCOORD0) { VS_OUTPUT_MY Out; Out.Pos = mul(matWorldViewProj, vPosition); float3 P = mul(matWorldView, vPosition).xyz; //position in view space Out.Tex0 = tc; Out.Color = tex2D(MeshTextureSampler, In.Tex0); //apply fog float d = length(P); float4 vWorldPos = (float4)mul(matWorld,vPosition); Out.Fog = get_fog_amount_new(d, vWorldPos.z); return Out; } PS_OUTPUT ps_my(VS_OUTPUT_MY In) { PS_OUTPUT Output; Output.RGBColor = In.Color; Output.RGBColor.a *= tex2D(MeshTextureSampler, In.Tex0).a; return Output; } technique my_shader { pass P0 { VertexShader = vs_my(); PixelShader = ps_my(); } }