1 Dispositivos, rastreamento e graus de liberdade

Antes de desenhar qualquer coisa, o aparelho precisa responder onde ele está — e a resposta errada dá enjoo em quem está de visor.

Este capítulo termina com um programa que não desenha nada, e é esse o argumento dele. O que fica são três coisas: o vocabulário que descreve quanto movimento um aparelho acompanha, o mecanismo pelo qual ele descobre onde está sem ajuda de fora, e as condições em que esse mecanismo falha. Leia com um aparelho concreto em mente — o celular do seu bolso serve — e pergunte a cada seção o que ele responderia.

1.1 Um passo à frente, e a imagem fica onde estava

Seis números descrevem qualquer objeto sólido no espaço, e um aparelho que acompanha só três deles produz mal-estar em minutos.

Vista um visor barato e dê um passo à frente. Seu corpo avança meio metro. Os canais semicirculares do ouvido interno registram a aceleração desse avanço e avisam ao cérebro que houve deslocamento. Seus olhos, dentro do visor, veem uma cena que não se aproximou de nada. Duas informações incompatíveis sobre o mesmo evento, e nenhum árbitro para decidir qual delas mente.

O desconforto que sai daí não se conserta com mais quadros por segundo. Ele também não melhora com textura mais caprichada nem com iluminação melhor. A causa é um sensor que não está dentro do aparelho, e software nenhum alcança isso. JERALD trata o mecanismo em detalhe, e a literatura de fatores humanos o documenta há décadas.

flowchart TD
    A[Um passo a frente<br/>com o visor no rosto] --> B[Ouvido interno e musculos<br/>registram o avanco]
    A --> C[Olhos registram<br/>cena imovel]
    B --> D{Dois relatos<br/>sobre o mesmo evento}
    C --> D
    D --> E[Nenhum arbitro<br/>decide qual mente]
    E --> F[Mal-estar<br/>sem defeito de imagem]
Figura 1: O desacordo entre o que o corpo registra e o que os olhos veem, quando o aparelho não acompanha o deslocamento.

O problema tem data bem anterior a qualquer visor barato. Em 1968, Ivan Sutherland montou o primeiro visor acoplado à cabeça, e ele pesava demais para ser usado como se usa um chapéu. A saída foi pendurá-lo no teto do laboratório por um braço mecânico articulado, que rendeu ao conjunto o apelido de espada de Dâmocles, pela peça suspensa sobre a cabeça de quem se sentava embaixo. O que ele exibia eram arestas de arame, sem cor e sem superfície.

Acontece que o braço não estava ali só para segurar peso. Cada junta articulada informava um ângulo, e da combinação dos ângulos saía onde a cabeça estava e para onde ela apontava. O primeiro visor da área já respondia à pergunta de posição, e respondia com engenharia mecânica. Quase sessenta anos depois, um visor de três graus não responde a ela. Em compensação, cabe na mochila.

1.1.1 Seis maneiras de mexer um livro

Pegue um livro fechado e segure-o à sua frente. Dá para movê-lo à esquerda e à direita, para cima e para baixo, para perto e para longe do rosto. São três deslocamentos independentes, no sentido de que executar um não obriga a executar os outros. Agora, sem tirá-lo do lugar, gire-o como um volante, como uma porta e como uma pizza sobre a mesa.

Acabou. Não existe uma sétima maneira, e o livro na sua mão comprova isso em dez segundos. Qualquer movimento que você faça com ele é combinação dessas seis, e a afirmação é geométrica em vez de convencional. A palavra rígido é o que faz a conta fechar: o livro não amassa nem entorta.

Os três giros herdaram nomes da navegação, e a bibliografia inteira os usa. Girar como um volante é rolagem. Girar como quem diz sim com a cabeça é arfagem. Girar como quem diz não é guinada. Os deslocamentos não ganharam nomes tão pitorescos, e costumam ser chamados apenas de translação em cada eixo.

Complementar — grau de liberdade e pose

Um grau de liberdade é uma direção de movimento independente das demais: a variação nela não é determinada pelas variações nas outras. Um corpo rígido no espaço tridimensional tem seis, sendo três de translação e três de rotação. A pose é o par formado pela rotação e pela translação que levam esse corpo da origem combinada até onde ele está agora.

O menor exemplo concreto cabe numa frase. A pose de uma xícara sobre a mesa é onde ela está no tampo, mais para que lado a alça aponta. Note o que a definição não exige. Ela não pede que o corpo seja pequeno. Não pede que ele se mova devagar. E não diz uma palavra sobre a precisão com que a pose é conhecida. Precisão é outro assunto, e a pose continua sendo pose mesmo quando quem a estima erra feio.

flowchart LR
    subgraph T[Translacao: onde o corpo esta]
        T1[deslocamento<br/>no eixo lateral]
        T2[deslocamento<br/>no eixo vertical]
        T3[deslocamento<br/>no eixo frontal]
    end
    subgraph R[Rotacao: para onde ele aponta]
        R1[giro em torno<br/>do eixo lateral]
        R2[giro em torno<br/>do eixo vertical]
        R3[giro em torno<br/>do eixo frontal]
    end
    R --> A[Aparelho de tres graus:<br/>estima so a metade de cima]
    T --> B[Aparelho de seis graus:<br/>estima as duas metades]
    R --> B
Figura 2: Os seis graus de liberdade de um corpo rígido, separados entre os três giros e os três deslocamentos.

1.1.2 A metade barata e a metade cara

Por que um fabricante deixaria metade dos graus de fora? O motivo é de custo antes de ser técnico. Rotação se mede com sensores inerciais pequenos e baratos, os mesmos que qualquer celular carrega há anos. A medida se estabiliza em fração de segundo. Essa é a metade barata, e ela vem quase de graça em qualquer aparelho.

Translação é outra história. Um sensor inercial mede aceleração, e da aceleração até a posição são duas integrações seguidas. Cada uma amplifica o erro da anterior, e o resultado desanda depressa: o aparelho passa a acreditar que desliza enquanto está parado. Medir deslocamento de forma confiável exige olhar para o mundo, o que significa câmera, processamento e bateria.

Daí sai uma exigência de projeto que atravessa todo o resto do livro. Um ambiente que não sabe quantos graus o aparelho acompanha acaba propondo a alguém um movimento que ele não segue. É questão de tempo. Isso não é curiosidade técnica de quem gosta de sensor. É pré-condição para não fazer mal a quem usa o que você construiu.

A explicação cobre menos do que parece, e esticá-la sai caro. Nem todo mal-estar em ambiente imersivo vem do desacordo entre canais sensoriais. Peso do equipamento, lentes mal ajustadas e cansaço visual produzem desconforto por outras vias, e a bibliografia os trata separadamente. O que se afirma aqui é mais estreito e mais firme. Este mecanismo específico decorre de rastrear menos graus do que o corpo executa. E não se corrige por software.

1.1.3 Do que se declarou ao que o aparelho responde

No capítulo anterior, ao separar imersão de presença e de registro, escrevemos duas declarações sobre o ambiente em construção: qual é o domínio dele e o que cada regime pretende fazer com o mundo de quem observa.

Uma dessas declarações dizia que o regime aumentado espera compor a imagem gerada sobre uma captura do ambiente real.

Repare no verbo. Espera.

Era afirmação escrita antes de qualquer aparelho ter sido consultado, e o valor dela estava exatamente em poder ser desmentida. Agora existe quem a desminta.

O aparelho responde, e ele pode discordar. Quando as duas coisas batem, a declaração sai confirmada e nada muda. Quando não batem, o ganho é maior: descobre-se que a declaração estava errada, e a descoberta vem da única entidade com autoridade para fazê-la.

A virada é essa. Até aqui o projeto afirmava. Daqui em diante ele pergunta e escuta.

E a consulta de suporte que já existia — este aparelho entra neste regime? — continua valendo inteira. O que se acrescenta agora é a consulta de recursos, a que só a sessão aberta responde.

1.2 Quem olha para quem

Há dois lugares possíveis para os sensores, e a escolha entre eles decide de que jeito o rastreamento quebra.

O braço de Sutherland pertence a uma família que ainda existe. Nela, a sala é instrumentada: equipamentos fixos, montados em posições conhecidas, observam o aparelho e calculam onde ele está. As juntas articuladas viraram meios ópticos, e o princípio geométrico ficou intacto desde 1968. A outra família inverte tudo, e põe os sensores dentro do próprio aparelho, olhando para fora.

A pergunta que os dois arranjos respondem é a mesma. Então por que a escolha entre eles importa? As respostas têm qualidades diferentes, falham de maneiras diferentes e cobram em colunas diferentes da conta. Confundir esses três aspectos é o que produz a escolha errada de equipamento em projeto real. E a escolha errada costuma aparecer só no dia da demonstração.

1.2.1 O que cada arranjo cobra

Comece pelo que o arranjo de dentro para fora dispensa, que é instalação. Tira-se o aparelho da caixa, veste-se, e ele funciona em qualquer sala. Não há equipamento a montar, alinhamento a conferir nem cabo atravessando o chão. Para um produto vendido a quem não é técnico, isso decide entre existir mercado e não existir.

A conta chega na palavra qualquer. Ele funciona em qualquer sala que ofereça o que ele precisa, e o que ele precisa é textura visual. Parede branca e lisa não tem canto algum a reconhecer, e luz fraca não dá o contraste necessário para separar um elemento do outro. Há ainda uma dependência fácil de esquecer e cara de descobrir tarde: o arranjo pressupõe que o ambiente fique parado.

Uma cortina balançando. Uma televisão ligada. Um grupo de pessoas circulando pela sala. O aparelho reconhece elementos que se movem por conta própria e conclui que quem se moveu foi ele, e então corrige uma posição que estava certa, guiado por uma referência que trocou de lugar sem avisar. O sintoma é a imagem deslizando sozinha, e a causa está do lado de fora do equipamento, num lugar onde nenhuma linha de código do ambiente alcança e nenhuma consulta de capacidade jamais vai reportar.

O arranjo de fora para dentro compra uma propriedade que a outra família não tem. A medida não é incremental: cada leitura é feita contra a referência fixa, e não contra a leitura anterior, então os erros não se acumulam. Um sistema desses roda horas sem derivar, porque não há de que derivar. Em troca, ele é da sala, e não do aparelho.

Aspecto De dentro para fora De fora para dentro
Instalação nenhuma montagem e alinhamento prévios
Área útil onde houver textura e luz onde o equipamento fixo alcança
Acúmulo de erro incremental, sujeito a deriva absoluto, sem acúmulo
Falha típica ambiente liso, luz baixa, cena que se move linha de visão obstruída
Portabilidade total nenhuma
flowchart TD
    P{De onde parte<br/>a observacao?} -- Da sala fixa --> E[Infraestrutura externa]
    P -- Do proprio aparelho --> S[Sensores embarcados]
    E --> E1[Medida que nao deriva]
    E --> E2[Fronteira nitida:<br/>fora da area, nao existe]
    S --> S1[Depende da aparencia<br/>do ambiente]
    S --> S2[Sem fronteira:<br/>fora da area, funciona pior]
    E1 --> C[A pose chega igual<br/>nos dois casos]
    S1 --> C
    C --> D[A diferenca so aparece<br/>no dia da falha]
Figura 3: Os dois arranjos de rastreamento, o que cada um garante e o ponto em que a diferença entre eles aparece.

Duas linhas dessa tabela apontam para lados opostos, e isso não é coincidência. A ausência de acúmulo vem justamente de existir uma referência fixa, e uma referência fixa é o que impede o sistema de ser portátil. Sutherland fez essa troca em 1968, e ela continua de pé. Do lado do código, porém, os dois arranjos chegam idênticos. A pose vem com o mesmo formato e o mesmo significado. Saber qual deles está em uso só ajuda no dia em que ela deixa de vir.

1.3 O cartógrafo no escuro

Uma pessoa no escuro, com uma lanterna fraca, precisa desenhar a planta da casa enquanto anda por ela — e o visor faz literalmente isso.

Imagine alguém deixado numa casa desconhecida, sem luz, com uma lanterna fraca e a incumbência de desenhar a planta baixa. Essa pessoa não pode acender as lâmpadas, não recebeu mapa nenhum e não sabe por qual porta entrou. Precisa andar pelos cômodos e desenhar o que encontra, e o desenho é a única memória de onde ela já esteve.

É o problema exato que um aparelho de rastreamento autônomo enfrenta ao ser ligado numa sala nova. A formulação que resume o que ele faz vale para o resto deste livro. O aparelho desenha a planta da sala enquanto atravessa a sala, e corrige o desenho toda vez que reconhece um canto por onde já passou.

1.3.1 O círculo entre o mapa e a posição

Por que isso é difícil? A dificuldade não está em nenhuma das duas tarefas isoladas. Suponha que a pessoa da casa escura recebesse a planta pronta. Descobrir onde está seria comparar o que a lanterna ilumina com o desenho na mão. Se ela soubesse exatamente onde está a cada instante, desenhar a planta seria anotar o que vê a partir de cada posição conhecida.

O problema é que ela não tem nenhuma das duas. Para saber onde está, precisa do mapa. Para desenhar o mapa, precisa saber de onde está olhando. Cada uma das duas informações é pré-requisito da outra, e é isso que faz o problema ser interessante em vez de trivial.

A saída é estimar as duas juntas, aceitando que as duas fiquem aproximadas. A pessoa avança um passo, estima quanto avançou, anota o que vê a partir da posição estimada, e repete. O mapa cresce com erro embutido, e a posição carrega o mesmo erro, porque um foi calculado a partir do outro. É por isso que localização e mapeamento precisam da palavra simultâneos: as duas metades compõem um cálculo só.

O que sustenta a estimativa a cada passo é o deslocamento aparente do que a câmera vê. A quina de um móvel. A junção de duas paredes. A textura de um quadro. Cada um desses pontos muda de lugar na imagem conforme o aparelho se move. A geometria que liga esse deslocamento aparente ao movimento real é bem estabelecida. HARTLEY e ZISSERMAN a tratam em profundidade, assim como SZELISKI no contexto de visão computacional aplicada.

Só que todo passo estimado carrega erro, e os erros se somam. Depois de atravessar a casa inteira, a pessoa pode estar a metros de onde acredita estar, com uma planta deformada na mesma proporção. Esse acúmulo é a deriva mencionada na seção anterior, e sem correção ele torna o método inútil para sessões longas.

1.3.2 O canto reconhecido muda o passado

E quando o desenho deixa de bater com o que se vê? A correção vem de um evento específico. A pessoa entra num cômodo e reconhece uma quina que já tinha desenhado. Ela sabe onde essa quina está no desenho, e está vendo onde ela está agora em relação ao próprio corpo. Se as duas coisas discordam, ou o desenho está errado, ou a posição está errada — o que dá no mesmo, porque foram calculados juntos.

Aí vem a parte engenhosa. O sistema não corrige apenas a posição atual. Ele redistribui a discrepância por todo o percurso feito desde a última passagem por ali. O mapa inteiro se deforma para fazer as duas observações da mesma quina coincidirem. É como puxar uma corda frouxa pelas duas pontas: o ajuste acontece ao longo de todo o comprimento, não só onde a mão está.

flowchart LR
    A[Observar o ambiente<br/>no quadro atual] --> B[Reconhecer pontos<br/>distintos na imagem]
    B --> C[Estimar o movimento<br/>desde o quadro anterior]
    C --> D[Atualizar a posicao<br/>estimada de quem observa]
    D --> E[Acrescentar ao mapa<br/>os pontos ainda nao vistos]
    E --> F{Algum ponto ja<br/>estava no mapa?}
    F -- nao --> A
    F -- sim --> G[Redistribuir o erro<br/>acumulado no percurso]
    G --> A
Figura 4: O ciclo de estimativa e o ramo de correção que dispara quando um ponto já mapeado reaparece.

Duas consequências saem daí, e quem usa o aparelho percebe as duas. A qualidade do rastreamento melhora conforme a pessoa circula pelo mesmo espaço, porque cada reencontro é uma chance de correção. E a primeira travessia de um espaço novo é sempre a pior, já que não há nada a reconhecer ainda. Quem monta uma demonstração numa sala em que nunca entrou está escolhendo o pior momento possível.

A correção diz algo sobre a natureza do que o aparelho entrega, e esse algo contraria o que se espera de um sensor. A posição informada num instante qualquer não tem o estatuto de uma medida de régua. É a melhor estimativa disponível ali, sujeita a revisão retroativa quando informação nova chegar. O aparelho pode, literalmente, mudar de ideia sobre onde você esteve.

Isso atrapalha quem espera de um sensor o comportamento de um relógio. Duas leituras feitas no mesmo lugar físico, em momentos diferentes da sessão, podem devolver coordenadas diferentes. Nenhuma das duas está errada — a origem foi reposicionada entre elas. Um objeto virtual que guardasse apenas as coordenadas numéricas de onde foi deixado apareceria deslocado depois de uma correção grande.

É por isso que existe a âncora, e aqui ela deixa de ser item de catálogo. Ancorar um objeto não é anotar as coordenadas dele. É pedir ao aparelho que mantenha aquele objeto preso a um ponto do mapa. E que ele atualize as coordenadas sozinho toda vez que o mapa for corrigido. A âncora sobrevive à correção; a coordenada guardada à mão, não.

Há ainda uma assimetria temporal no mecanismo, e ela explica um comportamento que costuma ser atribuído a defeito. A correção redistribui o erro pelo percurso já feito, e portanto melhora a coerência do que foi mapeado. Ela não melhora a estimativa do próximo passo, que continua tão incerta quanto era. O rastreamento fica mais confiável sobre o espaço conhecido, e não fica mais preciso ao avançar para o desconhecido.

Nada disso é chamada de função sua. O mapa é construído pelo sistema do aparelho, num processo a que o ambiente não tem acesso. O que a sessão entrega são consequências do mapa, e são quatro. A pose de quem observa. O resultado de lançar um raio contra as superfícies reais reconhecidas. Os planos identificados no ambiente. E a âncora corrigida. Um aparelho que não concede nenhuma das quatro está declarando, sem dizer a palavra, que não tem mapa algum a oferecer.

Uma figura com prazo de validade. O cartógrafo no escuro descreve bem o que acontece quando o aparelho precisa descobrir o ambiente sozinho. Ela vai deixar de valer mais adiante neste livro, no ponto em que o registro passa a se apoiar num padrão impresso conhecido de antemão. Ali não existe planta a desenhar nem canto a reconhecer. Existe um gabarito que o sistema já sabia como era antes de a câmera ser ligada. Quando chegarmos lá, vou dizer que a figura acabou.

1.3.3 Três maneiras de o mapa se perder

Se todo o edifício se apoia em reconhecer pontos distintos no que a câmera vê, o que acontece quando não há pontos distintos a reconhecer? O cartógrafo com a lanterna apagada não desenha nada, e o aparelho diante da parede branca está exatamente nessa situação. As condições que derrubam o rastreamento saem do mecanismo, e são três.

A primeira é a superfície sem textura. Parede recém-pintada, cortina lisa, piso monocromático: nada ali constitui um ponto que possa ser reconhecido de novo no quadro seguinte. Uma região uniforme deslocada continua parecendo a mesma região uniforme. O sintoma ajuda no diagnóstico. Quem usa vira o rosto para a parede lisa e a posição começa a derivar. Ao girar de volta para o lado do móvel, ela salta para o lugar certo.

A segunda é a iluminação pobre, e ela pede uma distinção que costuma passar batido. O que atrapalha é a ausência de contraste, e escuridão é só uma das formas de produzi-la. Uma sala com pouca luz uniforme é pior para o rastreamento do que uma sala escura com uma janela clara ao fundo. A segunda ainda oferece uma fronteira nítida. O sensor precisa de diferença, não de brilho.

A terceira é o movimento brusco, e o mecanismo dela é óptico. Uma câmera integra luz durante um intervalo para formar cada quadro. Se o aparelho gira depressa nesse intervalo, o ponto distinto se espalha por vários lugares da imagem e deixa de aparecer nítido em um só. O canto some entre um quadro e o seguinte. Girar a cabeça rapidamente é, para o rastreamento, o mesmo que fechar os olhos por um instante.

Uma quarta condição não pertence à lista, e nomeá-la evita confusão. A cena que se move por conta própria não impede o reconhecimento: ela produz reconhecimentos falsos. Por isso o sintoma é outro — em vez de derivar por falta de informação, o aparelho se move por causa de informação errada.

Ausência de pose não é erro de programação. As três condições acontecem fora do aparelho e são invisíveis para o código: não há sensor de textura a consultar, nem medidor de luz da sala. O que existe é uma consequência única e observável. A cada quadro o ambiente pede a pose. Quando o aparelho perdeu a conta de onde está, ela simplesmente não vem. A primeira reação de quem encontra um quadro sem pose é procurar defeito no próprio código, e essa procura consome horas. Tratar a ausência como caso normal do laço de renderização é decisão de projeto. É o que separa um ambiente que degrada com elegância de um que trava.

Contar essas ausências transforma uma impressão em medida. “O rastreamento estava ruim” é uma frase que não se confronta com nada. “A pose faltou numa fração dos quadros observados, com uma lacuna máxima de tantos quadros seguidos” é uma frase que outra pessoa pode reproduzir e comparar entre aparelhos. Um caso precisa ficar fora da contagem. Quando alguém abre o menu do sistema do visor, a sessão continua viva e a pose deixa de vir. O ambiente saiu de primeiro plano.

Feita a contagem, aparece a tentação de interpretá-la, e é aqui que muitos relatórios se estragam. Qual das três condições causou a perda? Daqui não se sabe, e o motivo é estrutural: as três produzem exatamente o mesmo sintoma. Um relatório que afirma “rastreamento degradado por iluminação insuficiente” inventou o laudo junto com a medida. Alguém vai acender uma lâmpada para resolver um problema que era a parede lisa.

Prefiro o relatório que nomeia as três causas prováveis e declara que não as distingue. A razão é prática: ele manda a pessoa certa olhar para os três lugares certos. Daí sai um princípio de projeto que ultrapassa a sonda. Contagem e interpretação ficam em lugares separados no código, sempre. Quando as duas moram juntas, o instrumento passa a produzir a conclusão que quem o escreveu esperava. E ninguém percebe, porque o número e a conclusão saem da mesma linha.

1.4 Nenhuma plataforma diz quantos graus ela rastreia

A pergunta mais natural sobre um aparelho não tem resposta na interface de programação, e a ausência foi decidida em vez de esquecida.

Sabendo que graus de liberdade preveem desconforto, a pergunta se impõe sozinha. Quantos este aparelho acompanha? Onde se lê esse número? A resposta incomoda: em lugar nenhum. Não existe propriedade, consulta ou campo que informe isso nas plataformas de programação de ambientes imersivos em uso hoje.

A primeira reação é supor descuido de quem escreveu a especificação. Pois é, e não foi. O navegador que executa o ambiente não conversa com o sensor, não sabe que sensores existem dentro do aparelho e não tem como auditá-los. Ele conversa com o sistema do próprio aparelho, e o que esse sistema entrega é de outra natureza.

1.4.1 Um espaço de referência é uma promessa

Se o número não existe, o que existe no lugar dele? O que a plataforma entrega são espaços de referência, e pedir um deles é pedir uma promessa específica. Peço que as poses venham medidas a partir do chão do ambiente. Peço que venham medidas a partir de onde a pessoa estava quando a sessão começou. Peço que venham medidas a partir da própria cabeça de quem observa.

Complementar — espaço de referência

Um espaço de referência é um sistema de coordenadas com origem e orientação definidas, contra o qual as poses entregues pelo aparelho são expressas. A relação que importa liga o sistema interno de rastreamento ao sistema em que o ambiente trabalha. Conceder um espaço é afirmar que ela pode ser mantida coerente ao longo da sessão. É essa afirmação que o código consegue consultar — e não um número de sensores.

O menor exemplo concreto: pedir a origem no chão é pedir que a altura zero coincida com o piso físico onde a pessoa está. A definição não exige que essa origem seja precisa. Não exige que ela permaneça imóvel se o mapa for corrigido. E não diz nada sobre a frequência com que a pose é atualizada.

Aí mora a inferência que salva o problema. Um espaço ancorado no chão exige saber onde o chão está em relação a quem observa, e essa relação muda quando a pessoa anda. Um aparelho que apenas gira não sustenta essa promessa, porque não sabe que a pessoa andou. Conceder o espaço com chão é, portanto, evidência forte de que há posição rastreada.

flowchart TD
    A[Pedir espacos<br/>de referencia] --> B{Qual foi<br/>concedido?}
    B -- So o que acompanha<br/>quem observa --> C[Tres graus]
    B -- O ancorado<br/>no chao --> D[Seis graus]
    B -- So o intermediario --> E[Indeterminado]
    E --> F[Escrever nao da para saber daqui,<br/>em vez de apostar no mais provavel]
Figura 5: A inferência de graus de liberdade a partir dos espaços de referência concedidos, com o terceiro caso devolvendo indeterminado.

O caso do meio é genuinamente ambíguo. Origem próxima de quem observa no início da sessão é compatível com três graus e com seis, e nada mais na sessão desempata. A tentação é apostar no mais provável, já que hoje a maioria dos aparelhos que chega até aí acompanha seis. Não apostamos, e a razão vale mais que a regra. Um relatório que afirma seis graus com evidência fraca vai ser citado como se fosse medida. O engano aparece no dia da demonstração, num aparelho diferente.

Essa postura tem custo, e é honesto declará-lo. Um relatório com indeterminados parece menos competente que um relatório com números em todas as linhas. Quem o lê pela primeira vez pode concluir que a sondagem ficou pela metade. É um custo de aparência, pago em troca de não afirmar o que não se sabe.

1.4.2 O nome do aparelho não é fonte

Por que não ler simplesmente o nome do aparelho? O caminho existe e é usado em código de produção. O navegador expõe uma cadeia de identificação que descreve o programa, a versão e, às vezes, o modelo. Basta procurar nela o nome do visor e consultar uma tabela com as capacidades de cada modelo conhecido.

Funciona por um tempo, e depois falha de três maneiras conhecidas. A cadeia é editável por quem usa. Ela é deliberadamente imitada por aparelhos que querem receber o mesmo conteúdo que os concorrentes. E envelhece: modelo novo não está na tabela, e a tabela precisa de manutenção que ninguém faz.

Existe um problema mais fundo que os três, e ele não é de manutenção. O nome do modelo descreve o que o aparelho consegue fazer em condições de fábrica. O que interessa ao ambiente é o que ele está fazendo agora, nas mãos desta pessoa, nesta sala, com estas permissões concedidas. Um visor de seis graus com a câmera obstruída não entrega seis graus, e o nome dele continua o mesmo.

Há um argumento em favor da identidade que merece resposta honesta, porque ele não é tolo. Em alguns casos, saber o modelo permite contornar defeitos conhecidos daquele equipamento, e consulta de capacidade nenhuma revela defeito. É uso legítimo. O que não se sustenta é usar a identidade para responder a uma pergunta que a capacidade responde melhor. Graus de liberdade é exatamente uma dessas.

1.5 Silêncio não é a mesma coisa que não

Três respostas chegam pelo mesmo canal, e confundir duas delas produz um relatório alinhado, completo e falso.

Além dos espaços de referência, a plataforma expõe uma segunda família de perguntas. Um conjunto de capacidades opcionais pode ser solicitado ao abrir a sessão, e quatro delas interessam aqui. Lançar raio contra superfícies reais. Receber os planos reconhecidos. Prender objetos a âncoras corrigidas. Acompanhar a pose das mãos sem controle.

O ambiente pede o que quer usar, e o aparelho concede o que consegue. Parece simples, até se perguntar o que significa não receber alguma coisa. Um computador de mesa sem câmera não faz detecção de superfície real, e nenhuma configuração muda isso. Já um visor capaz pode ter tido a permissão recusada, ou a política do navegador bloqueou, ou o sistema desativou o recurso.

Do lado de quem escreve o código, os dois chegam idênticos. A plataforma não distingue, e a razão é defensável. Informar por que um recurso foi negado vazaria detalhes da configuração de quem usa para qualquer página visitada. A distinção precisa ser construída à mão. Sem ela, o relatório trata “não tem” e “não quis” como a mesma coisa. A diferença entre os dois diagnósticos é a diferença entre trocar de equipamento e clicar num botão.

flowchart TD
    A[Recurso pedido<br/>ao abrir a sessao] --> B{A sessao declarou<br/>a lista do que ligou?}
    B -- Nao ha lista --> I[Indeterminado:<br/>nao houve resposta]
    B -- Ha lista --> C{O nome consta<br/>dela?}
    C -- Sim --> G[Concedido]
    C -- Nao --> N[Negado:<br/>houve resposta, e foi nao]
    I --> X[Tratar como negado<br/>afirma o que o aparelho nunca disse]
Figura 6: Os três estados de um recurso opcional, decididos pela existência da lista de concessões e pela presença do nome nela.

Conclusivo — três estados, e o terceiro é o que evita o erro caro

Concedido: o nome consta da lista reportada pela sessão, e o recurso pode ser usado. Negado: a lista existe e o nome não está nela; houve resposta, e ela foi negativa. Indeterminado: não há lista a consultar, porque declará-la é opcional; não houve resposta. Sem o terceiro estado, um navegador competente que apenas não declara o que ligou aparece descrito como aparelho que negou tudo. Tabela preenchida, alinhada e falsa.

1.6 O diagnóstico que não chega a quem está de visor

Toda essa informação é inútil se ela aparecer num lugar que quem está com o aparelho no rosto não consegue abrir.

Suponha resolvido tudo o que veio até aqui. O ambiente consulta os espaços de referência e infere os graus com a devida prudência. Classifica cada recurso em três estados e conta os quadros sem pose. Resta uma pergunta que parece de acabamento e não é: onde esse resultado aparece?

A resposta natural de quem programa para a web é o console de depuração. Ela funciona perfeitamente, e funciona para uma pessoa só: quem está sentada diante do computador em que o código foi escrito. Veja só o que acontece com o resto do mundo. Quem está com um visor no rosto não abre painel de desenvolvedor, não vê aviso de rede e não fica sabendo que houve exceção.

O efeito se repete com quase todo mundo que constrói um ambiente pela primeira vez. Alguém escreve o programa, testa no computador, envia para o visor e toca o botão. Nada acontece. O botão parece quebrado, e a busca começa pelo tratador do evento, que está correto. Houve uma recusa, informada por um canal que ninguém dentro do visor consegue consultar.

1.6.1 Traduzir a recusa em frase

Quando um pedido de sessão é recusado, o que chega ao código é um erro cru. Um nome de classe e uma frase curta, em inglês, escrita para quem implementa navegadores. Exibir esse texto tal como veio é tecnicamente honesto e praticamente inútil, porque ele não separa as situações que quem está usando precisa separar.

São três as recusas que acontecem de verdade, e cada uma pede uma ação diferente. O aparelho pode não sustentar o modo pedido, e aí resta usar outro regime. O pedido pode ter partido sem gesto de quem usa, ou fora de conexão cifrada, e aí a correção é de quem escreveu. E pode já haver uma sessão aberta, resquício de uma tentativa anterior que não encerrou.

Traduzir essas três em frases que digam o que aconteceu e o que fazer não é enfeite de interface. É a diferença entre alguém concluir que o programa está com defeito e alguém entender que o aparelho respondeu não. A segunda conclusão é verdadeira e leva à ação certa; a primeira manda a pessoa depurar código que está correto.

Existe um erro mais sutil que os três, e ele passa por competência. O ambiente captura a exceção, impede que a página quebre e não mostra nada. De fora, tudo parece bem: página intacta, sem mensagem vermelha, sem travamento. De dentro do visor, o que se vê é um botão que não reage. Capturar a falha e silenciá-la é pior do que deixá-la aparecer.

1.6.2 O endereço que responde no lugar do aparelho

Como um relatório perfeitamente coerente pode estar inteiramente errado? A armadilha vem de uma exigência de segurança da plataforma, e ela é fácil de esquecer justamente porque não produz mensagem de erro. As capacidades imersivas só são expostas em conexão cifrada. Numa página servida sem cifragem, a interface de programação simplesmente não está lá.

Toda consulta responde, então, exatamente como responderia num aparelho sem suporte algum. O visor mais capaz do laboratório, aberto no endereço errado, produz um relatório idêntico ao de um computador antigo. Nada nesse relatório denuncia o engano: as linhas estão preenchidas, os estados são coerentes entre si, a conclusão é plausível. É a causa número um de diagnóstico errado, e ela custa uma tarde inteira de quem investiga um equipamento perfeito.

A defesa cabe numa linha, escrita antes de qualquer outra informação. Ela responde a uma pergunta que precede todas as demais: o que vem abaixo é informação sobre o aparelho, ou sobre o endereço? Uma linha, no topo, que evita a tarde perdida.

flowchart LR
    A[Pagina carrega] --> B[Primeiro tempo:<br/>contexto seguro, interface exposta,<br/>tipos de sessao declarados]
    B --> C[Botao grande<br/>espera o gesto]
    C --> D[Segundo tempo:<br/>sessao aberta]
    D --> E[Espacos concedidos,<br/>recursos ligados,<br/>fontes de entrada,<br/>quadros com e sem pose]
    E --> F[Encerrar sempre,<br/>inclusive se algo falhar]
    F --> G[Relatorio legivel<br/>no proprio aparelho]
Figura 7: Os dois tempos da sondagem, separados pelo gesto que o navegador exige antes de abrir a sessão.

Repare que a sondagem se parte em dois tempos, e a divisão vem de outra regra da plataforma. Entrar em sessão imersiva exige um gesto explícito de quem usa, e o navegador recusa o pedido que não venha de um. Há, portanto, o que se descobre ao carregar a página e o que só a sessão aberta responde. Duas restrições de apresentação decorrem disso, e as duas parecem gosto. O botão precisa ser grande, porque mirar de visor é apontar um raio a um metro do alvo. O texto também, porque a resolução angular do aparelho é bem menor que a de um monitor a meio metro dos olhos.

1.7 O ambiente que pergunta antes de desenhar

A primeira peça executável do percurso não desenha um triângulo sequer, e é ela que decide o que todas as outras poderão assumir.

Todo o vocabulário do capítulo cabe em três arquivos pequenos, e vale ler os três com o argumento na cabeça.

O primeiro traduz a inferência conservadora dos graus de liberdade. Ele parte do que a sessão concedeu, e não do que o navegador diz ser.

devices/graus.ts
// ---------------------------------------------------------------------------
// Graus de liberdade e classe do aparelho, inferidos do que foi concedido.
//
// A API XR não expõe um número de graus de liberdade. Não há propriedade a ler,
// e isso não é omissão: o navegador não sabe o que o sensor faz, sabe o que o
// runtime do aparelho aceitou entregar. O que existe para inferir é o conjunto de
// espaços de referência concedidos, e a inferência tem limites que este arquivo
// registra em vez de esconder.
//
// O porquê de a inferência ser conservadora: um relatório que afirma "seis graus
// de liberdade" com base em evidência fraca é pior que um relatório que diz "não
// dá para saber daqui". O primeiro será citado; o segundo, verificado.
// ---------------------------------------------------------------------------

/**
 * O que se pode afirmar sobre o rastreamento de posição a partir do que a sessão
 * concedeu — três graus (só orientação) contra seis (orientação e posição).
 */
export type GrausDeLiberdade = 'tres' | 'seis' | 'indeterminado';

/**
 * Classe do aparelho deduzida da capacidade declarada, e não do nome que o
 * navegador diz ter.
 *
 * A escolha é deliberada e é o conteúdo do módulo: a cadeia de identificação do
 * navegador é editável, imitada por outros aparelhos e envelhece a cada versão.
 * O que a sessão concede é o que o aparelho faz agora, na mão de quem está
 * usando.
 */
export type ClasseDeAparelho =
  | 'sem-api'
  | 'somente-janela'
  | 'visor-sem-posicao'
  | 'visor-com-posicao'
  | 'aparelho-de-mao-com-camera';

export interface LeituraDeEspacos {
  /** Espaços de referência que a sessão de fato entregou quando pedidos. */
  readonly concedidos: readonly string[];
  /** O modo de sessão em que a leitura foi feita. */
  readonly modo: 'immersive-vr' | 'immersive-ar';
  /** Havia alguma fonte de entrada com pose de punho declarada. */
  readonly comPoseDePunho: boolean;
}

/**
 * Regra da inferência, escrita por extenso porque é ela que o estudante precisa
 * poder contestar:
 *
 * - `local-floor` ou `bounded-floor` concedidos exigem que o aparelho saiba onde
 *   está o chão em relação a quem observa. Um visor que só gira não tem como
 *   sustentar isso, então a concessão é evidência forte de posição rastreada.
 * - `viewer` sozinho é o mínimo que qualquer sessão entrega: a origem acompanha
 *   quem observa, e translação alguma é observável a partir dela. Evidência forte
 *   da ausência.
 * - `local` no meio do caminho é ambíguo de verdade. A especificação o descreve
 *   como origem próxima de quem observa no início da sessão, e um aparelho de três
 *   graus pode concedê-lo mantendo a posição sempre na origem. Daí
 *   `indeterminado`, e não uma aposta.
 */
export function grausDeLiberdade(leitura: LeituraDeEspacos): GrausDeLiberdade {
  const temChao: boolean =
    leitura.concedidos.includes('local-floor') ||
    leitura.concedidos.includes('bounded-floor') ||
    leitura.concedidos.includes('unbounded');
  if (temChao) {
    return 'seis';
  }
  if (leitura.concedidos.length === 1 && leitura.concedidos[0] === 'viewer') {
    return 'tres';
  }
  return 'indeterminado';
}

/**
 * Classifica o aparelho pela combinação de modo de sessão, posição rastreada e
 * tipo de mira das fontes de entrada.
 *
 * `modosSuportados` vem da consulta feita sem sessão alguma — a mesma do módulo
 * anterior —, e por isso esta função responde mesmo quando nenhuma sessão chegou
 * a abrir.
 */
export function classificarAparelho(
  modosSuportados: readonly string[],
  graus: GrausDeLiberdade,
  temApiXr: boolean,
): ClasseDeAparelho {
  if (!temApiXr) {
    return 'sem-api';
  }
  const suportaVr: boolean = modosSuportados.includes('immersive-vr');
  const suportaAr: boolean = modosSuportados.includes('immersive-ar');

  if (!suportaVr && !suportaAr) {
    return 'somente-janela';
  }
  // Aparelho que faz realidade aumentada e não faz sessão imersiva completa é o
  // celular: a câmera vê o mundo, mas ninguém veste a tela no rosto.
  if (suportaAr && !suportaVr) {
    return 'aparelho-de-mao-com-camera';
  }
  return graus === 'tres' ? 'visor-sem-posicao' : 'visor-com-posicao';
}

/** Frase curta e legível para cada classe, usada no relatório. */
export function descreverClasse(classe: ClasseDeAparelho): string {
  switch (classe) {
    case 'sem-api':
      return 'Navegador sem a API XR, ou página fora de contexto seguro.';
    case 'somente-janela':
      return 'Aparelho que só sustenta o regime de janela — é o caso do desktop do laboratório.';
    case 'visor-sem-posicao':
      return 'Visor que acompanha a rotação da cabeça e não acompanha o deslocamento.';
    case 'visor-com-posicao':
      return 'Visor que acompanha rotação e deslocamento, com o chão do ambiente como referência.';
    case 'aparelho-de-mao-com-camera':
      return 'Aparelho de mão que compõe o virtual sobre a imagem da própria câmera.';
  }
}

Repare que o caso do meio devolve indeterminado. É a recusa da aposta, escrita como valor de retorno em vez de comentário.

O segundo arquivo constrói, à mão, a distinção que a plataforma não entrega. Ele classifica cada recurso pedido contra a lista que a sessão reportou — e trata a ausência da lista como terceira coisa.

devices/recursos.ts
// ---------------------------------------------------------------------------
// Catálogo dos recursos opcionais que a sonda consulta, e a classificação do
// que o aparelho respondeu sobre cada um.
//
// Este arquivo existe para separar duas coisas que o vocabulário corrente
// confunde: o recurso que o aparelho NÃO TEM e o recurso que ele TEM e NÃO
// CONCEDEU. A API XR trata os dois de um jeito só na hora de pedir — passam
// ambos por `optionalFeatures` e simplesmente não aparecem depois —, e é por
// isso que a distinção precisa ser feita aqui, à mão, em vez de esperada da
// plataforma.
//
// O terceiro estado é o que mais dá trabalho e o que mais evita erro:
// `indeterminado`. A sessão só reporta o que concedeu através de
// `enabledFeatures`, e essa propriedade é opcional na especificação — um
// navegador pode entrar em sessão sem dizer o que ligou. Sem o terceiro estado,
// esse navegador apareceria no relatório como aparelho que negou tudo.
// ---------------------------------------------------------------------------

/**
 * O que se sabe sobre um recurso depois de a sessão abrir.
 *
 * - `concedido`: o nome está em `enabledFeatures`, e o recurso pode ser usado.
 * - `negado`: a sessão reportou a lista e o nome não está nela — o aparelho ou o
 *   navegador recusou, e a razão não é exposta.
 * - `indeterminado`: a sessão não reportou lista alguma. Não é negativa; é
 *   ausência de resposta, e tratá-la como negativa produz relatório confiante e
 *   errado.
 */
export type EstadoDeRecurso = 'concedido' | 'negado' | 'indeterminado';

/** Um recurso opcional da API XR, com o motivo de ele estar no catálogo. */
export interface RecursoOpcional {
  /** O nome exato aceito por `optionalFeatures` — não traduzir. */
  readonly nome: string;
  /** O que ele habilita no percurso, em uma frase. */
  readonly paraQueServe: string;
}

/**
 * Os recursos que a Bancada consulta.
 *
 * A lista é curta de propósito: pedir tudo o que a especificação prevê faria a
 * sonda demorar mais e algumas plataformas recusarem a sessão inteira por causa
 * de um item exótico. Cada entrada aqui é um recurso de que algum módulo adiante
 * realmente depende.
 *
 * `depth-sensing` ficou de fora, e a razão é técnica: ele exige um dicionário de
 * configuração próprio no pedido de sessão (formato de dado e ordem de uso), e um
 * pedido malformado derruba a sessão inteira em vez de apenas negar o recurso.
 * Consultá-lo custaria, aqui, o risco de perder tudo o mais.
 */
export const RECURSOS_CONSULTADOS: readonly RecursoOpcional[] = [
  {
    nome: 'local-floor',
    paraQueServe:
      'origem no chão do espaço físico — é o que faz a bancada nascer na altura certa',
  },
  {
    nome: 'bounded-floor',
    paraQueServe:
      'origem no chão mais os limites da área livre que o aparelho conhece',
  },
  {
    nome: 'unbounded',
    paraQueServe: 'espaço sem fronteira declarada, para percursos longos',
  },
  {
    nome: 'hit-test',
    paraQueServe:
      'lançar um raio contra as superfícies reais que o aparelho encontrou',
  },
  {
    nome: 'anchors',
    paraQueServe:
      'prender um objeto virtual a um ponto do mapa e deixar o aparelho corrigi-lo',
  },
  {
    nome: 'plane-detection',
    paraQueServe: 'receber os planos que o aparelho reconheceu no ambiente',
  },
  {
    nome: 'hand-tracking',
    paraQueServe:
      'pose das mãos sem controle — fora do núcleo do percurso, e consultado só para registro',
  },
];

/**
 * Classifica um recurso contra a lista que a sessão reportou.
 *
 * `concedidos` vem de `XRSession.enabledFeatures`, que é opcional na
 * especificação: `undefined` significa "esta sessão não diz", e é o que produz
 * `indeterminado`.
 */
export function estadoDoRecurso(
  nome: string,
  concedidos: readonly string[] | undefined,
): EstadoDeRecurso {
  if (concedidos === undefined) {
    return 'indeterminado';
  }
  return concedidos.includes(nome) ? 'concedido' : 'negado';
}

Veja também o comentário sobre o recurso de leitura de profundidade, deixado fora do catálogo. Ele é o exemplo concreto do item cujo pedido malformado custaria a sondagem inteira.

O terceiro arquivo transforma a falha em medida. Ele conta quadros com pose e quadros sem, separa a sessão que saiu de foco, e deixa o julgamento numa função à parte.

devices/estabilidade.ts
// ---------------------------------------------------------------------------
// A falha como medida, e não como anedota.
//
// A pose de quem observa pode simplesmente não vir. A API prevê isso: o quadro
// entrega a pose ou entrega nada, e nada significa que o aparelho perdeu, naquele
// instante, a conta de onde está. É o único sintoma de degradação de rastreamento
// que se lê de dentro do código, sem instrumento e sem sensor extra.
//
// Este contador existe porque o módulo cobra prever a falha, e previsão que não
// se confronta com contagem alguma é opinião. Ele é deliberadamente burro: conta
// quadros com pose e quadros sem, e mais nada. A interpretação fica na função de
// diagnóstico, separada de propósito — misturar contagem e julgamento é o que
// produz medidor que sempre concorda com quem o escreveu.
// ---------------------------------------------------------------------------

export interface Estabilidade {
  readonly quadros: number;
  readonly quadrosSemPose: number;
  /** Maior sequência ininterrupta de quadros sem pose. */
  readonly maiorLacuna: number;
  /** Quadros descartados por a sessão não estar em primeiro plano. */
  readonly quadrosOcultos: number;
}

/**
 * Acumula a contagem quadro a quadro.
 *
 * O parâmetro `visivel` é o que separa perda de rastreamento de sessão que saiu
 * de foco. Quando quem usa abre o menu do sistema do visor, a sessão continua
 * viva, os quadros continuam chegando e a pose deixa de vir — e contar isso como
 * falha de sensor acusaria o aparelho de um defeito que é comportamento normal
 * do sistema operacional.
 */
export class ContadorDeEstabilidade {
  private quadros: number = 0;
  private quadrosSemPose: number = 0;
  private quadrosOcultos: number = 0;
  private maiorLacuna: number = 0;
  private lacunaCorrente: number = 0;

  public registrar(temPose: boolean, visivel: boolean): void {
    this.quadros += 1;

    if (!visivel) {
      this.quadrosOcultos += 1;
      this.lacunaCorrente = 0;
      return;
    }

    if (temPose) {
      this.lacunaCorrente = 0;
      return;
    }

    this.quadrosSemPose += 1;
    this.lacunaCorrente += 1;
    if (this.lacunaCorrente > this.maiorLacuna) {
      this.maiorLacuna = this.lacunaCorrente;
    }
  }

  public resultado(): Estabilidade {
    return {
      quadros: this.quadros,
      quadrosSemPose: this.quadrosSemPose,
      maiorLacuna: this.maiorLacuna,
      quadrosOcultos: this.quadrosOcultos,
    };
  }
}

/**
 * Traduz a contagem numa frase, com as condições que a bibliografia da disciplina
 * aponta como causas de degradação de rastreamento óptico: superfície sem textura
 * (não há canto a reconhecer), iluminação pobre (não há contraste para extrair
 * canto algum) e movimento brusco (a imagem borra e o canto some entre quadros).
 *
 * A frase não afirma qual das três aconteceu. Não dá para saber daqui, e escolher
 * uma seria inventar o diagnóstico junto com a medida.
 */
export function diagnosticar(estabilidade: Estabilidade): string {
  if (estabilidade.quadros === 0) {
    return 'Nenhum quadro foi entregue — a sessão não chegou a produzir imagem.';
  }
  if (estabilidade.quadrosSemPose === 0) {
    return 'A pose veio em todos os quadros observados. A janela de observação é curta, e ausência de falha aqui não é promessa de estabilidade em uso prolongado.';
  }
  const proporcao: number = Math.round(
    (estabilidade.quadrosSemPose / estabilidade.quadros) * 100,
  );
  return (
    `A pose faltou em ${proporcao}% dos quadros, com lacuna máxima de ` +
    `${estabilidade.maiorLacuna} quadros seguidos. As causas prováveis são superfície ` +
    'sem textura, iluminação pobre ou movimento brusco — e daqui não se distingue qual delas.'
  );
}

Note a frase que a função de diagnóstico devolve. Ela nomeia as três causas prováveis e declara, na mesma linha, que daqui não se distingue qual delas aconteceu.

É a diferença entre um medidor e um laudo.

1.8 Cinco caixas que sobrevivem ao próximo lançamento

A classificação que fecha o inventário não cita marca nem modelo, e por isso não precisa ser atualizada quando um aparelho novo chegar.

Quantas caixas bastam para descrever qualquer aparelho que chegue ao ambiente? Três perguntas geram cinco categorias, e nenhuma delas parte de fabricante, geração de produto ou faixa de preço. Que modos de sessão o aparelho declara sustentar. Quantos graus a concessão de espaços permite inferir, pela inferência de Seção 1.4. E se a interface de programação está sequer disponível.

Duas vezes três daria oito, e a assimetria é proposital. Algumas combinações não ocorrem na prática, e forçar uma taxonomia completa produziria caixas que nada habita. Categoria vazia é ruído, porque obriga quem lê a considerar um caso que nunca vai encontrar.

A primeira caixa é a do aparelho sem a interface disponível. Ou o navegador não a implementa, ou a página está fora de conexão cifrada. Do lado de dentro do código, as duas situações são indistinguíveis. A segunda é o aparelho que só sustenta o regime de janela, caso do computador de mesa e caso base de todo o percurso. A terceira e a quarta se separam pela inferência: o visor que gira e não anda, e o que acompanha o corpo pelo ambiente. A quinta sustenta o regime aumentado e não o imersivo completo, que é a descrição exata do celular.

1.8.1 Três coisas que esta classificação não promete

Toda taxonomia útil tem um recorte, e declará-lo é o que impede que ela seja usada onde não vale. Esta classifica o que o aparelho faz, e nada além disso. Ela não diz se ele é confortável, não diz se roda bem, não diz quantos quadros por segundo entrega nem se a imagem é nítida.

Dois aparelhos na mesma caixa podem ser radicalmente diferentes para quem os veste. Um deles pode ser pesado a ponto de se tornar insuportável em uso prolongado, e a classificação não tem linha para isso. Um deles pode entregar metade dos quadros do outro, e os dois continuam juntos. Nenhuma dessas grandezas está ao alcance da consulta, e fingir que estão seria o mesmo defeito que derruba a cadeia de identificação.

Há uma ausência mais sentida que as outras. A classificação não diz nada sobre a qualidade do rastreamento, só sobre a existência dele. Um visor que acompanha translação com precisão excelente e outro que a acompanha mal caem na mesma caixa. A concessão do espaço de referência é a mesma nos dois casos. A qualidade só aparece na contagem de quadros sem pose, que é medida separada e de outra natureza.

Isso não é limitação a corrigir numa versão futura. É consequência de a classificação se basear em promessas declaradas, e promessa declarada não vem com métrica de cumprimento, do mesmo jeito que um cardápio informa que há sobremesa sem dizer se ela vale a pena. O aparelho diz que sustenta; o quanto ele sustenta bem, só o uso responde.

flowchart TD
    A[Vocabulario que descreve<br/>capacidade sem citar marca] --> S[Saldo do capitulo]
    B[Consulta feita de verdade<br/>no lugar de suposicao] --> S
    C[Distincao entre negado<br/>e sem resposta] --> S
    D[Sintoma medivel<br/>de perda de rastreamento] --> S
    S --> P{O que continua<br/>sem resposta?}
    P --> P1[Escala e alcance de braco]
    P --> P2[Taxa de quadros sob carga]
    P --> P3[Conforto em uso prolongado]
    P1 --> N[Nada disso se responde<br/>sem desenhar nada:<br/>o proximo passo e a cena]
    P2 --> N
    P3 --> N
Figura 8: O saldo do inventário e as três perguntas que ficam em aberto.

O que fica pronto, então, é uma consulta honesta sobre o aparelho. O que falta é qualquer coisa a desenhar nele. Daqui em diante existe cena. Objetos com posição, orientação e escala, uma câmera que os observa, e um laço que produz imagem enquanto a sessão durar. Cada capacidade sondada aqui deixa de ser linha de relatório e vira decisão de projeto. O espaço concedido escolhe onde nasce a origem do mundo. Os graus inferidos escolhem que movimentos podem ser pedidos.

A pergunta que abre o próximo capítulo é a que este deixou sem resposta de propósito. Sabendo onde o observador está, como se organiza aquilo que ele vai ver? A resposta é uma estrutura de dados, e ela é mais velha que qualquer visor citado aqui.

Tarefa 1: Construir a sonda de capacidades

A primeira peça executável do ambiente não desenha coisa alguma. Ela pergunta ao aparelho o que ele oferece — que tipos de sessão suporta, que recursos opcionais concede, que fontes de entrada declara, quantos graus de liberdade rastreia — e guarda a resposta numa estrutura que o resto do ambiente possa consultar.

O que fica pronto é essa consulta feita de verdade contra o aparelho, com a distinção preservada entre recurso ausente e recurso negado. Assumir capacidade que o aparelho nunca declarou é a origem da maior parte das falhas silenciosas dos capítulos seguintes.

Tarefa 2: Tornar o relatório visível

Um relatório que só existe no console de depuração serve a quem escreveu o código e a mais ninguém. O resultado da sonda precisa aparecer em algum lugar que qualquer pessoa consiga ler no próprio aparelho.

A tarefa está cumprida quando o mesmo endereço, aberto em aparelhos de classes diferentes, produz relatórios diferentes e legíveis. É o instante em que a palavra dispositivo deixa de ser tópico narrado e passa a ser algo que se lê na tela.