Operasi Matematika Per-Component

Dengan HLSL, Anda dapat memprogram shader pada tingkat algoritma. Untuk memahami bahasa, Anda harus tahu cara mendeklarasikan variabel dan fungsi, menggunakan fungsi intrinsik, menentukan jenis data kustom dan menggunakan semantik untuk menghubungkan argumen shader ke shader lain dan ke alur.

Setelah Anda mempelajari cara menulis shader di HLSL, Anda harus mempelajari tentang panggilan API sehingga Anda dapat: mengkompilasi shader untuk perangkat keras tertentu, menginisialisasi konstanta shader, dan menginisialisasi status alur lainnya jika perlu.

Jenis Vektor

Vektor adalah struktur data yang berisi antara satu dan empat komponen.

bool    bVector;   // scalar containing 1 Boolean
bool1   bVector;   // vector containing 1 Boolean
int1    iVector;   // vector containing 1 int
float3  fVector;   // vector containing 3 floats
double4 dVector;   // vector containing 4 doubles

Bilangan bulat yang segera mengikuti jenis data adalah jumlah komponen pada vektor.

Inisialisasi juga dapat dimasukkan dalam deklarasi.

bool    bVector = false;
int1    iVector = 1;
float3  fVector = { 0.2f, 0.3f, 0.4f };
double4 dVector = { 0.2, 0.3, 0.4, 0.5 };

Atau, jenis vektor dapat digunakan untuk membuat deklarasi yang sama:

vector <bool,   1> bVector = false;
vector <int,    1> iVector = 1;
vector <float,  3> fVector = { 0.2f, 0.3f, 0.4f };
vector <double, 4> dVector = { 0.2, 0.3, 0.4, 0.5 };

Jenis vektor menggunakan kurung sudut untuk menentukan jenis dan jumlah komponen.

Vektor berisi hingga empat komponen, yang masing-masing dapat diakses menggunakan salah satu dari dua set penamaan:

  • Set posisi: x,y,z,w
  • Set warna: r,g,b,a

Pernyataan ini mengembalikan nilai di komponen ketiga.

// Given
float4 pos = float4(0,0,2,1);

pos.z    // value is 2
pos.b    // value is 2

Set penamaan dapat menggunakan satu atau beberapa komponen, tetapi tidak dapat dicampur.

// Given
float4 pos = float4(0,0,2,1);
float2 temp;

temp = pos.xy  // valid
temp = pos.rg  // valid

temp = pos.xg  // NOT VALID because the position and color sets were used.

Menentukan satu atau beberapa komponen vektor saat membaca komponen disebut menggeliat. Contohnya:

float4 pos = float4(0,0,2,1);
float2 f_2D;
f_2D = pos.xy;   // read two components 
f_2D = pos.xz;   // read components in any order       
f_2D = pos.zx;

f_2D = pos.xx;   // components can be read more than once
f_2D = pos.yy;

Masking mengontrol berapa banyak komponen yang ditulis.

float4 pos = float4(0,0,2,1);
float4 f_4D;
f_4D    = pos;     // write four components          

f_4D.xz = pos.xz;  // write two components        
f_4D.zx = pos.xz;  // change the write order

f_4D.xzyw = pos.w; // write one component to more than one component
f_4D.wzyx = pos;

Penugasan tidak dapat ditulis ke komponen yang sama lebih dari sekali. Jadi sisi kiri pernyataan ini tidak valid:

f_4D.xx = pos.xy;   // cannot write to the same destination components 

Selain itu, spasi nama komponen tidak dapat dicampur. Ini adalah penulisan komponen yang tidak valid:

f_4D.xg = pos.rgrg;    // invalid write: cannot mix component name spaces 

Mengakses vektor sebagai skalar akan mengakses komponen pertama vektor. Dua pernyataan berikut setara.

f_4D.a = pos * 5.0f;
f_4D.a = pos.r * 5.0f;

Jenis Matriks

Matriks adalah struktur data yang berisi baris dan kolom data. Data dapat berupa salah satu jenis data skalar, namun, setiap elemen matriks adalah jenis data yang sama. Jumlah baris dan kolom ditentukan dengan string baris demi kolom yang ditambahkan ke jenis data.

int1x1    iMatrix;   // integer matrix with 1 row,  1 column
int2x1    iMatrix;   // integer matrix with 2 rows, 1 column
...
int4x1    iMatrix;   // integer matrix with 4 rows, 1 column
...
int1x4    iMatrix;   // integer matrix with 1 row, 4 columns
double1x1 dMatrix;   // double matrix with 1 row,  1 column
double2x2 dMatrix;   // double matrix with 2 rows, 2 columns
double3x3 dMatrix;   // double matrix with 3 rows, 3 columns
double4x4 dMatrix;   // double matrix with 4 rows, 4 columns

Jumlah maksimum baris atau kolom adalah 4; angka minimum adalah 1.

Matriks dapat diinisialisasi ketika dinyatakan:

float2x2 fMatrix = { 0.0f, 0.1, // row 1
                     2.1f, 2.2f // row 2
                   };   

Atau, jenis matriks dapat digunakan untuk membuat deklarasi yang sama:

matrix <float, 2, 2> fMatrix = { 0.0f, 0.1, // row 1
                                 2.1f, 2.2f // row 2
                               };

Jenis matriks menggunakan tanda kurung sudut untuk menentukan jenis, jumlah baris, dan jumlah kolom. Contoh ini membuat matriks floating-point, dengan dua baris dan dua kolom. Salah satu jenis data skalar dapat digunakan.

Deklarasi ini mendefinisikan matriks nilai float (angka floating-point 32-bit) dengan dua baris dan tiga kolom:

matrix <float, 2, 3> fFloatMatrix;

Matriks berisi nilai yang diatur dalam baris dan kolom, yang dapat diakses menggunakan operator struktur "." diikuti oleh salah satu dari dua set penamaan:

  • Posisi kolom baris berbasis nol:
    • _m00, _m01, _m02, _m03
    • _m10, _m11, _m12, _m13
    • _m20, _m21, _m22, _m23
    • _m30, _m31, _m32, _m33
  • Posisi kolom baris berbasis satu:
    • _11, _12, _13, _14
    • _21, _22, _23, _24
    • _31, _32, _33, _34
    • _41, _42, _43, _44

Setiap set penamaan dimulai dengan garis bawah diikuti dengan nomor baris dan nomor kolom. Konvensi berbasis nol juga menyertakan huruf "m" sebelum nomor baris dan kolom. Berikut adalah contoh yang menggunakan dua set penamaan untuk mengakses matriks:

// given
float2x2 fMatrix = { 1.0f, 1.1f, // row 1
                     2.0f, 2.1f  // row 2
                   }; 

float f_1D;
f_1D = matrix._m00; // read the value in row 1, column 1: 1.0
f_1D = matrix._m11; // read the value in row 2, column 2: 2.1

f_1D = matrix._11;  // read the value in row 1, column 1: 1.0
f_1D = matrix._22;  // read the value in row 2, column 2: 2.1

Sama seperti vektor, set penamaan dapat menggunakan satu atau beberapa komponen dari set penamaan.

// Given
float2x2 fMatrix = { 1.0f, 1.1f, // row 1
                     2.0f, 2.1f  // row 2
                   };
float2 temp;

temp = fMatrix._m00_m11 // valid
temp = fMatrix._m11_m00 // valid
temp = fMatrix._11_22   // valid
temp = fMatrix._22_11   // valid

Matriks juga dapat diakses menggunakan notasi akses array, yang merupakan sekumpulan indeks berbasis nol. Setiap indeks berada di dalam tanda kurung siku. Matriks 4x4 diakses dengan indeks berikut:

  • [0] [0], [0][1], [0][2], [0][3]
  • [1] [0], [1][1], [1][2], [1][3]
  • [2] [0], [2][1], [2][2], [2][3]
  • [3] [0], [3][1], [3][2], [3][3]

Berikut adalah contoh mengakses matriks:

float2x2 fMatrix = { 1.0f, 1.1f, // row 1
                     2.0f, 2.1f  // row 2
                   };
float temp;

temp = fMatrix[0][0] // single component read
temp = fMatrix[0][1] // single component read

Perhatikan bahwa operator struktur "." tidak digunakan untuk mengakses array. Notasi akses array tidak dapat menggunakan menggeliat untuk membaca lebih dari satu komponen.

float2 temp;
temp = fMatrix[0][0]_[0][1] // invalid, cannot read two components

Namun, akses array dapat membaca vektor multi-komponen.

float2 temp;
float2x2 fMatrix;
temp = fMatrix[0] // read the first row

Seperti halnya vektor, membaca lebih dari satu komponen matriks disebut menggeliat. Lebih dari satu komponen dapat ditetapkan, dengan asumsi hanya satu ruang nama yang digunakan. Ini semua adalah tugas yang valid:

// Given these variables
float4x4 worldMatrix = float4( {0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} );
float4x4 tempMatrix;

tempMatrix._m00_m11 = worldMatrix._m00_m11; // multiple components
tempMatrix._m00_m11 = worldMatrix.m13_m23;

tempMatrix._11_22_33 = worldMatrix._11_22_33; // any order on swizzles
tempMatrix._11_22_33 = worldMatrix._24_23_22;

Masking mengontrol berapa banyak komponen yang ditulis.

// Given
float4x4 worldMatrix = float4( {0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} );
float4x4 tempMatrix;

tempMatrix._m00_m11 = worldMatrix._m00_m11; // write two components
tempMatrix._m23_m00 = worldMatrix._m00_m11;

Penugasan tidak dapat ditulis ke komponen yang sama lebih dari sekali. Jadi sisi kiri pernyataan ini tidak valid:

// cannot write to the same component more than once
tempMatrix._m00_m00 = worldMatrix._m00_m11;

Selain itu, spasi nama komponen tidak dapat dicampur. Ini adalah penulisan komponen yang tidak valid:

// Invalid use of same component on left side
tempMatrix._11_m23 = worldMatrix._11_22; 

Pengurutan Matriks

Urutan pengemasan matriks untuk parameter seragam diatur ke column-major secara default. Ini berarti setiap kolom matriks disimpan dalam satu register konstanta. Di sisi lain, matriks besar baris mengemas setiap baris matriks dalam satu register konstanta. Pengemasan matriks dapat diubah dengan direktif #pragmapack_matrix , atau dengan row_major atau kata kunci column_major .

Data dalam matriks dimuat ke dalam daftar konstanta shader sebelum shader berjalan. Ada dua pilihan tentang cara data matriks dibaca: dalam urutan utama baris atau dalam urutan kolom-utama. Urutan kolom-utama berarti bahwa setiap kolom matriks akan disimpan dalam satu register konstanta, dan urutan utama baris berarti bahwa setiap baris matriks akan disimpan dalam satu register konstanta. Ini adalah pertimbangan penting untuk berapa banyak register konstan yang digunakan untuk matriks.

Matriks besar baris ditata seperti berikut:

11
21
31
41

12
22
32
42

13
23
33
43

14
24
34
44

 

Matriks kolom-utama ditata seperti berikut ini:

11
12
13
14

21
22
23
24

31
32
33
34

41
42
43
44

 

Urutan matriks row-major dan column-major menentukan urutan komponen matriks dibaca dari input shader. Setelah data ditulis ke dalam register konstanta, urutan matriks tidak berpengaruh pada bagaimana data digunakan atau diakses dari dalam kode shader. Juga, matriks yang dinyatakan dalam tubuh shader tidak dikemas ke dalam register konstan. Urutan pengemasan row-major dan column-major tidak berpengaruh pada urutan pengemasan konstruktor (yang selalu mengikuti pengurutan baris-utama).

Urutan data dalam matriks dapat dideklarasikan pada waktu kompilasi atau pengkompilasi akan memesan data saat runtime untuk penggunaan yang paling efisien.

Contoh

HLSL menggunakan dua jenis khusus, jenis vektor dan jenis matriks untuk membuat pemrograman grafik 2D dan 3D lebih mudah. Masing-masing jenis ini berisi lebih dari satu komponen; vektor berisi hingga empat komponen, dan matriks berisi hingga 16 komponen. Ketika vektor dan matriks digunakan dalam persamaan HLSL standar, matematika yang dilakukan dirancang untuk bekerja per komponen. Misalnya, HLSL mengimplementasikan perkalian ini:

float4 v = a*b;

sebagai perkalian empat komponen. Hasilnya adalah empat skalar:

float4 v = a*b;

v.x = a.x*b.x;
v.y = a.y*b.y;
v.z = a.z*b.z;
v.w = a.w*b.w;

Ini adalah empat perkalian di mana setiap hasil disimpan dalam komponen terpisah dari v. Ini disebut perkalian empat komponen. HLSL menggunakan matematika komponen yang membuat penulisan shader sangat efisien.

Ini sangat berbeda dari perkalian yang biasanya diimplementasikan sebagai produk titik yang menghasilkan skalar tunggal:

v = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;

Matriks juga menggunakan operasi per komponen di HLSL:

float3x3 mat1,mat2;
...
float3x3 mat3 = mat1*mat2;

Hasilnya adalah kalikan per komponen dari dua matriks (dibandingkan dengan perkalian matriks 3x3 standar). Matriks per komponen mengalikan menghasilkan istilah pertama ini:

mat3.m00 = mat1.m00 * mat2._m00;

Ini berbeda dari perkalian matriks 3x3 yang akan menghasilkan istilah pertama ini:

// First component of a four-component matrix multiply
mat.m00 = mat1._m00 * mat2._m00 + 
          mat1._m01 * mat2._m10 + 
          mat1._m02 * mat2._m20 + 
          mat1._m03 * mat2._m30;

Versi kelebihan beban dari kasus handel fungsi intrinsik multiply di mana satu operand adalah vektor dan operand lainnya adalah matriks. Seperti: vektor * vektor, vektor * matriks, matriks * vektor, dan matriks * matriks. Contohnya:

float4x3 World;

float4 main(float4 pos : SV_POSITION) : SV_POSITION
{
    float4 val;
    val.xyz = mul(pos,World);
    val.w = 0;

    return val;
}   

menghasilkan hasil yang sama seperti:

float4x3 World;

float4 main(float4 pos : SV_POSITION) : SV_POSITION
{
    float4 val;
    val.xyz = (float3) mul((float1x4)pos,World);
    val.w = 0;

    return val;
}   

Contoh ini melemparkan vektor pos ke vektor kolom menggunakan transmisi (float1x4). Mengubah vektor dengan mentransmisikan, atau menukar urutan argumen yang disediakan untuk mengalikan setara dengan mengubah urutan matriks.

Konversi transmisi otomatis menyebabkan fungsi intrinsik perkalian dan titik mengembalikan hasil yang sama seperti yang digunakan di sini:

{
  float4 val;
  return mul(val,val);
}

Hasil perkalian ini adalah vektor 1x4 * 4x1 = 1x1. Ini setara dengan produk titik:

{
  float4 val;
  return dot(val,val);
}

yang mengembalikan nilai skalar tunggal.

Jenis Data (DirectX HLSL)