Por que usar Time.deltaTime nas funções de leitura?

12

No meu entender, uma função Lerp interpola entre dois valores ( ae b) usando um terceiro valor ( t) entre 0e 1. Em t = 0, o valor a é retornado, em t = 1, o valor bé retornado. Em 0,5, o valor entre ae bé retornado.

(A figura a seguir é uma etapa suave, geralmente uma interpolação cúbica)

insira a descrição da imagem aqui

Estive navegando nos fóruns e, nesta resposta , encontrei a seguinte linha de código:transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime);

Pensei comigo mesmo: "que idiota, ele não tem idéia", mas como tinha mais de 40 votos positivos, tentei e, com certeza, funcionou!

float t = Time.deltaTime;
transform.rotation = Quaternion.Slerp(transform.rotation, toRotation, t);
Debug.Log(t);

Eu tenho valores aleatórios entre 0.01e 0.02para t. A função não deve interpolar adequadamente? Por que esses valores são empilhados? O que há no lerp que eu não entendo?

AzulShiva
fonte
1
A é geralmente a posição, que muda e, portanto, a amostragem a 1/60 (60 fps) apenas moveria o objeto pela interpolação de 0,16, estreitando continuamente a distância entre A e B (portanto, a amostra é cada vez menor).
Sidar
Você logou e leu com tt ... essas são variáveis ​​diferentes.
user253751

Respostas:

18

Veja também esta resposta .

Existem duas maneiras comuns de usar Lerp:

1. Mistura linear entre um começo e um fim

progress = Mathf.Clamp01(progress + speedPerTick);
current = Mathf.Lerp(start, end, progress);

Esta é a versão com a qual você provavelmente está mais familiarizado.

2. Facilidade exponencial em direção a um alvo

current = Mathf.Lerp(current, target, sharpnessPerTick);

Observe que nesta versão o currentvalor aparece como saída e como entrada. Ele desloca a startvariável, por isso estamos sempre começando de onde quer que nos mudemos na última atualização. É isso que fornece essa versão da Lerpmemória de um quadro para o outro. A partir deste ponto inicial em movimento, movemos então uma fração da distância em direção à targetditada por um sharpnessparâmetro.

Esse parâmetro não é mais uma "velocidade", porque nos aproximamos do alvo de maneira semelhante ao zeno . Se sharpnessPerTickfosse 0.5, na primeira atualização nos moveríamos a meio caminho do nosso objetivo. Na próxima atualização, moveríamos metade da distância restante (portanto, um quarto da nossa distância inicial). Então, no próximo, nos moveríamos pela metade novamente ...

Isso proporciona uma "facilidade exponencial", onde o movimento é rápido quando está longe do alvo e diminui gradualmente à medida que se aproxima assintoticamente (embora com números de precisão infinita nunca o alcance em um número finito de atualizações - para nossos propósitos, fica perto o suficiente). É ótimo para perseguir um valor-alvo móvel ou suavizar uma entrada barulhenta usando uma " média móvel exponencial ", geralmente usando um sharpnessPerTickparâmetro muito pequeno como 0.1ou menor.


Mas você está certo, há um erro na resposta votada que você vincula. Não está corrigindo deltaTimeo caminho certo. Este é um erro muito comum ao usar esse estilo de Lerp.

O primeiro estilo de Lerpé linear, para que possamos ajustar linearmente a velocidade multiplicando por deltaTime:

progress = Mathf.Clamp01(progress + speedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);

Mas nossa flexibilização exponencial é não linear , portanto, apenas a multiplicação de nosso sharpnessparâmetro por deltaTimenão dará a correção correta do tempo. Isso aparecerá como uma trepidação no movimento, se a taxa de quadros flutuar, ou uma alteração na nitidez da flexibilização, se você passar de 30 para 60 de forma consistente.

Em vez disso, precisamos aplicar uma correção exponencial para nossa facilidade exponencial:

blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);
current = Mathf.Lerp(current, target, blend);

Aqui referenceFramerateestá apenas uma constante 30para manter as unidades sharpnessda mesma forma que estávamos usando antes de corrigir o tempo.


Há um outro erro discutível nesse código, que está usando Slerp- a interpolação linear esférica é útil quando queremos uma taxa de rotação exatamente consistente por todo o movimento. Mas se usarmos uma facilidade exponencial não linear de qualquer maneira, Lerpforneceremos um resultado quase indistinguível e é mais barato. ;) Os quaternions lerp são muito melhores que as matrizes, por isso geralmente é uma substituição segura.

DMGregory
fonte
1

Eu acho que o conceito principal que falta seria neste cenário A não é fixo. A é atualizado a cada etapa, no entanto, ao longo da interpolação que Time.deltaTime é.

Portanto, com A se aproximando de B a cada etapa, o espaço total da interpolação muda a cada chamada Lerp / Slerp. Sem fazer as contas reais, eu suspeitaria que o efeito não é o mesmo que o gráfico Smoothstep, mas é uma maneira barata de aproximar uma desaceleração quando A se aproxima de B.

Além disso, isso é frequentemente usado porque B também pode não ser estático. Caso típico pode ser uma câmera seguindo um jogador. Você deseja evitar instabilidade, fazendo a câmera pular para um local ou rotação.

Chris
fonte
1

Você está certo, o método Quaternion Slerp(Quaternion a, Quaternion b, float t)interpola entre ae bpela quantidade t. Mas observe o primeiro valor, não é o valor inicial.

Aqui, o primeiro valor dado ao método é a rotação atual do objeto transform.rotation. Portanto, para cada quadro, ele interpola entre a rotação atual e a rotação alvo _lookRotationpela quantidade Time.deltaTime.

É por isso que produz uma rotação suave.

Ludovic Feltz
fonte
2
Agora eu sinto idiota
AzulShiva
@AzulShiva Não se preocupe, isso acontece com todo mundo;)
Ludovic Feltz 31 /