DirectX11--HLSL语法入门(2)
时间:2022-12-14 22:18 来源:网络整理 作者:采集插件 点击:次
HLSL的结构体和C/C++的十分类似,它能够存听任意数目的标量,向量和矩阵类型,除此以外,它还能够存放数组或者别的结构体类型。结构体的成员访问也和C/C++类似: struct A { float4 vec; }; struct B { int scalar; float4 vec; float4x4 mat; float arr[8]; A a; }; // ... B b; b.vec = float4(1.0f, 2.0f, 3.0f, 4.0f); 变量的修饰符 关键字 含义static 该着色器变量将不会暴露给C++应用层,须要在HLSL中本身初始化,不然使用默认初始化 extern 与static相反,该着色器变量将会暴露给C++应用层 uniform 该着色器变量容许在C++应用层被改变,但在着色器执行的过程当中,其值始终保持不变(运行前可变,运行时不变)。着色器程序中的全局变量默认为既uniform又extern const 和C++中的含义相同,它是一个常量,须要被初始化且不能够被修改 类型转换 HLSL有着极其灵活的类型转换机制。HLSL中的类型转换语法和C/C++的相同。下面是一些例子: float f = 4.0f; float4x4 m = (float4x4)f; // 将浮点数f复制到矩阵m的每个元素当中 float3 n = float3(...); float3 v = 2.0f * n - 1.0f; // 这里1.0f将会隐式转换成(1.0f, 1.0f, 1.0f) float4x4 WInvT = float4x4(...); float3x3 mat = (float3x3)WInvT; // 只取4x4矩阵的前3行前3列 typedef关键字和C++同样,typedef关键字用来声明一个类型的别称: typedef float3 point; typedef const float cfloat; point p; // p为float3 cfloat f = 1.0f; // f为const float 运算符的一些特例本教程不列出关键字,在学习的时候再逐渐接触须要用到的会好一点。 C/C++中能用的运算符在HLSL中基本上都能用,也包括位运算。这里只列出运算符的一些特例状况。 模运算符%不只能够用于整数,还能用于浮点数。并且,要进行模运算就必须保证取模运算符左右操做数都具备相同的符号(要么都为正数,要么都为负数)。 基于运算符的向量间的运算都是以份量为展开的。 例如: float3 pos = {1.0f, 2.0f, 3.0f}; float3 p1 = pos * 2.0f; // (2.0f, 4.0f, 6.0f) float3 p2 = pos * pos; // (1.0f, 4.0f, 9.0f) bool3 b = (p1 == p2); // (false, true, false) ++pos; // (2.0f, 3.0f, 4.0f)所以,若是乘法运算符的两边都是矩阵,则表示为矩阵的份量乘法,而不是矩阵乘法。 最后是二元运算中变量类型的提高规则: 对于二元运算来讲,若是运算符左右操做数的维度不一样,那么维度较小的变量类型将会被隐式提高为维度较大的变量类型。可是这种提高仅限于标量到向量的提高,即x会变为(x, x, x)。可是不支持像float2到float3的提高。 对于二元运算来讲,若是运算符左右的操做数类型不一样,那么低精度变量的类型将被隐式提高为高精度变量的类型,这点和C/C++是相似的。 控制流 条件语句HLSL也支持if, else, continue, break, switch关键字,此外discard关键字用于像素着色阶段抛弃该像素。 条件的判断使用一个布尔值进行,一般由各类逻辑运算符或者比较运算符操做获得。注意向量之间的比较或者逻辑操做是获得一个存有布尔值的向量,不可以直接用于条件判断,也不能用于switch语句。 判断与动态分支基于值的条件分支只有在程序执行的时候被编译好的着色器汇编成两种方式:判断(predication)和动态分支(dynamic branching)。 若是使用的是判断的形式,编译器会提早计算两个不一样分支下表达式的值。而后使用比较指令来基于比较结果来"选择"正确的值。 而动态分支使用的是跳转指令来避免一些非必要的计算和内存访问。 着色器程序在同时执行的时候应当选择相同的分支,以防止硬件在分支的两边执行。一般状况下,硬件会同时将一系列连续的顶点数据传入到顶点着色器并行计算,或者是一系列连续的像素单元传入到像素着色器同时运算等。 动态分支会因为执行分支指令所带来的开销而致使必定的性能损失,所以要权衡动态分支的开销和能够跳过的指令数目。 一般状况下编译器会自行选择使用判断仍是动态分支,但咱们能够经过重写某些属性来修改编译器的行为。咱们能够在条件语句前能够选择添加下面两个属性之一: 属性 描述[branch] 根据条件值的结果,只计算其中一边的内容,会产生跳转指令。默认不加属性的条件语句为branch型。 [flatten] 两边的分支内容都会计算,而后根据条件值选择其中一边。能够避免跳转指令的产生。 用法以下: [flatten] if (x) { x = sqrt(x); } 循环语句 (责任编辑:admin) |