Operadores

Expressões são sequências de variáveis e literais pontuadas por operadores. Os operadores determinam como as variáveis e literais são combinados, comparados, selecionados e assim por diante. Os operadores incluem:

Nome do operador Operadores
Operadores aditivos e multiplicativos +, -, *, /, %
Operador matriz [i]
Operadores de Atribuição =, +=, -=, *=, /=, %=
Coerções binárias Regras C para float e int, regras C ou intrínsecas HLSL para bool
Operadores bit a bit ~, <<, >>, &, |, ^, <<=, >>=, &=, |=, ^=
Operadores matemáticos boolianos &&, ||, ?:
Operador de coerção (tipo)
Operador vírgula ,
Operadores de comparação <, >, ==, !=, <=, >=
Operadores prefixos ou pós-fixos ++, --
Operador Estrutura .
Operadores unários !, -, +

 

Muitos dos operadores são por componente, o que significa que a operação é executada independentemente para cada componente de cada variável. Por exemplo, uma única variável de componente tem uma operação executada. Por outro lado, uma variável de quatro componentes tem quatro operações realizadas, uma para cada componente.

Todos os operadores que fazem algo no valor, como + e *, trabalham por componente.

Os operadores de comparação exigem um único componente para funcionar, a menos que você use a função intrínseca all ou any com uma variável de vários componentes. A operação a seguir falha porque a instrução if requer um único bool, mas recebe um bool4:

if (A4 < B4)

As seguintes operações são bem-sucedidas:

if ( any(A4 < B4) )
if ( all(A4 < B4) )

Os operadores de coerção binária asfloat, asint e assim por diante trabalham por componente, exceto para asdouble cujas regras especiais são documentadas.

Operadores de seleção como ponto, vírgula e colchetes de matriz não funcionam por componente.

Os operadores de coerção alteram o número de componentes. As seguintes operações de coerção mostram sua equivalência:

(float) i4 ->   float(i4.x)
(float4)i ->   float4(i, i, i, i)

Operadores aditivos e multiplicativos

Os operadores aditivos e multiplicativos são: +, -, *, /, %

int i1 = 1;
int i2 = 2;
int i3 = i1 + i2;  // i3 = 3
i3 = i1 * i2;        // i3 = 1 * 2 = 2
i3 = i1/i2;       // i3 = 1/3 = 0.333. Truncated to 0 because i3 is an integer.
i3 = i2/i1;       // i3 = 2/1 = 2
float f1 = 1.0;
float f2 = 2.0f;
float f3 = f1 - f2; // f3 = 1.0 - 2.0 = -1.0
f3 = f1 * f2;         // f3 = 1.0 * 2.0 = 2.0
f3 = f1/f2;        // f3 = 1.0/2.0 = 0.5
f3 = f2/f1;        // f3 = 2.0/1.0 = 2.0

O operador módulo retorna o restante de uma divisão. Isso produz resultados diferentes ao usar números inteiros e de ponto flutuante. Os inteiros restantes que são fracionários serão truncados.

int i1 = 1;
int i2 = 2;
i3 = i1 % i2;      // i3 = remainder of 1/2, which is 1
i3 = i2 % i1;      // i3 = remainder of 2/1, which is 0
i3 = 5 % 2;        // i3 = remainder of 5/2, which is 1
i3 = 9 % 2;        // i3 = remainder of 9/2, which is 1

O operador módulo corta um restante fracionário ao usar inteiros.

f3 = f1 % f2;      // f3 = remainder of 1.0/2.0, which is 0.5
f3 = f2 % f1;      // f3 = remainder of 2.0/1.0, which is 0.0

O operador % é definido apenas nos casos em que ambos os lados são positivos ou ambos os lados são negativos. Ao contrário de C, ele também opera em tipos de dados de ponto flutuante, bem como inteiros.

Operador matriz

O operador de seleção de membros da matriz "[i]" seleciona um ou mais componentes em uma matriz. É um conjunto de colchetes que contêm um índice baseado em zero.

int arrayOfInts[4] = { 0,1,2,3 };
arrayOfInts[0] = 2;
arrayOfInts[1] = arrayOfInts[0];

O operador matriz também pode ser usado para acessar um vetor.

float4 4D_Vector = { 0.0f, 1.0f, 2.0f, 3.0f  };         
float 1DFloat = 4D_Vector[1];          // 1.0f

Ao adicionar um índice adicional, o operador matriz também pode acessar uma matriz.

float4x4 mat4x4 = {{0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} };
mat4x4[0][1] = 1.1f;
float 1DFloat = mat4x4[0][1];      // 0.0f

O primeiro índice é o índice de linha baseado em zero. O segundo índice é o índice de coluna baseado em zero.

Operadores de Atribuição

Os operadores de atribuição são: =, +=, -=, *=, /=

Valores literais podem ser atribuídos às variáveis:

int i = 1;            
float f2 = 3.1f; 
bool b = false;
string str = "string";

O resultado de uma operação matemática também pode ser atribuído às variáveis:

int i1 = 1;
i1 += 2;           // i1 = 1 + 2 = 3

Uma variável pode ser usada em qualquer lado do sinal de igual:

float f3 = 0.5f;
f3 *= f3;          // f3 = 0.5 * 0.5 = 0.25

A divisão para variáveis de ponto flutuante é a esperada porque os decimais restantes não são um problema.

float f1 = 1.0;
f1 /= 3.0f;        // f1 = 1.0/3.0 = 0.333

Tenha cuidado se você estiver usando inteiros que podem ficar divididos, especialmente quando o truncamento afeta o resultado. Este exemplo é idêntico ao exemplo anterior, exceto para o tipo de dados. O truncamento causa um resultado muito diferente.

int i1 = 1;
i1 /= 3;           // i1 = 1/3 = 0.333, which gets truncated to 0

Coerções binárias

A operação de coerção entre int e float converterá o valor numérico nas representações apropriadas seguindo as regras C para truncar um tipo int. Converter um valor de um float para um int e de volta para um float resultará em uma conversão com perdas com base na precisão do destino.

As coerções binárias também podem ser executadas usando Funções Intrínsecas (DirectX HLSL), que reinterpretam a representação de bit de um número no tipo de dados de destino.

asfloat() // Cast to float
asint()   // Cast to int 
asuint()  // Cast to uint

Operadores bit a bit

O HLSL suporta os seguintes operadores bit a bit, que seguem a mesma precedência que C em relação a outros operadores. A tabela a seguir descreve os operadores.

Observação

Os operadores bit a bit exigem o Shader Model 4_0 com hardware Direct3D 10 e superior.

 

Operador Função
~ Not lógico
<< Shift esquerda
>> Deslocamento para direita
& Logical And
| Lógico Or
^ Xor lógico
<<= Deslocamento para esquerda igual
>>= Deslocamento para direita igual
&= E igual
|= Ou igual
^= Xor igual

 

Os operadores bit a bit são definidos para operar apenas nos tipos de dados int e uint. A tentativa de usar operadores bit a bit em tipos de dados float ou struct resultará em um erro.

Operadores matemáticos boolianos

Os operadores matemáticos boolianos são: &&, ||, ?:

bool b1 = true;
bool b2 = false;
bool b3 = b1 && b2 // b3 = true AND false = false
b3 = b1 || b2                // b3 = true OR false = true

Ao contrário da avaliação de interrupção de &&, ||, e ?: em C, as expressões HLSL nunca interrompem uma avaliação porque são operações vetoriais. Todos os lados da expressão são sempre avaliados.

Os operadores booleanos funcionam por componente. Isso significa que, se você comparar dois vetores, o resultado será um vetor contendo o resultado booleano da comparação de cada componente.

Para expressões que usam operadores booleanos, o tamanho e o tipo de componente de cada variável são promovidos para serem os mesmos antes de a operação ocorrer. O tipo promovido determina a resolução na qual a operação ocorre, bem como o tipo de resultado da expressão. Por exemplo, uma expressão int3 + float seria promovida a float3 + float3 para avaliação, e seu resultado seria do tipo float3.

Operador cast

Uma expressão precedida por um nome de tipo entre parênteses é um tipo de coerção explícita. Uma coerção de tipo converte a expressão original no tipo de dados da coerção. Em geral, os tipos de dados simples podem ser convertidos para os tipos de dados mais complexos (com uma coerção de promoção), mas apenas alguns tipos de dados complexos podem ser convertidos em tipos de dados simples (com uma coerção de rebaixamento).

Apenas a coerção do lado direito é legal. Por exemplo, expressões como (int)myFloat = myInt; são ilegais. Use myFloat = (float)myInt; em vez disso.

O compilador também executa a coerção de tipos implícitos. Por exemplo, as duas expressões a seguir são equivalentes:

int2 b = int2(1,2) + 2;
int2 b = int2(1,2) + int2(2,2);

Operador vírgula

O operador vírgula (,) separa uma ou mais expressões que devem ser avaliadas em ordem. O valor da última expressão na sequência é usado como o valor da sequência.

Aqui está um caso que vale a pena chamar a atenção. Se o tipo de construtor for acidentalmente deixado do lado direito do sinal de igual, o lado direito agora contém quatro expressões, separadas por três vírgulas.

// Instead of using a constructor
float4 x = float4(0,0,0,1); 

// The type on the right side is accidentally left off
float4 x = (0,0,0,1); 

O operador vírgula avalia uma expressão da esquerda para a direita. Isso reduz o lado direito para:

float4 x = 1; 

A HLSL usa promoção escalar neste caso, então o resultado é como se isso fosse escrito da seguinte maneira:

float4 x = float4(1,1,1,1);

Neste exemplo, deixar de fora o tipo float4 do lado direito é provavelmente um erro que o compilador não consegue detectar porque esta é uma instrução válida.

Operadores de comparação

Os operadores de comparação de valores são: <, >, ==, !=, <=, >=.

Compare valores maiores (ou menores que) qualquer valor escalar:

if( dot(lightDirection, normalVector) > 0 )
   // Do something; the face is lit
if( dot(lightDirection, normalVector) < 0 )
   // Do nothing; the face is backwards

Ou compare valores iguais (ou não iguais a) qualquer valor escalar:

if(color.a == 0)
   // Skip processing because the face is invisible

if(color.a != 0)
   // Blend two colors together using the alpha value

Ou combine ambos e compare valores maiores ou iguais a (ou menor que ou igual a) qualquer valor escalar:

if( position.z >= oldPosition.z )
   // Skip the new face because it is behind the existing face
if( currentValue <= someInitialCondition )
   // Reset the current value to its initial condition

Cada uma dessas comparações pode ser feita com qualquer tipo de dados escalar.

Para usar operadores de comparação com tipos vetoriais e matriciais, use a função intrínseca all ou any.

Essa operação falha porque a instrução if requer um único bool, mas recebe um bool4:

if (A4 < B4)

Essas operações são bem-sucedidas:

if ( any(A4 < B4) )
if ( all(A4 < B4) )

Operadores prefixos ou pós-fixos

Os operadores prefixo e pós-fixo são: ++, --. Os operadores prefixos alteram o conteúdo da variável antes que a expressão seja avaliada. Os operadores pós-fixos alteram o conteúdo da variável depois que a expressão é avaliada.

Nesse caso, um loop usa o conteúdo de i para acompanhar a contagem de loops.

float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };

for (int i = 0; i<4; )
{
    arrayOfFloats[i++] *= 2; 
}

Como o operador de incremento pós-fixo (++) é usado, arrayOfFloats[i] é multiplicado por 2 antes de i ser incrementado. Isso poderia ser ligeiramente reorganizado para usar o operador de incremento prefixo. Este é mais difícil de ler, embora ambos os exemplos sejam equivalentes.

float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };

for (int i = 0; i<4; )
{
    arrayOfFloats[++i - 1] *= 2; 
}

Como o operador prefixo (++) é usado, arrayOfFloats[i+1 - 1] é multiplicado por 2 depois que i é incrementado.

O operador de decremento prefixo e decremento pós-fixo (--) são aplicados na mesma sequência que o operador de incremento. A diferença é que o decremento subtrai 1 em vez de adicionar 1.

Operador estrutura

O operador de seleção de membros da estrutura (.) é um ponto. Dada esta estrutura:

struct position
{
float4 x;
float4 y;
float4 z;
};

Ela pode ser lida da seguinte maneira:

struct position pos = { 1,2,3 };

float 1D_Float = pos.x
1D_Float = pos.y

Cada membro pode ser lido ou escrito com o operador estrutura:

struct position pos = { 1,2,3 };
pos.x = 2.0f;
pos.z = 1.0f;       // z = 1.0f
pos.z = pos.x      // z = 2.0f

Operadores unários

Os operadores unários são: !, -, +

Operadores unários operam em um único operando.

bool b = false;
bool b2 = !b;      // b2 = true
int i = 2;
int i2 = -i;       // i2 = -2
int j = +i2;       // j = +2

Precedência de operador

Quando uma expressão contém mais de um operador, a precedência do operador determina a ordem de avaliação. A precedência do operador para HLSL segue a mesma precedência que C.

Comentários

Chaves ({,}) iniciam e terminam um bloco de instruções. Quando um bloco de instrução usa uma única instrução, as chaves são opcionais.

Instruções (DirectX HLSL)