Pular para o conteúdo
Fundamento · Funções de formaIniciante

Mix, clamp e remap

Misture dois valores, mantenha um valor dentro de um intervalo e leve-o de um intervalo para outro.

Como funciona

Boa parte da matemática de um shader se resume a levar números de um intervalo para outro: uma coordenada de 0 a 1 vira uma cor, uma onda de -1 a 1 vira um brilho, uma distância vira uma opacidade. Três operações pequenas fazem quase tudo isso.

mix: de a até b

mix(a, b, t) é a interpolação linear, chamada de lerp no HLSL e na maioria dos editores de nós. Ela devolve a quando t é 0, b quando t é 1 e a linha reta entre os dois no meio do caminho: a * (1.0 - t) + b * t. Funciona com floats e com vetores (componente a componente), então mistura cores tão fácil quanto números. Usando a altura da UV como t, duas cores fazem um céu:

vec3 horizon = vec3(0.95, 0.55, 0.3);
vec3 zenith  = vec3(0.2, 0.35, 0.8);
vec3 sky     = mix(horizon, zenith, uv.y); // um degradê vertical

t também pode ser um vetor, que mistura cada componente na sua própria proporção. E um t fora de 0 a 1 não para nas pontas: o mix continua pela mesma reta (extrapola), o que às vezes é útil e muitas vezes é um bug.

clamp: fique no intervalo

clamp(x, lo, hi) limita x ao intervalo de lo a hi; é o mesmo que min(max(x, lo), hi). Limitar entre 0 e 1 é tão comum que o HLSL e os editores de nós têm um nome para isso, saturate. Use clamp antes de um mix quando t puder sair do intervalo, e antes de passar um valor que não pode ser negativo, como a base do pow.

remap: de um intervalo para outro

O GLSL não tem uma função de remap, mas ela sai em dois passos. Primeiro, desfaça um mix: dado um valor x entre a e b, descubra o quanto ele já andou. Essa é a lerp inversa:

float t = (x - a) / (b - a); // 0 em a, 1 em b

Depois, passe esse t para um mix em direção ao novo intervalo:

float remap(float x, float inLo, float inHi, float outLo, float outHi) {
    float t = clamp((x - inLo) / (inHi - inLo), 0.0, 1.0);
    return mix(outLo, outHi, t);
}

O clamp faz os valores fora do intervalo de entrada grudarem nas pontas; tire-o quando a reta tiver que continuar. O intervalo de entrada não pode ser vazio: quando inLo é igual a inHi, a divisão é por zero.

Vale decorar um caso especial. x * 0.5 + 0.5 leva um valor de -1 a 1, como o de sin() ou o de uma coordenada centralizada, para 0 a 1, e x * 2.0 - 1.0 faz o caminho de volta.

Leitura complementar