HLSL Shader编程基础总结

本文总结了HLSL(High-Level Shader Language)的基础知识,包括变量类型、typedef关键字、变量前缀,以及顶点着色器和像素着色器的原理与实现。详细介绍了HLSL中的常量、uniform、const、static等变量前缀的用法,同时讲解了顶点着色器和像素着色器的关键函数和实现步骤。在开发经验部分,分享了如何处理顶点着色器和像素着色器的实际案例,如卡通着色、多重纹理融合,以及雾化效果的实现技巧。
摘要由CSDN通过智能技术生成

基本前提概念

    Shader是一种映射到GPU硬件汇编语言上的高级语言,Shader中的数据类型是要声明类型和用途的,用途其实就是和着色器寄存器关联,输入位置寄存器,输出位置寄存器,输出颜色寄存器等。Shader HLSL中的颜色是rgba的类型,不要弄错了。Shader中的每一个类型,函数,字符串都是有含义的。
    顶点和像素着色器中,常量表用SetDefault传入Device将常量表中的字段常量设置初值,避免忘记赋值了而没有初值的情况。
D3DXCompileShaderFromFile用D3DXSHADER_SKIPVALIVATION当已确认可用时不进行任何代码验证可以提高效率不要老用D3DXSHADER_DEBUG那只是开发测试时候用的。

HLSL语言基础总结

I、变量类型

1. 标量类型:
bool, int, half为16位浮点数,float, double。
初始化和赋值:

const static int g_nArraySize = 3;

const static int g_nArraySize2 = {3};

const static int g_nArraySize3 = int(3);


2.向量类型:
1)类型
vector是4D向量,每个分量都是float类型。
vector<T, n>其中n为1到4,T为需要的向量分量类型。
 float2,float3,float4定义的是2维,3维,4维的向量。
2)初始化(直接或者构造函数方式赋值)

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);

float3 = float3(0, 0, 0);

3)访问和赋值
使用数组下标的语法访问向量的一个分量,例如访问第i个向量分量,用 vec[i] = 2.0f。
可以像访问结构的成员一样访问向量vec的一个分量,使用已定义的分量名x,y,z,w,r,g,b和a,如:

vec.x = vec.r = 1.0f;

vec.y = vec.g = 2.0f;

vec.z = vec.b = 3.0f;

vec.w = vec.a = 4.0f;

名称为r,g,b和a的分量分别对应x,y,z和w的分量。当使用向量来表示颜色时,RGBA符号是更适合的,因为它加强了向量所表示的颜色。

考虑向量u = (ux, uy, uz, uw),假设我们要拷贝u的所有分量到一个像v = (ux, uy, uy, uw)这样的向量v。最直接的方法可能是逐个从u往v拷贝每个分量。但不管怎样,HLSL提供了一种特殊的语法做这些无序的拷贝,它叫做swizzles:

vector u = {l.0f, 2.0f, 3.0f, 4.0f};

vector v = {0.0f, 0.0f, 5.0f, 6.0f};

v = u.xyyw; // v = {1.0f, 2.0f, 2.0f, 4.0f}

拷贝数组时,我们不必拷贝每个分量。例如,我们可以仅拷贝x和y分量:

vector u = {1.0f, 2.0f, 3.0f, 4.0f};

vector v = {0.0f, 0.0f, 5.0f, 6.0f};

v.xy = u; // v = {l.0f, 2.0f, 5.0f, 6.0f}


3.矩阵类型:
1)类型
matrix为4x4的矩阵,每个元素的类型都是float类型。
matrix<T,m,n>中的m,n为1~4之间。
float2x2, float3x3, float4x4
intmxn也是可以的。
2)初始化(直接或者构造函数方式赋值)
int2x2 m = {1, 2, 3, 4};

float2x2 f2x2 = float2x2(1.0f, 2.0f, 3.0f, 4.0f);

3)访问和赋值
可以用二维数组的下标语法访问矩阵中的项,例如M[i] [j] = value;

此外,我们可以像访问结构的成员那样访问矩阵M的项。下列条目已定义:

以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]; // get the ith row vector in M

4.数组类型
数组类型和C++一样,声明时候可以不用初始化。
例如:

float  M[4][4];

half   p[4];

vector v[12];


但是数组的大小是有限制的,不能太大了。
Constant registers,一个 Constant register可以存放一个vector也就是4个float, 那么Pix Shader可以存放56个常量matrix类型,只是理论上的。

在C++程序中可以用传递数组的方式为HLSL传递向量或者矩阵。例如:
FLOAT texSize[2] = {imageInfo.Width * 1.0f, imageInfo.Height * 1.0f};
 MultiTexCT->SetFloatArray(Device, TexSizeHandle, texSize, 2);

 float 变长数组问题,总是希望程序里面传递一个参数给HLSL来确定HLSL的数组或者矩阵大小,但是不能做到,只能通过其它途径解决。
const static int g_num = num;
float fVec[g_num] [g_num];
形式给出,而不能用float fVec[g_num * 2]一维的来处理。
这样设置也是不行的,因为不支持变长的数组,还是需要明确的数组,如果要计算就在应用程序计算设置进去,然后HLSL根据需要标记进行区分。
或者设置一个足够大的二维数组,然后只用少部分的;或者设计一个少的数组,然后用多次Pass绘制来解决,绘制时候:
for each mesh being rendered
for each light affecting the mesh
if (first light)
render first light with ambient and no blending
else
render nth light with no ambient and additive belnding



5.结构体
结构的定义和在C++里一样。但是,HLSL里的结构不能有成员函数。

struct MyStruct

{

     matrix T;

     vector n;

     float  f;

     int    x;

     bool   b;

};

MyStruct s; // instantiate

s.f = 5.0f; // member access


II、typedef关键字

HLSL的typedef关键字功能和C++里的完全一样。例如,我们可以给类型vector<float, 3>用下面的语法命名:
typedef vector<float, 3> point;
然后,不用写成:
vector<float, 3> myPoint;
我们只需这样写:
point myPoint;
这里是另外两个例子,它展示了如何对常量和数组类型使用typedef关键字:
typedef const float CFLOAT;
typedef float point2[2];

III、变量前缀

extern——如果变量以extern关键字为前缀,就意味着该变量可在着色器外被访问,比如被C++应用程序。仅全局变量可以以extern关键字为前缀。不是static的全局变量默认就是extern

uniform——如果变量以uniform关键字为前缀,就意味着此变量在着色器外面被初始化,比如被C++应用程序初始化,然后再输入进着色器。


const——HLSL中的const关键字和C++里的意思一样。也就是说,如果变量以const为前缀,那此变量就是常量,并且不能被改变。

static——如果带static关键字前缀,若它是全局变量,就表示它不是暴露于着色器之外的。换句话说,它是着色器局部的。如果一个局部变量 以static关键字为前缀,它就和C++中static局部变量有相同的行为。也就是说,该变量在函数首次执行时被一次性初始化,然后在所有函数调用中 维持其值。如果变量没有被初始化,它就自动初始化为0。


shared——如果变量以shared关键字为前缀,就提示效果框架:变量将在多个效果间被共享。仅全局变量可以以shared为前缀。

volatile——如果变量以volatile关键字为前缀,就提示效果框架:变量将时常被修改。仅全局变量可以以volatile为前缀

其它关键字:

注意内建类型,sampler, texture, compile, decl关键字用法。

IV、运算符和类型转换

    运算符,%符号可以用于整数和浮点数,左右操作数需同号。
    许多HLSL的运算(+,-, * , / )都是在向量的分量级上进行的,v++,u++, u*v。
比较运算也是在分量上操作的。
    类型转换和C一样,可以强转且类型精度,维度会提升。
双目运算符 中的类型提升
值类型提升 bool < int < half < float < double.
维数提升 float定义了转换到float2,float3 不同分量上是取相同的数值,故可以转换为float2, float3。而float2没有定义转换到float3的,所以不能进行提升转换。


V、语句

 for循环不能从负数开始,且前后定义的for里面的变量不能重复,否则会导致冲突。

VI、自定义函数和内置函数

按值传递,不支持递归,总是内联的(所以函数要尽量短小)。
函数形参增加了in, out, inout修饰,默认是int类型。
内置的函数对各标量,向量,矩阵类型的大部分都做了重载,故是可以直接使用的。

顶点着色器和像素着色器原理和实现

1.原理

顶点着色器的输入是从物体坐标系开始包含位置、顶点法向量、纹理UV值 输出是到设备规范坐标系的顶点位置、颜色和纹理uv值(没有了顶点法向量),过程中包括变换和光照,还有对顶点的大小,点焊接,面拆分,LOD边坍塌,LOD反演点拆分等技术 后面对顶点经过屏幕变换,背面剔除,深度测试, 根据顶点对面片进行uv插值计算,或者颜色(或光照材质颜色)插值。 进入像素着色器,像素着色器的输入是插值后的像素UV纹理坐标和像素的颜色,像素着色器狭隘的说是代替多纹理融合着色阶段(操作单个像素和每个像素的纹理坐标的能力)其中采用器索引和第i层纹理的关联可以在程序中用D3DXCONSTANT_DESC TexDesc来关联,内部功能包含了alpha测试,stencil测试,颜色融合;像素着色器的输出是像素的颜色


顶点着色器(3D shaders)

顶点数据流: 顶点格式中相同的类型索引含义和HLSL 顶点着色器输入结构含义,通过Usage使用类型和UsageIndex使用类型索引进行映射关联 。如果不是很复杂的顶点声明其实可以用FVF在程序里面做, FVF会在渲染管道过程中会转换为顶点声明
  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值