<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title><![CDATA[Cinevva Blog]]></title>
        <link>https://app.cinevva.com</link>
        <description><![CDATA[Novidades do produto, notas de criadores e experimentos.]]></description>
        <lastBuildDate>Sun, 16 Aug 2026 04:34:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>scripts/build-rss.mjs</generator>
        <copyright>Copyright 2024-present Cinevva</copyright>
        <item>
            <title><![CDATA[Simulando um runtime que diz não: como isolamos o navegador para agir como o WeChat]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-07-19-sandboxing-wechat-strict-mode</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-07-19-sandboxing-wechat-strict-mode</guid>
            <pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Nosso criador de jogos com IA agora gera jogos portáveis para o WeChat, mas uma regra de prompt é apenas uma sugestão. Por isso, incorporamos um sandbox de modo estrito à prévia: um script que faz o navegador lançar erros onde o WeChat faria o mesmo, enviando as violações diretamente de volta ao ciclo de depuração da própria IA. Confira o design, a lista de bloqueios e as quatro exceções que mais nos ensinaram.]]></description>
            <content:encoded><![CDATA[<h1>Simulando um runtime que diz não: como isolamos o navegador para agir como o WeChat</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/wechat-strict-sandbox-hero.jpg" alt="Uma pequena cena de jogo 3D dentro de uma cúpula de vidro transparente, com ícones de ferramentas riscados flutuando do lado de fora" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Lançamos recentemente um <a href="/pt-BR/news/2026-07-19-wechat-mini-game-mode">modo de minijogo do WeChat</a> em nosso Criador de Jogos: um perfil de build que mantém os jogos gerados por IA dentro do subconjunto da plataforma web capaz de sobreviver à portabilidade para o runtime do WeChat. O perfil consiste em um conjunto de regras de geração. Nada de interface em DOM, Pointer Lock, áudio que não seja MP3 ou código dinâmico. O modelo segue essas regras da mesma forma que modelos seguem regras, ou seja: geralmente.</p>
<p>“Geralmente” não basta quando toda violação é invisível no navegador e fatal após a exportação. Um jogo com uma barra de vida em HTML funciona perfeitamente em nossa prévia e produz uma tela em branco no WeChat, pois os minijogos do WeChat não têm DOM. Por isso, criamos algo que transforma essas regras de sugestões em leis da física: um sandbox de modo estrito que faz o próprio navegador se recusar a fazer o que o WeChat não consegue. Este artigo explica como ele funciona.</p>
<h2>A decisão central: simular a ausência, não a presença</h2>
<p>A abordagem óbvia seria emular o WeChat: implementar <code>wx.createCanvas</code>, <code>wx.onTouchStart</code>, <code>wx.setStorageSync</code> e executar os jogos sobre a superfície <code>wx.*</code> emulada. Seguimos o caminho oposto, e o motivo está no formato do pipeline de exportação.</p>
<p>Os jogos no modo WeChat ainda são escritos usando APIs do navegador. No empacotamento, um adaptador (seguindo a abordagem padrão da comunidade, a weapp-adapter) mapeia essas APIs do navegador para <code>wx.*</code>. Isso significa que nossos jogos nunca chamam <code>wx.*</code> diretamente, portanto um emulador de <code>wx</code> testaria caminhos de código que não existem. O que realmente impede uma portabilidade não é a ausência de <code>wx.*</code> no navegador. É a <em>presença</em> de APIs do navegador sem equivalente no WeChat, usadas silenciosamente durante a geração. <code>document.createElement('div')</code>. <code>requestPointerLock()</code>. IndexedDB. Um arquivo <code>.ogg</code> que é decodificado sem problemas no Chrome, mas nunca funcionará no WeChat para iOS.</p>
<p>Portanto, o sandbox simula a ausência. Tudo o que o WeChat não tem é removido, bloqueado ou sinalizado na prévia, e o jogo é desenvolvido desde o primeiro frame dentro da interseção entre as duas plataformas.</p>
<h2>Onde ele fica: em um service worker que já existia</h2>
<p>A prévia do nosso editor tem um caminho de entrega incomum que tornou isso quase gratuito. Os jogos no editor não são servidos pela rede. Um service worker intercepta solicitações para <code>/game/*</code> e entrega arquivos do IndexedDB, permitindo que o iframe da prévia seja recarregado instantaneamente sem uma ida e volta ao servidor. Esse worker já injeta dois arquivos virtuais em cada página de jogo: um script de captura de erros que encaminha a saída do console e as falhas ao editor, e um inspetor de cena em tempo real.</p>
<p>O sandbox é o terceiro arquivo virtual, <code>_wechat-strict.js</code>, injetado no <code>&lt;head&gt;</code> da página imediatamente após o script de captura e antes da execução de qualquer código do jogo. A ordem é essencial por dois motivos. Ele precisa ser executado depois da captura para que suas chamadas a <code>console.error</code> já sejam interceptadas e encaminhadas ao editor, e antes dos módulos do jogo para que os bloqueios estejam ativos quando a primeira linha de código do jogo for executada.</p>
<p>A ativação usa um truque de uma linha. A opção WeChat no criador é persistida em <code>localStorage</code>, e o iframe da prévia tem a mesma origem que o editor, portanto o harness simplesmente lê o sinalizador diretamente:</p>
<pre><code class="language-js">try {
  if (localStorage.getItem('cinevva-gc-profile') !== 'wechat-minigame') return;
} catch (e) { return; }
</code></pre>
<p>Para qualquer jogo que não seja do WeChat, o script faz uma única comparação de strings e retorna imediatamente. Sem sinalizador de build, sem ida e volta ao servidor e sem estado para sincronizar com o service worker.</p>
<h2>A lista de bloqueios e a taxonomia de dois níveis</h2>
<p>Nem toda violação merece a mesma resposta, portanto o harness diferencia dois níveis. APIs que <em>não existem</em> no WeChat lançam erros, porque é isso que acontece após a exportação, e uma falha durante a geração é a prévia honesta de uma falha na análise. Recursos que <em>existem, mas falham mais tarde</em> são sinalizados com erros explícitos no console sem alterar o comportamento, pois bloqueá-los ocultaria o estado real do jogo da pessoa que está trabalhando nele.</p>
<p>O nível que lança erros: criação de qualquer elemento de interface HTML, tanto por <code>createElement</code> quanto por <code>createElementNS</code> (o Three.js cria seu canvas pela variante NS, então os dois caminhos precisam da mesma barreira), <code>eval</code>, <code>new Function</code>, <code>requestPointerLock</code> e registro de service workers. Cada erro lançado inclui a correção em sua mensagem:</p>
<pre><code class="language-js">throw violate(&quot;document.createElement('&quot; + tag + &quot;') — WeChat mini games have no DOM. &quot; +
  &quot;Draw UI on canvas (offscreen 2D canvas -&gt; THREE.CanvasTexture on a screen-space quad) &quot; +
  &quot;and hit-test taps yourself.&quot;);
</code></pre>
<p>O nível de sinalização: leitura de <code>indexedDB</code> (retorna <code>undefined</code>, exatamente como o WeChat, além de um erro indicando o uso de <code>localStorage</code>), áudio em qualquer formato que o WeChat para iOS não consiga decodificar (verificado em três pontos: no construtor <code>Audio</code>, no setter <code>src</code> do protótipo do elemento de mídia e nas URLs buscadas), solicitações de rede para qualquer origem que não seja a do próprio jogo ou nossa CDN de assets (no WeChat, elas exigem um domínio incluído em uma lista de permissões e registrado no ICP) e qualquer ocorrência do Tone.js.</p>
<p>Também há uma auditoria pós-carregamento executada pouco depois de a página se estabilizar: ela percorre o <code>&lt;body&gt;</code> e relata qualquer elemento HTML vindo da marcação do próprio jogo, em vez de uma criação dinâmica. HUDs estáticos passam despercebidos por uma barreira em <code>createElement</code>, então a barreira sozinha não é suficiente.</p>
<p>Cada relatório elimina duplicatas por mensagem e tem um limite de cinquenta ocorrências por sessão. Um bug de loop invertido que cria uma div por frame produz um erro, não uma enxurrada que afoga o sinal relevante.</p>
<h2>As quatro exceções que mais nos ensinaram</h2>
<p>Criar um sandbox consiste, em grande parte, em decidir o que <em>não</em> bloquear, e cada exceção que abrimos surgiu quando nossas próprias ferramentas pararam de funcionar durante o desenvolvimento.</p>
<p><strong>Nosso depurador usa eval.</strong> A ferramenta <code>execute_js</code> do criador, usada pela IA para inspecionar o jogo em execução, avalia código pelo manipulador de mensagens do script de captura, que chama <code>eval</code>. Bloquear <code>eval</code> de forma ingênua teria cegado os próprios olhos da IA. A solução: antes de aplicar o bloqueio, o harness armazena a função real em uma propriedade não enumerável, e o manipulador do script de captura recorre a ela:</p>
<pre><code class="language-js">Object.defineProperty(window, '__cinevvaRealEval', { value: window.eval, enumerable: false });
window.eval = function () { throw violate('eval() is banned in WeChat mini games.'); };
// capture script, at message time:
returnValue = (window.__cinevvaRealEval || eval)(e.data.code);
</code></pre>
<p>O código do jogo que tentar usar <code>eval</code> ainda falha. O depurador não.</p>
<p><strong>O gravador também cria elementos.</strong> Nosso gravador de reels é injetado no iframe do jogo como um <code>&lt;script&gt;</code> criado com o próprio <code>document.createElement</code> do iframe, e baixa os vídeos concluídos por meio de um clique sintético em <code>&lt;a&gt;</code>. Bloquear essas tags teria impedido a gravação exclusivamente no modo WeChat. Por isso, <code>script</code> continua permitido e <code>a</code> gera um aviso sem lançar erro. Um jogo que inclua um link de verdade ainda é sinalizado, e as ferramentas continuam funcionando.</p>
<p><strong>Os eventos de teclado permanecem.</strong> A IA verifica os controles com uma ferramenta que sintetiza pressionamentos de teclas e depois lê como o estado do jogo mudou. Suprimir a entrada de teclado para simular um celular teria destruído o ciclo de verificação de controles que detecta bugs de câmera invertida. A prioridade para toque é aplicada pelas regras de geração e pelo esquema de controles do perfil, não pelo sandbox.</p>
<p><strong>O WebGL2 permanece, e o motivo merece uma seção própria abaixo.</strong> A versão inicial da lista de bloqueios impedia <code>getContext('webgl2')</code> para forçar a renderização “compatível com WebGL1” exigida pelo perfil. Só que o Three.js <a href="https://github.com/mrdoob/three.js/releases/tag/r163">removeu completamente o suporte ao WebGL1 na r163</a>, e fixamos a versão r181: bloquear o WebGL2 deixaria todos os jogos 3D desse modo em branco. A regra de renderização do perfil era incoerente, escrita a partir de uma visão desatualizada da plataforma. A auditoria do sandbox acabou auditando a própria especificação do sandbox, e investigar esse ponto nos levou a uma conclusão importante.</p>
<h2>A questão do WebGL2, respondida corretamente</h2>
<p>Manter a criação de contexto intacta levantou a pergunta óbvia: se nosso motor exige WebGL2, os jogos deixam de funcionar em dispositivos com WeChat que só têm WebGL1? A investigação nos deu a visão mais clara que temos sobre os requisitos mínimos de renderização da plataforma, então aqui está ela, com fontes.</p>
<p>No Android, o runtime de minijogos do WeChat oferece WebGL2 em qualquer biblioteca-base moderna, e o hardware GLES3 subjacente é praticamente universal. A verdadeira questão é o iOS. A própria documentação de engenharia do WeChat sobre <a href="https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/WebGL2.html">suporte ao WebGL2</a> e o <a href="https://developers.weixin.qq.com/minigame/dev/guide/performance/perf-high-performance-plus.html">modo high-performance+</a> deixa claro que, no iPhone, o WebGL2 só está disponível corretamente no runtime high-performance+, que exige uma versão recente do cliente WeChat (8.0.45 ou superior, ainda mais recente no iOS 14) e, na prática, iOS 15.5 ou posterior. Segundo a própria descrição da Tencent, o modo high-performance comum executa WebGL2 “com mais problemas”, e o runtime normal do iOS não oferece suporte algum.</p>
<p>A parte realmente perigosa é o formato da falha. Em ambientes sem suporte, <code>getContext('webgl2')</code> pode retornar um contexto válido em aparência, mas quebrado, em vez de null, um comportamento que desenvolvedores <a href="https://developers.weixin.qq.com/community/develop/doc/000aee72868a381d6c261686651c00">pediram diretamente à Tencent que corrigisse</a>. Um jogo que confia no valor retornado não falha de forma clara nesse ambiente. Ele renderiza artefatos ou exibe silenciosamente uma tela preta.</p>
<p>Por isso, lidamos com a questão em três camadas. O sandbox não interfere na criação do contexto WebGL2, pois bloqueá-la entraria em conflito com nosso próprio motor. Agora, o perfil de geração exige uma barreira de inicialização em todos os jogos: a criação do renderer envolvida em try/catch, seguida por uma verificação funcional (<code>typeof gl.createVertexArray === 'function'</code>, uma capacidade exclusiva do WebGL2 que um contexto mentiroso não terá), exibindo em caso de falha uma mensagem amigável desenhada no canvas, como “atualize o WeChat”, em vez de uma tela em branco. Esse é o mesmo padrão usado por minijogos convertidos a partir do Unity, motivo pelo qual vemos solicitações de atualização em jogos publicados, em vez de telas pretas. E o exportador, quando for lançado, fixará o modo high-performance+ no <code>game.json</code> para que o caminho compatível seja o padrão.</p>
<p>Qual é o custo de público de exigir WebGL2? Usuários em versões anteriores ao iOS 15.5 ou em clientes WeChat anteriores à versão 8.0.45 — poucos pontos percentuais em 2026, mas concentrados em dispositivos mais antigos, o que importa mais para alguns gêneros do que para outros. Se dados reais de distribuição algum dia mostrarem que vale a pena buscar essa parcela, temos uma alternativa barata de reserva: fixar o perfil do WeChat no Three r162, a última versão compatível com WebGL1. Os jogos do perfil usam apenas APIs centrais estáveis, portanto o downgrade consiste em uma alteração de uma linha no import map, deliberadamente reservada até que os dados a justifiquem.</p>
<h2>Fechando o ciclo com o modelo</h2>
<p>Esta é a parte que faz tudo isso valer a pena para um produto centrado em IA. Depois de cada build, o agente do criador chama uma ferramenta que lê o console do jogo e aguarda a conclusão da inicialização. O script de captura encaminha <code>console.error</code> para esse fluxo. Assim, uma violação <code>[wechat-strict]</code> não é um aviso que uma pessoa pode ignorar ao rolar a tela. Ela chega exatamente ao canal que o modelo já verifica antes de declarar que um build está concluído, na mesma interação e com a correção descrita na mensagem.</p>
<p>O perfil de geração fecha a última lacuna com uma instrução: trate toda mensagem <code>[wechat-strict]</code> como um bug que impede o build, corrija a causa e nunca tente detectar ou contornar o harness. Um jogo que só funciona com o sandbox desativado é um jogo que falhará após a exportação, e isso é explicado claramente ao modelo.</p>
<p>Na prática, o sandbox transforma um problema impreciso de conformidade (“o modelo seguiu todas as quatorze regras?”) no ciclo de depuração em que o sistema já é bom (“o console mostra um erro; corrija-o”).</p>
<h2>O que um navegador não consegue simular</h2>
<p>A seção da honestidade. Este sandbox detecta violações da superfície de APIs, que, segundo nossa estimativa, representam a grande maioria dos problemas que inviabilizam uma portabilidade. Ele não consegue detectar: desempenho em um celular real, execução de JavaScript sem JIT no iOS, peculiaridades da implementação de WebGL do WeChat ou diferenças de comportamento de codecs além das extensões de arquivo. Para isso, é necessário usar o runtime real.</p>
<p>Portanto, o sandbox é o primeiro de três níveis. O segundo, quando nosso exportador passar a gerar projetos para o WeChat DevTools, será o simulador oficial, controlado de forma headless pela CLI do DevTools e pelo miniprogram-automator: inicializar o pacote exportado, confirmar que o primeiro frame é renderizado e simular um toque. O terceiro é o caminho oficial em dispositivos, com códigos QR de prévia e depuração remota em celulares físicos, onde vivem as verdades que nenhum outro ambiente revela. Cada nível é mais lento e mais fiel que o anterior, e a função de cada um é tornar raras as idas ao próximo.</p>
<p>Se quiser experimentar o modo, procure a opção “Modo de minijogo do WeChat” no <a href="/pt-BR/create">Criador de Jogos</a>. Para conhecer o contexto comercial e regulatório de tudo isso, consulte <a href="/pt-BR/blog/2026-07-19-wechat-mini-games-guide-for-developers-outside-china">nosso guia de campo para alcançar o meio bilhão de jogadores do WeChat</a>.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developers.weixin.qq.com/minigame/dev/guide/runtime/js-support.html">Documentação oficial do WeChat: suporte a JavaScript em minijogos</a></li>
<li><a href="https://developers.weixin.qq.com/minigame/en/dev/guide/best-practice/adapter.html">Documentação oficial do WeChat: a camada de adaptação</a></li>
<li><a href="https://developers.weixin.qq.com/minigame/dev/guide/performance/perf-high-performance-plus.html">Documentação oficial do WeChat: modo high-performance+</a></li>
<li><a href="https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/WebGL2.html">Documentação de engenharia do WeChat: suporte à renderização WebGL2 em minijogos</a></li>
<li><a href="https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/iOSOptimization.md">Documentação de engenharia do WeChat: modos high-performance e high-performance+ no iOS</a></li>
<li><a href="https://developers.weixin.qq.com/community/develop/doc/000aee72868a381d6c261686651c00">Comunidade de desenvolvedores do WeChat: contextos WebGL2 com falha devem retornar null</a></li>
<li><a href="https://developers.weixin.qq.com/miniprogram/en/dev/devtools/auto/">Documentação oficial do WeChat: miniprogram-automator</a></li>
<li><a href="https://github.com/mrdoob/three.js/releases/tag/r163">Notas de lançamento do Three.js r163 (suporte ao WebGL1 removido)</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Minijogos do WeChat: o maior mercado de jogos instantâneos no qual você não pode simplesmente publicar]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-07-19-wechat-mini-games-guide-for-developers-outside-china</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-07-19-wechat-mini-games-guide-for-developers-outside-china</guid>
            <pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[500 milhões de jogadores mensais, um mercado de US$ 5,6 bilhões que dobrou em um ano e uma plataforma que seu jogo web não consegue alcançar por meio de uma URL. O que os minijogos do WeChat realmente são, as regulamentações que você enfrentará como desenvolvedor de fora da China, a única forma de monetização que não exige licença e como o novo modo de minijogos da Cinevva se encaixa nisso.]]></description>
            <content:encoded><![CDATA[<h1>Minijogos do WeChat: o maior mercado de jogos instantâneos no qual você não pode simplesmente publicar</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/wechat-mini-games-market-hero.jpg" alt="Um smartphone exibindo um minijogo diante de um portão com um selo de aprovação, ao lado de um documento carimbado e moedas" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Se você cria jogos web, existe um mercado de meio bilhão de jogadores mensais que seus jogos não conseguem alcançar, por melhor que seja o suporte a dispositivos móveis. Ele fica dentro do WeChat, quase dobrou sua receita no ano passado e, para entrar nele, é necessário tanto fazer uma adaptação técnica quanto passar por um processo regulatório que a maioria dos desenvolvedores ocidentais nunca ouviu mencionar.</p>
<p>Acabamos de lançar a primeira parte da nossa resposta a isso: um <a href="/pt-BR/news/2026-07-19-wechat-mini-game-mode">modo experimental para minijogos do WeChat</a> no Cinevva Game Creator. Este post conta a história completa: o que a plataforma realmente é, quais são as regras e como seria um caminho realista para um desenvolvedor de fora da China.</p>
<h2>O mercado, em números que são fáceis de subestimar</h2>
<p>O WeChat é o aplicativo de comunicação padrão da China, com uma base combinada de mais de 1,4 bilhão de usuários ativos mensais. Os minijogos ficam dentro dele como experiências que começam com um toque: não exigem instalação nem loja de aplicativos e podem ser iniciados por uma mensagem de chat, uma busca ou um compartilhamento. Em 2025, a Tencent anunciou que a plataforma de minijogos havia ultrapassado 500 milhões de usuários ativos mensais, com mais de um bilhão de jogadores registrados e cerca de 500 mil desenvolvedores.</p>
<p>O dinheiro também é real. O mercado chinês de minijogos gerou 39,8 bilhões de yuans em 2024, cerca de US$ 5,6 bilhões, um crescimento anual de 99%. Aproximadamente 69% desse valor veio de compras dentro dos jogos e 31%, de publicidade. Tenha essa divisão em mente, porque a participação dos anúncios acaba sendo muito importante para desenvolvedores estrangeiros.</p>
<p>Para dar uma noção da escala: a receita de minijogos naquele único ano é comparável à de todo o mercado global de jogos web no qual se apoiam plataformas como Poki, CrazyGames e <a href="http://itch.io">itch.io</a>. Não é um nicho. É um universo paralelo com regras próprias.</p>
<h2>Por que seu jogo web não pode ser simplesmente adaptado</h2>
<p>Um minijogo do WeChat parece um jogo web e é escrito em JavaScript, então a suposição natural é que a adaptação levaria um fim de semana. Não leva, e os motivos são estruturais.</p>
<p>Os minijogos não são executados em um navegador. Eles rodam no ambiente de execução próprio do WeChat, que oferece um canvas, WebGL e um conjunto de APIs <code>wx.*</code>. Não há DOM nem CSS. Cada botão, barra de vida, menu e caixa de diálogo do seu jogo precisa ser desenhado no canvas e ter a detecção de toques feita pelo seu próprio código. Se a interface do seu jogo for feita em HTML, ela simplesmente não existe no WeChat.</p>
<p>O modelo de entrada é diferente. Não há Pointer Lock nem mouse, então uma câmera de FPS no estilo desktop precisa ser controlada ao arrastar o dedo. Não há teclado no dispositivo principal. Se seu jogo não puder ser jogado integralmente apenas por toque, ele não poderá ser jogado.</p>
<p>Os pacotes são limitados a 4 MB no pacote principal e a 20 MB no total com o uso de subpacotes, e tudo o que seu jogo executa precisa estar dentro desse pacote revisado. Isso nos leva à regra que molda toda a plataforma: o WeChat proíbe código executado dinamicamente. Nada de <code>eval</code>, <code>new Function</code> ou download de JavaScript durante a execução, e a Tencent rejeita ativamente aplicativos que incorporam interpretadores de JS para contornar essa restrição. Cada jogo é um artefato fechado e revisado.</p>
<p>Essa última regra é o motivo pelo qual nunca haverá um “aplicativo-portal” que transmita jogos web para dentro do WeChat, seja nosso ou de qualquer outra empresa. Cada jogo passa pela análise como um minijogo independente. Qualquer ferramenta que queira ajudar você nesse processo precisa permitir a produção de pacotes limpos, autossuficientes e baseados exclusivamente em canvas. Foi em torno dessa restrição de design que criamos nosso novo modo.</p>
<h2>A realidade regulatória</h2>
<p>Agora vem a parte que não tem nada a ver com código. A China regulamenta jogos como mídia publicada, e os requisitos se acumulam em várias camadas. Nenhum deles é secreto, mas quase ninguém fora da China os explica de forma clara. Então, aqui estão eles.</p>
<p><strong>Registro ICP.</strong> Desde o fim de 2023, todo miniprograma e minijogo precisa concluir um registro ICP (um cadastro junto às autoridades chinesas, realizado por meio da plataforma WeChat). O processo normalmente leva de uma a três semanas.</p>
<p><strong>Comprovação de direitos autorais para cada título.</strong> Todo jogo precisa de um registro de direitos autorais de software ou da certificação eletrônica de direitos autorais, que é mais rápida. A via eletrônica leva aproximadamente de dez a quinze dias por jogo. Esse é um custo por título, o que é relevante se você pretende lançar muitos jogos pequenos.</p>
<p><strong>O banhao, se você cobrar dinheiro.</strong> Qualquer jogo com compras internas precisa de uma licença de publicação de jogos emitida pela Administração Nacional de Imprensa e Publicações, conhecida universalmente como banhao. Dois fatos definem o que isso significa para você como desenvolvedor estrangeiro. Primeiro, somente uma empresa chinesa pode solicitá-la. Segundo, títulos importados passam por um processo de aprovação separado e mais lento, que historicamente leva de seis a oito meses, quando há algum andamento. Na prática, um desenvolvedor de fora da China só consegue publicar um jogo monetizado por meio de uma editora nacional licenciada, que envia o jogo, detém a licença e cuida da operação.</p>
<p><strong>A exceção para jogos gratuitos com anúncios.</strong> Um jogo gratuito, sem nenhum tipo de pagamento, não precisa de banhao. Ele ainda pode gerar receita por meio dos componentes de publicidade do próprio WeChat — e lembre-se de que os anúncios representaram 31% da receita dos minijogos em 2024, mais de US$ 1,7 bilhão. Esse é o único caminho que um pequeno desenvolvedor pode seguir sem um parceiro empresarial chinês, e é assim que boa parte da cauda longa do mercado realmente opera.</p>
<p>O resumo honesto: lançar um minijogo gratuito sustentado por anúncios envolve uma burocracia medida em semanas. Lançar um jogo monetizado é um projeto de desenvolvimento de negócios medido em meses, com um parceiro que você precisará escolher com cuidado.</p>
<h2>O que o modo de minijogos da Cinevva faz a respeito disso</h2>
<p>Não podemos cuidar da sua documentação. O que podemos fazer é garantir que o jogo em si nunca seja o impedimento, desde o primeiro prompt.</p>
<p>Com o modo de minijogos do WeChat ativado, o Game Creator cria o jogo dentro do subconjunto portátil: interface desenhada no canvas em vez de HTML; controles projetados primeiro para toque, com a câmera controlada ao arrastar em vez de Pointer Lock; renderização ajustada ao desempenho de um celular intermediário, com uma tela alternativa adequada para dispositivos sem suporte real a WebGL2; um orçamento rígido de assets, com áudio apenas em MP3; sem imports dinâmicos nem carregamento de código durante a execução; e salvamentos mapeados para a API de armazenamento do WeChat. O jogo continua rodando no navegador como qualquer jogo da Cinevva, então você pode criá-lo, testá-lo e compartilhá-lo normalmente. Ele apenas permanece dentro do subconjunto que resiste à adaptação.</p>
<p>O exportador que gera um projeto do WeChat DevTools pronto para abrir a partir de um desses jogos está em desenvolvimento. Escolhemos essa ordem de propósito. Um exportador pode empacotar seu código, mas não pode desfazer uma HUD em HTML nem reformular controles que funcionam apenas com mouse. A portabilidade é uma característica da forma como o jogo é criado, por isso o modo veio primeiro.</p>
<p>Se você já leu nosso <a href="/pt-BR/guides/wechat-mini-game-engines">guia de engines para minijogos</a> ou o <a href="/pt-BR/guides/publish-wechat-douyin-mini-games">passo a passo para publicar minijogos no WeChat e no Douyin</a>, este é o mesmo raciocínio aplicado à geração: a adaptação mais barata é aquela que você nunca precisa fazer.</p>
<h2>Um plano realista para um desenvolvedor de fora da China</h2>
<p>Aqui está o que realmente faríamos com um primeiro título. Crie algo pequeno e com sessões curtas, porque é isso que a plataforma recompensa: os sucessos são jogos que fazem o jogador querer tentar mais uma vez, não épicos. Desenvolva primeiro para o modo retrato e exclusivamente para toque. Lance o jogo gratuitamente com anúncios, o que mantém você fora do território do banhao enquanto descobre se os jogadores chineses realmente querem seu jogo. Cuide você mesmo do registro ICP e da certificação eletrônica de direitos autorais; são processos tediosos, mas administráveis. E somente quando o jogo já tiver demonstrado boa retenção e você estiver diante de um potencial real de compras internas, comece a conversar com editoras tendo números em mãos, porque uma negociação com uma editora é muito diferente quando seu jogo já tem público.</p>
<p>Essa é a estratégia que o novo modo foi criado para atender: tornar quase gratuito o processo de produzir jogos candidatos, para que você possa descobrir o que funciona antes de começar a gastar com as exigências regulatórias.</p>
<p>Se você planeja entrar nesse mercado, gostaríamos muito de ouvir você enquanto o modo ainda está tomando forma. <a href="/pt-BR/contact">Entre em contato</a> ou comece a criar no <a href="/pt-BR/create">Game Creator</a>.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.pocketgamer.biz/wechat-mini-games-crosses-500m-monthly-active-users-as-tencent-shifts-focus-to-retention/">PocketGamer.biz: minijogos do WeChat ultrapassam 500 milhões de usuários ativos mensais</a></li>
<li><a href="https://www.ichongqing.info/2025/07/02/wechat-mini-games-hits-1b-users-becomes-global-magnet-for-game-developers/">iChongqing: minijogos do WeChat atingem 1 bilhão de usuários</a></li>
<li><a href="https://www.oreateai.com/blog/2025-wechat-mini-game-ecosystem-development-report-indepth-analysis-of-market-trends-and-typical-cases/7f0b62beb192f52409ae047d0bbae184">Oreate: relatório de desenvolvimento do ecossistema de minijogos do WeChat em 2025 — dados de mercado de 2024</a></li>
<li><a href="https://developers.weixin.qq.com/minigame/dev/guide/runtime/js-support.html">Documentação oficial do WeChat: suporte a JavaScript e proibição de código dinâmico</a></li>
<li><a href="https://developers.weixin.qq.com/minigame/en/dev/guide/best-practice/adapter.html">Documentação oficial do WeChat: a camada de adaptação para código no estilo de navegador</a></li>
<li><a href="https://nikopartners.com/game-regulations-in-china-everything-you-need-to-know/">Niko Partners: regulamentação de jogos na China</a></li>
<li><a href="https://clearlaunch.dev/regulations/china-game-approval">ClearLaunch: requisitos e fiscalização da aprovação de jogos na China — banhao</a></li>
<li><a href="https://appinchina.co/blog/the-complete-guide-to-wechat-mini-games/">AppInChina: o guia completo de minijogos do WeChat</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Lucros recordes, demissões recordes: como desenvolvedores de jogos estão realmente conseguindo novas vagas em 2026]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-07-02-game-industry-layoffs-getting-rehired</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-07-02-game-industry-layoffs-getting-rehired</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A indústria de jogos faturou US$ 195,6 bilhões no ano passado e, ainda assim, demitiu um terço de seus profissionais. Estes são os dados reais sobre as demissões, por que o mercado de trabalho ficou tão brutal e o único fator que está realmente ajudando as pessoas a conseguirem novas vagas.]]></description>
            <content:encoded><![CDATA[<h1>Lucros recordes, demissões recordes: como desenvolvedores de jogos estão realmente conseguindo novas vagas em 2026</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<p>A indústria de jogos faturou cerca de <strong>US$ 195,6 bilhões</strong> em 2025. Seu melhor ano de todos os tempos. Nesse mesmo período, aproximadamente <strong>um em cada três desenvolvedores perdeu o emprego</strong>.</p>
<p>Leia essas duas frases novamente. Elas não deveriam poder coexistir, mas aqui estamos. Se você foi demitido nos últimos dois anos, já sabe que essa conta não parece uma desaceleração. Parece que estão dizendo que o barco está bem enquanto é você quem está caindo no mar.</p>
<p>Já fui desenvolvedora indie. Vi amigos publicarem o selo “Open to Work” três vezes em dezoito meses. Portanto, esta não é uma opinião polêmica de quem está olhando de fora. Vamos analisar o que realmente aconteceu e, depois, a parte mais importante para quem está procurando emprego agora: o que de fato está ajudando as pessoas a conseguirem novas vagas.</p>
<h2>Os números são piores do que a percepção geral</h2>
<p>A onda de demissões começou em 2022 e nunca parou de verdade. Não foi apenas um trimestre ruim. É uma reestruturação que já dura quatro anos.</p>
<table>
<thead>
<tr>
<th>Ano</th>
<th>Vagas cortadas na indústria de jogos</th>
</tr>
</thead>
<tbody>
<tr>
<td>2022</td>
<td>~8.500</td>
</tr>
<tr>
<td>2023</td>
<td>~10.500</td>
</tr>
<tr>
<td>2024</td>
<td>~14.600 (o pico)</td>
</tr>
<tr>
<td>2025</td>
<td>~9.200</td>
</tr>
<tr>
<td>2026</td>
<td>acima do ritmo de 2025, com projeção de ~11.580</td>
</tr>
</tbody>
</table>
<p>Isso representa aproximadamente <strong>44 mil a 55 mil empregos eliminados</strong> no período, dependendo do levantamento em que você confia (<a href="https://en.wikipedia.org/wiki/2022%E2%80%932026_video_game_industry_layoffs">Wikipedia</a>, <a href="https://gamesbeat.com/global-gamings-workforce-grew-0-6-in-4-years-but-north-americas-shrank-11-5-amir-satvat/">GamesBeat</a>). O relatório State of the Game Industry 2026 da GDC, que ouviu mais de 2.300 profissionais, constatou que <strong>28% dos desenvolvedores no mundo perderam o emprego nos últimos dois anos — e 33% nos EUA</strong> (<a href="https://www.gamesindustry.biz/gdc-survey-reveals-layoffs-up-6-36-of-industry-using-ai-and-overwhelming-support-for-unionisation-in-the-us">GamesIndustry.biz</a>). Metade afirmou que seu empregador atual ou mais recente realizou demissões nos últimos doze meses.</p>
<p>A América do Norte sofreu o pior impacto. O levantamento da força de trabalho feito por Amir Satvat, a coisa mais próxima de um censo contínuo que esta indústria possui, mostra que a força de trabalho regional encolheu enquanto o número global de profissionais praticamente não mudou. Cerca de <strong>19% dos profissionais de jogos da América do Norte foram afetados</strong> (<a href="https://gamesbeat.com/global-gamings-workforce-grew-0-6-in-4-years-but-north-americas-shrank-11-5-amir-satvat/">GamesBeat</a>).</p>
<p>Eis o dado que explica por que procurar emprego parece impossível mesmo depois de a contratação ter “se recuperado”. Satvat estima que cerca de <strong>288 mil pessoas estavam procurando trabalho na indústria de jogos</strong> durante esse período. Recém-formados, veteranos demitidos e profissionais em transição de carreira, todos concorrendo ao mesmo conjunto limitado de vagas. Hoje, quando você envia um currículo para uma vaga, não está competindo com uma dúzia de pessoas. Está competindo com um estádio inteiro.</p>
<h2>Por que um ano recorde ainda resultou em demissões</h2>
<p>A receita não desapareceu. Ela se concentrou. Um número cada vez menor de gigantes de jogos como serviço fica com uma parcela desproporcional do mercado, o que significa menos apostas novas, menos equipes novas e menos projetos de médio porte, que antes absorviam profissionais. O investimento privado em jogos caiu mais de 50% em 2025 (<a href="https://respawn.outlookindia.com/gaming/gaming-news/record-profits-record-layoffs-inside-gamings-2026-paradox">Outlook Respawn</a>). Os estúdios reagiram como costumam reagir: cancelaram projetos, reduziram orçamentos e transferiram trabalho para prestadores de serviço.</p>
<p>Portanto, o lucro é real, a dor é real e ambos apontam para a mesma causa. Isso é pouco consolo para quem está atualizando a caixa de entrada sem parar, mas revela algo útil. Este não é um mercado esperando voltar repentinamente a 2021. As pessoas que estão conseguindo novas vagas também não estão esperando por isso. Elas estão se adaptando à maneira como as contratações realmente funcionam agora.</p>
<h2>O mercado de trabalho tem um problema de autenticidade</h2>
<p>Converse com qualquer pessoa que avalia candidatos hoje e você ouvirá a mesma reclamação. Elas estão sobrecarregadas. Anúncios de vagas recebem centenas de candidaturas em poucas horas, e a maioria parece ter sido escrita pela mesma pessoa porque, de certa forma, foi produzida pela mesma ferramenta.</p>
<p>Recrutadores disseram à TechRound que o mais difícil não é identificar o uso de IA, mas que a enxurrada de “conteúdo bem otimizado e sem contexto” torna quase impossível encontrar a pessoa que realmente entende a função (<a href="https://techround.co.uk/news/linkedin-ai-slop-content-recruitment-process/">TechRound</a>). O próprio LinkedIn agora afirma detectar conteúdo genérico escrito por IA em cerca de 94% dos casos e limita silenciosamente seu alcance (<a href="https://techround.co.uk/news/linkedin-ai-slop-content-recruitment-process/">TechRound</a>).</p>
<p>Inverta essa perspectiva e ela se torna a melhor notícia de todo este artigo. Quando tudo soa igual, ser específico é um superpoder. Em um feed repleto do mesmo acabamento impecável, a pessoa que mostra trabalho e raciocínio reais não apenas se destaca. Ela é o único sinal em meio ao ruído.</p>
<h2>Evidências vencem acabamento, sempre</h2>
<p>Especificamente para vagas na indústria de jogos, o que realmente faz diferença não é um PDF mais bonito. É uma evidência que alguém possa experimentar. Um recrutador com 12 anos de carreira resumiu de forma direta: uma build jogável ou um demo reel ajudam mais do que quase qualquer outro elemento da candidatura.</p>
<p>Essa é a mudança. Um currículo diz ao recrutador que você entende de design. Um trecho jogável permite que ele sinta isso em 30 a 90 segundos. Não dá para falsificar uma mecânica funcional do mesmo modo que se falsifica um item em uma lista. Não dá para falsificar uma fase explorável, um shader em funcionamento, um rig que realmente se move ou um sinal de áudio interativo associado a uma cena real. A interatividade é uma prova que resiste à análise, porque por trás dela existe uma pessoa capaz de defender cada decisão.</p>
<p>E essa última parte é o que os recrutadores realmente estão avaliando. O método usado para identificar portfólios inflados com IA é simples: perguntar sobre o processo, não sobre o resultado. Alguém que gerou um estudo de caso consegue descrever o que está escrito, mas não as decisões por trás dele. Por isso, os portfólios mais fortes de 2026 apresentam os mesmos quatro elementos:</p>
<table>
<thead>
<tr>
<th>Camada</th>
<th>O que ela responde</th>
</tr>
</thead>
<tbody>
<tr>
<td>Contexto</td>
<td>O briefing, a plataforma e as restrições reais (orçamento, prazo e limitações técnicas)</td>
</tr>
<tr>
<td>Contribuição</td>
<td>O que ficou sob sua responsabilidade e o que foi feito pela equipe</td>
</tr>
<tr>
<td>Decisões</td>
<td>As concessões que você fez e o que manteve, mudou e rejeitou</td>
</tr>
<tr>
<td>Resultado</td>
<td>Resultados mensuráveis e verificáveis que qualquer pessoa possa conferir</td>
</tr>
</tbody>
</table>
<p>Essa parte sobre “manter, mudar e rejeitar” é a que a maioria ignora — e é justamente a que demonstra bom gosto. Bom gosto é o mais difícil de falsificar e é exatamente o que um estúdio está comprando: alguém capaz de escolher a ideia certa entre dez opções plausíveis. Esqueça a conclusão “aprendi muito”. Mostre a decisão que você tomou e por que ela proporcionou uma experiência melhor ao jogador.</p>
<h2>Uma palavra sobre IA, porque esta indústria conquistou o direito de estar com raiva</h2>
<p>Eu dirijo uma plataforma que usa ferramentas de IA e não vou fingir que essa tensão não existe. A mesma pesquisa da GDC constatou que <strong>52% dos desenvolvedores agora acreditam que a IA generativa está causando um impacto negativo na indústria, contra 18% dois anos atrás</strong>, e que a oposição mais forte vem de artistas, roteiristas e programadores (<a href="https://www.gamesindustry.biz/gdc-survey-reveals-layoffs-up-6-36-of-industry-using-ai-and-overwhelming-support-for-unionisation-in-the-us">GamesIndustry.biz</a>). Quando você acaba de ser demitido e ouve que um modelo talvez possa executar parte de seu antigo trabalho, “é só usar IA no seu portfólio” soa como um tapa, não como um conselho.</p>
<p>Então vou dizer com todas as letras. Ninguém é contratado entregando a um recrutador algo gerado por uma máquina que não consegue explicar. O trabalho precisa ser seu. Suas decisões, sua técnica, sua responsabilidade. Se você usou alguma ferramenta em uma parte do projeto, diga isso e mostre o seu critério aplicado ao resultado: o que manteve, o que descartou e por que sua decisão foi melhor para o jogador. Gestores de contratação confiam em quem trata uma ferramenta como um estagiário que precisa de orientação, não em quem se esconde atrás dela. A honestidade sobre seu processo transmite profissionalismo — e profissionalismo muitas vezes significa apenas “pensei nas consequências antes de publicar”.</p>
<h2>Onde colocar as evidências</h2>
<p>Uma dica prática, já que tantas dessas conversas começam no LinkedIn. O feed penaliza bastante os links externos, mas a seção Destaques do seu perfil não. Esse é o lugar que transforma um link em um cartão de pré-visualização organizado, sem qualquer penalidade, e fica no topo do perfil, onde os recrutadores olham primeiro. Fixe de três a cinco itens, começando pelo melhor trabalho, e lembre-se de que a maioria das pessoas acessará pelo celular. Comece com algo que possa ser aberto e experimentado em poucos segundos.</p>
<p>Mantenha tudo rápido e honesto. Uma demo que carrega em menos de dois segundos e apresenta uma ideia clara é melhor do que um projeto ambicioso que trava ou retorna um erro 404. Recrutadores que avaliam trinta portfólios em uma tarde não esperam, e um link quebrado transmite a ideia de um hábito igualmente falho.</p>
<h2>Por que isso importa para nós</h2>
<p>Na Cinevva, criamos jogos para navegador que podem ser jogados instantaneamente. Isso significa que grande parte do que produzimos é exatamente o que um portfólio moderno precisa: trabalho real que um recrutador pode abrir e jogar por meio de um único link, sem download e sem instalação. Estamos acompanhando esse problema de perto porque as pessoas afetadas são as nossas pessoas. Se você foi demitido, suas habilidades não evaporaram. O mercado ficou mais barulhento, e a maneira de demonstrar sua capacidade mudou.</p>
<p>A antiga pergunta era “onde você trabalhou?”. A nova é “mostre algo que eu possa jogar e explique por que você o criou dessa forma”. Essa segunda pergunta é mais difícil de manipular e melhor para quem realmente sabe fazer o trabalho. Se esse é o seu caso, este mercado é brutal, mas não está fechado. Ele está esperando por evidências nas quais possa confiar.</p>
<hr>
<h2>Fontes</h2>
<ul>
<li><a href="https://www.gamesindustry.biz/gdc-survey-reveals-layoffs-up-6-36-of-industry-using-ai-and-overwhelming-support-for-unionisation-in-the-us">State of the Game Industry 2026 da GDC: demissões, IA e sindicalização</a></li>
<li><a href="https://gamesbeat.com/global-gamings-workforce-grew-0-6-in-4-years-but-north-americas-shrank-11-5-amir-satvat/">GamesBeat: força de trabalho global de jogos cresceu 0,6% em quatro anos, enquanto a da América do Norte encolheu</a></li>
<li><a href="https://en.wikipedia.org/wiki/2022%E2%80%932026_video_game_industry_layoffs">Wikipedia: demissões na indústria de videogames entre 2022 e 2026</a></li>
<li><a href="https://respawn.outlookindia.com/gaming/gaming-news/record-profits-record-layoffs-inside-gamings-2026-paradox">Outlook Respawn: lucros recordes, demissões recordes</a></li>
<li><a href="https://www.gamedeveloper.com/business/industry-layoffs-are-seemingly-slowing-but-the-damage-has-already-been-done">Game Developer: as demissões estão desacelerando, mas o estrago já foi feito</a></li>
<li><a href="https://techround.co.uk/news/linkedin-ai-slop-content-recruitment-process/">TechRound: LinkedIn enfrenta uma onda de conteúdo de baixa qualidade gerado por IA</a></li>
</ul>
<hr>
<p><strong>Relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/blog/2026-01-18-skills-over-degrees">O mercado de trabalho está mudando: de credenciais para habilidades</a></li>
<li><a href="/pt-BR/blog/2026-01-18-ai-controversy-and-post-ai-economy">Controvérsia sobre IA, confiança e a economia pós-IA nos jogos</a></li>
<li><a href="/pt-BR/blog/2026-02-10-the-intuitive-mind">A mente intuitiva na era da IA</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Melhores jogos grátis de navegador para jogar agora (sem download)]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-06-30-best-free-browser-games-no-download</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-06-30-best-free-browser-games-no-download</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Uma seleção dos melhores jogos de navegador que você pode jogar instantaneamente, sem baixar nem instalar nada. Jogos indie bem avaliados de quebra-cabeça, roguelike, narrativa e ritmo.]]></description>
            <content:encoded><![CDATA[<h1>Melhores jogos grátis de navegador para jogar agora (sem download)</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<p>Bom, definitivamente não pretendíamos passar uma semana inteira no <a href="http://itch.io">itch.io</a>. O que começou como uma pesquisa rápida virou... bem, por volta da décima segunda hora, paramos de contar. A ideia era encontrar jogos que você pudesse jogar imediatamente — sem downloads, sem instaladores rodando em segundo plano. Você clica e já está jogando. Pronto.</p>
<p>Estes foram os escolhidos.</p>
<h2>Experiências interativas mais bem avaliadas</h2>
<div class="showcase-card">
<img src="/img/showcases/anxiety.png" alt="Captura de tela de Adventures With Anxiety" loading="lazy">
<p><strong>Adventures With Anxiety!</strong> — História interativa
Mais de 6.500 avaliações, com média de 4,9. É incrível que algo que obriga você a encarar a ansiedade tenha tantos fãs, não é? É desconfortável da melhor maneira possível — daquele tipo que fica com você. <a href="https://ncase.itch.io/anxiety">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/wbwwb.png" alt="Captura de tela de We Become What We Behold" loading="lazy">
<p><strong>We Become What We Behold</strong> — Crítica social
7.600 avaliações e nota 4,8/5. Dez minutos para terminar. Dias para parar de pensar nele. <a href="https://ncase.itch.io/wbwwb">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/six-cats-under.png" alt="Captura de tela de Six Cats Under" loading="lazy">
<p><strong>Six Cats Under</strong> — Aventura de quebra-cabeça
Aconchegante e inteligente. O visual faz boa parte do trabalho aqui, e isso joga a favor do game. Nota 4,8/5 entre mais de 6.400 pessoas. <a href="https://teambeanloop.itch.io/six-cats-under">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/friday-night-funkin.gif" alt="Captura de tela de Friday Night Funkin" loading="lazy">
<p><strong>Friday Night Funkin'</strong> — Jogo de ritmo
A esta altura, este jogo já está por toda parte. Mais de 11.700 avaliações, nota 4,7/5. <a href="https://ninja-muffin24.itch.io/funkin">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/sort-the-court.png" alt="Captura de tela de Sort the Court" loading="lazy">
<p><strong>Sort the Court!</strong> — Simulador de reino
Seu único trabalho: dizer sim ou não. Não parece grande coisa. Então duas horas desaparecem enquanto você ainda administra um pequeno reino. <a href="https://graebor.itch.io/sort-the-court">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/dragonsweeper.png" alt="Captura de tela de Dragonsweeper" loading="lazy">
<p><strong>Dragonsweeper</strong> — Roguelike de quebra-cabeça
Pegue Campo Minado e misture com sistemas de RPG. Nota 4,9/5 — talvez este seja mesmo o melhor jogo de toda a lista. <a href="https://danielben.itch.io/dragonsweeper">Jogue aqui</a></p>
</div>
<h2>Roguelike e estratégia (jogáveis no navegador)</h2>
<div class="showcase-card">
<img src="/img/showcases/backpack-hero.png" alt="Captura de tela de Backpack Hero" loading="lazy">
<p><strong>Backpack Hero</strong> — Roguelike de inventário
A versão para navegador não custa nada. A versão paga acrescenta mais conteúdo, mas a versão web gratuita se sustenta sozinha. <a href="https://thejaspel.itch.io/backpack-hero">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/vampire-survivors.png" alt="Captura de tela de Vampire Survivors" loading="lazy">
<p><strong>Vampire Survivors</strong> — Bullet heaven
É apenas uma demo — uma fase e sete personagens. Ainda assim, é suficiente para entender por que esse jogo consumiu o tempo livre de todo mundo. <a href="https://poncle.itch.io/vampire-survivors">Jogue aqui</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/dome-romantik.gif" alt="Captura de tela de Dome Romantik" loading="lazy">
<p><strong>Dome Romantik</strong> — Defesa e mineração
Começou como um projeto de game jam. Virou Dome Keeper. Ficou enorme. O protótipo original ainda continua ótimo. <a href="https://bippinbits.itch.io/dome-romantik">Jogue aqui</a></p>
</div>
<h2>Por que exatamente estes?</h2>
<p>Testamos cada um dos links. Não foi só uma olhada rápida — nós realmente sentamos e jogamos todos eles. Eles carregam sem complicação. Nenhuma esquisitice no navegador. As milhares de avaliações vieram de pessoas que nem precisaram tocar em um botão de download; elas clicaram e entraram direto no jogo. Esse caminho sem atrito entre “hmm, interessante” e realmente começar a jogar? A maioria das pessoas subestima o quanto isso muda as coisas.</p>
<h2>Explore mais jogos para web</h2>
<p>Ainda quer mais?</p>
<ul>
<li><a href="https://itch.io/games/top-rated/html5">Jogos HTML5 mais bem avaliados no itch.io</a></li>
<li><a href="https://itch.io/games/top-rated/genre-platformer/html5">Melhores jogos de plataforma em HTML5</a></li>
<li><a href="https://itch.io/games/top-rated/html5/year-2024">Jogos de 2024–2025</a></li>
</ul>
<h2>Jogos indie de destaque (download necessário)</h2>
<p>Alguns jogos excelentes não rodam no navegador. Estes valem a etapa extra:</p>
<div class="showcase-card">
<img src="/img/showcases/slice-dice.png" alt="Captura de tela de Slice & Dice" loading="lazy">
<p><strong>Slice &amp; Dice</strong> — Roguelike de dados
Roda no Windows, Mac e Android. <a href="https://tann.itch.io/slice-dice">Baixe no itch.io</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/stacklands.png" alt="Captura de tela de Stacklands" loading="lazy">
<p><strong>Stacklands</strong> — Construção de vila com cartas
Para Windows e Mac. <a href="https://sokpop.itch.io/stacklands">Baixe no itch.io</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/luck-be-a-landlord.png" alt="Captura de tela de Luck be a Landlord" loading="lazy">
<p><strong>Luck be a Landlord</strong> — Roguelike de caça-níquel
Parece só uma ideia chamativa. Não é. Há estratégia de verdade por trás daquela aparência de caça-níquel. <a href="https://trampolinetales.itch.io/luck-be-a-landlord">itch.io</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/patricks-parabox.png" alt="Captura de tela de Patrick's Parabox" loading="lazy">
<p><strong>Patrick's Parabox</strong> — Quebra-cabeças recursivos
Prepare-se para sentir o cérebro doer. E para gostar mesmo assim. <a href="https://patricktraynor.itch.io/patricks-parabox">itch.io</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/dome-keeper.jpg" alt="Captura de tela de Dome Keeper" loading="lazy">
<p><strong>Dome Keeper</strong> — Defesa e mineração
A versão completa daquele protótipo de game jam que mencionamos. <a href="https://store.steampowered.com/app/1637320/Dome_Keeper/">Encontre na Steam</a></p>
</div>
<div class="showcase-card">
<img src="/img/showcases/brotato.jpg" alt="Captura de tela de Brotato" loading="lazy">
<p><strong>Brotato</strong> — Sobrevivência em arena
<a href="https://store.steampowered.com/app/1942280/Brotato/">Disponível na Steam</a></p>
</div>
<hr>
<p>Se você cria jogos, vale refletir sobre isto: <strong>versões para navegador não colocam nada entre os jogadores e o seu trabalho</strong>. Nenhum download parado em uma fila e que eles acabarão esquecendo. Nenhuma tela de instalação pela qual precisam passar. Um clique e eles já estão formando opiniões sobre o que você criou. Esse tipo de imediatismo não recebe a atenção que merece — mas muda tudo.</p>
<p>Quer isso para o seu próprio jogo? Você pode criar um diretamente no navegador com o <a href="/pt-BR/create">criador de jogos da Cinevva</a>, e os jogos que destacamos aparecem no nosso <a href="/pt-BR/arcade">Arcade da comunidade</a>.</p>
<ul>
<li><a href="/pt-BR/creators">Para criadores de jogos</a></li>
<li><a href="/pt-BR/tutorials/ship-web-game-fast">Crie um jogo para web que carregue rápido</a></li>
</ul>
<style>
.showcase-card {
  margin: 1.5rem 0;
  padding: 1rem;
  border-radius: 8px;
  background: var(--vp-c-bg-soft);
}

.showcase-card img {
  max-width: 100%;
  height: auto;
  border-radius: 6px;
  margin-bottom: 0.75rem;
  display: block;
}

@media (min-width: 640px) {
  .showcase-card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1rem;
    align-items: start;
  }
  
  .showcase-card img {
    margin-bottom: 0;
    width: 200px;
  }
}
</style>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Engines de jogos web em 2026: PlayCanvas vs Three.js vs Babylon.js vs Unity WebGL]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-06-09-web-game-engines-2026-comparison</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-06-09-web-game-engines-2026-comparison</guid>
            <pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Uma comparação atual e honesta das principais formas de criar um jogo 3D no navegador em 2026: PlayCanvas (v2.20), Three.js, Babylon.js e Unity WebGL. O que cada uma é, para quem serve, suporte a WebGL2 vs WebGPU, licenciamento e como escolher.]]></description>
            <content:encoded><![CDATA[<h1>Engines de jogos web em 2026: PlayCanvas vs Three.js vs Babylon.js vs Unity WebGL</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/web-engines-2026-hero.jpg" alt="Uma janela de navegador mostrando uma cena 3D que passa de uma malha branca em wireframe à esquerda para uma paisagem low-poly colorida renderizada à direita" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Se você quer lançar um jogo 3D que rode no navegador em 2026, há quatro opções populares e algumas mais novas. Elas costumam ser agrupadas como &quot;engines de jogos web&quot;, mas são ferramentas realmente diferentes que resolvem problemas diferentes. Escolher a errada pode custar meses. Esta é uma comparação atual e baseada em fatos, escrita por pessoas que desenvolvem uma engine 3D para web profissionalmente e avaliaram todas essas opções antes de criar a nossa própria.</p>
<p>Vamos analisar cada opção, o que ela realmente é, para quem serve e onde deixa a desejar. Depois, apresentaremos um breve guia de decisão no final. Quando mencionamos números de versão, eles estão atualizados até julho de 2026.</p>
<blockquote>
<p>Quer conhecer todas as opções? Este post se concentra nas quatro principais alternativas para 3D. Para ver todas as 11 engines web classificadas e comparadas (incluindo opções 2D como Phaser, Defold e Construct), consulte nosso guia das <a href="/pt-BR/guides/web-game-engines-comparison">melhores engines de jogos web para 2026</a>.</p>
</blockquote>
<h2>Comparação rápida</h2>
<table>
<thead>
<tr>
<th></th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
<th>Cinevva</th>
</tr>
</thead>
<tbody>
<tr>
<td>O que é</td>
<td>Engine completa + editor hospedado</td>
<td>Biblioteca de renderização</td>
<td>Engine completa + editor</td>
<td>Engine para desktop, exportação para web</td>
<td>Plataforma de mundos nativa de IA</td>
</tr>
<tr>
<td>Arquitetura</td>
<td>Entidade-componente (ECS)</td>
<td>Grafo de cena</td>
<td>Grafo de cena + componentes</td>
<td>GameObject/componente</td>
<td>Grafo de cena + construtor de IA</td>
</tr>
<tr>
<td>Programação</td>
<td>TypeScript / JavaScript</td>
<td>JavaScript (você cria o restante)</td>
<td>TypeScript / JavaScript</td>
<td>C# (compilado para Wasm)</td>
<td>Linguagem natural + código</td>
</tr>
<tr>
<td>Renderizador</td>
<td>WebGL2, WebGPU em amadurecimento</td>
<td>WebGL2, WebGPU em expansão</td>
<td>WebGL2, WebGPU avançando</td>
<td>WebGL2 (exportação)</td>
<td>Somente WebGPU</td>
</tr>
<tr>
<td>Editor</td>
<td>Hospedado, comercial</td>
<td>Nenhum</td>
<td>Baseado na web, gratuito</td>
<td>Desktop, comercial</td>
<td>Dentro do mundo, imersivo</td>
</tr>
<tr>
<td>Física</td>
<td>Ammo / integrações</td>
<td>Você escolhe</td>
<td>Havok integrado</td>
<td>Integrada (PhysX)</td>
<td>Solucionador de personagem próprio</td>
</tr>
<tr>
<td>Licença</td>
<td>Engine MIT, editor proprietário</td>
<td>MIT</td>
<td>Apache 2.0</td>
<td>Proprietária</td>
<td>Proprietária</td>
</tr>
<tr>
<td>Melhor para</td>
<td>Jogos de navegador com fluxo de trabalho de estúdio</td>
<td>3D personalizado, controle total</td>
<td>Jogos com tudo incluído</td>
<td>Portar jogos existentes do Unity</td>
<td>Criação de jogos dentro do mundo com IA</td>
</tr>
</tbody>
</table>
<p>O restante deste post explica a tabela.</p>
<h2>PlayCanvas</h2>
<p>PlayCanvas é o mais próximo que a web tem de um fluxo de trabalho no estilo Unity. A engine é de código aberto sob a licença MIT e está na versão v2.20.6 em julho de 2026. Ela usa um sistema entidade-componente, permite escrever a lógica do jogo em TypeScript ou JavaScript e processa os assets por meio de um pipeline no servidor que gera arquivos GLB. Jogos comerciais reais rodam nela, e grandes empresas a utilizam em produção — a Snap é um exemplo público.</p>
<p>Vale a pena corrigir dois equívocos comuns sobre o PlayCanvas. Primeiro, embora a engine tenha licença MIT, o editor visual hospedado que a maioria das equipes realmente usa é um produto comercial, não de código aberto. Você pode usar a engine livremente sem ele, mas o fluxo de trabalho com editor e nuvem é a parte paga. Segundo, o PlayCanvas prioriza o WebGL2. Ele possui um caminho para WebGPU, mas esse caminho ainda está amadurecendo e não é o renderizador padrão. Portanto, não escolha o PlayCanvas hoje esperando usar shaders de computação WebGPU em produção.</p>
<p>A área em que o PlayCanvas realmente lidera é a renderização por 3D Gaussian splatting. O editor e visualizador SuperSplat, disponibilizados pela equipe como código aberto, estão entre as melhores ferramentas disponíveis para capturar e publicar cenas com splats na web, com streaming baseado em WebGPU para capturas de grande porte. Se ambientes fotorrealistas digitalizados são importantes para você, esse é um ótimo motivo para começar por aqui.</p>
<p><strong>Escolha o PlayCanvas se</strong> você quer um editor semelhante ao Unity e um pipeline gerenciado de assets para o navegador, além de estar criando o tipo de jogo que um estúdio desenvolveria. <strong>Procure outra opção se</strong> você precisa de computação WebGPU hoje ou quer evitar um editor comercial hospedado.</p>
<h2>Three.js</h2>
<p>Three.js não é uma engine de jogos. É uma biblioteca de renderização — e, de longe, a mais utilizada nesse segmento. Ela oferece um grafo de cena, câmeras, luzes, materiais, geometria, carregadores e um renderizador, e para por aí. Não há editor, física, sistema de entidades nem uma abordagem definida sobre como estruturar um jogo. Você adiciona tudo isso por conta própria ou recorre ao ecossistema.</p>
<p>Essa troca é o ponto central. Você obtém controle máximo e a maior comunidade de 3D para web, mas precisa criar ou reunir tudo o que fica acima do renderizador. Seu renderizador WebGPU tem evoluído de forma constante e já pode ser usado hoje, embora, assim como nas outras opções, o caminho WebGL2 continue sendo o padrão mais maduro. Three.js possui licença MIT.</p>
<p><strong>Escolha o Three.js se</strong> você quer controle total, uma base mínima e possui a capacidade técnica necessária para criar seus sistemas de jogo sobre ela. <strong>Procure outra opção se</strong> você quer receber um editor e sistemas de jogo prontos.</p>
<h2>Babylon.js</h2>
<p>Babylon.js é uma engine completa, licenciada sob Apache 2.0 e apoiada por uma equipe da Microsoft. Ao contrário do Three.js, ela já vem com os componentes que, de outra forma, você precisaria reunir: um modelo de componentes, integração com a engine de física Havok, um editor gratuito baseado na web e um pipeline de assets. O desenvolvimento do WebGPU avançou rapidamente e está entre os mais sofisticados nas engines de uso geral, embora a opção WebGL2 continue sendo mantida como caminho de ampla compatibilidade. O Babylon.js 9.0 (março de 2026) ampliou essa vantagem com iluminação em clusters nos dois backends, iluminação volumétrica baseada em shaders de computação WebGPU e suporte a sombras para 3D Gaussian splats.</p>
<p>Se você pensa &quot;quero uma engine completa, com física incluída, e não me importo em trabalhar dentro das convenções dela&quot;, o Babylon é uma excelente escolha padrão e provavelmente a opção gratuita com mais recursos disponíveis.</p>
<p><strong>Escolha o Babylon.js se</strong> você quer tudo incluído, física integrada e um editor gratuito. <strong>Procure outra opção se</strong> você quer o menor volume possível de dependências ou procura especificamente um fluxo de trabalho hospedado no estilo Unity.</p>
<h2>Unity WebGL</h2>
<p>Unity WebGL não é uma engine web, mas um destino de exportação. Você desenvolve no editor para desktop do Unity, em C#, e compila para um pacote WebGL que roda no navegador por meio de WebAssembly. Isso faz dela a resposta óbvia para um caso específico: você já possui um jogo feito no Unity e quer criar uma versão para navegador.</p>
<p>Para um projeto voltado primeiro à web, ela tem custos reais. O runtime e o tamanho do download são consideráveis, a inicialização é mais lenta do que em uma engine web nativa e o desempenho em navegadores móveis é um problema conhecido. O Unity é proprietário e sua saída WebGL usa WebGL2. Há um backend WebGPU experimental no Unity 6, mas ele vem desativado por padrão e ainda não está pronto para produção.</p>
<p><strong>Escolha o Unity WebGL se</strong> você tem um projeto Unity existente para levar ao navegador ou sua equipe já trabalha totalmente no Unity. <strong>Procure outra opção se</strong> o carregamento instantâneo em celulares intermediários é um requisito indispensável ou se você está começando do zero com foco prioritário na web.</p>
<h2>Onde entram a IA nativa e a criação dentro do mundo</h2>
<p>Todas as opções acima compartilham uma premissa: um desenvolvedor cria o jogo em uma mesa, usando um editor, e publica um runtime. Essa premissa é válida para a maioria dos projetos e, se ela descreve o seu, escolha uma das quatro opções acima.</p>
<p>Vale saber que essa já não é a única opção, pois uma categoria diferente está surgindo. Nós desenvolvemos a Cinevva, uma plataforma de mundos exclusiva para WebGPU na qual os jogos são criados de dentro do próprio mundo. Em vez de abrir um editor, você é um avatar dentro do espaço, descreve o que deseja e um construtor de IA transforma isso em terreno, objetos e comportamentos enquanto você permanece ali. Criação e experiência de jogo acontecem na mesma sessão. Por baixo dos panos, isso exigiu adotar exclusivamente o WebGPU, com terreno gerado por shaders de computação; desenvolver um solucionador de personagem próprio em vez de usar uma engine de física genérica; e criar um sistema de animação e retargeting. Escrevemos sobre isso em <a href="/pt-BR/blog/2026-06-08-why-we-built-our-own-webgpu-engine">por que criamos nossa própria engine WebGPU</a>.</p>
<p>Isso não substitui o PlayCanvas nem o Babylon. Se você é um desenvolvedor criando um jogo específico, essas são as ferramentas certas. A Cinevva atende ao objetivo diferente de permitir que pessoas que não desenvolvem engines criem e compartilhem espaços jogáveis simplesmente descrevendo-os. Mencionamos isso porque a pergunta &quot;qual engine de jogos web devo usar?&quot; tem cada vez mais uma quinta resposta, que nem sequer é uma engine.</p>
<h2>Matriz completa de recursos</h2>
<p>A tabela rápida acima apresenta os destaques. Esta é a versão detalhada, organizada por subsistema. Algumas observações importantes sobre como interpretá-la: &quot;Por conta própria&quot; significa que a engine não inclui o recurso, mas ele pode ser adicionado pelo ecossistema ou pelo seu próprio código. Isso é especialmente relevante para o Three.js, que é uma biblioteca de renderização por definição; portanto, nessa coluna, &quot;Por conta própria&quot; representa uma característica de sua filosofia, não uma deficiência. &quot;Somente na exportação&quot;, no caso do Unity, significa que o recurso existe no editor para desktop e é incorporado à compilação WebGL, em vez de ser nativo da web. As células dos concorrentes refletem os recursos prontos para uso e o comportamento bem documentado até meados de 2026. As células da Cinevva refletem o que funciona em nossa versão publicada, com &quot;planejado&quot; indicando o que já foi projetado, mas ainda não desenvolvido.</p>
<h3>Renderização</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Renderizador principal</td>
<td>Somente WebGPU</td>
<td>WebGL2 (WebGPU beta)</td>
<td>WebGL2 (WebGPU em expansão)</td>
<td>WebGL2 (WebGPU avançado)</td>
<td>WebGL2 (exportação)</td>
</tr>
<tr>
<td>Shaders de computação em produção</td>
<td>Sim (dependência central)</td>
<td>Beta</td>
<td>Via WebGPU</td>
<td>Sim (WebGPU)</td>
<td>Somente na exportação, limitado</td>
</tr>
<tr>
<td>Criação de shaders</td>
<td>Nós TSL + computação</td>
<td>Blocos de shader / GLSL</td>
<td>GLSL + nós (TSL)</td>
<td>Material por nós / GLSL / WGSL</td>
<td>ShaderLab / HLSL</td>
</tr>
<tr>
<td>Iluminação em clusters / forward+</td>
<td>Sim (froxel)</td>
<td>Sim</td>
<td>Por conta própria</td>
<td>Sim</td>
<td>Sim</td>
</tr>
<tr>
<td>Nuvens volumétricas e clima</td>
<td>Sim</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Parcial</td>
<td>Por conta própria</td>
</tr>
<tr>
<td>Ferramentas de 3D Gaussian splatting</td>
<td>Planejado</td>
<td>Sim (SuperSplat, líder)</td>
<td>Comunidade</td>
<td>Sim</td>
<td>Plugins</td>
</tr>
</tbody>
</table>
<h3>Mundo e terreno</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Streaming integrado de mundos grandes</td>
<td>Sim (chunks de 64 m)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Somente na exportação</td>
</tr>
<tr>
<td>Sistema de terreno</td>
<td>Mapa de altura híbrido + marching cubes/SDF</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Extensão</td>
<td>Integrado (desktop)</td>
</tr>
<tr>
<td>Escultura de terreno em tempo de execução</td>
<td>Sim (GPU)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Não (somente durante a edição)</td>
</tr>
<tr>
<td>Cavernas e saliências (topologia 3D real)</td>
<td>Sim (marching cubes)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
</tr>
<tr>
<td>Vegetação e grama instanciadas por GPU</td>
<td>Sim</td>
<td>Sim</td>
<td>Por conta própria</td>
<td>Sim</td>
<td>Sim</td>
</tr>
</tbody>
</table>
<h3>Física e personagem</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Engine de física</td>
<td>Solucionador cinemático próprio</td>
<td>Integração com Ammo</td>
<td>Por conta própria (Rapier/Cannon/Ammo)</td>
<td>Havok integrado</td>
<td>PhysX integrado</td>
</tr>
<tr>
<td>Dinâmica de corpos rígidos</td>
<td>Não (por decisão de projeto)</td>
<td>Sim</td>
<td>Por conta própria</td>
<td>Sim</td>
<td>Sim</td>
</tr>
<tr>
<td>Controlador de personagem</td>
<td>Sim (FSM multimodo)</td>
<td>Modelos / Ammo</td>
<td>Por conta própria</td>
<td>Sim</td>
<td>Integrado</td>
</tr>
<tr>
<td>Colisão integrada ao terreno (SDF)</td>
<td>Sim</td>
<td>Não</td>
<td>Por conta própria</td>
<td>Não</td>
<td>Não</td>
</tr>
</tbody>
</table>
<h3>Animação</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Animação esquelética</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim</td>
</tr>
<tr>
<td>Mesclagem / máquina de estados</td>
<td>Sim (FSM de resolução)</td>
<td>Sim (grafo de estados de animação)</td>
<td>Mixer (mesclagem por conta própria)</td>
<td>Sim</td>
<td>Sim (Mecanim)</td>
</tr>
<tr>
<td>Retargeting de esqueleto</td>
<td>Sim (pipeline)</td>
<td>Limitado</td>
<td>Comunidade</td>
<td>Parcial</td>
<td>Sim (humanoide)</td>
</tr>
<tr>
<td>Cinemática inversa</td>
<td>Planejado</td>
<td>Limitado</td>
<td>Comunidade</td>
<td>Sim</td>
<td>Sim</td>
</tr>
</tbody>
</table>
<h3>Multijogador e backend</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Multijogador integrado</td>
<td>Sim (autoritativo na borda)</td>
<td>Por conta própria (Photon/Colyseus)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria (Netcode, não nativo da web)</td>
</tr>
<tr>
<td>Mundo compartilhado persistente</td>
<td>Sim (Durable Objects por chunk)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
</tr>
<tr>
<td>Chat de voz espacial</td>
<td>Sim (WebRTC + HRTF)</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
<td>Por conta própria</td>
</tr>
</tbody>
</table>
<h3>Criação e autoria</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Editor</td>
<td>Dentro do mundo, imersivo</td>
<td>Hospedado, estilo desktop (comercial)</td>
<td>Nenhum (mínimo)</td>
<td>Baseado na web (gratuito)</td>
<td>Desktop (comercial)</td>
</tr>
<tr>
<td>Criação incorporada dentro do mundo</td>
<td>Sim</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
</tr>
<tr>
<td>Criação por linguagem natural / IA</td>
<td>Sim (construtor de IA)</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
</tr>
<tr>
<td>Programação</td>
<td>Linguagem natural + JS</td>
<td>TypeScript / JS</td>
<td>JavaScript</td>
<td>TypeScript / JS</td>
<td>C#</td>
</tr>
</tbody>
</table>
<h3>Assets e distribuição</h3>
<table>
<thead>
<tr>
<th>Recurso</th>
<th>Cinevva</th>
<th>PlayCanvas</th>
<th>Three.js</th>
<th>Babylon.js</th>
<th>Unity WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Pipeline de assets no servidor</td>
<td>Sim (GLB + LOD + KTX2 + Draco)</td>
<td>Sim (GLB)</td>
<td>Somente carregadores</td>
<td>Ferramentas de importação</td>
<td>Sim</td>
</tr>
<tr>
<td>Geração integrada de assets por IA</td>
<td>Sim (3D, imagem, áudio, música)</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
<td>Não</td>
</tr>
<tr>
<td>Busca federada de assets</td>
<td>Sim (onze provedores)</td>
<td>Loja de assets</td>
<td>Não</td>
<td>Não</td>
<td>Loja de assets</td>
</tr>
<tr>
<td>Roda no navegador, sem instalação</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim</td>
<td>Sim (pesado)</td>
</tr>
<tr>
<td>Licença da engine</td>
<td>Plataforma proprietária</td>
<td>Engine MIT, editor proprietário</td>
<td>MIT</td>
<td>Apache 2.0</td>
<td>Proprietária</td>
</tr>
</tbody>
</table>
<p>O padrão na matriz é o ponto central. As engines de uso geral distribuem seus pontos fortes horizontalmente: cada uma faz a maioria das coisas com competência e deixa o mundo, o backend e o fluxo de criação por sua conta. A Cinevva concentra-se verticalmente: faz menos coisas, mas controla todo o caminho, do renderizador até um mundo compartilhado que você cria por dentro. Nenhum desses formatos é abstratamente melhor. Eles respondem a perguntas diferentes.</p>
<h2>Como escolher</h2>
<p>Escolha a ferramenta de acordo com a situação, e não com uma lista de recursos.
Se você já tem um jogo feito em Unity, exporte-o com o Unity WebGL e aceite o tamanho dos arquivos. Se você faz parte de um estúdio que busca um fluxo de trabalho orientado por editor para jogos de navegador, use o PlayCanvas. Se você quer uma engine completa e gratuita, com física incluída, use o Babylon.js. Se você quer controle total e tem uma equipe capaz de desenvolver sobre um renderizador básico, use o Three.js. E se o seu objetivo não é criar um único jogo, mas permitir que as pessoas criem e joguem em um mundo compartilhado descrevendo coisas, essa é a categoria em que atuamos — e você pode <a href="/pt-BR/">experimentar o Cinevva</a>.</p>
<p>Independentemente da sua escolha, faça a avaliação com protótipos reais antes de se comprometer. Todas essas opções são capazes de sustentar um jogo de verdade, e o custo de trocar de tecnologia no meio do caminho são os meses que você deixou de dedicar ao lançamento.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Por que criamos nosso próprio motor WebGPU em vez de fazer um fork do PlayCanvas]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-06-08-why-we-built-our-own-webgpu-engine</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-06-08-why-we-built-our-own-webgpu-engine</guid>
            <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Three.js, Babylon.js, PlayCanvas, Unity WebGL: avaliamos todos eles antes de escrever nosso próprio renderizador. Veja no que cada um é bom, as quatro decisões que nos levaram a abandonar as soluções prontas e os casos em que você definitivamente não deve fazer o que fizemos.]]></description>
            <content:encoded><![CDATA[<h1>Por que criamos nosso próprio motor WebGPU em vez de fazer um fork do PlayCanvas</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/why-webgpu-engine-hero.jpg" alt="Um mundo estilizado em low poly renderizado pelo motor WebGPU da Cinevva: colinas ondulantes, uma caverna escavada em um penhasco, um rio sinuoso e um avatar caminhando por uma trilha durante a hora dourada" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Toda pessoa da área técnica que conhece o Cinevva World faz a mesma pergunta nos primeiros cinco minutos. Vocês são uma equipe pequena. Já existem motores 3D maduros para a web. Por que escreveram seu próprio renderizador, sua própria física de personagens e seu próprio sistema de animação, em vez de usar PlayCanvas ou Babylon como base e lançar mais rápido?</p>
<p>É uma pergunta justa, e “não foi inventado aqui” é a resposta errada. Fizemos a lição de casa. Testamos os motores existentes, lemos seu código-fonte e criamos protótipos sobre dois deles antes de decidir. Este post traz a versão honesta dessa decisão: no que as opções prontas são realmente boas, os quatro pontos em que nossas necessidades divergiram o bastante para justificar construir algo próprio e as situações em que você deveria escolher uma delas em vez de nos imitar. Documentamos o desenvolvimento em si em uma longa <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">série de engenharia sobre um mundo aberto no navegador</a>. Portanto, quando uma afirmação abaixo tiver um registro prático por trás dela, incluirei o link.</p>
<h2>As opções que realmente avaliamos</h2>
<p>Quatro coisas são chamadas de “motores de jogos para a web”, mas elas não são o mesmo tipo de ferramenta.</p>
<p><strong>Three.js</strong> é uma biblioteca de renderização, não um motor de jogos. Ela oferece um grafo de cena, materiais, carregadores e um renderizador, e então sai do seu caminho. Não há editor, física, sistema de entidades nem uma opinião sobre como seu jogo deve ser estruturado. Esse é tanto o atrativo quanto o custo. Você constrói por conta própria tudo o que fica acima do renderizador, mas nada atrapalha. Ela usa a licença MIT, tem o maior ecossistema desse segmento e, quando você encontra um bug obscuro de shader às duas da manhã, há uma resposta no fórum esperando por você.</p>
<p><strong>Babylon.js</strong> é um motor completo com grafo de cena, integração de física (Havok), pipeline de assets e editor web. Ele usa a licença MIT, conta com o apoio de uma equipe da Microsoft e seu trabalho com WebGPU tem avançado rapidamente. Se você quer uma solução completa e não se importa em trabalhar dentro da estrutura do motor, é uma ótima escolha padrão.</p>
<p><strong>PlayCanvas</strong> é o que mais se aproxima de uma “Unity para a web”. No momento em que este texto foi escrito, o motor estava na versão v2.19.6, lançada em 5 de junho de 2026, e o motor em si é open source sob a licença MIT. Porém, o que a maioria das pessoas realmente usa é o editor visual hospedado, e esse editor é um produto comercial, não open source. O PlayCanvas usa um sistema de entidade-componente, permite criar scripts em TypeScript ou JavaScript, processa assets por meio de um pipeline GLB no servidor e já foi usado para lançar jogos comerciais reais (a Snap, por exemplo, usa o PlayCanvas em produção). Seu renderizador roda em WebGL2, com um caminho WebGPU que ainda está amadurecendo, em vez de ser o padrão. Esse último detalhe importa mais do que parece, e voltarei a ele.</p>
<p><strong>Unity WebGL</strong> nem sequer é um motor web. É um destino de exportação. Você desenvolve no editor desktop da Unity e compila para um pacote WebGL. É a ferramenta certa quando você já tem um jogo na Unity e quer levá-lo ao navegador, e a ferramenta errada quando “carregar instantaneamente em uma aba de um celular intermediário” é um requisito obrigatório, porque o runtime e o peso do download vêm junto.</p>
<p>Qualquer uma dessas opções é uma base razoável para um jogo web comum. Nós não tínhamos um jogo web comum.</p>
<h2>Primeira decisão: WebGPU é nosso ponto de partida, não nossa linha de chegada</h2>
<p>A diferença que nos afasta de todas as soluções prontas é esta: para nós, WebGPU é um requisito, não um recurso que adotaremos no futuro.</p>
<p>Nosso terreno não é um heightmap estático. Ele é um híbrido entre um campo de heightmap transmitido sob demanda e chunks de marching cubes sustentados por um campo de distância com sinal, para que o mundo possa ter cavernas e saliências reais e os criadores possam esculpi-lo ao vivo. Os pincéis de escultura, a distribuição da vegetação e a geração da malha do terreno são executados como compute shaders. Sem computação, o mundo não perde apenas qualidade: ele simplesmente não roda.</p>
<p>Isso é o oposto de onde os motores de uso geral estão hoje. O suporte deles a WebGPU foi projetado como um aprimoramento progressivo sobre um renderizador centrado em WebGL2, com um caminho alternativo para navegadores que não oferecem suporte. O PlayCanvas, em particular, prioriza WebGL2, enquanto WebGPU ainda está em beta. Essa é a decisão correta para eles, porque sua função é executar a maior variedade possível de jogos na maior variedade possível de dispositivos. Nossa função é mais restrita e mais profunda, então tomamos a decisão oposta: passamos a usar apenas WebGPU no protótipo 13 e nunca olhamos para trás. Navegadores sem WebGPU não recebem uma versão reduzida; eles não são compatíveis, e acompanhamos isso como uma métrica de alcance em vez de fingir que um fallback para WebGL2 está a uma única opção de configuração de distância. Não está. Isso exigiria uma reescrita parcial dos nossos estágios de terreno e vegetação.</p>
<p>Construir sobre um motor centrado em WebGL2 significaria lutar contra as premissas de fallback em cada recurso de computação ou manter dois caminhos de renderização para sempre. Ter controle sobre o renderizador nos permitiu tratar a computação como base.</p>
<h2>Segunda decisão: um solucionador de movimento para personagens, não um motor de física</h2>
<p>A abordagem clássica é adicionar um motor de física. Nós tentamos. Um dos primeiros protótipos validou o Rapier rodando em um worker, e o resultado parecia bom.</p>
<p>Mesmo assim, escrevemos o nosso, e não há Rapier, Cannon nem Ammo em nenhuma parte da versão lançada. O motivo é o escopo. Não precisamos de corpos rígidos, articulações, ragdolls nem de um solucionador de restrições. Precisamos que uma única cápsula se mova corretamente sobre o terreno e que caminhar, deslizar, planar, escalar e nadar compartilhem uma única resposta para “estou no chão, em qual superfície e em que ângulo?”. Um motor de física de uso geral torna isso mais difícil, não mais fácil, porque esses modos acabam entrando em conflito com suas molas e amortecedores internos.</p>
<p>Por isso, nosso controlador de personagem é uma <a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">máquina de estados multicanal modular</a>. Cada modo é uma pequena unidade que informa se quer assumir o controle naquele quadro e, se vencer, define a velocidade e a direção. Eles disputam por prioridade em três canais — recurso, depois postura e, por fim, locomoção — e todos consultam o mesmo terreno. A colisão é determinada por uma sondagem de cápsula contra o heightmap ou o campo de distância com sinal, dependendo do chunk.</p>
<p>O detalhe de que mais me orgulho não tem glamour algum. A detecção do chão examina todas as superfícies na coluna vertical abaixo dos seus pés e escolhe a mais alta que esteja na altura da cápsula ou abaixo dela, em vez de confiar no gradiente do SDF. Na borda de uma saliência, a superfície lateral mais próxima é a face do penhasco, então uma normal baseada em gradiente oscila entre “chão” e “parede”, causando deslizamentos fantasmas. A consulta por coluna torna ficar em pé em uma beirada algo sem graça, que é exatamente o que você quer. Um colisor de heightfield pronto não nos daria isso de graça, porque, para começo de conversa, ele não consegue enxergar nosso terreno. O terreno reside na GPU, em nosso próprio formato. Nenhum motor externo consegue detectar colisões contra ele sem que copiemos o campo inteiro para o formato de colisores desse motor a cada edição.</p>
<h2>Terceira decisão: animações que funcionam em diferentes rigs</h2>
<p>Os avatares usam o rig Synty POLYGON, e nossos clipes de movimento vêm de uma grande biblioteca aberta de animações. Como o rig e os clipes compartilham os nomes dos ossos, o caso comum não exige retargeting em runtime: as trilhas simplesmente são vinculadas pelo nome. Descrevemos todo o pipeline de personagens em <a href="/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters">personagens universais</a>.</p>
<p>O trabalho interessante está nos casos em que eles não correspondem. Criamos uma etapa de retargeting que mapeia um esqueleto para outro, e fazê-la funcionar corretamente exigiu resolver três problemas específicos. Primeiro, é preciso alinhar as bind poses, porque combinar uma pose A com uma pose T acrescenta silenciosamente cerca de trinta graus por articulação. Depois, é preciso dimensionar o movimento da raiz e a passada usando duas proporções distintas: a distância do quadril ao chão para o eixo vertical e a proporção geral para o eixo horizontal. Por fim, é preciso testar os clipes adaptados contra os originais em uma taxa de amostragem fixa, para que as regressões apareçam antes que um jogador as encontre. Esse pipeline é o que nos permite adicionar novas fontes de animação e, no futuro, personagens enviados pelos criadores, sem corrigir manualmente cada clipe.</p>
<p>Acima disso, há uma máquina de estados de animação que escolhe a família correta de clipes a cada quadro com base no estado do movimento: variante de salto de acordo com a velocidade, intensidade da aterrissagem de acordo com o impacto, direção da inclinação pelo produto escalar da velocidade com o gradiente do terreno, paradas sincronizadas com a fase dos pés para que o personagem não deslize para interromper o movimento e um debounce de cem milissegundos para que esbarrar em uma parede não faça o personagem piscar entre os estados de caminhada e queda. Nada disso é exótico, mas é o tipo de coisa que você só consegue ao controlar essa camada.</p>
<h2>Quarta decisão: a parte que nenhum motor oferece</h2>
<p>As outras três decisões dizem respeito a como o mundo funciona. Esta diz respeito ao que o produto é, e é por isso que a comparação com o PlayCanvas é, no fim das contas, um erro de categoria.</p>
<p>PlayCanvas, Babylon, Unity e um aplicativo Three.js feito do zero partem todos da mesma premissa: você cria seu jogo em uma mesa, em um editor 2D, observando o mundo de fora, e depois aperta o botão de jogar e publica um runtime separado. Mesmo na edição dentro do próprio motor, você está em uma estação de trabalho manipulando uma cena que observa de fora.</p>
<p>O Cinevva World inverte essa lógica. Você cria de dentro do mundo, como um avatar no mesmo espaço em que seus jogadores estarão, descrevendo o que deseja e vendo isso aparecer. Criar e jogar fazem parte de uma única sessão contínua, não são uma etapa de desenvolvimento conectada a um runtime. As referências mais próximas são Roblox, Rec Room e Horizon Worlds, não motores 3D para a web, e até mesmo essas plataformas mantêm a maior parte da criação em uma mesa. O que torna isso viável sem um editor controlado por mouse é o construtor com IA, que transforma “coloque aqui um cais iluminado por lanternas” em geometria e posicionamento.</p>
<p>Não é possível simplesmente acoplar isso a um motor de uso geral, porque não se trata de um recurso de renderização. É a premissa de toda a stack, desde a maneira como o terreno pode ser editado em runtime até a forma como a rede trata cada objeto como algo que uma pessoa próxima acabou de criar.</p>
<h2>Quando você não deve fazer o que fizemos</h2>
<p>Esta é a parte que o gênero “criamos nosso próprio motor” geralmente ignora. Se você quer lançar um jogo para navegador neste trimestre, não nos imite. Use o PlayCanvas se quiser um editor no estilo da Unity e um pipeline gerenciado. Use o Babylon se quiser um motor completo, com física incluída, e estiver satisfeito em trabalhar dentro da estrutura dele. Use o Three.js se quiser o máximo de controle e o mínimo de imposições e tiver uma equipe capaz de construir as camadas acima dele. Faça a portabilidade a partir da Unity se já tiver um jogo feito nela.</p>
<p>Escrever seu próprio renderizador, sua própria física e suas próprias animações só é a decisão certa quando a premissa do seu produto é incompatível com as soluções prontas — quando o que você está construindo não é um jogo executado em um motor, mas um lugar que, por acaso, é feito de um. Esse era o nosso caso. Provavelmente não é o seu, e tudo bem. O objetivo de fazer uma avaliação honesta é saber em qual caso você se encontra antes de escrever a primeira linha.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Um mundo vazio é um mundo triste: o que toda plataforma de criação que sobreviveu ou morreu pode nos ensinar]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-06-06-how-creator-worlds-live-or-die</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-06-06-how-creator-worlds-live-or-die</guid>
            <pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Construímos uma IA que cria prédios inteiros em um mundo 3D compartilhado a partir de uma frase. Antes de abri-la para todos, estudamos os lançamentos de todas as plataformas comparáveis que conseguimos encontrar. O cemitério é enorme, os sobreviventes são poucos, e a linha que os separa é mais nítida do que o hype jamais admite. Aqui estão as histórias, os números e o que estamos fazendo de forma diferente.]]></description>
            <content:encoded><![CDATA[<h1>Um mundo vazio é um mundo triste: o que toda plataforma de criação que sobreviveu ou morreu pode nos ensinar</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<figure style="margin:1.5rem 0">
<img src="/img/blog/ai-builder-hero.webp" alt="O AI Builder da Cinevva construindo uma casa de dois andares a partir de um comando no chat" style="width:100%;border-radius:10px">
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Nosso AI Builder. Alguém digitou "construa um segundo andar", e a IA montou a laje, ergueu as paredes e, em seguida, ofereceu escadas e um telhado — tudo dentro de um mundo no qual outras pessoas podem entrar.</figcaption>
</figure>
<p>Agora podemos gerar mundos, e não estou falando de um vídeo de demonstração. A imagem acima mostra nosso editor. Alguém digitou &quot;construa um segundo andar&quot; em uma caixa de chat e viu a IA montar a laje, erguer as paredes frontal, laterais e traseira e oferecer a continuação com escadas e um telhado. Não são objetos decorativos nem texturas, mas estruturas inteiras, inseridas em um mundo no qual outras pessoas podem entrar. Nosso pipeline também já cria os componentes: modelos 3D, músicas, sons e materiais.</p>
<p>Então estamos perto de abrir o Cinevva World ao público. E, antes de fazer isso, fui ler os obituários.</p>
<p>Porque esta é a verdade incômoda sobre o que estamos construindo. Há quinze anos, equipes muito inteligentes e muito bem financiadas tentam fazer mundos criativos compartilhados funcionarem. Alguns se tornaram algumas das maiores criações que a humanidade já construiu. A maioria está morta. A diferença entre os dois resultados não é talento nem dinheiro, porque alguns dos mortos tinham mais de ambos do que jamais teremos. A diferença está em um pequeno conjunto de decisões que quase ninguém acerta na primeira tentativa. Foi isto que descobri.</p>
<h2>O cemitério</h2>
<p>Comecemos pelos fracassos, porque eles são mais honestos do que as histórias de sucesso. Os vencedores reescrevem a própria história como uma narrativa impecável. Dos perdedores, vazam memorandos.</p>
<h3>Meta Horizon Worlds: a sala vazia mais cara já construída</h3>
<p>A Meta gastou dezenas de bilhões em sua aposta no metaverso. A Reality Labs registrou um <a href="https://www.cnbc.com/2026/03/18/meta-horizon-worlds-metaverse-vr.html">prejuízo operacional de US$ 6,02 bilhões em um único trimestre</a>, divulgado no início de 2026, e vinha perdendo vários bilhões por trimestre havia anos. O carro-chefe era o Horizon Worlds. Veja o que esse dinheiro comprou.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/mNbz7-ejnZw" title="Horizon Worlds — trailer oficial" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Horizon Worlds, da Meta, trailer oficial. A plataforma na qual a Meta gastou bilhões deixará de oferecer suporte a VR em junho de 2026 para sobreviver como um aplicativo móvel.</figcaption>
</figure>
<p>Um memorando interno vazado, noticiado pela <a href="https://fortune.com/2022/10/07/metas-horizon-world-quality-poor-not-even-employees-using-says-metaverse-vp-leaked-memo/amp/">Fortune</a> e por <a href="https://ryanschultz.com/2022/10/07/leaked-internal-memos-from-meta-detail-problems-with-horizon-worlds-and-horizon-workrooms/">Ryan Schultz</a>, revelou que apenas cerca de 9% dos mundos construídos pelos criadores chegaram a ser visitados por 50 pessoas ou mais. A grande maioria não recebeu nenhuma visita além da do próprio criador. Um memorando resumiu o problema de engajamento em uma frase que deveria ser tatuada em todo fundador desse setor: <a href="https://kotaku.com/meta-facebook-horizon-worlds-vr-mark-zuckerberg-1849669048">&quot;um mundo vazio é um mundo triste&quot;</a>. Pesquisadores que queriam entrevistar usuários ativos só conseguiram encontrar 514 deles, porque a base de jogadores era pequena assim.</p>
<p>Os números de retenção eram piores. Dados vazados mostraram que <a href="https://mixed-news.com/en/horizon-worlds-leak-only-1-in-10-users-return-web-launch-coming/">apenas cerca de 1 em cada 10 usuários retornava</a> após o primeiro mês. A Meta reduziu discretamente sua própria meta para 2022, de 500 mil usuários mensais para 200 mil, e, mesmo assim, a plataforma tinha menos usuários simultâneos do que o VRChat e menos do que o Second Life, um jogo de 2003. Painéis internos de dogfooding mostravam que nem os próprios funcionários da Meta usavam o produto, o que levou a direção a começar a responsabilizar os gerentes pelos logins semanais. Um vice-presidente colocou a equipe em um &quot;confinamento de qualidade&quot; durante o restante do ano e admitiu, por escrito, que estavam trabalhando em um produto que ainda não havia encontrado adequação ao mercado.</p>
<p>Em março de 2026, a Meta <a href="https://www.cnbc.com/2026/03/18/meta-horizon-worlds-metaverse-vr.html">anunciou que encerraria totalmente o suporte a VR</a> e transformaria o Horizon Worlds em um aplicativo quase exclusivamente móvel, um ponto de entrada para pessoas sem headsets, funcionando, segundo a própria descrição da empresa, como o Roblox. O metaverso mais caro já construído terminou sua vida tentando se tornar aquilo que deveria ter sido desde o primeiro dia.</p>
<p>A lição não é que &quot;era cedo demais para a VR&quot;, embora fosse. A lição é que nenhuma quantidade de capital preenche um mundo vazio. Densidade de conteúdo não é algo que se possa comprar com um orçamento de marketing. Se os mundos estão vazios, as pessoas vão embora; e, quando as pessoas vão embora, os mundos continuam vazios. Esse ciclo devorou a Meta.</p>
<h3>Decentraland e The Sandbox: as cidades-fantasma de um bilhão de dólares</h3>
<p>Em outubro de 2022, um único dado incendiou o mundo das criptomoedas. A <a href="https://www.coindesk.com/web3/2022/10/07/its-lonely-in-the-metaverse-decentralands-38-daily-active-users-in-a-13b-ecosystem">DappRadar informou</a> que a Decentraland tinha 38 usuários ativos diários. A The Sandbox tinha 522. Na época, cada um desses projetos tinha uma capitalização de mercado de aproximadamente US$ 1,3 bilhão.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/Zg5vcdEeLOA" title="Teaser oficial da The Sandbox" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">The Sandbox, teaser oficial. Ela e a Decentraland tinham avaliações de aproximadamente US$ 1,3 bilhão cada, enquanto seus usuários diários somavam de algumas centenas a poucos milhares.</figcaption>
</figure>
<p>Os projetos contestaram, e as nuances importam mais do que a provocação. O número 38 contabilizava apenas carteiras únicas que faziam transações on-chain em um dia — o que a DappRadar chama de &quot;pagantes&quot; —, não pessoas que entravam e circulavam pelo mundo sem interagir com a blockchain. A Decentraland respondeu que tinha <a href="https://blockworks.com/news/metaverse-platforms-set-the-record-straight-about-daily-active-users">56.697 usuários conectados por mês</a> e, segundo outras estimativas, cerca de 8 mil por dia. A The Sandbox alegou ter 39 mil usuários diários durante uma temporada alfa, com retenção de 40% no 14º dia.</p>
<p>Mas pare um instante para refletir sobre essa diferença, porque ela é o número mais importante de todo este ensaio. Mesmo considerando os números mais favoráveis, temos avaliações bilionárias sustentadas, na melhor das hipóteses, por alguns milhares de usuários diários. Para comparar, a pesquisa mostrou que o Roblox, que não usa blockchain, tinha <a href="https://blockworks.com/news/metaverse-platforms-set-the-record-straight-about-daily-active-users">52 milhões de usuários ativos diários, mas apenas 11,3 milhões de usuários pagantes mensais</a> no mesmo período. A maioria das pessoas em mundos virtuais gratuitos faz transações raramente ou nunca. Isso vale para os dois lados. Significa que a provocação dos 38 usuários ativos diários foi injusta e que a avaliação de um bilhão de dólares era uma fantasia. Ambas mediam o número errado. A avaliação media especulação com tokens. A provocação media transações de carteiras. Nenhuma delas media se alguém estava se divertindo.</p>
<p>Essa é a armadilha das métricas de vaidade em sua forma mais pura. Contas cadastradas, capitalização de mercado do token, mercado total endereçável, repercussão na imprensa: nada disso diz se o seu mundo está vivo. Os únicos números que dizem são quantas pessoas voltam e se quem cria coisas continua criando.</p>
<h3>Dreams: a ferramenta mais bonita na qual ninguém conseguia permanecer</h3>
<p>A Media Molecule, estúdio responsável por LittleBigPlanet, passou a maior parte de uma década desenvolvendo Dreams, uma ferramenta de criação tão profunda que as pessoas produziram dentro dela jogos completos, curtas-metragens de animação e álbuns musicais. A crítica adorou. E, em <a href="https://www.pushsquare.com/news/2023/07/dreams-live-service-ending-as-media-molecule-couldnt-find-a-sustainable-path">setembro de 2023, o estúdio encerrou o serviço ao vivo</a>, afirmando claramente que não havia conseguido encontrar um modelo de negócio sustentável.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/2ltgkcoQzow" title="Dreams | Trailer da data de lançamento | PS4" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Dreams, da Media Molecule. Uma ferramenta de criação adorada pelas pessoas, cujo serviço ao vivo terminou em 2023 sem nenhuma forma de os criadores ganharem um centavo.</figcaption>
</figure>
<p>A análise póstuma é uma lista de erros autoinfligidos, e digo isso com enorme respeito, porque poderíamos cometer todos eles. Dreams <a href="https://www.pushsquare.com/news/2023/07/dreams-live-service-ending-as-media-molecule-couldnt-find-a-sustainable-path">nunca permitiu que os criadores monetizassem seu trabalho</a>. A loja interna prometida nunca foi lançada. Não havia itens cosméticos, passe de batalha, nada. Criadores sérios, do tipo que transforma uma ferramenta em uma plataforma, não tinham motivo para investir anos quando não havia possibilidade de retorno. O lançamento foi exclusivo para PS4, sem versão para PC nem versão nativa para PS5, o que limitou drasticamente seu público possível. E o cofundador <a href="https://icon-era.com/threads/media-molecule-co-founders-biggest-regret-with-dreams-was-not-focusing-on-multiplayer-understands-new-ip-is-more-of-a-game-than-a-creative-tool.11724/">Mark Healey disse mais tarde que seu maior arrependimento foi não ter priorizado o modo multijogador</a> e que a equipe passou a entender que sua criação funcionava melhor como jogo do que como ferramenta criativa.</p>
<p>Leia essa última parte novamente. O estúdio de UGC mais talentoso do mundo concluiu, depois do fato, que aquilo deveria ter sido primeiro um jogo e depois uma ferramenta. Essa é a frase mais importante de todo este estudo, e voltaremos a ela.</p>
<h3>O restante do cemitério</h3>
<p>O padrão se repete com uma consistência sombria.</p>
<p>A <a href="https://www.gamedeveloper.com/business/crayta-to-shut-down-after-launch-platform-stadia-bites-the-dust">Crayta</a>, uma plataforma colaborativa de criação de UGC, encerrou as atividades em março de 2023, e os usuários perderam tudo o que haviam criado. Sua desenvolvedora foi adquirida pela Meta em 2021, e mesmo assim a plataforma foi encerrada. A causa imediata: ela foi lançada no Google Stadia e, quando o Stadia morreu, a Crayta morreu junto. Depender de uma plataforma é como apontar uma arma carregada para a própria cabeça.</p>
<figure style="margin:1.5rem 0">
<img src="https://cdn.cinevva.com/blog/lived-or-died-crayta.webp" alt="Crayta, a plataforma colaborativa de criação de jogos" loading="lazy" style="width:100%;border-radius:10px">
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">A Crayta foi lançada no Google Stadia. Quando o Stadia foi encerrado em 2023, a Crayta foi junto, e os criadores perderam tudo o que haviam feito. Imagem do trailer.</figcaption>
</figure>
<p>Project Spark, o criador de jogos UGC da Microsoft, foi encerrado anos antes seguindo a mesma trajetória: uma ferramenta poderosa, mas sem pessoas suficientes que permanecessem. Sansar, o sucessor em VR do Second Life criado pela Linden Lab, foi vendido depois de não conseguir encontrar público. High Fidelity, a segunda empresa de metaverso de Philip Rosedale, o próprio fundador do Second Life, abandonou completamente os mundos virtuais.</p>
<figure style="margin:1.5rem 0">
<img src="https://cdn.cinevva.com/blog/lived-or-died-secondlife.webp" alt="Second Life, o mundo virtual criado por usuários da Linden Lab" loading="lazy" style="width:100%;border-radius:10px">
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Second Life, o mundo lançado pela Linden Lab em 2003, continua funcionando depois de mais de duas décadas. Seus próprios sucessores em VR, Sansar e High Fidelity, de Philip Rosedale, fracassaram. Imagem do trailer.</figcaption>
</figure>
<p>E, em um sinal impossível de ignorar porque está acontecendo agora, a Rec Room <a href="https://www.uploadvr.com/vrchat-statement-after-rec-room-and-horizon-worlds-fold/">anunciou que encerrará as atividades em 1º de junho de 2026</a>, afirmando que nunca alcançou lucratividade sustentada.</p>
<p>Fundadores de empresas de metaverso, construindo metaversos, não conseguiam permanecer nos próprios metaversos. Isso deveria aterrorizar qualquer pessoa que pense que a renderização é a parte difícil.</p>
<figure style="margin:1.5rem 0">
<img src="https://cdn.cinevva.com/blog/lived-or-died-recroom.webp" alt="Rec Room, o mundo social de UGC multiplataforma" loading="lazy" style="width:100%;border-radius:10px">
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Rec Room, um dos maiores mundos sociais de criação, alcançou mais de 150 milhões de jogadores ao longo de sua existência e, ainda assim, encerrou as atividades em 1º de junho de 2026, sem jamais transformar esse alcance em um negócio sustentável. Imagem do trailer.</figcaption>
</figure>
<h2>Os sobreviventes e o que realmente os salvou</h2>
<p>Agora, os vivos. O mais instrutivo é que nenhum deles venceu da maneira que você esperaria, e vários venceram apesar de quebrarem regras que os fracassos seguiram.</p>
<h3>Roblox: o sucesso repentino que levou doze anos</h3>
<p>Todo mundo cita o Roblox como modelo. Quase ninguém copia o que realmente aconteceu, porque o que realmente aconteceu foram doze anos parecendo um fracasso.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/LTnMKjXEnMY" title="ROBLOX — Trailer do jogo" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Roblox. Atraiu cerca de 100 pessoas em 2006 e levou doze anos para alcançar um crescimento exponencial, tudo graças à possibilidade de a comunidade criar os jogos.</figcaption>
</figure>
<p>O beta do Roblox em 2006 atraiu <a href="https://www.howtheygrow.co/p/how-roblox-grows">cerca de 100 entusiastas de tecnologia, com um pico de 30 a 40 pessoas simultâneas</a>. Depois disso, avançou com dificuldade por mais de uma década. Passou de 9 milhões de usuários no início de 2016 para 90 milhões em 2019 e 214 milhões em 2023. O fundador David Baszucki se recusou a abandonar a visão de conteúdo gerado por usuários, mesmo quando o Minecraft disparou à sua frente em 2009. Ele tratou o projeto como algo que precisava amadurecer lentamente, de propósito, e a decisão que fez a diferença foi permitir que a comunidade criasse todos os jogos, em vez de produzi-los internamente. Isso eliminou o custo de conteúdo e transformou cada criador em um motor de crescimento.</p>
<p>Mas aqui está a parte que os imitadores superficiais ignoram, e é por isso que mantenho uma segunda fonte aberta. A <a href="https://www.matthewball.co/all/roblox2024">análise de 2024</a> de Matthew Ball mostra o Roblox operando com um prejuízo brutal mesmo em escala gigantesca, com uma margem operacional de aproximadamente 38% negativos, apenas 6% dos usuários comprando sua moeda em um determinado mês e uma receita por usuário que representa uma fração da obtida pelas plataformas de console. O Roblox é o maior jogo do mundo e ainda tem dificuldades para ganhar dinheiro. O ciclo de crescimento é real, e a economia é difícil. Quem disser que uma plataforma de criação é uma licença para imprimir dinheiro está tentando vender alguma coisa para você.</p>
<p>A lição mais profunda, porém, está escancarada nesses números de usuários. A esmagadora maioria das centenas de milhões de usuários do Roblox nunca cria nada. Eles aparecem para <em>jogar</em> um catálogo de dezenas de milhões de jogos feitos por outras pessoas. Esse catálogo levou uma década e uma economia de pagamentos a desenvolvedores para crescer. O Roblox não é uma ferramenta de criação que as pessoas por acaso jogam. É um lugar para jogar no qual uma pequena parcela das pessoas por acaso cria. Essa ordem é tudo.</p>
<h3>Minecraft: o mundo que não podia estar vazio</h3>
<p>O Minecraft nunca teve o problema do mundo vazio, e entender o motivo é o ponto central de tudo.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/MmB9b5njVbA" title="Trailer oficial do Minecraft" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Minecraft, trailer oficial. A sobrevivência procedural significava que o mundo nunca estava vazio e nunca precisava de conteúdo de usuários para valer a pena jogar.</figcaption>
</figure>
<p>O Minecraft foi lançado como um jogo de sobrevivência com geração procedural. O mundo se constrói sozinho, infinitamente, e logo no primeiro minuto você não está encarando uma tela em branco e se perguntando o que fazer. A noite está caindo e alguma coisa vai matar você, então você cava. O jogo é divertido para uma pessoa, sem nenhum outro usuário e sem nenhum conteúdo gerado por usuários, para sempre, porque o gerador procedural é uma máquina inesgotável de conteúdo. Os mods, os servidores, os mapas personalizados, todo o ecossistema criativo — tudo isso cresceu <em>sobre</em> um jogo que já se vendia sozinho. O modo criativo veio depois que milhões de pessoas já estavam viciadas na sobrevivência. O boca a boca e o YouTube fizeram o resto.</p>
<p>O Minecraft resolveu o problema do arranque a frio ao nunca ter esse problema. Esse é o truque, e vamos roubá-lo.</p>
<h3>Jogos de navegador: a distribuição como toda a vantagem competitiva</h3>
<p>Depois há o gênero mais próximo do nosso verdadeiro mecanismo de entrega: o jogo de navegador sem download.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/oznO3GGzu2s" title="Trailer de Krunker.io" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Krunker, um FPS de navegador na tradição dos jogos .io. Toda a vantagem competitiva do gênero é a distribuição: um link que carrega um jogo completo sem download.</figcaption>
</figure>
<p><a href="https://news.viverse.com/post/what-are-io-games">Agar.io alcançou cerca de 5 milhões de jogadores diários em poucas semanas</a> após seu lançamento em 2015. <a href="http://Slither.io">Slither.io</a> acumulou 68 milhões de downloads em dispositivos móveis e 67 milhões de partidas no navegador. A pesquisa é direta sobre o motivo: <a href="https://news.viverse.com/post/what-are-io-games">“a vantagem competitiva do gênero .io é a própria distribuição, não a mecânica do jogo.”</a> Você clica em um link e já está jogando. Sem instalação, sem conta, sem loja de aplicativos. <a href="https://medium.com/the-megacool-blog/9-growth-hacks-from-viral-hit-game-agar-io-7d8f71f70a0c">A entrada no Agar.io exigia um único clique</a>: digite um nome, aperte jogar e pronto. Ele se espalhou por aplicativos de conversa da mesma forma que vídeos se espalham no TikTok.</p>
<p>Mas há uma lâmina escondida nesse presente, e ela também nos atinge. Nenhum download significa nenhum compromisso. A mesma ausência de atrito que faz alguém entrar também facilita sua saída, porque fechar uma aba não custa nada. A viralidade no navegador é real e, por padrão, também é superficial. Os jogos .io sobreviveram a isso sendo imediata e obviamente divertidos nos primeiros dez segundos. Os mundos de navegador que morreram eram fáceis de acessar e não davam motivo algum para você ficar depois de chegar.</p>
<h3>Among Us, Figma, Townscaper, VRChat: mais quatro formas de vencer</h3>
<p>Quatro exemplos rápidos, porque cada um isola uma alavanca diferente.</p>
<p><a href="https://www.esports.net/news/how-among-us-blew-up-online-two-years-after-release/">Among Us</a> foi lançado em 2018 e não teve nenhum resultado comercial durante dois anos. Os desenvolvedores quase o abandonaram. Então, em setembro de 2020, uma <a href="https://howtomarketagame.com/2020/09/14/among-us-the-4-lessons-of-their-viral-success/">sequência de streamers cada vez maiores</a> o descobriu, culminando em uma transmissão de várias horas de Sodapoppin, e ele se tornou um fenômeno global. O impulso inicial veio de um destaque na página principal do <a href="http://itch.io">itch.io</a> e de um público coreano que passou a representar metade de suas vendas. Lição: o grande salto pode acontecer anos após o lançamento, vindo de um canal que você não planejou, se você sobreviver tempo suficiente para ser descoberto.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/NSJ4cESNQfE" title="Trailer de lançamento de Among Us no Steam" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Among Us. Passou dois anos praticamente despercebido antes que uma sequência de streamers o transformasse em um fenômeno global em 2020.</figcaption>
</figure>
<p>O <a href="https://review.firstround.com/the-5-phases-of-figmas-community-led-growth-from-stealth-to-enterprise/">Figma</a> permaneceu em desenvolvimento secreto por cerca de três anos, foi lançado gratuitamente e esperou mais dois anos antes de começar a cobrar. Seu momento de ativação era o modo multijogador: o instante em que um designer percebia outra pessoa editando o mesmo arquivo. E aqui está a parte importante. O plano gratuito do Figma originalmente limitava você a 2 colaboradores, sufocando justamente o momento mágico que vendia o produto, e a empresa corrigiu isso tornando o número de colaboradores ilimitado, para que as pessoas realmente pudessem vivenciar aquilo. Eles deram o impulso inicial à comunidade abordando diretamente influenciadores de design no Twitter. Lição: encontre seu momento “ahá” e, então, remova todos os obstáculos entre um novo usuário e esse momento.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/n5gJgkO2Dg0" title="Apresentação de lançamento do Figma" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Figma. Seu momento de ativação era o modo multijogador, então eliminou o limite de colaboradores do plano gratuito que bloqueava justamente a magia que vendia o produto.</figcaption>
</figure>
<p><a href="https://www.gamedeveloper.com/game-platforms/how-townscaper-works-a-story-four-games-in-the-making">Townscaper</a> era um pequeno brinquedo criativo que se tornou um sucesso graças às capturas de tela. Seu desenvolvedor viu o número de seguidores no Twitter saltar de cerca de 20.000 para 90.000 apenas publicando clipes durante o desenvolvimento e interpretou isso como um sinal inicial de que o jogo teria um bom desempenho. Lição: se o que você criou é bonito e as pessoas o compartilham espontaneamente antes mesmo do lançamento, essa é a validação mais verdadeira que existe.</p>
<figure style="margin:1.5rem 0">
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:10px">
<iframe src="https://www.youtube-nocookie.com/embed/hqq25n6cQqo" title="Trailer de lançamento de Townscaper" loading="lazy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
</div>
<figcaption style="color:#9aa7b4;font-size:0.85rem;margin-top:0.5rem">Townscaper, trailer de lançamento. Um brinquedo criativo sem objetivos que se espalhou graças às capturas de tela compartilhadas espontaneamente pelas pessoas.</figcaption>
</figure>
<p>E o <a href="https://www.uploadvr.com/vrchat-statement-after-rec-room-and-horizon-worlds-fold/">VRChat</a>, nos mesmos meses em que tanto os bilhões da Meta quanto o Rec Room fracassaram, atingiu um recorde de quase 160.000 usuários simultâneos. Ele é movido pela comunidade, movido por criadores e não chega nem perto de ter o mesmo financiamento das iniciativas que morreram ao seu redor. O VRChat atribui sua sobrevivência à lealdade e à criatividade da comunidade, não ao capital. Lição, nas palavras do próprio sobrevivente: a paixão de criadores engajados dura mais que o dinheiro.</p>
<h2>O que realmente separa os vivos dos mortos</h2>
<p>Coloque as histórias lado a lado e o padrão fica evidente.</p>
<table>
<thead>
<tr>
<th>Plataforma</th>
<th>Em que apostou</th>
<th>Onde chegou</th>
<th>A lição</th>
</tr>
</thead>
<tbody>
<tr>
<td>Minecraft</td>
<td>Sobrevivência procedural, divertida para um jogador</td>
<td>Jogo mais vendido de todos os tempos, mais de 350 milhões de cópias</td>
<td>Nunca tenha um mundo vazio</td>
</tr>
<tr>
<td>Roblox</td>
<td>Jogos criados pela comunidade mais pagamentos a criadores</td>
<td>Mais de 80 milhões de usuários diários ao longo de doze anos</td>
<td>Seja primeiro um lugar para jogar, depois uma ferramenta</td>
</tr>
<tr>
<td>Jogos .io</td>
<td>Jogo de navegador com um clique e sem download</td>
<td>Milhões de jogadores em semanas</td>
<td>A distribuição é a vantagem competitiva, o compromisso é superficial</td>
</tr>
<tr>
<td>Figma</td>
<td>Plano gratuito, modo multijogador em tempo real</td>
<td>Uma oferta de aquisição de cerca de US$ 20 bilhões</td>
<td>Elimine todas as barreiras até o momento “ahá”</td>
</tr>
<tr>
<td>Among Us</td>
<td>Um jogo social barato e paciência</td>
<td>3,8 milhões de usuários simultâneos, dois anos depois</td>
<td>Sobreviva tempo suficiente para ser encontrado</td>
</tr>
<tr>
<td>Townscaper</td>
<td>Um brinquedo bonito que valia uma captura de tela</td>
<td>Cerca de 380 mil vendas, disseminação orgânica</td>
<td>O compartilhamento espontâneo é o sinal mais verdadeiro</td>
</tr>
<tr>
<td>VRChat</td>
<td>Energia da comunidade e dos criadores</td>
<td>Cerca de 160 mil usuários simultâneos em 2026</td>
<td>A paixão dura mais que o capital</td>
</tr>
<tr>
<td>Horizon Worlds</td>
<td>Bilhões em capital, foco inicial em RV</td>
<td>RV encerrada em 2026, cerca de 1 em cada 10 retornava</td>
<td>Dinheiro não consegue preencher um mundo vazio</td>
</tr>
<tr>
<td>Decentraland / Sandbox</td>
<td>Uma economia de tokens antes de um jogo</td>
<td>Avaliações de cerca de US$ 1,3 bilhão, centenas de usuários diários</td>
<td>Uma avaliação de mercado não é um público</td>
</tr>
<tr>
<td>Dreams</td>
<td>Uma ferramenta profunda, sem economia nem modo multijogador</td>
<td>Serviço contínuo encerrado em 2023</td>
<td>Pague os criadores, lance o modo multijogador, seja um jogo</td>
</tr>
<tr>
<td>Crayta</td>
<td>Uma plataforma de CGU hospedada no Stadia</td>
<td>Encerrada com o Stadia, em 2023</td>
<td>Nunca construa sobre uma plataforma que pode expulsar você</td>
</tr>
<tr>
<td>Project Spark / Sansar</td>
<td>Ferramentas poderosas, nenhum motivo para voltar</td>
<td>Encerrados ou redirecionados</td>
<td>Uma ferramenta sem jogo não cria um hábito diário</td>
</tr>
</tbody>
</table>
<p>O que mata mundos criativos é a sala vazia. Horizon Worlds, Decentraland, Dreams, Crayta, Project Spark e Sansar morreram todos da mesma forma: poucas pessoas, pouco conteúdo, poucos motivos para voltar, em um ciclo que se retroalimenta. O problema do arranque a frio é a batalha contra o chefão. Todo o resto é uma fase de tutorial.</p>
<p>O que os salva é ter um motivo para existir antes que a multidão apareça. O Minecraft tinha a sobrevivência procedural. O Roblox tinha uma década de jogos cultivados pacientemente. Os jogos .io tinham diversão em dez segundos. Todo sobrevivente já valia seu tempo com um catálogo vazio. Toda vítima era um belo recipiente esperando que outras pessoas fizessem dele um lugar que valesse a visita. Esta é a confissão do Dreams transformada em princípio geral: crie primeiro um jogo e depois uma plataforma. A plataforma é o segundo ato. Você conquista esse direito sendo primeiro algo que as pessoas usam por seu próprio valor.
As métricas que realmente preveem a sobrevivência não são as que aparecem no comunicado à imprensa. As pesquisas são consistentes quanto a isso. O sinal definitivo de adequação do produto ao mercado é que sua <a href="https://newsletter.pmcurve.com/p/retention-masterclass">curva de retenção se estabiliza</a>: algum grupo de pessoas continua voltando para sempre. Uma curva que cai até zero significa que não há adequação, por maior que seja o topo do funil. As referências gerais do setor são <a href="https://solsten.io/blog/d1-d7-d30-retention-in-gaming">40% de retenção no dia 1, 20% no dia 7 e 10% no dia 30</a>, sendo o dia 30 o indicador que prevê a saúde no longo prazo. E, para qualquer produto com um lado voltado aos criadores, é no lado da oferta que você morre: <a href="https://forkoff.xyz/blog/founder-growth/two-sided-marketplace-cold-start-2026">cerca de 67% dos marketplaces que fracassam entram em colapso por falta de oferta</a>, não de demanda. A retenção de criadores é o sinal vital. Contas registradas e avaliações de mercado são métricas de vaidade.</p>
<p>Há mais algumas conclusões que emergem dessas histórias. Monetize seus criadores ou perca-os, porque Dreams provou que pessoas talentosas não investirão anos sem perspectiva de retorno. O multiplayer costuma ser o ponto central, não um recurso, segundo o próprio arrependimento da Media Molecule. Nunca construa sua casa em uma plataforma que possa despejá-lo, como mostrou Crayta. E não exigir download é uma faca de dois gumes: é o melhor canal de aquisição que existe e o mecanismo de compromisso mais fraco, então toda a responsabilidade recai sobre a qualidade dos primeiros sessenta segundos.</p>
<h2>A armadilha por trás de tudo isso</h2>
<p>Há mais uma camada, específica do momento em que vivemos, porque temos uma ferramenta que os mortos nunca tiveram. Podemos gerar mundos com IA. O que levanta um paradoxo sobre o qual mudei de opinião várias vezes nas últimas semanas.</p>
<p>Se é IA, cada mundo não deveria ser único, gerado do zero para cada pessoa e adaptado a ela? Mas, se o mundo de cada pessoa é único, como alguém vai esbarrar em outra pessoa, já que um momento compartilhado exige um lugar compartilhado? E, se o mundo precisa ser compartilhado de qualquer forma, por que usar IA, em vez de criar artesanalmente um lugar lindo como faria um estúdio de verdade?</p>
<p>Cada um desses impulsos, levado até as últimas consequências, termina em algo que não funciona. Um mundo gerado só para você é um mundo sem ninguém dentro. O Genie, do Google, pode criar um ambiente jogável a partir de uma frase, e ele desaparece um minuto depois: é para um jogador, sem memória, sem vizinhos. Esse é o beco sem saída solitário. Mas criar artesanalmente um único mundo fixo também é um beco sem saída: exige cem pessoas e vários anos de trabalho para preencher dois quilômetros quadrados à mão e, quando você termina, o mundo fica congelado, igual na centésima visita e na primeira, incapaz de crescer à medida que as pessoas chegam.</p>
<p>A saída é parar de tratar “o mundo” como uma coisa só. Ele tem três camadas, e a singularidade e o compartilhamento podem existir em camadas diferentes sem entrar em conflito. O chão onde todos pisam é um único mundo compartilhado, cuidadosamente selecionado segundo um padrão elevado, o ponto de encontro. O que você leva até ele — os recursos gerados por IA, o edifício que criou — é radicalmente único, e você deposita tudo isso <em>no</em> mundo compartilhado, de modo que sua singularidade se torna a descoberta de todos os outros. E seu percurso por ele — as missões que recebe, aquilo para onde ele direciona você — é personalizado sem custo, porque um percurso não é um lugar. A falsa escolha era entre “um mundo personalizado”, que mata o multiplayer, e “um percurso personalizado por um mundo compartilhado”, que o aprofunda. Queremos a segunda opção.</p>
<p>Portanto, o trabalho da IA nunca foi dar a cada pessoa um mundo privado. Seu trabalho é fazer com que um único mundo compartilhado pareça infinito, vivo e pessoal de explorar. A singularidade vem de tudo o que se acumula nele ao longo do tempo, de cada contribuição única dos criadores. Cada servidor de Minecraft é único. Todos em um determinado servidor o compartilham. Isso não é uma contradição. Esse é o grande truque, e a captura de tela no topo deste post — a IA construindo uma casa que permanece em um mundo no qual outras pessoas podem entrar — é esse truque concretizado.</p>
<h2>Como vamos lançar o Cinevva World</h2>
<p>Então, é nisto que tudo isso se transforma para nós. Não é um plano de marketing. São cinco apostas, feitas deliberadamente contra as cinco coisas que mataram todos na primeira metade deste texto. E vou avaliar cada uma de acordo com o quanto as evidências realmente a sustentam, porque um plano que você não pode avaliar é apenas um desejo com boa iluminação.</p>
<p><strong>Aposta número um, aquela em que eu colocaria a empresa em jogo: vamos lançar um jogo, não uma plataforma.</strong> Isso não é uma preferência, mas a conclusão mais consistente de todo o estudo. Minecraft, Roblox e Fortnite começaram com algo divertido para <em>fazer</em> e desenvolveram a camada de criação em torno disso. A Media Molecule, o estúdio de criação mais talentoso em atividade, concluiu depois dos fatos que Dreams deveria ter sido um jogo primeiro. Portanto, a primeira coisa que uma pessoa nova fará no Cinevva World não será olhar para um terreno vazio. Será participar de um ciclo de jogo com um objetivo, uma razão para estar lá numa terça-feira em que quase ninguém mais está online. A maioria das pessoas que chegar jogarão sem nunca construir, e isso faz parte do design, não é um fracasso. Esse é o verdadeiro formato do Roblox: centenas de milhões de pessoas que jogam, sustentadas por uma fina camada de pessoas que criam. O ciclo virtuoso é o segundo ato, e precisamos conquistá-lo. Tenho certeza dessa aposta porque o cemitério já realizou o experimento.</p>
<p><strong>Aposta número dois, aquela que honestamente é um risco: a IA nos permite produzir a oferta pela qual todos os outros morreram esperando.</strong> Quero explicar com precisão por que isso é uma aposta, não uma garantia, porque nenhuma plataforma deste estudo começou com IA generativa. Tenho um mecanismo, não uma prova. O mecanismo: aproximadamente dois terços das plataformas de dois lados morrem por falta de oferta, não de demanda. A solução clássica é simular a oferta até que ela se torne real, como quando o próprio DoorDash entregava a comida, a equipe do Reddit publicava usando identidades falsas e os fundadores do Roblox criaram manualmente os primeiros jogos durante anos. Podemos fazer isso com geração, a um custo e em uma velocidade que nenhum deles teve. Mas aqui está a parte que me mantém com os pés no chão, representada pela lápide do Decentraland: o Decentraland estava cheio de conteúdo gerado e atraía cerca de trinta pessoas por dia, porque algo bonito não é necessariamente algo que merece seu tempo. Portanto, não estou apostando que a IA torna o mundo divertido. Ela não consegue. Estou apostando que a IA elimina o gargalo de recursos para que cada hora do nosso escasso esforço humano seja dedicada à única coisa que realmente retém as pessoas: o ciclo de jogo. A IA constrói a colina com baixo custo. Nós criamos artesanalmente o motivo para escalá-la. E limitamos o conteúdo sintético inicial a <a href="https://forkoff.xyz/blog/founder-growth/two-sided-marketplace-cold-start-2026">aproximadamente 30%, convertendo-o em conteúdo de criadores reais em cerca de 60 dias</a>, porque uma oferta artificial que nunca se torna real destrói a confiança assim que alguém percebe.</p>
<p><strong>Aposta número três: publicamos nossos próprios critérios de encerramento.</strong> Essa é a diferença entre um plano fundamentado e um plano baseado apenas em esperança. A métrica que previu a sobrevivência em todos os casos foi se a <a href="https://newsletter.pmcurve.com/p/retention-masterclass">curva de retenção se estabiliza</a>, se algum grupo continua voltando em vez de decair até zero. Portanto, esse é o teste ao qual nos submeteremos publicamente. Se a retenção no dia 7 da versão alfa não se estabilizar, se os jogadores que nunca constroem não voltarem, se o conteúdo inicial gerado por IA for ignorado enquanto as pessoas passam direto por ele, a tese estará errada, e nós a mudaremos ou encerraremos o projeto. Vamos acompanhar jogadores e criadores como dois funis separados, com dois momentos de descoberta de valor distintos, porque os dados deixam claro que não são a mesma pessoa. E não fingiremos que o dinheiro faz o trabalho inicial. Com cinquenta usuários, ou até mil, as gorjetas e os ganhos do marketplace são praticamente zero, portanto ainda não retêm ninguém. A retenção inicial vem do ciclo de jogo ou não vem de lugar nenhum. Dreams é o comprovante do que acontece quando se inverte essa ordem.</p>
<p><strong>Aposta número quatro: começamos de propósito com um escopo reduzido.</strong> As pesquisas sobre produtos em rede são unânimes. Você não lança para um mercado; lança para uma <a href="https://www.lennysnewsletter.com/p/atomic-network">“rede atômica”</a>, o menor grupo denso o bastante para se manter vivo por conta própria. O Facebook começou em uma única universidade. A nossa já existe: os criadores da Cinevva que geraram recursos 3D, música e som e já têm um acervo para colocar no mundo. Cinquenta deles em um único mundo, no mesmo horário, com um objetivo e uma fronteira, formam algo mais denso e vivo do que cinquenta mil cadastros dispersos jamais formariam. As evidências sustentam fortemente essa aposta.</p>
<p><strong>Aposta número cinco, a menos sólida, e vou admitir isso: contamos a verdade sobre o vazio em vez de escondê-lo.</strong> Um mundo refinado e silencioso parece morto, e todos os projetos fracassados tentaram disfarçar isso com efeitos de ambientação. Vamos tentar o oposto. Essa é a iniciativa menos comprovada das cinco, mais próxima de um palpite do que de uma conclusão. As primeiras pessoas a entrar não estarão visitando um produto inacabado. Serão os cidadãos fundadores de um lugar que está vazio <em>porque chegaram cedo</em>, e aquilo que construírem ainda estará de pé quando os próximos mil chegarem.</p>
<p>Esse é o plano, avaliado com honestidade. Quatro apostas fortemente sustentadas pelo cemitério e uma aposta de verdade em uma ferramenta que nenhum dos mortos possuía, delimitada exatamente pela disciplina que lhes faltou. Podemos gerar a colina. Não podemos gerar a sensação de ser o primeiro a subi-la, nem o estranho que acena do topo. Nosso trabalho é tornar esse aceno possível, comprová-lo na curva de retenção e parar de nos enganar assim que a curva disser não.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando um mundo aberto no navegador, parte 30: Uma câmera que respeita as paredes]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-06-04-open-world-browser-part-30-collision-aware-camera</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-06-04-open-world-browser-part-30-collision-aware-camera</guid>
            <pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Vinte e nove partes construíram um mundo no qual você pode caminhar, nadar e construir, tudo observado por uma câmera que atravessava diretamente colinas e paredes de casas. A parte 30 corrige isso com uma haste sensível a colisões: um braço com mola que controla apenas o próprio comprimento, um contrato de colisores plugáveis para que terreno, objetos de cena e construções impeçam a passagem da câmera pela mesma interface e uma sonda de distância com sinal que respeita aquilo que um mapa de altura jamais conseguiria representar, saliências e cavernas. Tudo isso se integra como um pós-processamento sobre o OrbitControls sem reescrever uma única linha dos controles de órbita em que já confiávamos.]]></description>
            <content:encoded><![CDATA[<h1>Criando um mundo aberto no navegador, parte 30: Uma câmera que respeita as paredes</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um protótipo exploratório e reúne links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">Parte 29</a> nos deu um único controlador capaz de conduzir qualquer corpo. Agora o corpo se move corretamente. Ele caminha, desliza, nada, plana, entra em cavernas escalando e se abaixa sob saliências. O problema é o que o observa. Durante vinte e nove partes, a câmera foi um sistema de órbita padrão que seguia o jogador e fazia exatamente uma coisa inteligente para evitar constrangimentos: recusava-se a inclinar abaixo do horizonte, para não deslizar sob um terreno plano. Essa limitação entrega o problema. Ela existe porque a câmera não fazia ideia de onde a geometria do mundo realmente estava, então a única defesa contra atravessar superfícies era proibir os ângulos em que isso tinha maior probabilidade de acontecer. Aproxime-se de uma colina e a câmera ficava dentro dela. Entre em uma das cavernas de marching cubes da <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Parte 7</a> e você passava a olhar para o interior de uma rocha. Construa uma casa com as ferramentas de autoria da <a href="/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring">Parte 16</a>, fique em um cômodo e a câmera flutuava do lado de fora da parede, olhando para o revestimento. Esta parte faz a câmera respeitar a geometria tanto quanto o corpo já respeita.</p>
<h2>O formato do problema e o formato da solução</h2>
<p>Uma câmera em terceira pessoa tem uma tarefa difícil e uma dúzia de tarefas fáceis. As fáceis são acompanhar, suavizar e processar os controles de órbita, e já tínhamos tudo isso. A difícil é aquilo que a literatura acadêmica chama de restrição de visibilidade, e o estudo que todos citam, <em>Camera Control in Computer Graphics</em>, de Christie e Olivier, enquadra toda a área em torno dela: manter o personagem enquadrado e sem obstruções, respeitando o mundo. Em tempo de execução, isso se reduz a uma pergunta aparentemente simples, feita a cada quadro. O jogador é o pivô. O usuário girou e aplicou zoom até uma posição desejada da câmera, a certa distância atrás dele. Até onde a câmera realmente pode recuar ao longo dessa linha antes de penetrar em algo sólido? Responda a isso corretamente e a câmera se recolhe diante da colina, desliza pela haste quando você recua para um canto e para no teto da caverna em vez de atravessá-lo.</p>
<p>O padrão que resolve isso é antigo e comprovado. A Unreal o chama de spring arm, a Godot oferece um nó <code>SpringArm3D</code>, e o Cinemachine da Unity o divide entre um sistema de acompanhamento em terceira pessoa e uma extensão de desoclusão. A ideia é sempre a mesma. Pendure a câmera na extremidade de uma haste ancorada no pivô. Mantenha-a no comprimento desejado quando o caminho estiver livre, retraia-a em direção ao pivô quando algo estiver no caminho e faça-a retornar com efeito de mola quando o caminho ficar livre. <em>Real-Time Cameras</em>, de Mark Haigh-Hutchinson, escrito pelo responsável pelas câmeras de Metroid Prime, dedica capítulos inteiros aos modos de falha que transformam uma versão ingênua disso em algo capaz de deixar os jogadores enjoados. Adotamos o padrão e criamos nossa própria implementação, pequena o bastante para ser lida de uma só vez, em <code>public/world/src/camera-rig.mjs</code>.</p>
<figure style="margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12);border-radius:8px;overflow:hidden;background:#0d1117">
<svg viewBox="0 0 680 290" width="100%" role="img" aria-label="Uma haste com mola se retraindo ao longo da própria linha para que a esfera da sonda encoste em uma parede em vez de atravessá-la" style="display:block">
  <rect x="382" y="40" width="46" height="222" fill="#39424f" stroke="#5b6675"/>
  <text x="405" y="280" fill="#9aa7b4" font-size="13" text-anchor="middle" font-family="sans-serif">parede</text>
  <line x1="110" y1="212" x2="600" y2="70" stroke="#9aa7b4" stroke-width="2" stroke-dasharray="6 6"/>
  <line x1="110" y1="212" x2="360" y2="140" stroke="#6ea8fe" stroke-width="3"/>
  <circle cx="110" cy="212" r="6" fill="#e6edf3"/>
  <text x="110" y="234" fill="#e6edf3" font-size="13" text-anchor="middle" font-family="sans-serif">pivô (cabeça do jogador)</text>
  <circle cx="600" cy="70" r="9" fill="none" stroke="#9aa7b4" stroke-width="2"/>
  <text x="600" y="52" fill="#9aa7b4" font-size="13" text-anchor="middle" font-family="sans-serif">desejado (zoom do usuário)</text>
  <circle cx="360" cy="140" r="22" fill="rgba(110,168,254,0.18)" stroke="#6ea8fe" stroke-width="2"/>
  <circle cx="360" cy="140" r="5" fill="#6ea8fe"/>
  <text x="300" y="104" fill="#6ea8fe" font-size="13" text-anchor="middle" font-family="sans-serif">câmera + raio r da sonda</text>
  <text x="222" y="192" fill="#6ea8fe" font-size="13" font-family="sans-serif" transform="rotate(-16 222 192)">ℓ (limitado)</text>
</svg>
<figcaption style="padding:0.6rem 1rem;color:#9aa7b4;font-size:0.85rem;border-top:1px solid rgba(255,255,255,0.12);font-family:sans-serif">A haste mantém a câmera no zoom desejado pelo usuário até que um obstáculo invada seu caminho; então ela se retrai ao longo da mesma linha para que a esfera da sonda encoste na superfície, em vez de o plano próximo atravessá-la.</figcaption>
</figure>
<h2>Uma haste que controla apenas o próprio comprimento</h2>
<p>A regra de design que manteve o sistema pequeno foi tomada emprestada do trabalho com o controlador na Parte 29: controle uma única coisa por completo e recuse todo o resto. O sistema controla o comprimento da haste e nada mais. Guinada, inclinação, amortecimento da órbita, gestos de toque e da roda do mouse: tudo isso permanece com o OrbitControls, que já resolve bem esses aspectos e que não temos qualquer interesse em reescrever. Portanto, o sistema não é um controlador de câmera. É um pós-processamento executado depois dos cálculos de órbita que corrige exatamente um número: a distância entre o pivô e a câmera.</p>
<p>Essa decisão parece organizada, mas quase deu errado de imediato por causa da maneira como o OrbitControls funciona. No início de cada atualização, ele lê a posição atual da câmera e deriva dela o raio da órbita. Normalmente isso é imperceptível. Porém, no instante em que nosso sistema aproxima a câmera para desviar de uma parede, no quadro seguinte o OrbitControls lê essa posição encurtada, conclui que o usuário deve ter aproximado o zoom e incorpora o encurtamento ao zoom desejado pelo usuário. Depois de alguns quadros, a câmera desaba sobre a cabeça do jogador e não volta a se afastar. A solução consiste em duas chamadas que envolvem a atualização da órbita, e isso representa toda a integração. Antes da execução do OrbitControls, <code>beforeControls()</code> restaura a câmera à distância completa e não encurtada do quadro anterior, para que os cálculos da órbita sempre leiam o zoom verdadeiro do usuário. Depois da execução do OrbitControls, <code>afterControls(dt)</code> lê a posição desejada recém-calculada pela órbita, resolve a colisão, amortece o comprimento e posiciona a câmera onde ela realmente deve renderizar. A intenção do usuário e a correção de colisão nunca interferem uma na outra, e o movimento de aproximação continua exatamente tão responsivo quanto era antes da existência do sistema. O teste sem interface gráfica que escrevemos para o sistema verifica isso com precisão: empurre a haste contra uma parede por sessenta quadros, remova a parede e o comprimento retorna com efeito de mola ao zoom completo de dez metros do usuário, em vez de ficar preso à distância da colisão.</p>
<h2>Um contrato, qualquer colisor</h2>
<p>O sistema nunca pergunta do que o mundo é feito. Um colisor é apenas um objeto com um método <code>probe</code>, e o sistema fornece a ele um raio, uma distância máxima e o raio da sonda da câmera, recebendo de volta um número: a distância máxima que a câmera pode percorrer antes de ser bloqueada por esse colisor. O sistema consulta todos os colisores registrados e considera o impacto mais próximo. Esse é o contrato inteiro, seguindo o mesmo princípio empregado pelo controlador de personagem quando transformou a locomoção em comportamentos plugáveis. O terreno se conecta, os objetos de cena se conectam, as estruturas das construções se conectam, cada um por trás do mesmo <code>probe</code>, e o sistema permanece sem conhecer nenhum deles. Adicionar um novo tipo de obstáculo significa adicionar um colisor a uma lista, não editar a câmera.</p>
<p>Um detalhe do contrato prova seu valor: o raio da sonda. Não lançamos um raio fino do pivô até a câmera; lançamos uma esfera grande o bastante para conter o plano próximo da câmera. Um único raio impede que o centro da câmera atravesse a parede, mas o plano próximo tem largura, então seus cantos já estariam enterrados na parede antes que o raio central sequer registrasse um impacto. Varrer uma pequena esfera em vez de um raio é o que toda implementação publicada faz — a Unreal expõe isso como o tamanho da sonda — e essa é a diferença entre uma câmera que repousa corretamente contra uma superfície e outra que permite enxergar através dela nas bordas da tela.</p>
<p>Não estimamos esse raio; nós o derivamos. O ponto do plano próximo mais distante da câmera é um de seus cantos, e a distância até ele resulta diretamente da projeção. Com o plano próximo $n$ e o campo de visão vertical $\theta$, a meia-altura é $h = n\tan(\theta/2)$, a meia-largura é $w = h\cdot\text{aspect}$, e o canto fica em</p>
<p>$$
r_{\text{near}} = \sqrt{n^2 + w^2 + h^2}
$$</p>
<p>O raio da sonda é essa distância até o canto multiplicada por uma pequena margem de segurança, com um mínimo fixo para nunca cair abaixo de um valor razoável em um frustum muito estreito. Sempre que o campo de visão, a proporção da tela ou o plano próximo muda, o raio é recalculado, para que um redimensionamento da janela ou um zoom que altere a projeção não deixe silenciosamente a sonda pequena demais para cobrir os cantos que deveria proteger.</p>
<h2>O colisor que conhece as cavernas</h2>
<p>É no colisor do terreno que isso fica interessante, porque o terreno do nosso motor não é um mapa de altura. Desde a Parte 7, ele é um campo de distância com sinal, uma função que retorna a distância entre qualquer ponto no espaço e a superfície sólida mais próxima, além de indicar se esse ponto está dentro ou fora da rocha. Positivo é ar, negativo é rocha, e esse único fato permite que nossa câmera faça algo que uma câmera baseada em mapa de altura é estruturalmente incapaz de fazer. Um mapa de altura conhece a altura do solo em determinada coordenada x e z. Ele não tem o conceito de teto, porque só existe uma superfície acima de qualquer ponto. Assim, uma câmera baseada em mapa de altura pode impedir que você entre em uma colina, mas não faz ideia de que a borda de uma saliência ou o teto de uma caverna está suspenso sobre o pivô, então atravessa ambos sem parar. Um campo de distância conhece todas as superfícies em três dimensões, portanto a mesma sonda que impede a câmera de entrar na encosta de uma colina também a impede de atravessar o teto de uma caverna, sem um único caso especial.</p>
<figure style="margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12);border-radius:8px;overflow:hidden;background:#0d1117">
<svg viewBox="0 0 680 320" width="100%" role="img" aria-label="Sob uma saliência, um mapa de altura deixa a haste atravessar a rocha do teto, enquanto um campo de distância com sinal a detém na superfície inferior do teto" style="display:block">
  <path d="M680,320 L680,40 L250,40 L250,150 L430,150 L430,250 L250,250 L250,320 Z" fill="#39424f" stroke="#5b6675" stroke-width="1.5"/>
  <text x="340" y="205" fill="#9aa7b4" font-size="13" text-anchor="middle" font-family="sans-serif">caverna</text>
  <text x="540" y="100" fill="#5b6675" font-size="13" text-anchor="middle" font-family="sans-serif">rocha sólida</text>
  <line x1="330" y1="212" x2="230" y2="60" stroke="#9aa7b4" stroke-width="2" stroke-dasharray="6 6"/>
  <line x1="290" y1="150" x2="250" y2="90" stroke="#f85149" stroke-width="4"/>
  <circle cx="330" cy="212" r="6" fill="#e6edf3"/>
  <text x="330" y="234" fill="#e6edf3" font-size="13" text-anchor="middle" font-family="sans-serif">jogador</text>
  <circle cx="230" cy="60" r="10" fill="none" stroke="#f85149" stroke-width="2"/>
  <text x="230" y="42" fill="#f85149" font-size="13" text-anchor="middle" font-family="sans-serif">mapa de altura: atravessa ✗</text>
  <circle cx="305" cy="170" r="9" fill="rgba(86,211,100,0.2)" stroke="#56d364" stroke-width="2"/>
  <text x="420" y="172" fill="#56d364" font-size="13" text-anchor="middle" font-family="sans-serif">SDF: para no teto ✓</text>
</svg>
<figcaption style="padding:0.6rem 1rem;color:#9aa7b4;font-size:0.85rem;border-top:1px solid rgba(255,255,255,0.12);font-family:sans-serif">Um mapa de altura armazena uma superfície por coluna, portanto nunca detecta a placa de rocha acima do jogador e deixa a haste atravessar o teto. O campo de distância é negativo dentro dessa placa, então a sonda entra em contato com ela e a câmera permanece logo abaixo do teto da caverna.</figcaption>
</figure>
<p>Percorrer o campo com a sonda é uma técnica com nome próprio e um artigo acadêmico por trás. <em>Sphere Tracing</em>, de John Hart, publicado em 1996, é o método padrão para avançar um raio por um campo de distância, e o segredo é que o campo não informa apenas se você atingiu algo: ele também informa uma distância segura pela qual você pode avançar sem atingir nada. Assim, em vez de avançar lentamente em pequenos passos fixos, você amostra o campo, avança pela folga informada e repete o processo, dando passos longos pelo ar livre e passos curtos e cuidadosos conforme se aproxima de uma superfície.
Mas há uma ressalva imposta pelo nosso terreno. Um campo de distância verdadeiro informa a distância euclidiana real até a superfície mais próxima, e avançar todo o valor dessa folga é sempre seguro. Porém, onde o terreno ainda é um mapa de altura, em vez de voxels escavados, o campo que podemos amostrar de forma barata não representa a distância verdadeira, mas a folga vertical, o espaço diretamente abaixo até o chão. Em uma encosta, esse número superestima a distância que a câmera realmente pode percorrer, porque a rocha mais próxima está ao lado, não diretamente abaixo. A distância correta é menor por um fator que cresce com o gradiente, $\sqrt{1 + \lVert\nabla h\rVert^2}$, portanto um passo dimensionado conforme a folga informada ultrapassa o limite e pode saltar completamente por cima de uma crista. A solução é a sub-relaxação: avançar apenas uma fração da folga informada, em vez de percorrê-la por inteiro. Isso permanece seguro em inclinações de até aproximadamente sessenta graus e, nas regiões de voxels com distância verdadeira, não custa nada além de algumas amostras extras. Combinamos isso com um limite mínimo para o passo, para que uma parede de caverna com uma ou duas células de espessura nunca seja completamente ignorada, e com um limite máximo mais restrito, que mantém a marcha barata. Quando a esfera finalmente toca a superfície, uma breve bisseção refina o ponto de contato, e a câmera recua o equivalente ao próprio raio e permanece ali.</p>
<p>Em termos matemáticos, a haste é um raio $\mathbf{r}(t) = \mathbf{p} + t,\mathbf{d}$ que parte do pivô $\mathbf{p}$ na direção unitária $\mathbf{d}$, e a marcha avança por uma fração sub-relaxada da folga até o contato informada pelo campo, limitada nas duas extremidades:</p>
<p>$$
t_{n+1} = t_n + \operatorname{clamp}!\big(\lambda,(\Phi(\mathbf{r}(t_n)) - r),; s_{\min},; s_{\max}\big)
$$</p>
<p>Aqui, $\Phi$ é a distância com sinal, positiva no ar e negativa na rocha; $r$ é o raio da sonda; $\lambda \in (0, 1]$ é o fator de sub-relaxação que impede o campo superestimado do mapa de altura de saltar uma encosta; e $s_{\min}, s_{\max}$ são os limites do passo que impedem que uma parede fina seja ignorada e que a marcha se prolongue demais. O contato ocorre no primeiro $t$ em que $\Phi(\mathbf{r}(t)) \le r$, o que significa que a superfície da esfera alcançou a rocha, e a câmera permanece nesse comprimento de arco menos o próprio raio.</p>
<p>O colisor oferece ainda outro método como rede de segurança: a despenetração. A colisão deveria impedir que a câmera entrasse em um sólido, mas algumas situações conseguem escapar: um pivô atravessando uma parede fina o bastante para que a haste comece dentro dela; uma caverna recém-escavada pelas ferramentas de terreno enquanto a câmera estava na rocha que acabou de desaparecer; uma peça de construção posicionada ao redor da câmera. Nesses casos, o colisor verifica se a câmera terminou o quadro dentro de um sólido, onde $\Phi(\mathbf{c}) &lt; r$. Se isso aconteceu, ele lê o gradiente do campo, que aponta diretamente para o espaço aberto porque $\Phi$ aumenta à medida que se deixa a rocha, e desloca a câmera para fora nessa direção:</p>
<p>$$
\mathbf{c} \leftarrow \mathbf{c} + \big(r - \Phi(\mathbf{c})\big),\frac{\nabla \Phi(\mathbf{c})}{\lVert \nabla \Phi(\mathbf{c}) \rVert}
$$</p>
<p>Algumas dessas iterações convergem para a isosuperfície de valor $r$, e é isso que permite que uma ferramenta de escultura remova o chão sob a câmera e que a visão se recupere no quadro seguinte, em vez de ficar preta.</p>
<h2>Aproximar de imediato, afastar suavemente e não se assustar com mourões</h2>
<p>Uma haste que simplesmente salta para a distância de colisão a cada quadro é pior do que não ter haste alguma, porque o mundo está cheio de objetos finos atrás dos quais a câmera passa por um único quadro — um mourão, uma luminária, um tronco — e uma câmera que avança bruscamente para desviar de cada um e logo recua da mesma forma causa náusea. Tanto o livro de Haigh-Hutchinson quanto a muito estimada palestra de Itay Keren sobre movimento de câmera chegam à mesma intuição: a câmera deve reagir a ameaças e perigos mais depressa do que relaxa depois que eles passam. Por isso, o amortecimento é deliberadamente assimétrico. Quando um oclusor aparece e a haste precisa encurtar, ela se recolhe quase instantaneamente, porque um quadro atravessando a geometria é feio e o jogador perdoa um recolhimento rápido. Quando o oclusor sai do caminho e a haste quer se alongar, ela se afasta lentamente, e apenas depois que um curto temporizador de permanência com espaço continuamente livre chega ao fim. Essa permanência é a histerese que elimina o sobressalto. Gire rapidamente a câmera passando por um mourão fino e ele nunca permanece fora do caminho por tempo suficiente para disparar a extensão lenta; assim, a câmera desliza por ele como se nem estivesse ali, exatamente como seus olhos esperam. O Cinemachine apresenta a mesma ideia por meio de valores separados de amortecimento ao entrar e ao sair de uma colisão, e é essa assimetria que faz o comportamento parecer o de um operador de câmera, em vez do de uma mola.</p>
<p>No código, isso se resume a uma linha de suavização exponencial cuja taxa muda de acordo com o sinal da variação. Se $\ell$ é o comprimento atual da haste, $a$ é o comprimento permitido pela colisão neste quadro e $\Delta t$ é o tempo do quadro, então</p>
<p>$$
\ell \leftarrow \ell + (a - \ell)\big(1 - e^{-k,\Delta t}\big), \qquad
k = \begin{cases}
k_{\text{in}}  &amp; a \le \ell \[2pt]
k_{\text{out}} &amp; a &gt; \ell
\end{cases}, \quad k_{\text{in}} \gg k_{\text{out}}
$$</p>
<p>e o ramo de afastamento suave só é executado depois que o espaço livre se mantém durante o tempo de permanência $\tau$. A forma $1 - e^{-k,\Delta t}$ é importante por mais do que sua aparência elegante. Ela fixa a constante de tempo da resposta em $1/k$, independentemente da taxa de quadros, de modo que a câmera produz a mesma sensação a 30 ou 144 quadros por segundo. Já a mistura ingênua com valor constante, $\ell \leftarrow \ell + \alpha(a - \ell)$, reagiria depressa demais em uma máquina rápida e de maneira arrastada em uma lenta.</p>
<p>O último comportamento se destina aos interiores apertados que deram início a toda esta parte. Quando a haste se recolhe tanto que a câmera fica praticamente em cima do jogador, ocultamos o avatar do próprio jogador e deixamos a visão próxima da primeira pessoa. É o que <em>Breath of the Wild</em> faz em um santuário apertado e o recurso ao qual a maioria dos jogos em terceira pessoa recorre em um canto, porque a alternativa — uma câmera prensada contra a parede, olhando para a parte de trás de uma cabeça — não serve para nada. O rig expõe um único sinalizador para isso, e o loop do mundo lê esse sinalizador e alterna a visibilidade do avatar local. Com menos de um metro de haste, você está efetivamente em primeira pessoa, as paredes são respeitadas e, assim que recua para um cômodo com espaço, o avatar reaparece gradualmente e a haste se estende.</p>
<h2>O que será conectado a seguir</h2>
<p>O colisor de terreno é lançado hoje e representa a metade difícil, porque o terreno está por toda parte e um campo de distância é algo trabalhoso de sondar. Os objetos de cena e as estruturas de construção dos protótipos de autoria são a metade fácil, e o contrato já está pronto para eles. Um segundo colisor, um colisor por raycast, lança um raio do pivô em direção à câmera contra uma lista de malhas e informa o impacto mais próximo da mesma forma que o colisor de terreno. A versão barata lança um único raio, o que funciona bem até a quantidade de objetos de cena aumentar; a evolução consiste em substituir o raio por uma esfera varrida usando o three-mesh-bvh, biblioteca de Garrett Johnson que envolve uma malha em uma hierarquia de volumes delimitadores para que consultas espaciais sejam executadas em tempo logarítmico, em vez de usar força bruta. De qualquer modo, o rig não muda. Ele consulta uma lista maior de colisores e usa o impacto mais próximo, que é justamente a razão de termos criado primeiro o contrato e depois os colisores.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Uma haste responsável por um único número.</strong> O rig de câmera é um pós-processamento sobre o OrbitControls, não um substituto para ele. Ele controla o comprimento da haste e deixa guinada, inclinação, zoom e manipulação de gestos a cargo do controlador orbital em que já confiávamos. A integração consiste em duas chamadas delimitadoras: <code>beforeControls()</code> restaura a distância total do quadro anterior para que os cálculos orbitais leiam o zoom real do usuário, em vez de confundirem o encurtamento causado por uma colisão com uma aproximação por dolly; e <code>afterControls(dt)</code> resolve a colisão e grava a posição renderizada. Sem esse par, a câmera desaba sobre o jogador ao longo de alguns quadros.</p>
<p><strong>Um contrato conectável de colisores.</strong> Um colisor é qualquer objeto com um <code>probe</code> que responda à pergunta: “até onde a câmera pode viajar a partir do pivô ao longo deste raio antes que você a bloqueie?”. O rig consulta todos os colisores e usa o impacto mais próximo, sem precisar saber se o obstáculo é o terreno, um objeto de cena ou uma parede. Essa é a mesma disciplina de controlar uma única coisa usada pelo <a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">controlador conectável de personagem</a> na locomoção. A sonda é uma esfera varrida dimensionada para conter o plano próximo, não um raio fino, de modo que os cantos do frustum nunca atravessem uma superfície que o raio central deixaria passar. Seu raio é derivado da projeção — a distância até um canto do plano próximo, $\sqrt{n^2 + w^2 + h^2}$, multiplicada por uma margem de segurança — e recalculado sempre que o campo de visão, a proporção da tela ou o plano próximo muda.</p>
<p><strong>Uma sonda de terreno por distância com sinal que respeita saliências e cavernas.</strong> Como o terreno é um <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">campo de distância com sinal</a>, em vez de um mapa de altura, a mesma sonda que impede a câmera de atravessar uma encosta também a impede de atravessar o teto de uma caverna — caso que um raycast contra um mapa de altura é estruturalmente incapaz de detectar. A marcha usa o sphere tracing no estilo de Hart, avançando por uma fração sub-relaxada da folga informada pelo campo e limitada nas duas extremidades, porque as regiões de mapa de altura informam a folga vertical, em vez da distância verdadeira, e superestimam quanto a câmera pode se mover em uma encosta; portanto, um passo completo saltaria sobre uma crista. Uma etapa de despenetração orientada pelo gradiente funciona como rede de segurança e recupera a visão quando o chão é esculpido e removido sob a câmera.</p>
<p><strong>Amortecimento assimétrico com temporizador de permanência.</strong> A haste se recolhe rapidamente quando surge um oclusor e se afasta devagar depois que ele sai do caminho, mas somente após um curto intervalo de espaço continuamente livre. Assim, passar rapidamente por um mourão nunca faz a câmera avançar de forma brusca. Abaixo de um limiar de recolhimento, o rig sinaliza uma visão próxima da primeira pessoa, e o loop do mundo oculta o avatar local — a solução padrão para interiores apertados, em vez de deixar a câmera enterrada em uma parede.</p>
<h2>Referências</h2>
<p>A formulação do controle da câmera como uma restrição de visibilidade vem de Marc Christie e Patrick Olivier, <a href="https://people.irisa.fr/Marc.Christie/Publications/2008/CON08.html"><em>Controle de câmera em computação gráfica</em></a> (Computer Graphics Forum, 2008). A marcha pelo campo de distância vem de John C. Hart, <a href="https://graphics.stanford.edu/courses/cs348b-20-spring-content/uploads/hart.pdf"><em>Sphere Tracing: um método geométrico para o ray tracing com antisserrilhamento de superfícies implícitas</em></a> (The Visual Computer, 1996). O padrão de braço com mola e sua colisão por esfera de sondagem são documentados no <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/using-spring-arm-components-in-unreal-engine">Componente Spring Arm</a>, da Epic, e no <a href="https://docs.unity3d.com/Packages/com.unity.cinemachine@3.1/manual/CinemachineDeoccluder.html">Desoclusor do Cinemachine</a> e <a href="https://docs.unity3d.com/Packages/com.unity.cinemachine@3.1/manual/CinemachineThirdPersonFollow.html">Acompanhamento em terceira pessoa</a>, da Unity. A intuição sobre movimento e amortecimento vem de Mark Haigh-Hutchinson, <em>Câmeras em tempo real</em> (Morgan Kaufmann, 2009), e Itay Keren, <a href="https://www.gamedeveloper.com/design/scroll-back-the-theory-and-practice-of-cameras-in-side-scrollers"><em>Scroll Back: teoria e prática de câmeras em jogos de rolagem lateral</em></a> (GDC 2015). O caminho de evolução do colisor de malhas é o <a href="https://github.com/gkjohnson/three-mesh-bvh">three-mesh-bvh</a>, de Garrett Johnson.</p>
<hr>
<p>Parte 30 de 30.
Anterior: <a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">Parte 29 — Um controlador, qualquer corpo</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Não renderize a grama atrás da colina: descarte de oclusão com reconhecimento do terreno]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-19-terrain-occlusion-culling</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-19-terrain-occlusion-culling</guid>
            <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Oito técnicas de descarte de oclusão avaliadas para um mundo aberto no navegador, a que vence porque nosso terreno é um mapa de alturas e um protótipo interativo que permite compará-las na sua própria cena.]]></description>
            <content:encoded><![CDATA[<h1>Não renderize a grama atrás da colina: descarte de oclusão com reconhecimento do terreno</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>A <a href="/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion">Parte 28</a> apresentou o Spike 57 e comparou seus quatro caminhos de descarte em alguns parágrafos. Esta é a versão completa: por que escolhemos esses quatro, quais eram os outros quatro e o raciocínio por trás daquele que vamos lançar.</p>
<p>Há uma colina entre a câmera e um campo. Você não precisa renderizar o campo. Todo motor AAA moderno sabe disso. A maioria dos motores 3D para navegador não sabe — inclusive o nosso, até a semana passada.</p>
<p>Este post é o resultado de fazer a pesquisa com o devido rigor, fundamentá-la em nossa base de código real, criar um protótipo funcional e avaliar as técnicas de acordo com o quanto elas ajudam <em>especificamente a nós</em>, considerando que hoje entregamos para WebGL e amanhã para WebGPU.</p>
<p>Você pode experimentar o protótipo ao vivo abaixo antes de continuar a leitura. <kbd>T</kbd>/<kbd>Y</kbd>/<kbd>U</kbd>/<kbd>I</kbd> alternam entre os caminhos de descarte, <kbd>C</kbd> percorre as predefinições de câmera e <kbd>B</kbd> pinta em vermelho, com estrutura de arame, os chunks ocultados, para que você possa ver o que o teste remove. O HUD informa quantas instâncias cada etapa rejeita.</p>
<div style="position:relative;padding-bottom:62%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.08)">
<iframe src="/pt-BR/spikes/57-terrain-occlusion/" title="Spike 57 — Descarte de oclusão do terreno" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#1a1f2a" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/57-terrain-occlusion/" target="_blank">Abrir o Spike 57 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=57-terrain-occlusion">Ver o código-fonte</a></p>
<h2>O que nosso motor faz hoje (e o que não faz)</h2>
<p>Nosso gerenciador de chunks em streaming carrega ao redor do jogador um anel de chunks de 64 m, distribuídos em três níveis de LOD. Cada chunk contém árvores instanciadas espalhadas sobre o mapa de alturas. A lógica de descarte, que fica em <code>Chunk.updateObjectVisibility</code>, é uma simples verificação de distância: árvores a até 60 m são renderizadas com a malha completa; entre 60 m e 140 m, são renderizadas como outdoors; além disso, nada é renderizado.</p>
<p>Esse descarte deixa passar duas enormes categorias de desperdício:</p>
<ol>
<li>Tudo que está dentro do disco de 60 m, <em>mas atrás da câmera</em>. Reordenamos o buffer de instâncias sempre que o jogador se move mais de 4 m, mas não testamos o frustum de visão. Assim, em média, metade do disco é enviada à GPU apenas para ser recortada depois da transformação dos vértices.</li>
<li>Tudo que está dentro do disco de 60 m, <em>mas atrás de uma colina</em>. Nosso terreno tem 80 m de variação vertical e muitos vales. Quando a câmera está em um vale, a maior parte da vegetação dentro do raio de descarte fica geometricamente oculta pela crista mais próxima. Mesmo assim, nós a desenhamos.</li>
</ol>
<p>A segunda categoria é o foco deste post. É também aquela que os motores AAA resolvem com um conjunto de técnicas engenhosas que, à primeira vista, não se adapta facilmente ao navegador.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/wavnKZNSYqU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<p><em>Gilbert Sanders, da Guerrilla, explica como Horizon Zero Dawn renderiza a vegetação de seu mundo aberto. O conjunto de descartes ocupa os últimos 20 minutos e é uma verdadeira aula sobre por que “desenhar menos” é melhor do que “desenhar mais rápido”.</em></p>
<h2>Oito técnicas, avaliadas para o nosso caso</h2>
<p>Estudei o conjunto moderno de técnicas de descarte de oclusão e avaliei cada componente de acordo com o quanto ele ajuda em um mundo aberto procedural, baseado em mapa de alturas e executado no navegador. Dois eixos: valor (quanto desperdício remove em nossa cena) e custo (esforço de engenharia, além de quanto da nossa stack precisa mudar). O raciocínio completo está no arquivo de pesquisa por trás deste post; aqui está a versão resumida.</p>
<p><strong>1. Descarte de frustum por instância. Alto valor, baixo custo.</strong> Atualmente, não testamos o frustum de forma alguma para nossa vegetação instanciada. Adicionar esse teste corta aproximadamente metade do trabalho em qualquer visão, custa cerca de trinta linhas de código e pode ser lançado hoje em WebGL. É, de longe, o maior ganho fácil, e deveríamos tê-lo implementado meses atrás.</p>
<p><strong>2. Raycast de horizonte no mapa de alturas. Alto valor, custo baixo a médio.</strong> Avance um raio da câmera por cada instância candidata, amostrando a altura do terreno ao longo do caminho. Se o terreno em algum momento subir acima do raio, a instância está ocluída. Isso funciona precisamente porque nosso mundo é um mapa de alturas, o que reduz a visibilidade a um problema unidimensional ao longo da direção horizontal. A versão densa tem complexidade O(passos) por instância, por quadro. A versão acelerada (o próximo item) reduz isso para O(log passos).</p>
<p><strong>3. Aceleração com pirâmide de alturas máximas. Alto valor, custo médio.</strong> Um mipmap 2D do mapa de alturas no qual cada texel armazena a altura máxima do terreno dentro de sua área. Isso permite que o raycast de horizonte avance em passos largos quando o entorno é plano e refine o teste apenas sobre colinas. Essa é a estrutura que torna a técnica nº 2 barata o suficiente para produção.</p>
<p><strong>4. Oclusão por Z hierárquico (Hi-Z/HZB). Alto valor, alto custo.</strong> Construa um mipmap do buffer de profundidade, projete os limites de cada instância no espaço da tela e teste-os contra o nível de mip adequado. Esse é o padrão moderno executado na GPU, usado pelo Nanite da Unreal, pela geometria virtual da Bevy e pelo porte do VTK para WebGPU. Ele funciona para tudo, não apenas para o terreno, mas exige WebGPU, desenhos indiretos e uma etapa de computação. Compensa quando estamos renderizando milhões de lâminas de grama, não milhares de árvores.</p>
<p><strong>5. Hi-Z em duas passagens (ao estilo Nanite). Ganho marginal sobre a nº 4, alto custo.</strong> Renderize novamente a profundidade do quadro atual depois da passagem 1 para evitar o artefato de desoclusão de um quadro. Só vale a pena quando estivermos avançados o suficiente no caminho orientado pela GPU para que o custo seja incremental.</p>
<p><strong>6. Rasterizador de oclusão por software (Frostbite/Intel MOC). Valor médio, alto custo.</strong> Rasterize na CPU um buffer de profundidade de baixa resolução contendo grandes oclusores. Latência de leitura zero. As implementações de referência são escritas em C++ com AVX/SSE; portá-las para WASM é um projeto de verdade, e nosso raycast no mapa de alturas captura a maior parte dos mesmos ganhos com uma fração do trabalho.</p>
<p><strong>7. PVS pré-calculado. Baixo valor, alto custo.</strong> Excelente para mapas estáticos da era de Quake. Nosso terreno é procedural e infinito, portanto qualquer pré-processamento teria que acontecer durante o streaming dos chunks, o que custa quase tanto quanto simplesmente calcular a visibilidade em tempo de execução. Descartado.</p>
<p><strong>8. Descarte de horizonte ao estilo Cesium. Nenhum valor para nós, custo médio.</strong> Projetado para elipsoides planetários. Nosso mundo é relativamente plano e limitado; a matemática não se aplica e não faria nada ou descartaria objetos incorretamente. Descartado.</p>
<p>Portanto: implementar agora as técnicas nº 1 e nº 2 + nº 3 na CPU, em WebGL. Planejar a nº 4 para a migração para WebGPU. Ignorar as demais.</p>
<h2>Por que o raycast no mapa de alturas vence em terrenos no navegador</h2>
<p>O conselho padrão em qualquer palestra moderna sobre renderização é: “construa um buffer Hi-Z”. A <a href="https://www.youtube.com/watch?v=eviSykqSUUw">análise aprofundada de Brian Karis sobre o Nanite na SIGGRAPH 2021</a> é a referência canônica, e vale a pena assisti-la pelo menos uma vez.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/eviSykqSUUw" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<p>Essa é a resposta certa para um motor que já executa tudo por meio de desenhos indiretos orientados pela GPU. A maioria dos motores para navegador, incluindo o nosso, não é esse tipo de motor. Temos buffers de instâncias gerenciados pela CPU, chamadas de desenho em WebGL e nenhuma etapa de computação. Acoplar Hi-Z a essa stack significa, ao mesmo tempo, portar o motor para WebGPU, reescrever o pipeline de vegetação com desenhos indiretos e adicionar uma passagem de construção da pirâmide de profundidade. Isso representa um trimestre de trabalho até chegar ao primeiro quadro que comprova a ideia.</p>
<p>O raycast no mapa de alturas funciona na CPU, em WebGL e com os dados que já temos. Ele usa um fato sobre nosso mundo do qual os motores AAA não podem tirar proveito: <strong>nossos oclusores são descritos por uma função de altura unidimensional</strong>. Amostrar essa função ao longo de um raio exige duas leituras de array e uma multiplicação. Um buffer Hi-Z teria que descobrir o mesmo fato pixel por pixel.</p>
<p>A técnica foi publicada pela primeira vez como “Horizon Occlusion Culling for Hierarchical Terrains” na IEEE Visualization 2002 (<a href="https://saksagan.ceng.metu.edu.tr/courses/ceng591/RAPORLAR/HacerYalim.pdf">PDF</a>). Ela permanece na caixa de ferramentas há duas décadas porque tem as características certas: é barata quando o terreno é plano, cara apenas onde realmente há colinas e trivialmente paralelizável.</p>
<h2>A pirâmide de alturas máximas, ilustrada</h2>
<p>O raycast ingênuo amostra <code>terrainHeight</code> em cerca de 24 pontos ao longo de cada raio e para antecipadamente se o terreno o atravessar. Isso funciona bem para milhares de árvores. Com centenas de milhares de lâminas de grama, o desempenho desmorona.</p>
<p>A solução é um mipmap do mapa de alturas no qual cada texel armazena a altura <strong>máxima</strong> dentro de sua área:</p>
<pre><code>nível 0 (256×256, 2,25 m por texel): altura máxima de 4×4 amostras deslocadas
nível 1 (128×128, 4,5 m  por texel): max(0,0), max(1,0), max(0,1), max(1,1)
nível 2 ( 64×64,  9,0 m por texel): mesma redução, um nível acima
...
nível 8 (   1×1,  576 m por texel): máximo global
</code></pre>
<p>Quando o segmento do raio for longo e plano, amostre um nível mais grosseiro: uma única leitura informa que “nenhum terreno neste quadrado de 9 m ultrapassa 12 m de altitude, e o raio está a 30 m nesse ponto, portanto podemos continuar”. Somente quando um texel grosseiro indicar que “o terreno <em>pode</em> estar acima do raio” é preciso descer um nível e refinar o teste. Toda a estrutura ocupa algumas centenas de KB e é construída em dezenas de milissegundos.</p>
<p>Em forma de diagrama:</p>
<pre><code>                                            raio a partir do olho
        olho 1,7 m                        o─────────────────────►
              o─────────────────────────·─·─·─·─·─·──────────────
              │                          \                       │
              │   nível 3 (passo enorme)  \   nível 0 (refinar)  │
              │   &quot;nenhum terreno acima    \   &quot;colina de 9 m    │
              │    de 8 m&quot;                      aqui!&quot;            │
        ──────┴────────────/▔▔▔\─────────────/▔▔▔▔▔\─────────────
                              colina A (8 m)  colina B (12 m)
                                                ↑
                                         bloqueia aqui
</code></pre>
<p>Para o segmento do raio que passa pelas proximidades da colina A, a leitura do nível 3 (“a altura máxima nesta área de 18 m de largura é 8 m”) já nos informa que o raio, a uma altitude de 1,7 m mais alguns metros de subida, está livre. Pulamos 36 m de avanço com uma única consulta. Sobre a colina B, o nível 3 informa “o máximo aqui é 12 m”; descemos um nível, e o nível 0 informa “sim, há 12 m exatamente neste texel”, então rejeitamos a instância.</p>
<p>A construção da pirâmide está em <code>height-pyramid.mjs</code>, e os caminhos de descarte que a utilizam estão em <code>cull.mjs</code>. Ambos podem ser lidos no <a href="/pt-BR/blog/spike-source?spike=57-terrain-occlusion">navegador de código-fonte do protótipo</a>.</p>
<h2>O que o protótipo realmente mostra</h2>
<p>Abra o Spike 57 acima e percorra os quatro caminhos:</p>
<ul>
<li><strong>T0</strong> é o que a versão de produção faz hoje. Apenas distância. Em C1 (fundo do vale), o HUD informa cerca de 12.000 lâminas de grama visíveis.</li>
<li><strong>T1</strong> adiciona descarte de frustum por instância. A contagem visível cai aproximadamente pela metade porque tudo que está atrás da câmera ou fora das laterais é removido antes de chegar à GPU.</li>
<li><strong>T2</strong> adiciona o raycast de força bruta no mapa de alturas. Em C1, o número cai mais 60–80%, porque a maior parte do campo está do outro lado da crista mais próxima. A coluna Cull-ms aumenta porque estamos amostrando <code>terrainHeight</code> cerca de 24 vezes por instância.</li>
<li><strong>T3</strong> substitui a força bruta pela pirâmide de alturas máximas. Cull-ms volta a ficar próximo de T1, mantendo o ganho de visibilidade. Este é o caminho que realmente deve ser lançado.</li>
</ul>
<p>O padrão corresponde ao observado em motores de produção. A série de duas partes de Acerola sobre renderização de grama (<a href="https://www.youtube.com/watch?v=Y0Ko0kvwfgA">Como os jogos renderizam tanta grama?</a>, <a href="https://www.youtube.com/watch?v=PNvlqsXdQic">O que fiz para otimizar a grama do meu jogo</a>) é a explicação mais acessível no YouTube sobre por que a etapa de descarte — e não a etapa de sombreamento — é onde você deve concentrar seu trabalho de engenharia.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/Y0Ko0kvwfgA" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<h2>A câmera C3 e o caso de falha no “topo da colina”</h2>
<p>A terceira predefinição de câmera do protótipo coloca a câmera no topo de uma colina, olhando para toda a área de jogo. Nesse caso, o descarte de horizonte quase não faz nada, porque não há terreno entre a câmera e a maior parte do mundo. O HUD mostra que T2/T3 reduzem a contagem visível em talvez 5–10% em relação a T1.</p>
<p>Isso é uma característica, não um bug. A técnica para de funcionar exatamente onde deveria: quando não há nada para ocluir. O descarte de frustum ainda está fazendo trabalho útil, o descarte por distância ainda limita o orçamento e o teste de horizonte se reduz elegantemente a uma operação nula. Se você implementar isso, precisa verificar se o caso sem efeito também é barato. É por isso que a pirâmide de alturas máximas importa mesmo quando nenhum raio seria rejeitado: frequentemente, o nível mais grosseiro é suficiente para confirmar que “nada está bloqueado”.</p>
<h2>O que vamos lançar a seguir</h2>
<p>Há três coisas a fazer, nesta ordem.</p>
<p>Primeiro, portar T1 e T3 para <code>Chunk.updateObjectVisibility</code> na versão de produção. A pirâmide deve ficar um nível acima, no gerenciador de chunks, porque abrange mais de um chunk. O descarte permanece em <code>Chunk</code> para que o agrupamento existente por chunk continue funcionando. Esforço estimado: um dia, incluindo os testes.
Segundo, fazer o mesmo com a grama assim que ela estiver disponível. O protótipo atual espalha 12.000 lâminas por uma área jogável de 576 m; a densidade em produção deve ser aproximadamente uma ordem de grandeza maior. As rotinas de culling na CPU processam 12.000 em menos de um milissegundo, e é a aceleração pela pirâmide que mantém esse desempenho com 120.000.</p>
<p>Terceiro, quando migrarmos para <code>THREE.WebGPURenderer</code>, portar o mesmo loop para um compute shader. Os metadados se tornam um storage buffer. O culling grava os argumentos de <code>drawIndirect</code>. A pirâmide é enviada como uma textura 2D com a redução por máximo já incorporada. A estrutura do código permanece praticamente idêntica, e essa é justamente a ideia: não estamos apostando a migração em um novo algoritmo; estamos levando um algoritmo que já temos para uma pista mais rápida.</p>
<p>A Guerrilla apresentou a versão disso executada na GPU para o sistema de posicionamento procedural de Horizon Zero Dawn; o pipeline de renderização importa menos do que as estruturas de dados, e as deles têm o mesmo formato:</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/ToCozpl1sYY" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<p>O Hi-Z ainda justificará seu lugar quando lançarmos construções e elementos de cenário densos que ocluam em direções que o mapa de altura não consegue representar. Mas a pirâmide do mapa de altura continuará no pipeline porque é estritamente mais barata que o Hi-Z para instâncias próximas ao solo, e a grama que atravessa a silhueta de uma crista é exatamente o caso que o Hi-Z trata pior.</p>
<h2>Referências</h2>
<p>A justificativa completa, em ordem de prioridade, e as decisões arquiteturais estão acima; estas são as fontes canônicas de cada técnica, aproximadamente na ordem em que aparecem na pilha.</p>
<ul>
<li>Yalim &amp; Akman, <a href="https://saksagan.ceng.metu.edu.tr/courses/ceng591/RAPORLAR/HacerYalim.pdf">Culling de oclusão por horizonte para terrenos hierárquicos</a> (IEEE Visualization 2002, o artigo original sobre culling por horizonte em terrenos).</li>
<li>Turitzin, <a href="https://miketuritzin.com/post/hierarchical-depth-buffers/">Buffers de profundidade hierárquicos</a> (a explicação mais clara de como as cadeias de mipmaps Hi-Z são construídas e consultadas).</li>
<li>VKGuide, <a href="https://vkguide.dev/docs/gpudriven/compute_culling/">Culling baseado em compute</a> (referência de pipeline de culling orientado pela GPU; o porte para WebGPU é mecânico).</li>
<li>Kruskonja, <a href="https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501">Culling de oclusão em duas passagens</a> (a arquitetura no estilo Nanite explicada fora dos slides da Epic).</li>
<li>Karis et al., <a href="https://www.youtube.com/watch?v=eviSykqSUUw">Nanite — Uma análise aprofundada (SIGGRAPH 2021)</a> (a referência moderna de como é o culling orientado pela GPU em escala AAA).</li>
<li>Karis, <a href="https://www.youtube.com/watch?v=NRnj_lnpORU">Palestra principal da HPG 2022: a jornada até o Nanite</a> (contexto mais amplo sobre por que a etapa de culling é importante).</li>
<li>Sanders, <a href="https://www.youtube.com/watch?v=wavnKZNSYqU">Entre tecnologia e arte: a vegetação de Horizon Zero Dawn</a> (a pilha completa de culling de vegetação AAA).</li>
<li>Guerrilla, <a href="https://www.youtube.com/watch?v=ToCozpl1sYY">Posicionamento procedural em tempo de execução baseado em GPU em Horizon Zero Dawn</a> (posicionamento instanciado e visibilidade por instância).</li>
<li>Scthe, <a href="https://github.com/Scthe/nanite-webgpu">Nanite WebGPU</a> (uma implementação de referência em WebGPU, incluindo HZB).</li>
<li>Kitware, <a href="https://www.kitware.com/webgpu-occlusion-culling-in-vtk/">Culling de oclusão com WebGPU no VTK</a> (o primeiro HZB em produção no navegador de que tenho conhecimento).</li>
<li>Acerola, <a href="https://www.youtube.com/watch?v=Y0Ko0kvwfgA">Como os jogos renderizam tanta grama?</a> e <a href="https://www.youtube.com/watch?v=PNvlqsXdQic">O que fiz para otimizar a grama do meu jogo</a> (a introdução acessível em vídeo).</li>
<li>RasterGrid, <a href="https://www.rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/">Culling de oclusão baseado em mapas Hi-Z</a> (a introdução ao Hi-Z que continua sendo a referência canônica).</li>
<li>Intel, <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/masked-software-occlusion-culling.html">Culling de oclusão por software com máscaras</a> (a família de rasterizadores para CPU, caso você algum dia decida que a opção nº 6 vale a pena).</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 29: Um controlador, qualquer corpo]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character</guid>
            <pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O spike 58 cria um controlador de personagem no qual o motor de física não sabe nada sobre caminhar, nadar ou planar. Cada comportamento é um controlador plugável; o mesmo motor controla o jogador, um cavalo montável e um NPC autônomo; e tudo funciona sem interface gráfica no Node. O spike 59 conecta a ele um avatar com animações redirecionadas sem alterar nenhum controlador, e o spike 60 conecta um pacote da Synty que não precisa de redirecionamento algum, por trás de um seletor de clipes que pode ser testado unitariamente.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 29: Um controlador, qualquer corpo</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e contém links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion">parte 28</a> abordou grama e oclusão. Vinte e oito partes construíram um mundo onde se pode estar: terreno que você consegue interpretar, água onde pode nadar, vegetação que se mantém convincente até o horizonte, um servidor que se lembra do que você alterou. Esta parte trata daquilo que se move por tudo isso e é a recompensa de todo o trabalho no motor, porque o objetivo não é um controlador de jogador. É um controlador que não se importa com o corpo que está controlando, de onde vieram suas animações ou se há uma pessoa ou uma IA no comando. O spike 58 constrói o movimento como uma pilha de comportamentos plugáveis sobre um motor de física que não sabe nada sobre locomoção e depois comprova a arquitetura executando três corpos diferentes em instâncias do mesmo motor. O spike 59 conecta um avatar real com animações redirecionadas a esse controlador sem alterar uma única linha dele. O spike 60 conecta um pacote de animações diferente, que não precisa de redirecionamento algum, e extrai a lógica de seleção de clipes para algo que pode realmente ser testado.</p>
<h2>Um motor de física que não sabe nada sobre caminhar</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/58-pluggable-character/" title="Spike 58: Personagem plugável" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/58-pluggable-character/" target="_blank">Abrir o spike 58 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=58-pluggable-character">Ver código-fonte</a></p>
<p>A regra de design é rigorosa, e esse é justamente o ponto: o motor da cápsula não contém nenhuma lógica de locomoção. Nada de caminhada, corrida, salto, atrito, limite de velocidade máxima ou sequer gravidade. Ele integra uma cápsula cinemática contra o terreno e nada mais. Cada comportamento de locomoção — caminhar, deslizar, planar, escalar, nadar, agachar, gerenciar vigor — vive em um controlador independente registrado no motor. A cada quadro, o motor atualiza todos os controladores, pergunta a cada um se deseja assumir o controle e permite que aquele de maior prioridade escreva a velocidade. Caminhar não tem status especial. É apenas o controlador de menor prioridade que sempre responde que sim, portanto é o padrão quando nenhum outro é acionado. Um controlador consiste em seis funções pequenas: um <code>tick</code>, que atualiza seu próprio estado interno a cada quadro, mesmo quando não está ativo; um predicado puro <code>wantsControl</code>, que reivindica o quadro; um <code>applyForces</code>, executado apenas pelo vencedor, que escreve a velocidade e aplica sua própria gravidade, caso queira alguma; além dos opcionais <code>onEnter</code>, <code>onExit</code> e <code>stateName</code>. O controlador de natação retorna <code>ownsCollision: true</code> em <code>applyForces</code> para assumir o tratamento do terreno, porque, caso contrário, sua mola de flutuabilidade entraria em conflito com o ajuste dos pés ao solo feito pelo motor.</p>
<p>Até mesmo os elementos que não são locomoção acabam passando por esse contrato, e foi isso que forçou a ideia seguinte. Um projeto anterior, de canal único, submetia tudo a uma única arbitragem. Isso significava que um monitor de vigor ou uma postura agachada precisava fingir ser locomoção e então recusar o controle com um truque de <code>wantsControl</code> que retornava falso, apenas para executar sua contabilidade interna. A solução divide os controladores em canais nomeados que fazem a arbitragem de forma independente e são aplicados em uma ordem fixa: recurso, depois postura e, por fim, locomoção. O recurso é executado primeiro porque suas escritas, como a redução do vigor, são lidas pelos demais. A postura vem em seguida porque a redução da altura da cápsula ao agachar precisa acontecer antes que o controlador de caminhada a leia para limitar a velocidade máxima. A locomoção é executada por último e controla a escrita da velocidade a cada quadro. Assim, um observador de vigor, um modificador de agachamento e um controlador de natação ativo coexistem de forma limpa, cada um em seu próprio canal, sem que nenhum controlador precise mentir sobre o que é. A demonstração visualiza tudo isso com uma cápsula simples que muda de cor de acordo com o controlador ativo, permitindo observar a arbitragem em ação: o verde da caminhada muda para laranja em uma encosta íngreme quando o deslizamento assume o controle, para ciano no lago quando a natação vence e para creme no ar quando você aciona a planagem.</p>
<h2>Um motor, três corpos</h2>
<p>O verdadeiro teste de “não conter nenhuma lógica de locomoção” não é o jogador. É verificar se o mesmo motor, sem alterações, consegue controlar algo que nem sequer seja um jogador. <code>createCapsuleEngine</code> é uma fábrica pura, sem estado no nível do módulo, singletons ou efeitos colaterais por instância, então o spike a instancia três vezes. O jogador é uma instância com o conjunto completo de controladores. Um cavalo montável é uma segunda instância que registra apenas um controlador de caminhada, tornando toda a ideia literal: o vocabulário de movimentos de um corpo é apenas o conjunto de controladores registrados. Assim, o cavalo é mais rápido em terreno plano e fisicamente não consegue escalar, nadar ou planar, porque esses controladores nunca foram adicionados. Um NPC autônomo é uma terceira instância, atualizada a cada quadro junto com o jogador e conduzida por um controlador de perambulação que sintetiza sua própria entrada para que o corpo se desloque sozinho, sem ninguém tocando no teclado. O motor nunca fica sabendo que um de seus corpos é um cavalo ou que outro é controlado por IA. Todos são o mesmo integrador de cápsula com listas diferentes de controladores.</p>
<p>A montaria é a única parte que deliberadamente fica fora do motor. Alternar o controle entre o jogador e o cavalo exige coordenar dois motores, e um controlador é executado dentro de um único motor e não consegue enxergar além dessa fronteira. Por isso, a lógica de montaria fica no nível do host: é uma pequena máquina de estados que decide qual motor avança neste quadro, congela o outro, desloca suavemente o cavaleiro até a sela usando um smoothstep ao longo de três quartos de segundo e informa à câmera qual corpo deve acompanhar. A fábrica do motor nunca fica sabendo de nada disso. Essa é a fronteira que a arquitetura traça e mantém. Comportamentos pertencentes a um único corpo são controladores; a coordenação entre corpos é responsabilidade do host. Manter essas responsabilidades separadas é o motivo pelo qual um quarto ou quadragésimo corpo não exigiria nada de novo.</p>
<h2>Testado sem navegador</h2>
<p>Como o motor não acessa <code>window</code>, <code>document</code> nem Three.js, tudo funciona sem interface gráfica. O spike inclui um executor para Node que simula a interface do terreno, avança o motor quadro a quadro com entradas programadas e faz asserções sobre o estado resultante. Assim, regressões extremamente difíceis de detectar por testes manuais são capturadas por um script: a transição entre saltar e aterrissar, os limiares de entrada e saída da natação, a desativação automática da escalada quando uma superfície fica plana e as taxas de consumo de vigor. Fornecer duas vezes a mesma sequência de entradas e intervalos de tempo e verificar se o estado de saída é idêntico byte a byte garante que o motor seja determinístico, propriedade da qual uma versão em rede acabará dependendo. Vale preservar uma regressão detectada pelo executor: sair de um terreno transitável para uma encosta mais íngreme do que o limite caminhável acionava o controlador de deslizamento e lançava o jogador de volta morro acima. A correção foi uma condição de descida. O deslizamento só começa se a cápsula estiver realmente caindo sobre a encosta. Assim, caminhar horizontalmente contra uma face íngreme agora bloqueia o movimento de forma limpa em vez de provocar um deslizamento, e o teste que afirma que “caminhar contra uma encosta não transitável bloqueia o jogador” impede que o problema retorne.</p>
<h2>Conectando um avatar real sem tocar nos controladores</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/59-pluggable-with-avatar/" title="Spike 59: Controlador plugável com avatar" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/59-pluggable-with-avatar/" target="_blank">Abrir o spike 59 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=59-pluggable-with-avatar">Ver código-fonte</a></p>
<p>O spike 59 testa se a camada de controladores está realmente desacoplada do corpo. Ele substitui a cápsula colorida por um personagem real com esqueleto, o rig FBX 3MIKE com clipes da Quaternius Universal Animation Library redirecionados para ele, e os controladores não mudam em nada. A interface entre as camadas é uma única string. Cada controlador já informa o nome de um estado por meio de <code>stateName</code> — idle, walk, run, jump, fall, land, slide, glide, swim, swimIdle —, e a camada do avatar mapeia esse nome para um clipe redirecionado por meio de uma tabela de aliases e faz uma transição suave ao trocar. O controlador de caminhada escolhe dinamicamente seu próprio subestado usando a condição de estar no chão, a velocidade vertical e a velocidade horizontal. Assim, um único controlador de caminhada conduz os estados parado, caminhando, correndo, saltando, caindo e aterrissando, enquanto o avatar apenas segue o nome informado. Como este spike é para um único jogador, o avatar descarta a indireção por inteiros na transmissão e a abstração de múltiplos personagens dos spikes anteriores em rede, mapeando o nome do estado diretamente para um clipe. A prova é que toda a evolução visual da cápsula para um humano com rig não alterou nenhuma linha do código de locomoção, exatamente o benefício que um controlador plugável deve proporcionar.</p>
<h2>Um pacote que dispensa redirecionamento e um seletor que você pode testar</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/60-polygon-animations/" title="Spike 60: Animações POLYGON" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/60-polygon-animations/" target="_blank">Abrir o spike 60 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=60-polygon-animations">Ver código-fonte</a></p>
<p>O spike 60 conecta um terceiro corpo, o pacote POLYGON Base Locomotion da Synty, e o carregador é quase inexistente. Cada clipe é fornecido como um FBX independente contendo uma cópia incorporada do mesmo esqueleto da Synty mais uma animação pré-calculada. Como o rig do personagem e todos os clipes usam nomes de ossos idênticos, basta obter o clipe em <code>fbx.animations[0]</code> e reproduzi-lo diretamente no mixer do personagem, sem envolver nenhuma biblioteca de redirecionamento. O Three.js resolve os alvos das trilhas de animação pelo nome do osso, e não pela identidade do objeto, então um pacote no estilo Synty ou Mixamo criado para o rig correspondente simplesmente funciona. Esse é o contraste deliberado com o fluxo da UAL do spike anterior, que exige um redirecionamento pesado porque os clipes de origem e o rig de destino foram criados para esqueletos diferentes. Mesmo controlador, mesma interface baseada no nome do estado, dois pipelines de animação completamente diferentes por trás dela.</p>
<p>A outra metade do spike 60 torna testável a lógica de seleção de clipes. Escolher qual clipe reproduzir envolve muitos limiares subjetivos, e essa lógica estava enterrada dentro da camada do avatar, ao lado do FBXLoader e do DOM, onde não podia ser exercitada. O spike extrai o seletor para funções puras que não acessam nem Three.js nem a janela: elas recebem um registro simples do jogador — velocidade, velocidade horizontal, direção para a qual está voltado, condição de estar no chão, normal do solo e velocidade de impacto — e um substituto de ação de clipe, retornando uma string de alias do clipe. Isso permite que os limiares existam como constantes nomeadas e verificadas por asserções. O salto é dividido em variantes de caminhada, corrida e sprint de acordo com faixas de velocidade alinhadas ao limiar real de sprint do controlador de caminhada; as aterrissagens são divididas em suaves, médias e fortes pela velocidade de impacto; as variantes de clipes de subida e descida são acionadas por um produto escalar de projeção da inclinação, com uma zona morta para clipes planos abaixo de aproximadamente sete graus; uma transição de parado para locomoção sempre é resolvida para a frente, para que uma partida imóvel nunca reproduza um clipe ao contrário; e uma transição de parada é condicionada a um tempo mínimo no loop, porque os clipes de parada por fase do pé da Synty contêm cerca de um segundo de desaceleração criada pelo animador, que parece ridícula quando acrescentada a um passo que o jogador mal chegou a dar. Um pequeno estabilizador de estado adia o estado de queda por alguns quadros, para que uma perda de contato com o chão por um único quadro ao atravessar uma emenda não faça a animação piscar. Extrair tudo isso da camada de renderização significa que as regras podem passar por testes unitários sem uma GPU, aplicando a mesma disciplina de execução sem interface gráfica que o próprio motor recebeu no spike 58. Essa é a diferença entre ajustar a sensação da locomoção por tentativa e erro e poder defini-la com precisão.</p>
<h2>Tecnologia mencionada neste capítulo</h2>
<p><strong>Um motor de física sem lógica de locomoção.</strong> O motor da cápsula integra um corpo cinemático contra o terreno e não contém caminhada, corrida, salto, atrito, limite de velocidade ou gravidade. Cada comportamento é um controlador registrado que expõe <code>tick</code>, <code>wantsControl</code>, <code>applyForces</code> e, opcionalmente, <code>onEnter</code>/<code>onExit</code>/<code>stateName</code>. Caminhar é apenas o comportamento padrão de menor prioridade que sempre responde que sim, e um controlador pode retornar <code>ownsCollision: true</code> para assumir o tratamento do terreno — a natação faz isso para que sua mola de flutuabilidade não entre em conflito com o ajuste dos pés ao solo.</p>
<p><strong>Canais de arbitragem independentes.</strong> Os controladores são registrados em canais nomeados — recurso, postura e locomoção — que fazem a arbitragem separadamente e são aplicados em uma ordem fixa. Assim, observadores como vigor e modificadores como agachamento coexistem com a locomoção ativa em vez de fingirem assumir o controle para depois recusá-lo. As escritas do recurso, como o vigor, são lidas pela postura e pela locomoção; as escritas da postura, como a altura da cápsula ao agachar, são lidas pelo limite de velocidade da locomoção; a locomoção é executada por último e controla a escrita da velocidade.
<strong>Uma fábrica, muitos corpos.</strong> <code>createCapsuleEngine</code> é uma fábrica pura, sem singletons, portanto o mesmo motor controla o jogador, um cavalo montável que apenas caminha e um NPC com direção autônoma alimentado por entradas sintéticas. O repertório de movimentos de um corpo corresponde exatamente ao conjunto de controladores registrados; assim, o cavalo não pode escalar nem nadar porque esses controladores nunca foram adicionados. A lógica de montaria fica no nível do host, não em um controlador, pois coordena dois motores que não conseguem acessar um ao outro. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<p><strong>Testes headless e determinísticos.</strong> O motor não acessa <code>window</code>, <code>document</code> nem Three.js, portanto um ambiente de testes Node o controla quadro a quadro e verifica saltos e aterrissagens, limiares de natação, encerramento automático de escaladas e consumo de energia. Reproduzir duas vezes entradas idênticas e verificar se as saídas também são idênticas comprova o determinismo, e um teste de regressão fixa a condição de descida que impede uma caminhada horizontal contra uma superfície íngreme de fazer o jogador deslizar para trás.</p>
<p><strong>Vinculação de avatar independente do corpo, com um seletor testável.</strong> Os controladores informam uma string com o nome do estado, e a camada visual a mapeia para um clipe; assim, trocar uma cápsula por um avatar 3MIKE + UAL redirecionado não exige nenhuma alteração no código de locomoção. Os clipes Synty POLYGON compartilham os nomes dos ossos com o rig e são reproduzidos sem redirecionamento (o Three.js vincula as trilhas pelo nome do osso), ao contrário do redirecionamento pesado usado no fluxo UAL. O seletor de clipes é extraído para funções puras, com constantes nomeadas para faixas de velocidade do salto, intensidade da aterrissagem, variantes de projeção em inclinações, uma transição de repouso sempre para a frente, uma proteção de tempo mínimo para a transição de parada e um eliminador de oscilações do estado de queda. Assim, a sensação da locomoção pode ser testada por unidade, em vez de ser ajustada por tentativa e erro.</p>
<hr>
<p>Parte 29 de 30.
Anterior: <a href="/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion">Parte 28 — Grama até o horizonte e um terreno que se oculta</a>
Próxima: <a href="/pt-BR/blog/2026-06-04-open-world-browser-part-30-collision-aware-camera">Parte 30 — Uma câmera que respeita as paredes</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 28: Grama até o horizonte e terreno que se oculta]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion</guid>
            <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 56 faz meio milhão de tufos de grama parecerem um campo, em vez de confete verde, com um único kernel de computação e um truque de quads cruzados. O Spike 57 compara quatro maneiras de descartar o que uma colina está ocultando e constata que o caminho acelerado também é o geometricamente correto.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 28: Grama até o horizonte e terreno que se oculta</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain">Parte 27</a> construiu a ilha e deu a ela um terreno que não parece repetitivo. Esta parte aborda as duas coisas que fazem o terreno parecer habitado, em vez de vazio. O Spike 56 trata da grama, o detalhe da superfície que transforma uma encosta texturizada em um lugar pelo qual você caminharia, e o problema é fazer um campo parecer um campo, em vez de riscos espalhados. O Spike 57 é o oposto de desenhar mais: trata-se de não desenhar o que uma colina já está ocultando, e o resultado interessante é que a forma rápida de fazer esse teste também se revelou a forma correta.</p>
<h2>Grama que parece um campo, não confete</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/56-grass/" title="Spike 56: Grama" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/56-grass/" target="_blank">Abrir o Spike 56 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=56-grass">Ver código-fonte</a></p>
<p>A decisão central é geométrica. Uma única lâmina fina e afilada ocupa menos de um pixel na maioria dos ângulos de câmera, então meio milhão de lâminas planas parecem confete verde esparso, em vez de um campo. A solução é um tufo de quads cruzados: três quads afilados girados em intervalos de sessenta graus ao redor do eixo vertical local, de modo que, em qualquer direção de visualização, pelo menos um quad fique quase perpendicular à câmera e cada instância cubra aproximadamente três larguras de lâmina de área real na tela. Essa é a diferença entre ver riscos verdes e ver grama.</p>
<p>Um kernel de computação WebGPU posiciona cada tufo uma única vez na inicialização. Ele transforma o índice da instância em cinco fluxos aleatórios descorrelacionados por meio de hash, escolhe uma posição XZ dentro da área, amostra a altura do terreno usando o mesmo FBM empregado pela malha de terreno da CPU (uma adaptação para TSL com mod que preserva o sinal, para que os valores coincidam exatamente e as lâminas fiquem sobre a superfície, em vez de flutuar acima dela), calcula uma normal por diferenças centrais e sorteia largura, altura e matiz para cada tufo. No lado da renderização, há um grafo de vértices TSL que ignora por completo a matriz de instância e escreve diretamente no espaço de recorte: ele redimensiona o quad cruzado unitário, gira o eixo vertical local até a normal do terreno usando a fórmula de Rodrigues e o translada para a posição do tufo. O LOD por distância vem de graça, sem uma etapa de descarte, porque o grafo de vértices multiplica a altura do tufo por $1 - \text{smoothstep}(\text{fadeNear}, \text{fadeFar}, \text{dist})$. Assim, tufos distantes se achatam até altura zero e deixam de consumir preenchimento. O vento usa duas oitavas de seno e cosseno sobre as coordenadas XZ do tufo no mundo, somadas ao tempo, aplicadas horizontalmente e moduladas pela fração da altura para manter a base ancorada enquanto as pontas se movem. Uma leve inclinação orientada pela câmera também inclina cada tufo na direção do observador, fazendo com que eles se abram em perspectiva em vez de parecerem cartões planos.</p>
<p>A receita de cores foi tomada emprestada do shader URP de Breath of the Wild criado por NedMakesGames: sombreamento plano, com a cor da lâmina interpolada de um tom da raiz para um tom da ponta ao longo da fração da altura; os tons da raiz e da ponta, por sua vez, são interpolados entre duas paletas pelo valor de matiz de cada tufo. Isso produz o aspecto salpicado em dois tons de um campo de grama real de BotW. A iluminação difusa usa a normal do terreno em relação ao sol, com um piso de luz ambiente, porque as normais individuais dos quads cruzados geram ruído demais para serem sombreadas separadamente em um visual estilizado. O campo inteiro usa uma única chamada de desenho e é posicionado e animado inteiramente na GPU.</p>
<h2>Quatro maneiras de descartar o que uma colina está ocultando</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/57-terrain-occlusion/" title="Spike 57: Oclusão do terreno" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/57-terrain-occlusion/" target="_blank">Abrir o Spike 57 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=57-terrain-occlusion">Ver código-fonte</a></p>
<p>O Spike 57 compara quatro caminhos de descarte no mesmo mundo procedural transmitido pelo cliente de produção, escalado verticalmente em 1,8 vez para que as colinas realmente ocultem a vegetação. O T0 usa apenas a distância, correspondendo ao que a visibilidade de chunks da versão atual já faz. O T1 acrescenta descarte pelo frustum, o maior ganho barato isolado, removendo tudo que está fora do cone de visão. O T2 acrescenta um teste de horizonte que leva o terreno em conta: ele percorre um raio do olho até cada instância e a rejeita se o mapa de alturas ultrapassar o raio em qualquer ponto do caminho. Assim, uma árvore atrás de uma crista é descartada mesmo que esteja dentro do frustum. O T3 mantém o mesmo teste de horizonte, mas o acelera com uma pirâmide de alturas máximas. Já o T4 transfere todo o teste de distância, frustum e horizonte para um kernel de computação TSL, que grava uma escala de visibilidade por instância, lida pelo material da vegetação para colapsar os vértices ocultos. Um contador que mantém a visibilidade por dois quadros suaviza a cintilação de um único quadro quando um ponto de amostragem cai logo dentro ou fora de um texel enquanto a câmera se move levemente. Ele é propositalmente curto, pois qualquer duração maior mascara erros algorítmicos em vez de corrigi-los.</p>
<p>É na pirâmide que o spike mostra seu valor, e a lição está em fazer o teste corresponder ao que realmente aparece na tela. O terreno renderizado é uma malha de triângulos com vértices em uma grade fixa de dois metros, e o rasterizador interpola linearmente entre os vértices. Portanto, a altura real renderizada dentro de qualquer retângulo é o máximo dos vértices contidos nele, nunca o pico do ruído contínuo entre eles. Se a pirâmide de oclusão amostrar o ruído em uma grade mais fina, ela encontrará picos fantasmas que a malha nunca exibe e começará a bloquear raios que a câmera claramente consegue atravessar. Por isso, os texels da base da pirâmide ficam exatamente sobre a grade de vértices, cada um armazenando o máximo de seus quatro vértices de canto, enquanto os níveis superiores usam reduções máximas 2x2 convencionais. Isso a torna exata em relação à malha renderizada. Para o teste de raio a cada passo, o código deliberadamente faz amostragem pontual da malha com interpolação bilinear, em vez de consultar a pirâmide, pois uma consulta AABB menor que um texel retorna o máximo do texel inteiro e aumenta a altura em vários metros nas cristas íngremes — exatamente o tipo de oclusão excessiva que ocultaria objetos visíveis.</p>
<p>O resultado surpreendente é que o T2, a referência de força bruta, é o que está errado. Como o T2 faz amostragem pontual diretamente no ruído contínuo, ele detecta picos entre vértices da malha que não são renderizados. Assim, ele causa uma pequena oclusão excessiva e oculta vegetação que o jogador realmente consegue ver. O caminho da pirâmide é mais rápido, graças à redução AABB de $O(\log N)$ para testes no nível dos chunks, e também mais correto geometricamente, pois só pode retornar alturas que a malha de fato exibe. Esse é o ponto central do spike: a estrutura acelerada não é uma concessão de qualidade aceita em troca de velocidade; é a versão que corresponde à realidade. O descarte de chunks executa um teste de cinco pontos por chunk — os quatro cantos superiores mais o centro na altura máxima do chunk — e exclui da coleção de oclusores a área ocupada pelo próprio chunk, para que ele nunca oclua a si mesmo. O caminho recomendado para produção é o T4: enviar os metadados das instâncias e o campo de alturas para buffers de armazenamento e executar o mesmo loop em um shader de computação com desenho indireto, já que o renderizador está migrando para WebGPU de qualquer maneira.</p>
<h2>Tecnologia mencionada neste capítulo</h2>
<p><strong>Grama de quads cruzados na GPU.</strong> Três quads afilados girados em intervalos de sessenta graus por tufo garantem um quad quase perpendicular a partir de qualquer ângulo. Assim, meio milhão de instâncias parecem um campo, em vez de confete menor que um pixel. Um kernel de computação posiciona cada tufo (altura do terreno FBM amostrada com o mesmo mod que preserva o sinal usado pela malha da CPU, normal por diferenças centrais, tamanho e matiz por tufo), e um grafo de vértices TSL ignora a matriz de instância para redimensionar, girar até a normal pela fórmula de Rodrigues, transladar e aplicar vento modulado pela altura. O LOD por distância é gratuito: os tufos colapsam até altura zero por $1 - \text{smoothstep}(\text{fadeNear}, \text{fadeFar}, \text{dist})$. A cor segue a receita de BotW de NedMakesGames, com um gradiente da raiz à ponta sobre duas paletas misturadas por matiz. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<p><strong>Descarte por oclusão no horizonte do terreno.</strong> Quatro caminhos comparados no mundo de produção: distância; distância mais frustum; mais um teste de raio no mapa de alturas que rejeita instâncias atrás de uma crista; mais uma pirâmide de alturas máximas que acelera o teste; e uma adaptação para computação TSL. A pirâmide faz amostragens exatamente na grade de vértices de dois metros usada para a tesselação da malha (texel da base = máximo dos quatro vértices de canto, seguido por reduções máximas 2x2 nos níveis superiores), de modo que ela retorna apenas alturas que o rasterizador realmente exibe.</p>
<p><strong>Acelerado e correto, não uma troca.</strong> A amostragem pontual do ruído contínuo — o caminho T2 de força bruta — encontra picos entre vértices da malha que nunca são renderizados, causando oclusão excessiva de vegetação visível. A pirâmide baseada na grade de vértices é mais rápida — redução AABB de $O(\log N)$ — e geometricamente exata. Por isso, os testes de raio a cada passo fazem amostragem bilinear da malha, em vez de consultar o máximo por texel da pirâmide, que aumentaria as alturas em metros nas cristas íngremes. O descarte de chunks usa um teste de cinco pontos e exclui a área ocupada pelo próprio chunk para impedir a auto-oclusão. O caminho de produção envia os metadados e o campo de alturas para buffers de armazenamento, permitindo o descarte por shader de computação com desenho indireto.</p>
<hr>
<p>Parte 28 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain">Parte 27 — Uma ilha criada com ruído e terreno que parece terreno</a>
Próxima: <a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">Parte 29 — Um controlador, qualquer corpo</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 27: Uma ilha criada com ruído, um terreno que parece terreno]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain</guid>
            <pubDate>Wed, 13 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 54 cria uma ilha convincente usando apenas ruído: FBM com distorção de domínio moldado por uma atenuação radial, seguido por erosão hidráulica que esculpe uma drenagem real e, depois, biomas atribuídos com base em elevação e umidade. O Spike 55 resolve o mais antigo dos problemas de textura, o ladrilho que se repete, com quatro modos de amostragem e aquele que realmente vence.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 27: Uma ilha criada com ruído, um terreno que parece terreno</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-12-open-world-browser-part-26-water">Parte 26</a> colocou água no mundo. Esta parte constrói a terra sob ela, em duas etapas que refletem como um lugar real passa a existir. O Spike 54 é o terreno em si: não esculpido à mão, mas criado a partir de ruído e depois desgastado até adquirir a drenagem e o litoral de um lugar pelo qual a água realmente correu. O Spike 55 é a superfície desse terreno: ele resolve o problema que derrota quase todo solo procedural, que é a aparência repetitiva de uma textura em ladrilhos. Juntos, os dois respondem a uma pergunta: um criador consegue obter uma ilha plausível e um solo que realmente pareça solo sem que um artista precise mexer em nenhum dos dois? A resposta é sim, desde que você copie as receitas certas.</p>
<h2>Um litoral que o ruído, sozinho, não consegue criar</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/54-procgen-island/" title="Spike 54: Ilha procedural" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/54-procgen-island/" target="_blank">Abrir o Spike 54 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=54-procgen-island">Ver código-fonte</a></p>
<p>O mapa de altura segue as receitas da Red Blob Games, que consistem em uma pilha de pequenas transformações, cada uma corrigindo uma falha específica do ruído bruto. O movimento browniano fracionário soma várias oitavas de ruído Perlin para que o terreno tenha tanto colinas amplas quanto detalhes finos, mas o FBM simples parece suave e alinhado à grade. Por isso, o ponto de amostragem passa primeiro por uma distorção de domínio: cada coordenada é deslocada por outro campo de ruído, $p' = p + \text{noise}(p)\cdot s$, o que curva as cristas e produz algo orgânico, em vez de preso aos eixos. Depois, a redistribuição, que eleva a altitude a uma potência $e^{,k}$, empurra os tons médios para baixo, fazendo com que o mundo tenha vales planos e picos agudos, em vez de tudo ficar na mesma inclinação suave. O elemento que transforma o resultado em uma ilha, e não em colinas infinitas, é uma atenuação radial: calcule uma distância euclidiana a partir do centro, $d = \min(1, \sqrt{n_x^2 + n_y^2})$, e misture a elevação em direção a $1 - d$ para que o terreno desça até o oceano nas bordas. Um segundo campo de ruído independente se torna um mapa de umidade, que não afeta a forma, mas depois alimenta a etapa de biomas.</p>
<p>Isso produz uma forma, mas ainda é uma forma feita de ruído. Ela não tem rios, vales escavados pela água nem leques de sedimentos, porque nada jamais fluiu por sua superfície. A solução é a erosão hidráulica, adaptada da implementação de Sebastian Lague licenciada sob a MIT. Milhares de gotas d'água surgem em posições aleatórias e descem pelas encostas, cada uma preservando inércia para não fazer curvas bruscas em ângulo reto. Elas recolhem sedimentos quando aceleram e os depositam quando desaceleram ou se acumulam, enquanto um pincel circular pré-calculado espalha cada evento de erosão por um pequeno raio, fazendo com que a escavação seja suave, em vez de um risco de um único pixel. A evaporação encerra a gota ao longo de sua vida útil. Execute gotas suficientes e o terreno desenvolverá aquilo que o ruído não consegue imitar: redes de drenagem convergentes, vales que se alargam rio abaixo e áreas planas onde os sedimentos se assentaram.</p>
<h2>Erosão na GPU e rios que sabem onde estão</h2>
<p>A erosão na CPU está correta, mas é lenta. Por isso, o spike também a adapta para um shader de computação WebGPU, e a parte interessante é aquilo que as operações atômicas da GPU obrigam você a fazer. As alturas são armazenadas como <code>i32</code> de ponto fixo, com escala de um milhão, porque a WGSL não oferece soma atômica de floats, e a única forma de permitir que milhares de gotas depositem e removam sedimentos da mesma célula em paralelo, sem condições de corrida, é usar <code>atomicAdd</code> e <code>atomicSub</code> com inteiros. A conversão de ida e volta do ponto fixo significa que, às vezes, uma célula pode ficar ligeiramente negativa sob forte arrasto simultâneo, o que é inofensivo e corrigido na leitura. A versão para CPU pré-calcula uma tabela de pincéis por célula, mas, em uma grade de $1001^2$, essa tabela ocupa cerca de 100 MB. Por isso, a versão para GPU recalcula o pincel de cada gota em tempo real, trocando um pouco de aritmética por uma grande economia de memória. As posições iniciais das gotas continuam vindo da CPU por meio de um gerador Mulberry32, para que as execuções sejam determinísticas.</p>
<p>Mais duas passagens transformam o terreno desgastado em um mapa legível. A drenagem usa o método de acumulação de fluxo D8 da Red Blob: coloque uma unidade de chuva em cada célula, ordene todas as células pela elevação em ordem decrescente e passe a água acumulada de cada célula para seu único vizinho mais baixo. Assim, a água se acumula ao longo dos vales naturais, e qualquer célula cuja acumulação ultrapasse um limite é marcada como rio. O problema é que um mapa de altura erodido tem depressões, rebaixamentos locais sem uma saída descendente, e a água que entra em uma depressão simplesmente para, interrompendo a acumulação. Portanto, antes da drenagem, o preenchimento de depressões de Planchon-Darboux eleva cada uma delas até um pouco acima de seu vizinho mais baixo, $\text{minNeighbour} + \epsilon$, iterando por menos de vinte passagens até que toda célula tenha por onde drenar. Por fim, os biomas são atribuídos segundo o método da Red Blob: uma consulta em dois eixos que cruza elevação e umidade, de modo que uma célula alta e seca se transforme em rocha, enquanto uma baixa e úmida se torne pântano, com substituições baseadas na inclinação aplicadas por cima. Faces íngremes são forçadas a virar rocha, independentemente da umidade; a faixa da linha d'água se torna praia; e as maiores elevações recebem neve. O resultado é uma ilha que você entende de imediato, gerada de ponta a ponta a partir de duas sementes de ruído.</p>
<h2>O ladrilho que não para de se repetir</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/55-terrain-shading/" title="Spike 55: Sombreamento de terreno" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/55-terrain-shading/" target="_blank">Abrir o Spike 55 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=55-terrain-shading">Ver código-fonte</a></p>
<p>Um terreno precisa de um material que cubra quilômetros usando uma textura com apenas alguns metros de largura, e, no instante em que você amplia um ladrilho para cobrir o solo, o olhar percebe a repetição. O Spike 55 é um teste A/B de quatro modos de amostragem sobre a mesma geometria e iluminação, de modo que a única variável seja a forma como a textura é lida. A referência é <code>plain</code>: uma amostra nas coordenadas UV escaladas, com uma repetição evidente a cada poucos metros, que existe apenas como aquilo que os outros modos precisam superar. As camadas PBR gratuitas vêm da Poly Haven por meio de sua CDN: textura difusa, o mapa ARM empacotado (oclusão de ambiente, rugosidade e metalicidade) e um mapa normal GL, tudo sob CC0, carregado com anisotropia 8 e mipmaps.</p>
<p>Os outros três são abordagens diferentes para combater a repetição. O modo <code>hex-linear</code> usa a grade triangular de Heitz-Neyret: uma malha triangular inclinada é posicionada sobre a superfície, e cada fragmento fica dentro de um triângulo cujos três vértices recebem amostras com deslocamentos aleatórios, combinadas por pesos baricêntricos para que regiões vizinhas leiam partes diferentes da textura e a repetição em grande escala desapareça. Mas uma mistura linear simples de três amostras calcula a média de suas cores nas bordas dos triângulos, deixando um padrão triangular visível, exatamente o artefato encontrado pelo spike 48. O modo <code>hex-vp</code> é a correção publicada no mesmo artigo de 2018: depois da soma baricêntrica, subtraia a cor média da textura, redimensione pelo inverso da raiz quadrada da soma dos pesos ao quadrado para manter a variância constante e, então, acrescente novamente a média. Isso mantém o contraste estável durante a mistura, fazendo os triângulos desaparecerem. É por isso que o material realiza antecipadamente uma leitura 1 por 1 de cada textura carregada para estimar sua cor média. O quarto modo, <code>iq-untiled</code>, é a técnica 3 de Texture Repetition, de Inigo Quilez: duas amostras deslocadas, misturadas por um padrão de variação de baixa frequência, usando apenas duas amostras em vez de três e sem qualquer estrutura triangular desde o início — a opção barata que ainda apresenta bons resultados.</p>
<p>Sobre qualquer modo pode ser aplicada uma projeção triplanar opcional, que permite ao mesmo material revestir um penhasco sem esticar. Em vez de uma única coordenada UV, o shader amostra três projeções nos eixos do mundo: superfícies voltadas para X leem o plano YZ, Y lê ZX e Z lê XY. Depois, combina tudo usando <code>abs(normalWorld)</code> elevado a uma potência de nitidez entre quatro e oito, para que uma inclinação de 45 graus não sobreponha as três projeções em uma massa indefinida. O teste deixa a compensação clara: a mistura hexagonal com preservação de variância vence em qualidade, enquanto a técnica de duas amostras de Quilez é a opção mais consciente do desempenho. Ambas superam o modo simples por uma margem grande o bastante para que nenhum criador deva publicar um projeto usando o ladrilho simples.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Geração de mapas de altura por composição de receitas.</strong> As receitas da Red Blob Games compõem ruído bruto para formar terreno: FBM com distorção de domínio ($p' = p + \text{noise}(p)\cdot s$) para cristas orgânicas, redistribuição $e^{,k}$ para vales planos e picos agudos e uma atenuação radial euclidiana combinada em direção a $1 - d$ para rebaixar as bordas até o oceano e formar uma ilha. Um segundo campo de ruído funciona como mapa de umidade para a atribuição de biomas. Consulte <a href="/pt-BR/guides/landscape-generation-browser">geração de paisagens</a>.</p>
<p><strong>Erosão hidráulica na CPU e na GPU.</strong> O modelo de gotas de Sebastian Lague (inércia, capacidade de transporte, pincel circular pré-calculado para deposição e evaporação) esculpe uma drenagem real que o ruído não consegue imitar. A versão de computação WebGPU armazena alturas como <code>i32</code> de ponto fixo em uma escala de um milhão, permitindo que as gotas usem <code>atomicAdd</code>/<code>atomicSub</code> na mesma célula em paralelo, recalcula o pincel de cada gota em tempo real para evitar uma tabela de cerca de 100 MB em $1001^2$ e gera as posições iniciais usando um gerador Mulberry32 determinístico.</p>
<p><strong>Drenagem D8 com preenchimento de depressões.</strong> A acumulação de fluxo D8 da Red Blob deposita uma unidade de chuva por célula, ordena as células pela elevação em ordem decrescente e transfere a água para o vizinho mais baixo de cada uma, marcando como rios aquelas que ultrapassam um limite. Primeiro, o preenchimento de depressões de Planchon-Darboux eleva cada rebaixamento local até $\text{minNeighbour} + \epsilon$ (iterando por menos de vinte passagens), para que a água nunca fique presa e a acumulação permaneça conectada. Uma consulta em dois eixos, cruzando elevação e umidade, atribui os biomas, com substituições baseadas na inclinação que forçam rocha em penhascos, praia na linha d'água e neve nos picos.</p>
<p><strong>Quatro maneiras de eliminar a repetição de texturas.</strong> Sobre geometria e iluminação idênticas: <code>plain</code> (uma amostra, com repetição evidente), <code>hex-linear</code> (grade triangular de Heitz-Neyret, cuja mistura linear deixa um padrão de triângulos), <code>hex-vp</code> (a correção com preservação de variância da seção 3.3 do EGSR 2018, que subtrai a média, redimensiona pelo inverso da raiz quadrada dos pesos ao quadrado e acrescenta novamente a média, exigindo uma leitura média de 1×1 por textura) e <code>iq-untiled</code> (a técnica 3 de duas amostras de Inigo Quilez). A projeção triplanar amostra três planos nos eixos do mundo, combinados por <code>abs(normalWorld)</code> elevado a uma potência de 4 a 8, para que os penhascos não apresentem estiramento. A preservação de variância vence em qualidade; as duas amostras vencem em custo. Consulte <a href="/pt-BR/guides/landscape-generation-browser#terrain-materials">materiais de terreno</a>.</p>
<hr>
<p>Parte 27 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-12-open-world-browser-part-26-water">Parte 26 — Três maneiras de criar água</a>
Próxima: <a href="/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion">Parte 28 — Grama até o horizonte e um terreno que se esconde</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 26: Três maneiras de criar água]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-12-open-world-browser-part-26-water</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-12-open-world-browser-part-26-water</guid>
            <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 51 criou água com reflexos em espaço de tela e esbarrou em sua limitação fundamental: ela não consegue refletir o que a câmera nunca viu. O Spike 52 adotou um espelho planar, o padrão usado por todos os jogos lançados, e eliminou um bug que engolia reflexos escuros. O Spike 53 integrou uma biblioteca de água pronta para produção para vermos como é um resultado finalizado.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 26: Três maneiras de criar água</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters">Parte 25</a> vestiu os avatares. Esta parte trata de água e contém três spikes, porque a água é a superfície em que um atalho barato e a solução correta parecem idênticos em uma captura de tela, mas completamente diferentes em movimento. O Spike 51 cria reflexos em espaço de tela, a opção tentadora, e esbarra diretamente em sua limitação inerente. O Spike 52 muda para o método que todos os jogos lançados realmente usam. O Spike 53 integra uma biblioteca de água pronta para vermos a distância entre o resultado “finalizado” e o ponto em que estamos. Os três compartilham a mesma camada de refração, portanto a única variável que muda entre os dois primeiros é a forma como o reflexo é calculado.</p>
<h2>Reflexo a partir da tela que você já tem</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/51-water-ssr-caustics/" title="Spike 51: Água com SSR" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/51-water-ssr-caustics/" target="_blank">Abrir o Spike 51 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=51-water-ssr-caustics">Ver código-fonte</a></p>
<p>O reflexo em espaço de tela reutiliza o quadro que já foi renderizado. Para cada pixel da água, você reflete o raio de visão na superfície, percorre o buffer de profundidade com esse raio refletido e, quando ele passa por trás de uma superfície registrada, encontra aquilo que a água reflete, amostrado diretamente do buffer de cores. A adaptação segue o <code>SSRNode</code> do three.js linha por linha, que, por sua vez, segue a introdução a SSR de lettier. O percurso é feito por DDA no espaço de tela: projeta-se o início e o fim do raio em coordenadas de pixels e avança-se pelo eixo mais longo, com uma amostra por pixel. A profundidade do raio refletido em cada passo exige interpolação com correção de perspectiva, $\frac{1}{1/z_0 + s,(1/z_1 - 1/z_0)}$, porque interpolar linearmente o Z no espaço de visão é simplesmente incorreto e produz interseções no lugar errado.</p>
<p>Duas coisas tornam isso utilizável em vez de uma apresentação de slides. O percurso aproximado é limitado a 64 passos, pois um raio longo projetado sobre mil pixels poderia executar centenas de iterações por fragmento; um plano de água com um milhão de fragmentos, multiplicado por centenas de iterações e por algumas amostras de textura, resulta em uma cena a 30 fps. A qualidade controla o espaçamento efetivo dentro desse limite, em vez do número de iterações. Além disso, como um percurso aproximado limitado deixa faixas visíveis em degraus, um refinamento binário de seis iterações divide ao meio o intervalo entre o último ponto sem interseção e a interseção encontrada. Isso proporciona uma precisão de subpasso 64 vezes maior, suficiente para impedir que fragmentos vizinhos da água se fixem na mesma posição aproximada de interseção. Uma verificação final da distância entre ponto e linha confirma que o candidato realmente está sobre o raio de reflexão, em vez de apenas ter a mesma profundidade, usando uma tolerância de espessura ajustada automaticamente à largura, no espaço de visão, de um pixel naquela profundidade: menor de perto e maior à distância.</p>
<p>A parte honesta deste spike está escrita em seus próprios comentários: SSR não consegue refletir aquilo que a câmera principal nunca amostrou. A parte inferior de uma árvore, qualquer coisa fora da tela ou qualquer elemento oculto simplesmente não existe nos buffers e, portanto, não pode aparecer no reflexo. Essa é a “perda de informação do lado errado” que nenhuma melhoria na qualidade do percurso consegue corrigir — e é exatamente por isso que existe o próximo spike.</p>
<h2>O espelho que não pode mentir</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/52-water-planar/" title="Spike 52: Água planar" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/52-water-planar/" target="_blank">Abrir o Spike 52 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=52-water-planar">Ver código-fonte</a></p>
<p>Um reflexo planar renderiza a cena uma segunda vez, a partir de uma câmera espelhada em relação ao plano da água, em um alvo fora da tela, que então é amostrado pelo shader da água. Esse é o padrão canônico, usado pelo Water do UE5, pelo próprio <code>WaterMesh</code> do three.js, por ABZÛ e por Sea of Thieves, porque ele tem todos os pixels da cena à disposição, incluindo a geometria que o SSR jamais consegue ver. Em TSL, chega a ser anticlimático: <code>reflector()</code> aloca a câmera auxiliar espelhada e seu alvo de renderização; você adiciona esse alvo à malha para que ele seja atualizado a cada quadro e amostra sua cor. Quando as ondas forem adicionadas mais tarde, o reflexo irá oscilar com a adição de um deslocamento de distorção ao nó UV do refletor, exatamente como faz a linha correspondente do <code>WaterMesh</code>.</p>
<p>O bug que vale registrar estava na rede de segurança, não no espelho. Uma versão anterior misturava a saída do refletor com um céu procedural como fallback, ponderada pela magnitude da cor refletida, partindo da teoria de que um reflexo próximo de zero significava que o alvo não continha nada naquele ponto. Porém, a sombra escura da copa de uma árvore também tem baixa magnitude, então o valor limitado nunca atingia a intensidade máxima e esses pixels genuinamente escuros acabavam misturados ao céu claro. O sintoma detectado pelo usuário foi preciso: o alvo bruto do espelho, no modo de depuração, mostrava árvores escuras perfeitas, enquanto a renderização composta apresentava reflexos desbotados. O diagnóstico foi que o shader “faz as cores escuras desaparecerem”. A correção foi apagar código. O alvo do refletor é confiável depois do primeiro quadro, portanto não há necessidade alguma de fallback. Os dois spikes compartilham a mesma refração por baixo: amostrar a cena atrás da superfície, reconstruir a distância de cada pixel abaixo da linha-d’água e aplicar extinção de Beer-Lambert por canal, para que o vermelho desapareça em poucos metros enquanto o azul persiste, com uma máscara de céu para impedir que o plano de fundo distante receba neblina. O efeito Fresnel de Schlick mistura refração quando se olha diretamente para baixo, dentro da água, e reflexo quando se olha através de sua superfície.</p>
<h2>Como é um resultado finalizado</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/53-water-pro/" title="Spike 53: Água Pro" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/53-water-pro/" target="_blank">Abrir o Spike 53 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=53-water-pro">Ver código-fonte</a></p>
<p>O Spike 53 é a avaliação entre desenvolver ou comprar. Ele integra o <code>threejs-water-pro</code> como é distribuído, com seu preset tropical, clipmap oceânico padrão, acompanhamento de câmera e céu de Rayleigh, além de carregar a mesma ilha em glTF usada pela demonstração da própria biblioteca. O objetivo é enxergar a diferença entre um shader de água plana feito à mão e um sistema oceânico completo — e essa diferença é grande. Este sistema tem flutuabilidade que amostra a altura das ondas em vários pontos sob o casco de um navio, fazendo-o inclinar e balançar em vez de apenas subir e descer; um gerador de esteira; espuma na costa e na superfície; e uma passagem de máscara que impede a renderização da água dentro do casco, para que as ondulações não atravessem o convés.</p>
<p>A integração revelou o tipo de detalhe que só se aprende usando uma biblioteca, em vez de apenas ler seu README. As texturas de espuma são referenciadas pelo nome do arquivo no preset, mas o carregamento é responsabilidade do consumidor; sem elas, a costa parece uma borda rígida na linha-d’água, em vez de uma zona de arrebentação. A ilha é posicionada de modo que sua geometria subaquática desça além do fundo do oceano, permitindo que a malha do fundo da biblioteca oculte o anel externo do modelo sem deixar visível a borda do plano. Esse é o padrão pretendido pela biblioteca: trazer um modelo 3D, não sintetizar o terreno. E o antialiasing é deliberadamente desativado no renderizador em favor de uma passagem FXAA de pós-processamento, porque o MSAA mistura fragmentos das bordas com o plano de fundo antes da execução da neblina atmosférica baseada em profundidade, deixando uma fina franja escura em todos os pontos onde a geometria encontra a neblina. Resolver o serrilhado depois da neblina, em vez de antes, elimina essa franja. O que isso reduz é o risco da própria decisão: um oceano pronto para produção é um sistema grande e especializado e, nos casos em que precisamos de um, adotar uma biblioteca com manutenção ativa é melhor do que reconstruir do zero esteiras, flutuabilidade e espuma. Enquanto isso, o shader de espelho planar do Spike 52 continua sendo a solução certa para as águas interiores menores que um criador coloca em seu próprio mundo.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Água com reflexos em espaço de tela.</strong> Um raio de visão refletido percorre o buffer de profundidade em espaço de tela por meio de DDA, usando interpolação 1/z com correção de perspectiva, um limite rígido de passos para restringir o custo por fragmento e uma etapa de refinamento binário para remover as faixas deixadas pelo percurso limitado. Uma confirmação da distância entre ponto e linha, com espessura escalada pela profundidade, rejeita interseções falsas. O limite absoluto do método é que ele só consegue refletir a geometria já amostrada pela câmera principal, portanto superfícies fora da tela ou voltadas para o lado errado nunca aparecem. Consulte <a href="/pt-BR/guides/landscape-generation-browser#terrain-materials">materiais de terreno</a>.</p>
<p><strong>Reflexo com espelho planar.</strong> Uma câmera auxiliar espelhada em relação ao plano da água renderiza a cena em um alvo fora da tela, amostrado pelo shader da água, produzindo reflexos perfeitos em nível de pixel, inclusive da geometria que o SSR não consegue ver. Esse é o padrão usado pelo Water do UE5 e pelo <code>WaterMesh</code> do three.js, com a distorção das ondas aplicada como um deslocamento no nó UV do refletor. Um fallback de céu ponderado pela magnitude apagava incorretamente os pixels escuros dos reflexos; o alvo do refletor é confiável depois do primeiro quadro, portanto remover o fallback resolveu o problema.</p>
<p><strong>Refração com coloração de profundidade de Beer-Lambert.</strong> Os dois shaders amostram a cena atrás da superfície, reconstroem a profundidade de cada pixel do plano de fundo abaixo da linha-d’água e aplicam extinção exponencial por canal — o vermelho desaparece em poucos metros, enquanto o azul persiste —, compondo-a em direção a uma cor de neblina aquática, com uma máscara de céu para que o plano distante não receba neblina. O efeito Fresnel de Schlick combina refração em incidência normal com reflexo em ângulos rasantes.</p>
<p><strong>Adoção de uma biblioteca de água pronta para produção.</strong> O <code>threejs-water-pro</code> inclui um clipmap oceânico, céu de Rayleigh, flutuabilidade multiponto para a inclinação e o balanço de navios, esteiras, espuma e uma passagem de máscara do casco. Os detalhes do lado do consumidor importam: as texturas de espuma precisam ser carregadas explicitamente, a geometria subaquática da ilha deve descer além do fundo do oceano para que ele oculte suas bordas e o antialiasing deve ser executado como uma passagem FXAA de pós-processamento, em vez de MSAA, para evitar uma franja escura de neblina nas bordas da geometria.</p>
<hr>
<p>Parte 26 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters">Parte 25 — Um esqueleto para todos os trajes</a>
Próxima: <a href="/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain">Parte 27 — Uma ilha criada com ruído e um solo que parece solo</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como engines modernas vencem o overdraw da vegetação]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-11-foliage-overdraw</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-11-foliage-overdraw</guid>
            <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Pré-passes Z, impostores octaédricos, descarte de clusters controlado pela GPU, alfa com hash, buffers de visibilidade e sombras por campos de distância: o conjunto de técnicas que reduz o overdraw de vegetação de 8-15x para 1-2x em cenas de florestas densas.]]></description>
            <content:encoded><![CDATA[<h1>Como engines modernas vencem o overdraw da vegetação</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="/img/blog/foliage_overdraw_hero.webp" alt="Dossel de floresta densa com cartões de folhas sobrepostos e empilhados diante de um sol poente, sugerindo um alto nível de overdraw" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Uma floresta é uma das piores coisas que você pode pedir para uma GPU renderizar. Cada folha é um quad texturizado recortado por uma máscara alfa. Dezenas desses quads se empilham uns sobre os outros ao longo de cada raio de visão. O rasterizador não tem como saber antecipadamente quais fragmentos passarão no teste alfa, então a otimização padrão de early-Z que beneficia cenas opacas fica praticamente desativada. Como resultado, um único pixel da tela pode executar o shader completo das folhas 10 ou 15 vezes antes de o quadro ficar pronto. Isso é o overdraw da vegetação, e ele é a parte mais cara de um quadro de mundo aberto em qualquer jogo lançado na última década.</p>
<p>A boa notícia é que esse é um problema resolvido. Não no sentido de que alguém o corrigiu com um único truque, mas no sentido de que existe um conjunto de sete ou oito técnicas que, combinadas, reduzem o overdraw efetivo de 8-15x em uma floresta densa para 1-2x. Toda engine moderna inclui alguma versão desse conjunto. Veja quais técnicas fazem parte dele, por que cada uma existe e quais são as referências canônicas de cada uma.</p>
<h2>1. Por que o overdraw da vegetação é tão problemático</h2>
<p>Em uma cena opaca típica, a GPU faz a rejeição antecipada por profundidade: antes mesmo de executar o pixel shader, o hardware verifica o buffer de profundidade existente e ignora fragmentos que já estejam atrás de alguma coisa. Isso é praticamente gratuito e é o que mantém razoável o custo de geometrias densas.</p>
<p>A vegetação com teste alfa quebra esse mecanismo. O fragment shader precisa realmente ser executado para avaliar a textura alfa e chamar <code>discard</code> (ou <code>clip</code>) nos pixels removidos pela máscara. O hardware não consegue saber se um fragmento será descartado até que o shader seja executado, portanto, na maioria das GPUs, usar <code>discard</code> em qualquer ponto de um shader desativa totalmente o early-Z para aquela draw call. Em GPUs baseadas em tiles (dispositivos móveis, chips da série M e alguns consoles), isso pode desativar o Hi-Z e a compactação de profundidade do quadro inteiro. O pixel shader é executado uma vez para cada triângulo que cobre um pixel, e a maioria dessas avaliações termina em descarte.</p>
<p>Empilhe 10 quads de folhas diante de um único pixel da tela e o shader das folhas será executado 10 vezes. Multiplique isso por 4 milhões de pixels e o custo será brutal. O artigo de Marco Salvi, <a href="https://therealmjp.github.io/posts/to-earlyz-or-not-to-earlyz/">Usar early-Z ou não usar early-Z</a>, é a introdução mais acessível aos motivos pelos quais isso acontece no nível do hardware e é o lugar certo para começar caso você nunca tenha pensado no assunto.</p>
<img src="/img/blog/foliage_overdraw_layers.webp" alt="Vista lateral do raio de um único pixel da tela atravessando doze cartões de folhas sobrepostos, com cada cartão destacado para mostrar os fragmentos empilhados que passam por teste alfa" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<h2>2. O pré-passe de profundidade para geometria com máscara</h2>
<p>O maior ganho isolado — e a técnica incluída em toda engine moderna — é dividir a renderização da vegetação em dois passes. O primeiro passe grava apenas a profundidade, usando um shader mínimo que faz o teste alfa, descarta os pixels removidos pela máscara e não grava mais nada. O segundo passe renderiza o material completo com o teste de profundidade definido como &quot;igual&quot; e a gravação de profundidade desativada. Agora, cada pixel visível calcula a BRDF completa exatamente uma vez, independentemente de quantos quads de folhas estejam empilhados atrás dele.</p>
<p>Isso parece o dobro do trabalho porque os mesmos triângulos são processados duas vezes, mas o shader do pré-passe é tão barato (uma amostra de textura, um descarte e uma gravação de profundidade) que a economia no passe principal supera em muito o custo adicional. Em uma floresta densa, o passe principal deixa de executar o shader completo das folhas 10-15 vezes por pixel e passa a executá-lo exatamente uma vez.</p>
<p>Unreal, Frostbite, Decima e as ramificações modernas de Rage e Dunia fazem isso. Esse também é o principal motivo pelo qual materiais com máscara continuam sendo mais baratos do que materiais translúcidos em qualquer uma dessas engines.</p>
<p>Análises detalhadas:</p>
<ul>
<li>Pettineo, <a href="https://therealmjp.github.io/posts/to-earlyz-or-not-to-earlyz/">Usar early-Z ou não usar early-Z</a> (o artigo canônico sobre como <code>discard</code> interage com Hi-Z e early-Z).</li>
<li>Wihlidal, <a href="http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/">Otimizando o pipeline gráfico com computação</a> (GDC 2016, o pré-passe de profundidade da Frostbite e sua arquitetura de descarte controlada pelo pré-passe).</li>
<li>Sanders, <a href="https://www.gdcvault.com/play/1025066/Between-Tech-and-Art-The">Entre tecnologia e arte: a vegetação de Horizon Zero Dawn</a> (GDC 2018, incluindo a renderização de vegetação em dois passes da Decima).</li>
<li>Persson, <a href="http://www.humus.name/index.php?page=News&amp;ID=255">Algumas observações sobre Z</a> (ainda uma das explicações mais claras sobre testes de profundidade por igualdade e a relação custo-benefício dos pré-passes).</li>
</ul>
<h2>3. LODs agressivos e impostores octaédricos</h2>
<p>O segundo maior ganho é não renderizar as folhas quando isso não for necessário. Os assets de vegetação incluem vários níveis de LOD. O mais próximo usa a malha completa, com cartões individuais para as folhas. A uma distância média, as folhas são combinadas em cartões compostos mais densos (um grupo de 30 folhas se transforma em 1 cartão texturizado com a mesma silhueta). Além de um limite de distância, a árvore inteira se torna um <em>impostor</em>: uma pequena geometria texturizada com vistas pré-renderizadas da árvore a partir de vários ângulos.</p>
<p>O formato moderno é o <strong>impostor octaédrico</strong>: uma geometria de 8 faces texturizada com um atlas de vistas capturadas a partir de pontos em uma esfera por meio de mapeamento octaédrico. Em tempo de execução, o shader escolhe as duas ou três vistas pré-renderizadas mais próximas com base na direção da câmera e faz a transição entre elas. O resultado é um substituto composto por poucos triângulos, que parece tridimensional de qualquer ângulo e pode ter iluminação adequada, mapas de normais e até animação pelo vento. A implementação de Ryan Brucks, originalmente um plugin da comunidade e agora parte da Unreal, é a referência. As florestas com bilhões de árvores do Microsoft Flight Simulator são compostas essencialmente por impostores octaédricos em todos os lugares, exceto nas proximidades da câmera.</p>
<p>O maior ganho estrutural é que os impostores são <em>opacos ou quase opacos</em> à distância. O cartão composto de 30 folhas é um único quad com máscara em vez de 30 quads. O impostor da árvore inteira tem poucas faces, não milhares. O overdraw ao longe cai para quase zero.</p>
<p>Análises detalhadas:</p>
<ul>
<li>Brucks, <a href="https://shaderbits.com/blog/octahedral-impostors">Impostores octaédricos</a> (a referência canônica, com a matemática e a implementação na UE).</li>
<li>Halen, <a href="https://dev.epicgames.com/community/learning/tutorials/JmL3/octahedral-impostors-unreal-engine">Impostores octaédricos na Unreal Engine</a> (o fluxo de trabalho integrado à UE).</li>
<li>Häggström, <a href="https://www.diva-portal.org/smash/get/diva2:1135856/FULLTEXT01.pdf">Renderização de vegetação em tempo real</a> (uma tese clara que aborda cadeias de LOD, impostores e a matemática por trás deles).</li>
<li>Crytek, <a href="http://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-4-next-generation-speedtree-rendering">Integração do SpeedTree à CryEngine 3</a> (GPU Gems 3, ainda a melhor introdução às cadeias de LOD para árvores).</li>
</ul>
<h2>4. Descarte de clusters controlado pela GPU</h2>
<p>Mesmo com um pré-passe de profundidade, o próprio pré-passe tem um custo: ele ainda precisa <em>processar</em> cada triângulo de cada árvore visível (ou potencialmente visível). Engines modernas reduzem esse custo com o descarte de clusters controlado pela GPU, que elimina grupos inteiros de triângulos antes mesmo que o rasterizador os veja.</p>
<p>O pipeline funciona assim: cada malha é previamente dividida em clusters de 64 ou 128 triângulos, com uma caixa delimitadora justa e um cone de normais. Durante a renderização, um compute shader percorre a lista de instâncias, testa cada instância contra o frustum, depois testa cada cluster de cada instância visível contra o frustum e então faz um <em>teste de oclusão Hi-Z</em> de cada cluster restante contra a pirâmide de profundidade do quadro anterior. Galhos inteiros ocultos atrás de uma colina ou de outra árvore são descartados antes da execução de qualquer vertex shader. A saída é uma lista compacta de argumentos indicando &quot;renderize estes clusters&quot;, enviada diretamente para uma única chamada <code>DrawIndirect</code>.</p>
<p>É isso que permite renderizar uma floresta de 10.000 árvores em milissegundos, em vez de segundos. A palestra da Ubisoft sobre Assassin's Creed Unity apresentou esse pipeline em uma implementação de produção (20-40% dos triângulos descartados, 30-80% dos triângulos de sombras descartados e 10x mais instâncias na tela do que na geração anterior), e a palestra de Wihlidal sobre a Frostbite levou a técnica ainda mais longe. O Nanite da UE5 é o estágio final visível dessa trajetória: descarte de clusters até o nível dos pixels.</p>
<p>Análises detalhadas:</p>
<ul>
<li>Haar e Aaltonen, <a href="https://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">Pipelines de renderização controlados pela GPU</a> (SIGGRAPH 2015, a palestra fundamental sobre Assassin's Creed Unity).</li>
<li>Wihlidal, <a href="http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/">Otimizando o pipeline gráfico com computação</a> (GDC 2016, o pré-passe controlado pela GPU da Frostbite).</li>
<li>Karis, Stubbe, Wihlidal, <a href="https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf">Uma análise detalhada da geometria virtualizada do Nanite</a> (SIGGRAPH 2021, descarte de clusters com granularidade de meshlets).</li>
<li>Liktor, <a href="https://research.activision.com/publications/2021/09/geometry-rendering-pipeline-architecture-at-activision">Arquitetura do pipeline de renderização de geometria na Activision</a> (a versão do descarte de clusters usada em Call of Duty, 2021).</li>
</ul>
<h2>5. Ordenação de instâncias e clusters da frente para trás</h2>
<p>Quando o pré-passe começa a cumprir seu papel, a <em>ordem</em> passa a importar. O pré-passe grava a profundidade, mas apenas para os fragmentos que passam no teste alfa. Se você renderizar primeiro o fundo da floresta e deixar a frente por último, cada fragmento da frente sobrescreverá um fragmento de trás, e o shader do pré-passe ainda será executado para o fragmento de trás. Se você renderizar da frente para trás, cada draw call sucessiva preencherá uma parte maior do buffer de profundidade com valores menores, e o Hi-Z rejeitará cada vez mais fragmentos de trás antes da execução do shader.</p>
<p>É por isso que quase toda engine moderna ordena as instâncias de vegetação pela distância até a câmera antes de executar o pré-passe. A ordenação é barata (algumas centenas de milhares de instâncias na GPU usando radix sort) e transforma o próprio pré-passe em uma operação que reduz progressivamente seu trabalho. O descarte no nível dos clusters faz a ordenação com granularidade de meshlets pelo mesmo motivo. O pré-passe de profundidade e a ordenação da frente para trás formam o tipo de dupla em que cada técnica é boa sozinha, mas a combinação é excelente.</p>
<p>Análises detalhadas:</p>
<ul>
<li>Persson, <a href="http://www.humus.name/Articles/Persson_DepthInDepth.pdf">Profundidade em detalhes</a> (observações arquitetônicas sobre a ordenação do buffer de profundidade, a relação custo-benefício dos pré-passes e o comportamento do Hi-Z).</li>
<li>Giesen, <a href="https://fgiesen.wordpress.com/2011/07/04/a-trip-through-the-graphics-pipeline-2011-part-7/">Uma viagem pelo pipeline gráfico</a> (a explicação técnica detalhada de como a taxa de rejeição do Hi-Z depende da ordem de renderização).</li>
<li>Wihlidal, <a href="http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/">Otimizando o pipeline gráfico com computação</a> (GDC 2016, incluindo a ordenação de instâncias na GPU da Frostbite).</li>
</ul>
<h2>6. Transições de LOD com dithering e alfa com hash</h2>
<p>A outra grande armadilha são as transições graduais. A maneira ingênua de fazer a transição entre dois LODs (ou de fazer uma instância aparecer ou desaparecer gradualmente conforme a câmera se aproxima) é usar mesclagem alfa. Mas geometrias com mesclagem não podem gravar no buffer de profundidade, o que joga toda árvore em transição para o lento caminho translúcido e quebra o pré-passe. A solução é manter a geometria no caminho com máscara e fazer a transição <em>dentro do teste alfa</em>.</p>
<p>Duas técnicas principais:</p>
<ul>
<li><strong>Transições de LOD com dithering</strong> amostram um padrão Bayer 4x4 ou 8x8 (ou uma textura de ruído azul no espaço da tela) e o usam como modificador do limiar por pixel. Uma árvore com 50% de transição mantém um padrão quadriculado de pixels; os pixels ausentes são preenchidos pelo padrão complementar do LOD seguinte. O TAA resolve o padrão quadriculado e produz uma transição suave ao longo de dois ou três quadros. É barato, estável e combina com todo o restante da engine.</li>
<li><strong>Teste alfa com hash</strong> (Wyman e McGuire, I3D 2017) substitui o limiar alfa fixo de 0,5 por um limiar com hash por pixel no intervalo [0,1). Geometrias alfa distantes que normalmente desapareceriam por completo (porque o alfa com mipmap cai abaixo de 0,5) mantêm uma dispersão estável de pixels sobreviventes. Mais uma vez, o TAA faz o acabamento.</li>
</ul>
<p>As duas técnicas mantêm a vegetação no caminho opaco/com máscara em que o pré-passe de profundidade funciona, portanto você não paga o custo total da renderização translúcida apenas para fazer algo aparecer ou desaparecer gradualmente. <strong>Alfa para cobertura</strong> é a técnica equivalente da era do MSAA: ela converte o alfa em uma máscara de cobertura de subpixels, oferecendo transparência parcial sem sair do caminho com máscara. O problema é que o A2C só funciona realmente bem com MSAA, que a maioria dos renderizadores diferidos modernos já não usa.</p>
<p>Análises detalhadas:</p>
<ul>
<li>Wyman e McGuire, <a href="https://research.nvidia.com/labs/rtr/publication/wyman2017hashed/">Teste alfa com hash</a> (I3D 2017, o artigo canônico sobre alfa com hash).</li>
<li>Castaño, <a href="http://the-witness.net/news/2010/09/computing-alpha-mipmaps/">Como calcular mipmaps alfa</a> (blog de The Witness, a maneira correta de gerar mipmaps de texturas com teste alfa para que árvores distantes não desapareçam).</li>
<li>Yuksel, <a href="https://cemyuksel.com/research/alphadistribution/alpha_distribution.pdf">Distribuição alfa para testes alfa</a> (uma melhoria mais recente para mipmaps alfa).</li>
<li>NVIDIA, <a href="https://developer.nvidia.com/content/transparency-or-translucency-rendering">Teste alfa com antisserrilhamento</a> (um panorama sobre A2C, alfa com hash e alternativas com dithering).</li>
</ul>
<h2>7. Redução do custo de sombreamento em pixels com máscara</h2>
<p>Mesmo com um pré-passe perfeito e um descarte perfeito, você ainda precisa sombrear uma vez cada pixel visível da vegetação. As engines também reduzem esse custo:</p>
<ul>
<li><strong>BRDF mais barata</strong>. A folhagem é fosca e não precisa realmente de um caminho especular Cook-Torrance completo. Uma difusa Lambertiana envolvente mais uma aproximação especular de uma linha é mais do que suficiente.</li>
<li><strong>Mapas de normais com frequência mais baixa</strong>. As folhas já têm bastante ruído visual. Um mapa de normais de 256x256 parece igual a um de 1024x1024 nas distâncias de visualização típicas e economiza largura de banda.</li>
<li><strong>Sem paralaxe, anisotropia ou clearcoat</strong>. O conjunto de recursos PBR é desativado para as folhas.</li>
<li><strong>Transmissão fina de dois lados</strong> em vez de espalhamento subsuperficial completo. As folhas transmitem luz por trás, mas é possível simular isso com um único produto escalar com a luz traseira.</li>
<li><strong>Sombreamento em meia resolução</strong> em alguns motores. A folhagem é sombreada a 1/4 ou 1/2 da taxa de pixels e ampliada. O ruído estocástico do TAA oculta a reamostragem.</li>
<li><strong>Omitir texturas de detalhes e decalques</strong> em materiais mascarados por padrão.</li>
</ul>
<p>Cada uma dessas técnicas gera um pequeno ganho isoladamente. Combinadas, elas permitem que shaders de folhagem mascarada sejam executados de 2 a 3 vezes mais rápido que o material opaco equivalente.</p>
<p>Análises aprofundadas:</p>
<ul>
<li>Lagarde e de Rousiers, <a href="https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/">Migrando a Frostbite para renderização com base física 3.0, seções sobre folhagem e translucidez</a> (SIGGRAPH 2014, os ajustes PBR canônicos para materiais finos de dois lados).</li>
<li>Jimenez, <a href="https://www.iryoku.com/stare-into-the-future">Renderização de personagens de última geração</a> (a matemática da Lambertiana envolvente e da translucidez traseira, originalmente criada para pele, mas amplamente adaptada para folhas).</li>
<li>Sanders, <a href="https://www.gdcvault.com/play/1025066/Between-Tech-and-Art-The">Entre tecnologia e arte: a vegetação de Horizon Zero Dawn</a> (GDC 2018, com as simplificações do shader de folhagem da Decima).</li>
</ul>
<h2>8. Buffers de visibilidade e Nanite para materiais mascarados</h2>
<p>A solução mais limpa para o overdraw é <em>desacoplar completamente o sombreamento da rasterização</em>. Um buffer de visibilidade rasteriza a geometria em um buffer compacto (apenas o ID do triângulo e o ID da instância por pixel) e depois executa o material completo como um passe diferido que lê o buffer de visibilidade e sombreia cada pixel exatamente uma vez. Por definição, não há overdraw na etapa de sombreamento. O artigo de Burns e Hunt de 2013 apresentou essa técnica; o Nanite da UE5 é sua implementação com qualidade de produção, inclusive para folhagem mascarada desde a UE 5.5.</p>
<p>A diferença do Nanite é fazer isso com geometria virtualizada no nível de clusters, de modo que o próprio rasterizador use um caminho por software para triângulos menores que um pixel e mantenha o overdraw limitado. Materiais mascarados no Nanite precisam do recurso de &quot;rasterização programável&quot;: o teste alfa é executado durante o passe do buffer de visibilidade, mas o sombreamento do material ainda acontece uma vez por pixel visível na resolução diferida. O resultado é que uma folhagem Nanite muito densa, antes notoriamente lenta com materiais mascarados, agora é competitiva com a geometria opaca ou até mais barata, pois o overdraw durante o sombreamento é zero. Há uma contrapartida: muitas vezes é melhor processar geometria de árvores <em>opaca</em> e com muitos polígonos pelo Nanite do que manter árvores de poucos polígonos feitas com cartões mascarados, pois o caminho mascarado adiciona o custo da rasterização programável.</p>
<p>Análises aprofundadas:</p>
<ul>
<li>Burns e Hunt, <a href="http://jcgt.org/published/0002/02/04/">O buffer de visibilidade: uma abordagem de sombreamento diferido eficiente para o cache</a> (JCGT 2013, o artigo original).</li>
<li>Karis, Stubbe, Wihlidal, <a href="https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf">Uma análise aprofundada da geometria virtualizada do Nanite</a> (SIGGRAPH 2021, a arquitetura de produção).</li>
<li>Epic, <a href="https://media.gdcvault.com/gdc2024/Slides/GDC+slide+presentations/Nanite+GPU+Driven+Materials.pdf">Materiais do Nanite controlados pela GPU</a> (GDC 2024, com o pipeline de materiais mascarados e rasterização programável).</li>
<li><a href="https://www.sctheblog.com/blog/nanite-materials-notes/">Notas sobre &quot;Materiais do Nanite controlados pela GPU&quot;</a> (uma explicação clara feita por terceiros sobre a mesma palestra).</li>
</ul>
<h2>9. Representações de sombras separadas para a folhagem</h2>
<p>Mapas de sombras para folhagem com teste alfa são o segundo problema de sombras mais caro em qualquer frame de mundo aberto, atrás apenas das cascatas grandes. Cada cascata precisa do próprio pré-passe de profundidade, cada pré-passe executa o teste alfa e o custo se acumula rapidamente em 4 cascatas e dezenas de frustos de luz. Por isso, a maioria dos motores não renderiza as sombras da folhagem da mesma forma que renderiza sua cor.</p>
<p>Substituições comuns:</p>
<ul>
<li><strong>Sombras com campos de distância da malha</strong> (UE Lumen, motores personalizados). Cada malha tem um campo de distância com sinal pré-calculado. Um curto traçado de cone pelo SDF produz uma sombra suave sem jamais tocar na malha com teste alfa. Isso funciona particularmente bem para árvores porque o SDF captura a silhueta da copa como um único volume sólido e ignora os detalhes de cada folha.</li>
<li><strong>Cascatas de resolução mais baixa para a folhagem</strong>. As sombras da folhagem são gravadas em uma camada com metade da resolução e ampliadas com filtragem sensível às bordas. O olho não percebe a redução da resolução porque as sombras já são suaves.</li>
<li><strong>Mapas de sombras com alpha-to-coverage e MSAA</strong>. Em motores que ainda usam MSAA no caminho de sombras, o A2C produz sombras de folhagem com bordas suaves sem o custo completo do teste alfa.</li>
<li><strong>Sombras de cápsula para o tronco e os galhos grandes</strong>, campo de distância para a copa e teste alfa completo apenas na cascata mais próxima. Representações diferentes para distâncias diferentes, combinadas no passe de iluminação.</li>
<li><strong>Distância de desativação do WPO</strong>. O World Position Offset controlado pelo vento é desativado além de um limite para que os dados de sombra armazenados em cache continuem válidos entre frames. Os Virtual Shadow Maps da UE dependem muito disso.</li>
</ul>
<p>Análises aprofundadas:</p>
<ul>
<li>Epic, <a href="https://dev.epicgames.com/documentation/unreal-engine/distance-field-soft-shadows-in-unreal-engine">Sombras suaves com campos de distância na Unreal Engine</a> (a referência canônica da UE para sombras com campos de distância de malha).</li>
<li>Wright, <a href="https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-Lumen-Wright%20et%20al.pdf">Lumen: iluminação global em tempo real na Unreal Engine 5</a> (SIGGRAPH 2022, com a integração de SDFs de malha do Lumen para folhagem).</li>
<li>Epic, <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/virtual-shadow-maps-in-unreal-engine">Virtual Shadow Maps</a> (a arquitetura moderna de sombras da UE5, com regras específicas para desativação de WPO e cache da folhagem).</li>
<li>Persson, <a href="https://www.humus.name/index.php?page=3D&amp;ID=81">Mapas de sombras em cascata na prática</a> (a referência ainda canônica sobre CSM, com observações sobre objetos com teste alfa que projetam sombras).</li>
</ul>
<h2>10. Vento, animação e cache de sombras</h2>
<p>Um problema sutil relacionado: a maior parte da folhagem se move. A animação de vértices controlada pelo vento (World Position Offset na UE e equivalentes na Frostbite e na Decima) faz com que a geometria da folhagem não permaneça estável entre frames, o que invalida o cache de sombras e a reprojeção. Os motores modernos combatem isso de duas maneiras:</p>
<ul>
<li><strong>Limitar o WPO à distância</strong>. Além de um limite, a amplitude da animação do vento é reduzida suavemente até zero. De qualquer forma, o olho não consegue perceber o balanço a uma distância tão grande, e os caches de sombras continuam válidos.</li>
<li><strong>Incorporar o vento aos limites dos clusters</strong>. As caixas delimitadoras dos clusters são expandidas pelo deslocamento máximo do WPO, de modo que o culling permaneça conservador sem precisar reenviar dados a cada frame.</li>
<li><strong>Deslocamentos de fase por instância</strong>. Árvores idênticas usam uma semente aleatória por instância para deslocar a fase do vento, evitando que uma floresta inteira balance em sincronia sem pagar por uma animação exclusiva para cada árvore.</li>
</ul>
<p>Esse é o tipo de detalhe que não aparece em listas de técnicas, mas que, na prática, representa a diferença entre uma floresta de 3 ms e outra de 9 ms.</p>
<p>Análises aprofundadas:</p>
<ul>
<li>Sanders, <a href="https://www.gdcvault.com/play/1025066/Between-Tech-and-Art-The">Entre tecnologia e arte: a vegetação de Horizon Zero Dawn</a> (GDC 2018, com o pipeline de animação pelo vento e o cache de sombras da Decima).</li>
<li>McAuley, <a href="https://www.gdcvault.com/play/1022235/Rendering-the-World-of-Far">Renderizando o mundo de Far Cry 4</a> (GDC 2015, inclui a amostragem da grade de vento para vegetação).</li>
<li>Epic, <a href="https://dev.epicgames.com/community/learning/tutorials/qLk6/unreal-engine-virtual-shadow-maps">Folhagem e Virtual Shadow Maps</a> (as orientações da comunidade da UE5 sobre a distância de desativação do WPO e o cache de VSM).</li>
</ul>
<h2>11. A matemática combinada</h2>
<p>Nenhuma dessas técnicas é uma solução milagrosa. O interessante é o que acontece quando elas são combinadas:</p>
<ul>
<li>Um passe ingênuo de folhagem mascarada em uma cena de floresta densa apresenta de 8 a 15 vezes de overdraw efetivo. Cada pixel visível executa o shader de folha de 8 a 15 vezes.</li>
<li>Adicione um pré-passe de profundidade e o passe principal cai para cerca de 1x de overdraw, mas o próprio pré-passe ainda toca em tudo.</li>
<li>Adicione ordenação da frente para trás e o pré-passe começa a eliminar trabalho por conta própria.</li>
<li>Adicione culling na GPU no nível de clusters e o pré-passe toca apenas no que poderia estar visível.</li>
<li>Adicione cadeias de LODs e impostores, e a <em>quantidade</em> de quads visíveis cai em uma ordem de grandeza além dos 30 m.</li>
<li>Adicione o caminho de buffer de visibilidade/Nanite e a etapa de sombreamento realmente passa a ser executada uma vez por pixel, mesmo com sobreposição densa.</li>
<li>Adicione sombras com campos de distância e o custo das sombras deixa de crescer com o teste alfa.</li>
</ul>
<p>O principal resultado, repetido nas palestras indicadas acima: o overdraw efetivo cai de 8-15x para 1-2x, e o custo total da folhagem por frame diminui de 4 a 6 vezes em uma cena de floresta densa. Esse é o motivo pelo qual jogos modernos de mundo aberto conseguem renderizar florestas a mais de 60 fps em hardware de consumo.</p>
<img src="/img/blog/foliage_overdraw_pipeline.webp" alt="Diagrama mostrando o pipeline de renderização da folhagem como cinco etapas empilhadas: culling na GPU, pré-passe ordenado, passe de cor mascarado com depth-equal, sombreamento por buffer de visibilidade e caminho separado de sombras com campos de distância" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<h2>12. O que isso significa para o navegador</h2>
<p>A maior parte desse conjunto de técnicas pode ser adaptada diretamente para WebGPU. Já disponibilizamos culling controlado pela GPU, dispatch indireto, oclusão Hi-Z e pré-passes ordenados da frente para trás no <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">motor de mundo aberto para navegadores</a>. O pré-passe de profundidade para geometria mascarada é simples: a WebGPU aceita testes depth-equal e <code>discard</code> em shaders de fragmento, com as mesmas ressalvas sobre early-Z. Impostores octaédricos podem ser portados mecanicamente; a matemática consiste apenas no desdobramento de esfera para octaedro e na indexação do atlas.</p>
<p>As partes mais difíceis são as modernas. Um buffer de visibilidade em WebGPU exige gravar um ID de triângulo de 32 bits em um destino de renderização e resolver os materiais em um passe de computação em tela cheia; os componentes necessários existem, mas sua orquestração é complexa. Sombras com campos de distância da malha exigem uma textura 3D por asset e um curto traçado de cone, ambos ao alcance da WebGPU. Alfa com hash e LODs com dithering exigem apenas uma função de shader cada.</p>
<p>O caminho para a folhagem no navegador é o mesmo de todo o restante desse conjunto: disponibilizar primeiro as partes baratas e robustas (pré-passe, instâncias ordenadas, LODs, impostores, alfa com hash) e adicionar os mecanismos mais pesados (buffer de visibilidade, sombras com SDF de malha) por cima. O patamar mínimo do hardware dos navegadores finalmente está alto o bastante para que não exista nenhum motivo arquitetural pelo qual uma floresta em WebGPU não possa ter a mesma aparência e o mesmo desempenho que uma de console. Restam apenas motivos de engenharia, e motivos de engenharia são do tipo que apreciamos.</p>
<h2>Leituras adicionais sobre todo o conjunto</h2>
<p>Se você quiser uma única fonte que reúna tudo isso, o arquivo &quot;Advances in Real-Time Rendering in Games&quot; da SIGGRAPH (<a href="https://advances.realtimerendering.com/">advances.realtimerendering.com</a>) contém as palestras canônicas sobre folhagem e renderização controlada pela GPU desde 2014. Os <a href="http://www.adriancourreges.com/blog/">artigos sobre análise de desempenho da GPU</a> de Adrian Courrèges incluem decomposições frame a frame de GTA V e Horizon Zero Dawn que mostram, em ordem de produção, cada passe discutido aqui. Especificamente para a matemática do teste alfa, a <a href="https://cwyman.org/">página de pesquisas</a> de Chris Wyman apresenta os artigos sobre alfa com hash e transparência estocástica, acompanhados de shaders de referência. E os capítulos de <a href="https://www.realtimerendering.com/">Real-Time Rendering, 4ª edição</a> sobre transparência, amostragem e gerenciamento de profundidade continuam sendo o ponto de partida acadêmico.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 25: Um esqueleto, todos os trajes]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters</guid>
            <pubDate>Sun, 10 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 50 testou se um pacote de personagens CC0 poderia sustentar um produto de personalização de avatares. Descobrimos que quatro pacotes da Quaternius compartilham intencionalmente o mesmo esqueleto de 65 articulações, transformando o spike de um problema de pipeline de animação em um exercício de reassociação de esqueleto por nome.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 25: Um esqueleto, todos os trajes</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind">Parte 24</a> tornou o mundo persistente e deu movimento de vento à vegetação. Esta parte volta a tratar das pessoas que vivem nele, mas pelo lado do guarda-roupa: um criador consegue escolher um corpo-base, trocar de roupa, selecionar um penteado e reproduzir qualquer animação usando apenas um pacote gratuito de assets, sem criar rigs nem animações? Construir um rig personalizado não é o risco que precisamos eliminar. A pergunta que vale a pena responder é se um pacote CC0 consegue dar conta de tudo.</p>
<h2>Um pacote criado para funcionar em conjunto</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/50-universal-characters/" title="Spike 50 — Personagens universais" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/50-universal-characters/" target="_blank">Abrir o Spike 50 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=50-universal-characters">Ver código-fonte</a></p>
<p>A Quaternius oferece quatro assets que se encaixam quase bem demais: o Universal Base Characters, com corpos masculino e feminino, além de oito penteados e dois conjuntos de sobrancelhas; o Modular Character Outfits, dividido em partes para corpo, braços, pernas e pés, com acessórios como ombreiras e capuz; e os pacotes um e dois da Universal Animation Library, com 262 clipes no total abrangendo locomoção, combate, escalada, agricultura, pesca, poses ociosas e movimentos sociais. A inspeção dos arquivos glTF revelou por que eles se encaixam: cada uma das malhas nos quatro pacotes usa intencionalmente o mesmo esqueleto de 65 articulações no estilo UE5, com os mesmos nomes de articulações e as mesmas poses de vinculação, passando pela raiz, pelve e coluna até a cabeça, além de braços espelhados com quinze articulações de dedos de cada lado e pernas espelhadas. Nenhum redirecionamento é necessário. Isso é exatamente o oposto do problema de redirecionamento entre três esqueletos do Spike 35, no qual um rig facial de 213 ossos precisava se comunicar com um rig Mixamo de 65 ossos. Aqui, os quatro pacotes foram criados intencionalmente sobre o esqueleto da biblioteca de animações, e a correspondência é exata.</p>
<p>Isso transformou o spike em um exercício de vinculação, em vez de um problema de pipeline de animação. A camada-base carrega o glTF de um corpo, clona a cena com <code>SkeletonUtils.clone</code> para que cada personagem receba uma nova instância do esqueleto, em vez de propagar mutações dos ossos para a cena armazenada em cache pelo carregador, e guarda em cache esse esqueleto de 65 ossos como o esqueleto canônico, junto com o objeto de armadura que serve como raiz da animação.</p>
<h2>Reassociando um casaco a um corpo</h2>
<p>O truque que faz tudo funcionar é que não basta simplesmente mudar a malha de um traje para outro pai na cena, pois ela ainda referencia os ossos de seu próprio glTF, que ficam em uma subárvore diferente. Cada parte modular — o corpo de um camponês ou as pernas de um patrulheiro — chega como sua própria SkinnedMesh, com seu próprio clone da mesma armadura. A vinculação percorre os ossos do esqueleto de origem na ordem original, procura cada um pelo nome no esqueleto canônico e constrói um novo <code>Skeleton</code> com esses ossos reassociados, reutilizando as matrizes inversas de vinculação da origem. Em seguida, chama <code>SkinnedMesh.bind</code> com a matriz de vinculação identidade da origem. Após a vinculação, a malha se desacopla da cena de origem e passa a ter diretamente a raiz do personagem como pai. Como a matriz de vinculação captura a transformação global no momento da vinculação e a raiz permanece na identidade, a transformação global da própria malha nunca afeta a saída, e o skinning acontece inteiramente por meio dos ossos agora compartilhados.</p>
<p>Cabelos e sobrancelhas também são slots, e eles corrigiram uma suposição equivocada. A estrutura de pastas dizia “vinculado ao osso da cabeça”, o que sugeria que o cabelo seria uma malha estática associada à cabeça como filha, com um deslocamento pré-calculado. Não é. Cada arquivo de cabelo é uma SkinnedMesh com pesos distribuídos entre todas as 65 articulações. No cabelo comprido, os pesos se estendem à parte superior da coluna e ao pescoço, permitindo que ele caia naturalmente quando o personagem olha para baixo. Portanto, o cabelo segue o mesmo processo de reassociação por nome que qualquer parte de traje. O único caso especial é o slot de corpo do traje: quando ele é anexado, a malha do corpo-base é ocultada para evitar que atravesse o traje, enquanto braços, pernas, pés e acessórios apenas se sobrepõem, pois o pacote foi criado para que coexistam com o corpo-base visível nas áreas em que a roupa deixa a pele exposta.</p>
<h2>262 animações gratuitas</h2>
<p>Depois que o esqueleto é compartilhado, a animação chega a ser anticlimática. Os dois arquivos glb da biblioteca são carregados, seus clipes são reunidos em uma única lista identificada por nomes, e os nomes das trilhas nesses clipes referenciam os nomes exatos das articulações por meio de <code>bone.position</code>, <code>bone.quaternion</code> e <code>bone.scale</code>. Um <code>AnimationMixer</code> cuja raiz é a armadura do personagem encontra esses ossos pelo nome e os controla diretamente, fazendo uma transição suave ao trocar de animação. O painel direito do visualizador apresenta uma lista filtrável com todos os 262 clipes e um marcador de origem. Assim, pesquisar “fish” exibe todas as animações de pesca das duas bibliotecas. A iluminação padrão de três pontos, com sombras suaves, uma grade polar sob os pés e mapeamento de tons ACES, mantém as texturas-base estilizadas legíveis sem apagar os detalhes das sombras do traje escuro.</p>
<p>Isso reduz consideravelmente os riscos para o produto. O recurso de personalização de avatares pode ser lançado usando um pacote CC0, sem nenhum trabalho de rigging nem criação de animações: basta adicionar os quatro pacotes, conectar uma interface de guarda-roupa e você terá, de imediato, cerca de seis trajes vezes oito penteados vezes dois sexos vezes 262 animações de variação. O esqueleto de 65 articulações é pequeno o suficiente para que o custo do skinning na GPU seja desprezível, e o trabalho de skinning em lote do Spike 45 já comporta mais de 200 avatares em um rig desse tamanho. A licença CC0 não impõe restrições de uso, portanto o uso comercial em uma plataforma paga para criadores é permitido. A questão ainda em aberto são os trajes enviados pelos próprios usuários, um problema da Fase 3 que pode ser resolvido redirecionando-os, durante o upload, para esse esqueleto canônico — o inverso do redirecionamento em tempo de execução do Spike 35. Os assets ocupam cerca de 147 MB em disco como PNGs 2K brutos e arquivos glb de animação não compactados. Em produção, esse volume seria reduzido para aproximadamente 30 MB com compressão de texturas KTX2 e uma etapa de processamento com gltf-transform.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Um único esqueleto canônico em todo o pacote.</strong> Quatro pacotes da Quaternius — corpos-base, trajes modulares e duas bibliotecas de animação — usam o mesmo esqueleto de 65 articulações no estilo UE5, com nomes de articulações e poses de vinculação idênticos. Portanto, combiná-los não exige redirecionamento. Clonar a base com <code>SkeletonUtils.clone</code> fornece a cada personagem uma nova instância do esqueleto, em vez de modificar a cena armazenada em cache pelo carregador.</p>
<p><strong>Reassociação de esqueleto por nome.</strong> Cada parte modular traz sua própria SkinnedMesh, que referencia sua própria armadura. A reassociação percorre os ossos de origem em ordem, mapeia cada um pelo nome para o esqueleto canônico, constrói um novo <code>Skeleton</code> reutilizando as matrizes inversas de vinculação da origem e chama <code>SkinnedMesh.bind</code> com a matriz de vinculação identidade. Em seguida, define a raiz compartilhada do personagem como pai da malha, para que o skinning seja executado inteiramente pelos ossos compartilhados. O slot de corpo do traje oculta o corpo-base para evitar interseções; os demais slots são sobrepostos.</p>
<p><strong>Cabelo como slot com skinning.</strong> O cabelo não é um acessório estático associado ao osso da cabeça, mas uma SkinnedMesh com pesos distribuídos entre todas as 65 articulações. Cabelos compridos também recebem pesos na parte superior da coluna e no pescoço, para que caiam naturalmente quando o personagem olha para baixo. Eles seguem o mesmo processo de reassociação das partes de traje.</p>
<p><strong>Redirecionamento gratuito de animações baseado em nomes.</strong> Os clipes da Animation Library referenciam as articulações pelos nomes exatos. Assim, um <code>AnimationMixer</code> cuja raiz é a armadura compartilhada controla diretamente todos os 262 clipes com transições suaves, sem redirecionamento individual por clipe. A licença CC0 permite uso comercial, e o baixo número de articulações mantém o skinning na GPU leve o suficiente para reutilizar o processamento em lote do Spike 45. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<hr>
<p>Parte 25 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind">Parte 24 — Salvando um mundo e tornando o vento visível</a>
Próxima: <a href="/pt-BR/blog/2026-05-12-open-world-browser-part-26-water">Parte 26 — Água convincente em qualquer escala</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 24: Salvando um mundo e tornando o vento visível]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind</guid>
            <pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 47 tornou persistentes as edições do mundo: um Durable Object que arbitra a propriedade, persiste blobs binários por chunk e faz o terreno de todos os pares convergir por meio de um protocolo de edição comutativo. O Spike 49 portou fielmente para TSL os shaders de vento para Godot da Quaternius e depois investigou uma recompilação a cada frame que derrubava a cena para 1 fps.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 24: Salvando um mundo e tornando o vento visível</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice">Parte 23</a> colocou pessoas no mundo e deu voz a elas. Esta parte trata de fazer o mundo lembrar o que fizeram nele e de fazê-lo parecer vivo quando ninguém está interagindo com ele. O Spike 47 aborda persistência: um criador esculpe o terreno e posiciona objetos, e essas edições sobrevivem a um recarregamento, são sincronizadas com todos os outros pares e passam por uma arbitragem limpa quando duas pessoas editam ao mesmo tempo. O Spike 49 aborda vento: portar os shaders de vegetação de um pacote de natureza estilizada para que árvores, arbustos e grama se movam como o artista planejou, o que acabou virando uma batalha mais contra o compilador de shaders do que contra a matemática.</p>
<h2>Um mundo que se lembra</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/47-world-persistence/" title="Spike 47 — Persistência do mundo" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/47-world-persistence/" target="_blank">Abrir o Spike 47 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=47-world-persistence">Ver código-fonte</a></p>
<p>O cenário é uma sessão compartilhada de criação. Os jogadores percorrem um mundo, posicionam objetos com um clique, removem-nos com um clique do botão direito e esculpem o solo arrastando um pincel, elevando e rebaixando o mapa de altura ou escavando cavernas volumétricas no terreno SDF, o que promove um chunk para marching cubes. Tudo o que fazem persiste em um <code>WorldChunkDO</code> da Cloudflare, um Durable Object com autoridade do servidor e armazenamento SQLite próprio. Os clientes não gravam o estado diretamente. Eles enviam intenções, o DO arbitra e transmite o resultado, e o DO é a única fonte da verdade. Assim, quem entra pela primeira vez recebe um snapshot e chega exatamente ao mesmo mundo que todos os demais estão vendo.</p>
<p>Dois detalhes de persistência provaram seu valor. O terreno não é armazenado como um log de eventos que precisa ser reproduzido quando alguém entra; ele é armazenado como blobs binários por chunk que constituem a fonte da verdade e são enviados ao fim de cada pincelada. Assim, quem entra carrega diretamente os bytes confirmados em vez de executar novamente milhares de amostras do pincel. E esses blobs usam uma chave de armazenamento por chunk, em vez de uma única linha grande, porque um só chunk SDF ocupa 168 KB e o DO tem um limite de 2 MB por linha. Um índice de chunks registra quais chaves existem para que o DO possa reidratar todo o mapa ao despertar. A identidade é tratada com dois tipos de armazenamento no cliente: o id do jogador fica no <code>sessionStorage</code>, para que duas abas sejam dois pares distintos em vez de um par que sobrescreve a si mesmo no mapa de jogadores do DO, enquanto o nome de exibição fica no <code>localStorage</code>, para que uma alteração feita em uma aba seja refletida em todas as outras.</p>
<h2>Fazendo edições simultâneas convergirem</h2>
<p>A parte realmente difícil da criação multiplayer é o que acontece quando duas pessoas editam áreas sobrepostas do terreno no mesmo instante. O spike divide as edições de acordo com sua álgebra. Operações aditivas — elevar e rebaixar o mapa de altura, além de adicionar e subtrair no SDF — são comutativas: aplicá-las em qualquer ordem leva ao mesmo resultado. Por isso, elas seguem um fluxo otimista de carimbos, no qual cada cliente aplica a edição localmente e envia o carimbo, e o DO o transmite a todos sem coordenação. A ordem realmente não importa, então não há nada a coordenar.</p>
<p>As operações dependentes da ordem — suavizar e nivelar — foram o caso interessante. O primeiro projeto atribuía a elas um bloqueio de região: um cliente solicita o bloqueio ao pressionar o ponteiro, o DO o concede ou nega, o cliente armazena as amostras durante o pressionamento e, quando o ponteiro é solto, o DO aplica a pincelada inteira atomicamente. Funciona, mas é um protocolo separado, com seu próprio TTL de bloqueio e uma viagem de ida e volta para concessão ou negação. A solução mais limpa que o substituiu é um delta pré-calculado: o cliente de origem executa localmente o pincel de suavização ou nivelamento, envia a lista resultante de deltas por célula e cada par apenas soma esses deltas às próprias células, sem recalcular nada. Isso transforma uma operação dependente da ordem em uma operação comutativa ao congelar seu resultado na origem. Assim, todo o sistema de edição funciona com um único protocolo comutativo e uniforme, com convergência idêntica e sem bloqueio algum. Os bloqueios de objetos permanecem por outro motivo: o bloqueio por registro substituiu a exclusão restrita ao proprietário, de modo que qualquer par pode excluir qualquer objeto, a menos que alguém o tenha bloqueado, e somente quem detém o bloqueio pode removê-lo. Desfazer e refazer funcionam permitindo que o cliente defina o id de um objeto antes de posicioná-lo. Assim, ele conhece o id antes do eco do servidor e pode reverter suas próprias ações de modo determinístico. WebSockets em hibernação mantêm uma sala ociosa sem custo o tempo todo, a mesma característica que barateou o relay de avatares na parte anterior.</p>
<h2>O vento, portado fielmente e depois enfrentado</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/49-gpu-wind-props/" title="Spike 49 — Vento em objetos na GPU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/49-gpu-wind-props/" target="_blank">Abrir o Spike 49 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=49-gpu-wind-props">Ver código-fonte</a></p>
<p>O Spike 49 usa o Stylized Nature MegaKit da Quaternius e porta o vento do pacote para nossa stack. O pacote inclui quatro shaders-fonte para Godot, e a decisão certa foi fazer uma tradução fiel em vez de reinventá-los. O shader da casca tem uma função de vértice vazia, portanto os troncos são rígidos; uma tentativa anterior com máscara procedural fazia os troncos balançarem, e a correção foi simplesmente parar de aplicar vento à casca. As folhas recebem uma oscilação caótica por vértice produzida por um hash de pulso triangular, mascarada pela altura para que as copas se movam enquanto a base permanece fixa. A folhagem básica recebe, no espaço de mundo, uma oscilação de seno e cosseno modulada por ruído. A grama combina a folhagem básica com uma ondulação de linhas de vento, amostrando uma textura de ruído em movimento por meio de uma curva de potência para que somente as faixas claras da textura contribuam, o que produz as ondulações visíveis que atravessam um campo. O despacho segue a própria convenção de nomes de materiais do pacote. Assim, um material chamado <code>Leaves_Birch</code> é encaminhado para o caminho das folhas e <code>Grass_Common</code>, para o caminho da grama, sem adivinhação.</p>
<p>Uma surpresa do port é que a cor das folhas não está na textura. A Quaternius define a aparência das folhas inteiramente com um gradiente vertical e uma borda de Fresnel: o albedo é uma mistura entre uma cor extra na parte inferior da copa e a cor da folha no topo, determinada pela altura, com uma tonalidade de espalhamento subsuperficial adicionada como emissão e dimensionada por um termo de Fresnel $(1 - \mathbf{N}\cdot\mathbf{V})^3$. É por isso que uma passagem simples com textura parecia sem profundidade. As cores vêm do gradiente e da borda, não do mapa pintado. O port lê um atributo de vértice <code>heightFactor</code> pré-calculado em vez do Y local bruto, normalizado por grupo de folhas durante o carregamento a partir do Y no espaço de mundo. Assim, tanto o gradiente quanto a máscara de vento se comportam corretamente, não importa como a importação do FBX tenha girado os eixos locais de cada malha. As constantes de cor do Godot são marcadas como sRGB e convertidas para linear antes de chegarem ao shader, portanto o port faz a mesma conversão em vez de fornecer os valores sRGB claros como se fossem lineares e deixar a folhagem desbotada.</p>
<h2>A recompilação que devorou a taxa de frames</h2>
<p>O motivo pelo qual isso foi lançado primeiro com um FBX de referência, deixando o conjunto completo de materiais de vento de lado em um arquivo <code>.bak</code>, foi um bug de recompilação a cada frame que derrubava a cena para cerca de 1 fps. Sobrepor TSL personalizado aos materiais carregados do FBX fazia o Three.js reconstruir os programas de shader a cada frame, com <code>needsUpdate</code> efetivamente travado como ativo. A abordagem de diagnóstico consistiu em reduzir cada material de folhagem a uma passagem simples com textura, sem nós personalizados, e observar se o ciclo de recompilação persistia. Se parasse, o problema estava no grafo personalizado; se continuasse, a causa estava antes dele, na configuração de materiais do FBX ou no próprio Three.js. A correção que permitiu restaurar os shaders reais foi vincular cada diferença específica de material — cor das folhas, cor de SSS, intensidade e mistura — como uniforms. Assim, todos os assets de folhas compartilham um único programa compilado, em vez de o compilador emitir um shader novo para cada combinação exclusiva de cores e sobrecarregar a fila de compilação.</p>
<p>Vale a pena preservar mais dois elementos. A grama é renderizada como um <code>InstancedMesh</code> por arquivo de origem, e seu vento é calculado no espaço de mundo porque as fases dos senos dependem da posição no mundo. Mas o deslocamento precisa ser aplicado no espaço local antes da execução da matriz de instância, e o WGSL não tem <code>inverse()</code> para chamar. Para uma matriz de instância composta por translação, rotação em Y e escala uniforme, a inversa da parte superior 3×3 é simplesmente sua transposta dividida pela escala ao quadrado. Portanto, o shader multiplica o deslocamento no mundo pela matriz de modelo transposta e o divide pelo comprimento ao quadrado da primeira coluna da matriz, recuperando a escala sem uma raiz quadrada. Depois que a transformação de vértice reaplica a matriz, o movimento chega ao espaço de mundo exatamente como foi criado, independentemente da rotação ou da escala de cada tufo. Além disso, cada tufo executa um culling de frustum por vértice na GPU: ele projeta o centro da instância no espaço de clipe e, se cair fora do frustum com uma margem, recolhe todos os vértices para a origem local, fazendo os três vértices de cada triângulo coincidirem. O rasterizador descarta o triângulo degenerado e nenhum trabalho de fragmento, teste alfa ou sombra é executado para a grama fora da tela. Isso se soma ao culling grosseiro por esfera delimitadora de cada chunk que o Three.js já realiza, construído com <code>step</code> de ponto flutuante em vez de booleanos para que possa ser multiplicado diretamente na mistura de posições.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Persistência de mundo com autoridade do servidor.</strong> Um Durable Object <code>WorldChunkDO</code> arbitra o posicionamento de objetos e as edições do terreno, persiste blobs binários por chunk como fonte da verdade — para que quem entra carregue os bytes confirmados em vez de reproduzir um log de eventos — e armazena uma chave por chunk para permanecer abaixo do limite de 2 MB por linha do DO. O id do jogador fica no <code>sessionStorage</code>, para que as abas sejam pares distintos; o nome de exibição fica no <code>localStorage</code>, para que as alterações sejam propagadas entre as abas.</p>
<p><strong>Convergência comutativa de edições.</strong> As operações aditivas de terreno — elevar/rebaixar e adicionar/subtrair no SDF — são comutativas e seguem um fluxo otimista de carimbos sem coordenação. Operações dependentes da ordem — suavizar e nivelar — tornam-se comutativas pelo envio de deltas pré-calculados por célula, em vez da aquisição de um bloqueio de região. Assim, todo o sistema converge sob um protocolo único e uniforme, sem bloqueios. Bloqueios de objetos por registro substituem a exclusão restrita ao proprietário, e ids de objetos definidos pelo cliente permitem desfazer/refazer de maneira determinística antes da chegada do eco do servidor. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<p><strong>Port fiel de shaders do Godot para TSL.</strong> Os quatro shaders-fonte de vento da Quaternius são traduzidos linha por linha: casca rígida, oscilação de folhas mascarada pela altura, oscilação da folhagem no espaço de mundo e grama com uma ondulação de linhas de vento em movimento, despachados de acordo com a convenção de nomes de materiais do pacote. A cor das folhas vem de um gradiente de altura combinado com uma borda de SSS controlada por Fresnel, e não da textura. Além disso, as constantes de autoria em sRGB são convertidas para linear para que o visual corresponda às renderizações de referência.</p>
<p><strong>Como evitar recompilações de shaders a cada frame.</strong> Sobrepor TSL personalizado aos materiais FBX pode manter <code>needsUpdate</code> ativo e reconstruir os programas a cada frame, derrubando o desempenho para cerca de 1 fps. Vincular cada diferença específica de material como uniform permite que todas as variantes compartilhem um único programa compilado, em vez de emitir um shader novo para cada conjunto exclusivo de parâmetros. Uma passagem de diagnóstico com textura simples isola se a causa está no grafo personalizado ou na configuração anterior a ele.</p>
<p><strong>Vento no espaço de mundo em folhagem instanciada.</strong> O vento da grama é calculado no espaço de mundo e transformado de volta para o espaço local com uma inversa derivada manualmente — transposta sobre a escala ao quadrado — porque o WGSL não tem <code>inverse()</code>. Um culling de frustum por vértice na GPU recolhe os tufos fora da tela em um triângulo degenerado, evitando qualquer trabalho de fragmento ou sombra. Isso se soma ao culling por esfera delimitadora de cada chunk do Three.js.</p>
<hr>
<p>Parte 24 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice">Parte 23 — Cinquenta avatares e uma voz na sala</a>
Próxima: <a href="/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters">Parte 25 — Um esqueleto, todos os trajes</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 23: cinquenta avatares e uma voz na sala]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice</guid>
            <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 45 reduziu o custo de 50 personagens de 13 ms de JavaScript por quadro para uma única chamada de desenho com skinning em lote na GPU, além de passar duas horas investigando um erro de WGSL que acabou sendo causado por um morph target. O Spike 46 criou voz por proximidade com áudio espacial HRTF e depois removeu metade da cadeia de áudio para corresponder ao que Meet e Teams realmente oferecem.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 23: cinquenta avatares e uma voz na sala</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets">Parte 22</a> colocou um céu sobre o mundo. Esta parte coloca pessoas nele. Um mundo aberto em terceira pessoa precisa exibir mais de 50 personagens a qualquer momento, além de permitir que você ouça quem está ao seu lado. O Spike 45 trata da renderização: colocar tantos avatares animados na GPU sem sobrecarregar a thread principal. O Spike 46 trata do áudio: voz ponto a ponto que se desloca entre os canais e perde intensidade de acordo com a posição, ajustada para soar como uma videochamada normal, e não como uma demonstração técnica.</p>
<h2>Uma chamada de desenho para cinquenta dançarinos</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/45-avatar-network-sync/" title="Spike 45: sincronização de avatares pela rede" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/45-avatar-network-sync/" target="_blank">Abrir o Spike 45 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=45-avatar-network-sync">Ver código-fonte</a></p>
<p>O fluxo padrão do Three.js dá a cada personagem seu próprio <code>SkinnedMesh</code>, seu próprio <code>AnimationMixer</code>, seu próprio envio de matrizes de ossos e sua própria chamada de desenho. Com 50 avatares no Mac local, isso representava cerca de 13 ms de sobrecarga de JavaScript puro por quadro, antes mesmo de a GPU fazer qualquer coisa. A questão de redução de risco para toda a parte multijogador era saber se uma única arquitetura de skinning em lote conseguiria controlar esse custo e escalar linearmente com a quantidade de personagens.</p>
<p>A resposta é separar onde a animação é calculada de onde ela é desenhada. Três classes de personagens compartilham um único modelo FBX. O jogador local é um <code>Avatar</code> normal, um clone completo do esqueleto com seu próprio mixer, seguindo o fluxo padrão do Three.js, porque só existe um deles. Cada par remoto é um <code>VirtualSkeleton</code>: também um clone completo com seu próprio mixer executando os mesmos clipes, mas cada nó <code>SkinnedMesh</code> é removido imediatamente após a clonagem, de modo que apenas os ossos permaneçam. Ele nunca entra na cena. A cada quadro, depois que o mixer é atualizado e as matrizes se estabilizam, ele empacota <code>(bone.matrixWorld × boneInverse)</code> para todos os 100 ossos em um slot de um <code>Float32Array</code> compartilhado. O <code>BatchSkinnedRenderer</code> então mantém um <code>InstancedMesh</code> por parte da geometria, todos lendo de um único <code>StorageBufferAttribute</code> de matrizes de ossos dimensionado como <code>maxInstances × numBones × mat4</code>, ou seja, 60 × 100 × 64 = 384 KB. Um <code>MeshStandardNodeMaterial</code> com <code>positionNode</code> e <code>normalNode</code> personalizados lê quatro influências de ossos por vértice diretamente desse buffer de armazenamento. O resultado é um envio ao buffer de armazenamento e uma chamada de desenho por parte da geometria para toda a multidão, independentemente do número de pessoas. O skinning fica no vertex shader, e o custo de JavaScript por avatar cai para a execução de um mixer e a cópia de 100 matrizes.</p>
<p>O HUD que mede isso também precisou ser refeito. A versão antiga indicava “acima do orçamento” quando o tempo total de GPU do quadro ultrapassava 3 ms, mas o quadro sempre inclui o mapa de sombras, o chão e a malha completa com skinning do jogador local, que juntos consomem de 3 a 5 ms em hardware real, não importa quantos pares sintéticos existam. A solução é um orçamento que se calibra sozinho: enquanto não há avatares em lote, ele captura o tempo real da GPU como referência por meio de uma EMA rápida; depois, congela essa referência e aumenta linearmente o orçamento em 0,06 ms por avatar adicionado assim que os sintéticos aparecem. Ele exibe PASS quando ocioso em qualquer máquina e fica proporcionalmente mais rigoroso à medida que a multidão cresce.</p>
<h2>O bug era um rosto que você não conseguia ver</h2>
<p>As primeiras execuções no Chrome exibiam sombras prateadas no chão e nenhum avatar, acompanhadas de um erro de análise de WGSL: <code>cannot index type 'f32'</code> em uma linha que tentava indexar <code>object.nodeUniform2[i]</code>, embora o uniforme tivesse sido declarado como escalar. A parte honesta desta história é que a primeira correção estava errada e funcionou mesmo assim. A suposição era de que o fluxo de matrizes de instância do <code>InstancedMesh</code> gerava o código inválido, e substituí-lo por um <code>StorageInstancedBufferAttribute</code> fez o erro desaparecer no Chrome. Mas ele desapareceu porque o novo fluxo emitia um código de shader diferente, não porque corrigia a causa — o tipo mais perigoso de correção.</p>
<p>O verdadeiro culpado eram os morph targets. O 3MIKE.fbx inclui expressões faciais com blend shapes, a geometria clonada herda os <code>morphAttributes</code>, e o <code>MorphNode.setup()</code> do Three.js declara <code>morphTargetInfluences</code> como um <code>float</code> escalar e depois tenta chamar <code>.element(i)</code> dentro de um loop sintetizado, exatamente a indexação de escalar rejeitada pelo compilador. A correção é uma única linha: limpar <code>geometry.morphAttributes = {}</code> na geometria que não usa morphs, impedindo que o Three.js injete o <code>MorphNode</code>. A correção acidental para o Chrome permaneceu por algum tempo e depois voltou para nos prejudicar: no Safari, o fluxo de instâncias em armazenamento produziu <code>Vertex buffer is not big enough</code> 256 vezes, porque o backend WebGPU do Safari não o traduz corretamente. Revertê-lo foi a decisão certa, e o buffer de armazenamento simples para matrizes de ossos, que faz parte do núcleo da WebGPU em vez de ser um fluxo de instâncias gerado, funciona bem em todos os navegadores. A lição que vale levar adiante é esta: quando uma correção funciona em um navegador e você não consegue explicar o mecanismo, você corrigiu um sintoma; portanto, leia o WGSL realmente gerado. Um shim de <code>getCompilationInfo()</code> adicionado mais tarde ao spike transformou o genérico “module is not valid” do Three.js no erro real do Tint e compensou seu custo muitas vezes.</p>
<p>Ao lado disso há um truque relacionado para contornar o framework. O Three.js detecta os nomes de atributos padrão <code>skinIndex</code> e <code>skinWeight</code> e tenta injetar seu próprio <code>SkinningNode</code>, até mesmo em um <code>InstancedMesh</code> cujo <code>positionNode</code> personalizado já executa o skinning. Renomear esses atributos para <code>boneIndex</code> e <code>boneWeight</code> os oculta do framework, e o TSL personalizado passa a lê-los pelos novos nomes.</p>
<h2>Um relay que esquece você entre uma palavra e outra</h2>
<p>A primeira versão sincronizava os pares por <code>BroadcastChannel</code>, um substituto dentro do mesmo navegador que usava o formato e a cadência reais da comunicação, e o comentário do protocolo prometia que a troca pelo transporte real exigiria uma única linha. Cumprir essa promessa resultou em um <code>AvatarRoomDO</code>, um Cloudflare Durable Object de 74 linhas que nem sequer decodifica o quadro binário de 36 bytes. Ele encaminha cada mensagem sem alterações para todos os outros pares da sala, porque o id do remetente está incorporado ao quadro e cada destinatário filtra seu próprio eco no cliente. O relay não sabe absolutamente nada sobre identidades. WebSockets com hibernação tornam gratuita uma sala ociosa: o DO sai da memória entre as mensagens, e o runtime restaura os sockets com tags no próximo pacote. Com 10 eventos por segundo por par, são 36.000 solicitações ao DO por hora-par, cerca de meio centavo, com saída gratuita na Cloudflare e um custo aproximadamente 6 a 10 vezes menor do que uma arquitetura WebSocket equivalente na AWS.</p>
<p>A troca revelou um bug de máquina de estados que vale registrar. Um jogador remoto continuava andando depois de parar. A solicitação de animação verificava <code>this._state</code>, o clipe atualmente em reprodução, em vez do último nome enfileirado. Assim, quando duas mensagens de rede chegavam no mesmo tick, primeiro <code>walk</code> e depois <code>idle</code>, o <code>idle</code> era comparado com um estado que ainda não havia avançado e acabava silenciosamente descartado. O par ficava andando para sempre, porque os pacotes <code>idle</code> seguintes eram deduplicados antes disso como se nada tivesse mudado. A correção é sempre sobrescrever o nome pendente e deixar que o auxiliar de transição interrompa solicitações genuínas para o mesmo estado, algo que ele já fazia. Essa classe de bug é geral: uma verificação de deduplicação baseada no valor de referência errado descarta silenciosamente a entrada que realmente importa.</p>
<p>O Safari precisou de mais duas proteções. Ele abre o WebSocket mais rápido que o Chrome, então a primeira mensagem recebida de um par podia chegar antes que a construção do renderizador em lote terminasse, causando uma desreferência de null; descartar mensagens enquanto o renderizador não existe é seguro, pois os pares retransmitem a cada 100 ms. Além disso, <code>'gpu' in navigator</code> retornava true enquanto <code>requestAdapter()</code> retornava null, fazendo o Three.js recorrer silenciosamente ao WebGL2, no qual a cadeia de skinning com buffer de armazenamento não tem tradução válida e gerava uma enxurrada de erros. Verificar se existe um adaptador real e confirmar que o backend é de fato WebGPU transforma uma renderização degradada em uma mensagem clara na tela de carregamento. Havia até uma incompatibilidade de dialeto WGSL: o Three.js emite o moderno <code>@interpolate(flat, either)</code> de dois argumentos, que o compilador do WebKit ainda não implementou. Isso foi contornado reescrevendo o código-fonte do shader no caminho até <code>createShaderModule</code> para remover o segundo argumento, sem custo algum, porque a interpolação flat carrega o mesmo valor em todos os vértices de qualquer maneira.</p>
<h2>Voz que se desloca com a sala</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/46-proximity-voice/" title="Spike 46: voz por proximidade" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/46-proximity-voice/" target="_blank">Abrir o Spike 46 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=46-proximity-voice">Ver código-fonte</a></p>
<p>O Spike 46 implementa voz por proximidade: WebRTC ponto a ponto com áudio espacial HRTF, com o objetivo explícito de alcançar a qualidade do Google Meet e do Microsoft Teams em uma sala silenciosa ou moderadamente ruidosa. Um <code>VoiceRoomDO</code> cuida da sinalização como um relay JSON, enviando a cada novo par uma lista de participantes, anunciando entradas e saídas, encaminhando SDP e ICE para um par específico por tag do socket e transmitindo atualizações de posição que controlam os panners espaciais. Ele inclui o id do remetente em cada mensagem para impedir que os pares se passem uns pelos outros, e o áudio em si nunca passa pelo DO. Há um <code>RTCPeerConnection</code> por par remoto, e o par com o id lexicograficamente menor sempre faz a oferta, para que os dois lados concordem sobre quem inicia sem precisar de uma implementação completa de perfect negotiation.</p>
<p>No lado receptor, o áudio de cada par passa por um <code>PannerNode</code> configurado como HRTF com atenuação de distância inversa, e o <code>AudioListener</code> é atualizado a cada quadro com base na posição e na direção do jogador local usando <code>forwardX = sin(facing)</code>, <code>forwardZ = cos(facing)</code>, o que corresponde à convenção de orientação <code>atan2(wx, wz)</code> da cena. Uma peculiaridade do Chrome custou uma hora: um <code>MediaStream</code> consumido apenas pela Web Audio às vezes não busca os pacotes, então cada stream também é conectado a um elemento <code>&lt;audio&gt;</code> oculto e silenciado para forçar o decodificador a ser agendado. Quanto à qualidade, os navegadores usam por padrão Opus mono a cerca de 32 kbps. Por isso, o spike modifica a linha <code>fmtp</code> de todas as ofertas e respostas para elevá-la a 128 kbps, com FEC em banda habilitado e DTX desabilitado, e depois chama <code>setParameters</code> com uma taxa de bits máxima alta para garantir que o codificador realmente use o que o SDP anuncia. O FEC é a segunda maior melhoria audível depois do aumento da taxa de bits, recuperando perdas de pacotes sem renegociação.</p>
<h2>Removendo componentes até obter um áudio limpo</h2>
<p>A cadeia de áudio entregue é muito menor do que aquela com que comecei, e reduzi-la foi a verdadeira lição. A primeira versão tinha um filtro passa-altas, um limitador de cliques ajustado para capturar ruído de teclado, um compressor, um noise gate e um crossfade entre sinal processado e original, tudo controlado por um painel flutuante com mais de doze sliders. Quando o usuário relatou cliques audíveis do teclado, o instinto foi ajustar o limitador com mais agressividade e reduzir a mistura do sinal original, empilhando remendos. A resposta estrutural era que, depois que um removedor de ruído por ML entra na cadeia, o limitador de cliques, o gate e a maior parte do passa-altas tornam-se redundantes, porque o RNNoise é treinado especificamente com ruídos de teclado, mouse e digitação, e o recorte de amplitude é uma versão estritamente pior da mesma solução. Clientes de produção oferecem redução de ruído por ML, cancelamento de eco, ganho automático e um compressor suave para nivelamento — nada além disso. Portanto, quatro estágios foram removidos, assim como o painel de sliders e as opções de “escolha sua redução de ruído”, restando um único pipeline fixo.</p>
<p>Cada estágio restante justifica sua presença. O cancelamento de eco do navegador permanece ativado porque o RNNoise não trata eco e, sem ele, o retorno do alto-falante para o microfone não tem limites. A supressão de ruído do navegador fica desativada porque combiná-la com o RNNoise produz artefatos em fricativas; é preciso escolher apenas um removedor de ruído. O ganho automático do navegador permanece ativado, porque desativá-lo deixou o sinal baixo demais para o compressor processar, e o <code>DynamicsCompressorNode</code> da Web Audio não tem um parâmetro de ganho de compensação; o nivelamento amplo do navegador e o compressor rápido do spike operam em escalas de tempo diferentes e coexistem. O RNNoise funciona com 92% de sinal processado misturado a 8% de sinal original, porque pode suprimir em excesso consoantes não vozeadas como s, ch e f, cuja probabilidade de voz cai, e a pequena parcela de sinal original as preserva ao custo de um leve vazamento dos sons das teclas.
Duas funcionalidades completam o conjunto. O apertar para falar não alterna <code>track.enabled</code>, porque isso descarta tudo o que ainda está nos buffers do pipeline e corta a última sílaba ao soltar a tecla. Em vez disso, um <code>GainNode</code> próximo ao fim da cadeia faz uma rampa com <code>setTargetAtTime</code>: ataque rápido para preservar a primeira sílaba e liberação lenta para deixar a última consoante terminar, mantendo a faixa permanentemente habilitada. E um atraso de transmissão de cinco segundos, solicitado como um recurso no estilo de rádio, usa em paralelo um caminho de bypass e outro com <code>DelayNode</code>, com crossfade entre eles, além de um botão de corte que silencia imediatamente a saída atrasada e exibe uma contagem regressiva no HUD antes de o áudio voltar. Empacotar o removedor de ruído foi uma pequena saga à parte: o worklet publicado do RNNoise usa importações com especificadores simples que nenhuma CDN consegue resolver, então a solução foi criar um bundle local com esbuild, gerando um único arquivo autocontido de 1,9 MB com o WASM embutido em base64, versionado no repositório e referenciado por uma URL relativa ao módulo, para funcionar igualmente no servidor de desenvolvimento, no build do VitePress e no domínio personalizado. Se o worklet não carregar, a cadeia ainda produz áudio por meio de um filtro passa-altas simples e um compressor, e o HUD exibe a falha em vermelho.</p>
<h2>Tecnologias abordadas neste capítulo</h2>
<p><strong>Skinning em lote na GPU para multidões.</strong> Avatares remotos executam um <code>VirtualSkeleton</code> sem renderização (um clone completo com as malhas com skinning removidas, os ossos preservados e seu próprio mixer) que compacta <code>bone.matrixWorld × boneInverse</code> para cada osso em um <code>StorageBufferAttribute</code> compartilhado. Um <code>InstancedMesh</code> por peça de geometria lê essas matrizes em um <code>positionNode</code>/<code>normalNode</code> personalizado do TSL, de modo que toda a multidão custa um único upload para o buffer de armazenamento e uma única draw call por peça, com o trabalho de CPU por avatar limitado à atualização do mixer e à cópia de uma matriz. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<p><strong>Ler o WGSL gerado, não o sintoma.</strong> Um erro de compilação <code>cannot index type 'f32'</code> foi rastreado até o <code>MorphNode</code> do Three.js, que declarava <code>morphTargetInfluences</code> como escalar e tentava acessá-lo por índice; a correção foi limpar <code>morphAttributes</code> nas geometrias que não usam morphs. Uma primeira correção que apenas alterava qual caminho de shader era gerado mascarou a causa e depois quebrou no Safari. Renomear <code>skinIndex</code>/<code>skinWeight</code> para <code>boneIndex</code>/<code>boneWeight</code> oculta os atributos da injeção automática de <code>SkinningNode</code> do Three.js, permitindo que um material de skinning personalizado seja o único responsável pelos cálculos.</p>
<p><strong>Relays com Durable Objects em hibernação.</strong> Um <code>AvatarRoomDO</code> puramente binário encaminha frames de 36 bytes a todos os outros pares sem decodificá-los, com a identidade do remetente embutida no frame e o eco para o próprio remetente filtrado no cliente. WebSockets em hibernação tornam gratuita uma sala ociosa, e essa configuração custa cerca de meio centavo por par-hora a 10 Hz, muito abaixo do preço equivalente de WebSockets gerenciados. Uma proteção contra duplicação que comparava com o estado da animação em reprodução, em vez da última animação enfileirada, descartava silenciosamente mensagens de parada e deixava jogadores remotos presos em um loop de caminhada.</p>
<p><strong>Voz por proximidade via WebRTC com HRTF.</strong> Uma <code>RTCPeerConnection</code> por par, com os papéis de oferta e resposta decididos pela ordenação dos IDs dos pares, áudio encaminhado por um <code>PannerNode</code> com HRTF e um <code>AudioListener</code> atualizado a cada frame de acordo com a direção para a qual o jogador está olhando, além do Opus ajustado para 128 kbps com FEC em banda para maior resiliência. Um elemento <code>&lt;audio&gt;</code> oculto e silenciado força o Chrome a receber pacotes de um stream usado apenas pela Web Audio.</p>
<p><strong>Engenharia de áudio subtrativa.</strong> Alcançar uma qualidade comparável à do Meet e do Teams exigiu remover etapas, não adicioná-las: redução de ruído por ML, cancelamento de eco, ganho automático e um compressor suave, sem gate e sem limitador de cliques, porque um removedor de ruído por ML treinado com ruído de teclado torna redundante o corte por amplitude. O apertar para falar aplica uma rampa a um <code>GainNode</code> no fim da cadeia com um envelope assimétrico, em vez de alternar a faixa, para não cortar sílabas; e o worklet do removedor de ruído é distribuído como um único bundle autocontido do esbuild para evitar problemas de resolução de importações com especificadores simples.</p>
<hr>
<p>Parte 23 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets">Parte 22 — Nuvens que você pode iluminar e um culling que precisa ser alimentado</a>
Próxima: <a href="/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind">Parte 24 — Salvando um mundo e um vento que você pode ver</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 22: Nuvens que podem ser iluminadas e um sistema de descarte que precisa ser alimentado]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets</guid>
            <pubDate>Thu, 07 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 43 colocou na tela um céu fisicamente baseado e nuvens volumétricas em menos de 2 ms, com pores do sol que não exigem ajustes específicos para cada horário do dia. O Spike 44 implementou o descarte de meshlets na GPU e mostrou que a oclusão Hi-Z só funciona se os oclusores forem grandes o bastante para sobreviver a uma redução por máximo.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 22: Nuvens que podem ser iluminadas e um sistema de descarte que precisa ser alimentado</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer">Parte 21</a> tratou de uma técnica de renderização que não compensou. Esta parte apresenta uma que compensou e outra que precisou de uma correção cuidadosa para funcionar. O Spike 43 é o céu: uma atmosfera fisicamente baseada e nuvens volumétricas, a base que faz uma cena parecer um lugar em vez de uma demonstração técnica. O Spike 44 é o descarte de meshlets na GPU, no qual aprendemos que o teste de oclusão é tão bom quanto os oclusores que fornecemos a ele.</p>
<h2>Um céu criado pela física, não por um gradiente</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/43-clouds-atmosphere/" title="Spike 43: Nuvens e atmosfera" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/43-clouds-atmosphere/" target="_blank">Abrir o Spike 43 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=43-clouds-atmosphere">Ver código-fonte</a></p>
<p>Quase todo efeito climático cinematográfico depende de dois elementos de infraestrutura: uma atmosfera fisicamente baseada, para que as cores do céu e do sol acompanhem o horário do dia segundo a física, em vez de usar um gradiente ajustado manualmente; e um volume de nuvens volumétricas, para que o céu tenha estrutura 3D em vez de um cubemap pré-renderizado. O Spike 43 implementa exatamente esse par sobre a pilha existente de WebGPU e TSL, e nada mais, porque, quando esses dois elementos existem, o restante da pilha meteorológica — neblina, raios divinos, superfícies molhadas e neve — se transforma em uma série de extensões menores e conhecidas.</p>
<p>A atmosfera usa o modelo Hillaire 2020, um conjunto de tabelas de consulta calculadas em shaders de computação WGSL. Uma tabela de transmitância integra a luz solar através do perfil de densidade de Rayleigh, Mie e ozônio e só é recalculada quando o sol se move. Uma tabela de visualização do céu é recalculada a cada quadro porque isso é barato o suficiente para que condicionar a operação não justifique o código adicional, usando uma parametrização não linear ao redor do horizonte para evitar faixas visíveis. Por enquanto, o espalhamento múltiplo usa uma aproximação analítica em vez da tabela correta, e os pores do sol parecem corretos, então o atalho está bem disfarçado. As nuvens usam uma marcha de raios no estilo Schneider Nubis através de uma camada horizontal, com a forma definida por uma textura Perlin-Worley de 128³ erodida por uma textura Worley de 32³, ambas geradas na inicialização por computação, sem nenhuma busca pela rede. A iluminação usa extinção pela lei de Beer, uma função de fase de lóbulo duplo e uma aproximação de powder. O acoplamento essencial é que a cor solar das nuvens consulta a mesma tabela de transmitância a cada passo, fazendo a iluminação das nuvens acompanhar o pôr do sol sem uma segunda etapa de ajustes.</p>
<p>Em um M1, o conjunto inteiro leva de 1,1 a 2,0 ms com as nuvens em meia resolução, bem abaixo do orçamento de 6 ms, usa cerca de 14 MB de memória da GPU e roda acima de 100 FPS. As duas teses se confirmaram na prática. O pôr do sol é a cena de destaque, o momento que faz o renderizador parecer cinematográfico, e surge naturalmente da física sem ajustes específicos para cada horário do dia. E houve um feliz acidente: com a camada de nuvens parametrizada entre 800 m e 4.000 m, nuvens distantes no horizonte parecem cordilheiras escuras vistas de uma câmera baixa, oferecendo ao mundo um terreno de fundo sem que ninguém precise modelá-lo.</p>
<p>Uma observação de arquitetura que vale a pena preservar. A abordagem natural é desenhar primeiro o céu na swap chain e depois deixar o three.js desenhar a geometria por cima com <code>autoClear = false</code>. Isso não funciona com o renderizador WebGPU na r184, porque a flag não controla a operação de carregamento de cor como ocorre no WebGL, então o three.js sobrescreve o céu a cada quadro. A correção é renderizar o three.js em um alvo fora da tela e fazer a composição final (<code>mix(skyCloud, scene, scene.alpha)</code>, seguida por ACES e depois sRGB) em uma passagem própria que controla a swap chain.</p>
<h2>Um sistema de descarte tão bom quanto seus oclusores</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/44-meshlet-clusters/" title="Spike 44: Clusters de meshlets" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/44-meshlet-clusters/" target="_blank">Abrir o Spike 44 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=44-meshlet-clusters">Ver código-fonte</a></p>
<p>O Spike 44 compara o desempenho de quatro modos de renderização: forward simples, descarte de clusters na CPU, descarte por computação na GPU e um buffer de visibilidade com descarte por oclusão Hi-Z. O caminho Hi-Z é o mais interessante e tinha um bug discreto: o HUD dizia que a oclusão estava ativada, mas o contador “Descartados por Hi-Z” permanecia para sempre em exatamente 0,0%. O descarte por frustum funcionava, portanto a etapa anterior ao shader de descarte estava correta. A metade responsável pela oclusão não fazia nada, mas ainda cobrava o custo integral.</p>
<p>Um teste de oclusão Hi-Z projeta na tela a caixa delimitadora de um cluster, seleciona um nível mip da pirâmide de profundidade para que o retângulo na tela tenha cerca de 2×2 texels, amostra a profundidade do oclusor mais distante nesse retângulo e rejeita o cluster se seu ponto mais próximo ainda estiver além desse oclusor. A pirâmide de profundidade é construída a cada quadro, inicializando o mip 0 com uma pré-passagem de profundidade opaca e aplicando reduções por máximo nos níveis seguintes. A pré-passagem opaca inclui deliberadamente apenas oclusores sólidos — o solo e proxies individuais dos troncos das árvores — porque a folhagem com teste de alfa criaria lacunas capazes de enganar uma redução por máximo.</p>
<p>O bug era geométrico, não lógico. O proxy do tronco era uma caixa de 0,5 m por 4 m por 0,5 m. A 30 m, ela ocupa cerca de 17 pixels na tela. Mas um cluster típico de grama a 50 m seleciona o mip 5, no qual cada texel cobre 32 pixels da imagem original. Um tronco de 17 pixels não cobre por completo nem mesmo um único texel do mip 5, portanto todo texel que toca o tronco também toca o solo ao redor. A primeira redução por máximo de 2×2 seleciona o maior valor de profundidade, que corresponde ao solo mais distante atrás do tronco, e a profundidade do tronco é apagada já na primeira redução. Ao chegar ao mip 5, a pirâmide contém a profundidade do solo em quase toda parte; o cluster nunca está além do solo e, portanto, nada é ocultado.</p>
<p>A correção é tornar o proxy grande o bastante para dominar os texels em que aparece, dimensionando-o de acordo com a silhueta da árvore, e não com a madeira. Um proxy de aproximadamente 2 m por 6 m por 2 m ainda é menor do que a copa real, então folhas visíveis através de lacunas nunca são descartadas em excesso, mas ele é grande o bastante para sobreviver à redução por máximo até as distâncias relevantes, e o contador de oclusão imediatamente deixou de mostrar zero. A conclusão pode ser generalizada como uma regra para o motor de produção: qualquer objeto considerado confiável como oclusor Hi-Z precisa ser dimensionado de acordo com sua silhueta na tela, porque a eficácia do Hi-Z em cenas abertas com folhagem é determinada pela cobertura do oclusor no mip relevante, e não pela elegância da matemática do teste de profundidade. A oclusão de grama por grama não pode ocorrer de qualquer maneira, pois uma lâmina fica na mesma profundidade que o solo sob ela. Assim, os ganhos reais vêm de árvores ocultando folhagem distante e de árvores ocultando outras árvores.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>LUTs de atmosfera Hillaire 2020.</strong> Uma tabela de transmitância — a luz solar através do perfil de Rayleigh, Mie e ozônio — recalculada apenas quando o sol se move, combinada a uma tabela de visualização do céu calculada a cada quadro com uma parametrização não linear do horizonte, produz cores fisicamente baseadas para o céu e o sol que acompanham o horário do dia sem um gradiente ajustado manualmente. Uma aproximação analítica do espalhamento múltiplo substitui a tabela completa até que algum artefato exija o cálculo correto. O pôr do sol surge naturalmente da física, sem ajustes específicos para cada horário do dia.</p>
<p><strong>Nuvens volumétricas Schneider Nubis.</strong> Uma marcha de raios através de uma camada horizontal, cuja forma é definida por uma textura Perlin-Worley de 128³ gerada na inicialização e erodida por uma textura Worley de 32³, com iluminação baseada na extinção pela lei de Beer, uma fase de lóbulo duplo e um termo de powder. Amostrar a cor solar das nuvens a partir da mesma tabela de transmitância em cada passo faz a iluminação acompanhar gratuitamente o nascer e o pôr do sol. A marcha de raios em meia resolução custa aproximadamente quatro vezes menos do que em resolução completa, sem perda visível de qualidade nas distâncias típicas, o equilíbrio padrão em produção.</p>
<p><strong>Composição de WebGPU bruto com three.js na r184.</strong> Desenhar o céu na swap chain e renderizar a geometria do three.js por cima com <code>autoClear = false</code> não funciona porque a flag não controla a operação de carregamento de cor no backend WebGPU. Renderize o three.js em um alvo RGBA16F fora da tela e faça o <code>mix</code> final, seguido pelo mapeamento de tons e pela conversão para sRGB, em uma passagem que controle a swap chain.</p>
<p><strong>Descarte por oclusão Hi-Z e dimensionamento de oclusores.</strong> Uma pirâmide de profundidade construída por reduções por máximo permite que uma passagem de descarte na GPU rejeite clusters cujo ponto mais próximo esteja atrás do oclusor mais distante em seu retângulo na tela. O teste silenciosamente não faz nada se os oclusores forem pequenos demais para dominar um texel no mip selecionado, porque a primeira redução por máximo substitui a profundidade do oclusor pela do fundo mais distante atrás dele. Os oclusores devem ser dimensionados de acordo com sua silhueta na tela, não com seu núcleo físico. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<hr>
<p>Parte 22 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer">Parte 21 — Um renderizador mais rápido que não era mais rápido</a>
Próxima: <a href="/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice">Parte 23 — Cinquenta avatares e uma voz na sala</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 21: um renderizador mais rápido que não era mais rápido]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer</guid>
            <pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O experimento 40 implementou um buffer de visibilidade para grama densa, a técnica que todos dizem vencer em casos de overdraw, e constatou que ela perdeu para a renderização forward comum por 44% em alta densidade no Apple Silicon. Veja por quê e quando realmente vale a pena usá-la.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 21: um renderizador mais rápido que não era mais rápido</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um experimento e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion">Parte 20</a> simulou profundidade de superfície em um quad plano. Esta parte trata de uma decisão de arquitetura de renderização e é o experimento em que a resposta dos livros acabou se mostrando errada para o nosso hardware. A pergunta: para grama com teste de alfa em densidade cinematográfica, vista por uma câmera em terceira pessoa, precisamos de um buffer de visibilidade antes de avançar para a densidade de 200 jogadores? A recomendação predominante é um enfático sim. Nós o implementamos, medimos e a resposta foi não.</p>
<h2>A técnica que todos recomendam</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/40-visibility-buffer/" title="Experimento 40: buffer de visibilidade" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/40-visibility-buffer/" target="_blank">Abrir o experimento 40 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=40-visibility-buffer">Ver código-fonte</a></p>
<p>Um buffer de visibilidade divide a renderização em duas passagens. A passagem 1 rasteriza a geometria e grava apenas os IDs dos triângulos e das instâncias em um alvo inteiro compacto, junto com a profundidade, sem fazer nenhum sombreamento. A passagem 2 é uma etapa em tela cheia que lê os IDs em cada pixel coberto, busca novamente os vértices daquele triângulo, reconstrói os atributos interpolados e sombreia cada pixel visível exatamente uma vez. A proposta é uma rejeição perfeita de overdraw: o teste de profundidade é executado em fragmentos nos quais nenhum trabalho de sombreamento foi realizado, portanto o material custoso só é processado no que realmente aparece na tela.</p>
<p>O experimento executa dois caminhos no mesmo canvas e no mesmo dispositivo, para que a única variável seja onde ocorre o sombreamento. O caminho forward usa um <code>MeshStandardNodeMaterial</code> normal por meio do three.js. O caminho com vis-buffer é um pipeline WebGPU bruto de duas passagens, executado fora do three.js, que lê a textura de grama do three.js diretamente do backend, grava <code>(instanceId, triId)</code> em um alvo <code>RG32Uint</code> na passagem 1 e resolve a iluminação na passagem 2. Ambos compartilham uma única configuração de iluminação de referência.</p>
<p>Vale a pena preservar duas observações de implementação. O WebGPU ainda não tem um builtin <code>primitive_index</code> portátil em shaders de fragmento, então o truque é incorporar um ID de triângulo por vértice à geometria não indexada e lê-lo com interpolação <code>flat</code>. Isso triplica a contagem de vértices, mas é insignificante em um cartão de grama com 12 vértices. Além disso, compartilhar o canvas com o renderizador do three.js praticamente não causa problemas, desde que você nunca reconfigure o contexto nem altere as dimensões do canvas, pois ambos pertencem ao three.js. Medir o tempo do caminho forward foi a parte trabalhosa, porque o three.js não oferece nenhum hook para inserir consultas de timestamp da GPU dentro da sua passagem de renderização. A solução alternativa delimita o trabalho dele com duas passagens de timestamp sem operação, enviadas antes e depois, que a GPU executa na ordem de envio.</p>
<h2>Os números seguem na direção errada</h2>
<p>Em um Mac da série M, a aproximadamente 1080p, com lâminas de grama em cartões cruzados sobre um campo de 80 m:</p>
<p>Com 50.000 instâncias, o caminho com vis-buffer venceu por 25%: 4,13 ms contra 5,51 ms do forward. Com 100.000, houve empate. Com 200.000 instâncias, o forward venceu por 44%: 5,44 ms contra 7,80 ms do vis-buffer. O caminho com vis-buffer fica <em>relativamente pior</em> conforme a densidade aumenta, exatamente o oposto da crença popular de que ele vence justamente quando o overdraw é intenso.</p>
<h2>Por que o forward se mantém eficiente</h2>
<p>As GPUs Apple Silicon são renderizadores diferidos baseados em tiles, e isso muda todo o cálculo. O sombreamento forward em um TBDR tem uma etapa de remoção de superfícies ocultas executada antes do shader de fragmento: o rasterizador coleta todos os fragmentos mapeados para um tile, ordena-os por profundidade e apenas os sobreviventes, após o teste de alfa, chegam ao shader de fragmento. Portanto, o caminho forward já cumpre gratuitamente, dentro do hardware, a maior parte da promessa do buffer de visibilidade de “sombrear uma vez por pixel”. Conforme as lâminas ocupam mais espaço na tela, mais fragmentos são rejeitados pelo HSR antes de qualquer sombreamento ser disparado, e o custo efetivo por pixel do forward permanece praticamente constante em vez de crescer com o overdraw.</p>
<p>A passagem 1 do caminho com vis-buffer obtém o mesmo benefício do TBDR. O problema está todo na passagem 2. A passagem 2 lê a matriz da instância de cada pixel a partir de um buffer que, com 200.000 instâncias, ocupa 12,8 MB, muito mais do que qualquer cache da GPU. Pixels vizinhos na tela geralmente pertencem a instâncias de grama diferentes — a distribuição usa uma grade com jitter, então lâminas vizinhas têm IDs de instância arbitrários —, de modo que cada wave que acessa esse buffer sofre misses de cache divergentes. Sozinho, esse acesso aleatório e incoerente consome cerca de 4 ms por quadro. O forward evita isso por completo porque a matriz da instância chega junto com o vértice pelo caminho de atributos por instância. Assim, quando o shader de fragmento é executado, os dados transformados dos vértices já estão em registradores locais do tile, sem exigir leituras aleatórias na escala de megabytes.</p>
<p>Esse é exatamente o custo que a passagem de classificação de materiais do Nanite existe para amortizar: agrupar pixels por instância e despachar waves de computação ordenadas, para que as leituras de cada wave sejam coerentes. Nós não temos isso. Uma estimativa rápida indica que ordenar os pixels por instância reduziria esses 4 ms para talvez 1,5 a 2 ms e deslocaria o ponto de equilíbrio para 400.000 a 500.000 instâncias. Mas isso seria empilhar otimizações sobre uma arquitetura que, para começo de conversa, não está vencendo neste caso.</p>
<h2>A conclusão honesta e a auditoria que a sustentou</h2>
<p>Para folhagem em cartões cruzados com teste de alfa no WebGPU do Apple Silicon, o caminho forward com o pipeline TSL do three.js já tem custo igual ou inferior ao do vis-buffer. Toda a infraestrutura do vis-buffer não oferece nenhum benefício visível antes de ultrapassarmos bastante as 200.000 instâncias, e isso somente se também adicionarmos uma passagem de ordenação ou agrupamento. A decisão prática para o motor de produção é manter a combinação de forward, LOD e impostores dos experimentos anteriores e não investir em infraestrutura de vis-buffer até que GPUs discretas da NVIDIA ou AMD sejam o principal alvo de implantação — onde o custo do overdraw é mais linear — ou até migrarmos para uma arquitetura de meshlets, na qual o vis-buffer já é a saída natural.</p>
<p>Como esse resultado contraria a intuição, a conclusão só tem valor se a comparação for justa. Por isso, o experimento passou por uma auditoria completa. Vários bugs reais apareceram e foram corrigidos: um controle de escala das lâminas que dessincronizava silenciosamente os dois caminhos, metade das lâminas do forward sendo renderizada quase preta por causa de normais antiparalelas — corrigido com o truque canônico de folhagem com normais voltadas para cima — e o vis-buffer aparecendo cerca de duas vezes mais claro por usar um fator de Lambert escolhido manualmente em vez do valor <code>1/π</code>, que conserva energia, além de um termo de luz ambiente fixo e da ausência de mapeamento de tons. A correção copiou para WGSL a curva fílmica ACES exata do three.js e passou a ler, a cada quadro, as cores e intensidades das luzes diretamente das luzes reais da cena. A lacuna conhecida restante, a ausência de componente especular direta na passagem 2, favorece o vis-buffer na comparação. Isso significa que o forward realiza estritamente mais trabalho por pixel e, ainda assim, vence em alta densidade. Portanto, a conclusão principal é conservadora, não otimista. A única ressalva permanece: tudo isso é específico da série M, e o ponto de equilíbrio pode muito bem se inverter em uma placa discreta. Assim, vale repetir o teste antes de adotar definitivamente essa arquitetura para alvos que não sejam da Apple.</p>
<h2>Tecnologias abordadas neste capítulo</h2>
<p><strong>Renderização com buffer de visibilidade.</strong> A passagem 1 rasteriza a geometria e grava apenas os IDs dos triângulos e das instâncias, junto com a profundidade, sem realizar nenhum sombreamento. A passagem 2 é uma resolução em tela cheia que lê os IDs de cada pixel coberto, busca novamente o triângulo de origem, reconstrói atributos baricêntricos com correção de perspectiva e sombreia cada pixel visível uma vez. Como o WebGPU não oferece um <code>primitive_index</code> portátil para fragmentos, o ID do triângulo é incorporado como um atributo por vértice com interpolação <code>flat</code> em uma geometria não indexada.</p>
<p><strong>Remoção de superfícies ocultas em TBDR versus resolução diferida.</strong> Em uma GPU diferida baseada em tiles, como o Apple Silicon, o sombreamento forward já rejeita fragmentos ocluídos antes da execução do shader de fragmento. Assim, ele obtém gratuitamente a maior parte do benefício do buffer de visibilidade de sombrear uma única vez, e seu custo por pixel permanece praticamente constante conforme o overdraw aumenta. Já uma passagem de resolução com vis-buffer paga pelo acesso aleatório e incoerente a um grande buffer por instância — 12,8 MB com 200 mil instâncias —, que domina o custo em alta densidade, a menos que os pixels sejam primeiro ordenados ou agrupados por instância, como faz a classificação de materiais do Nanite.</p>
<p><strong>Compartilhamento de um canvas com o WebGPURenderer do three.js.</strong> Buffers de comandos WebGPU brutos podem ser intercalados corretamente com os envios do three.js na fila compartilhada, desde que você nunca volte a chamar <code>context.configure()</code> nem altere <code>canvas.width/height</code>, pois ambos pertencem ao renderizador. A medição do tempo de GPU no caminho forward, para a qual o three.js não oferece um hook, pode ser delimitada por duas passagens de renderização de timestamp sem operação enviadas ao redor da chamada de renderização, já que a GPU executa os buffers de comandos na ordem de envio.</p>
<p><strong>Validação de um benchmark contraintuitivo.</strong> Um resultado de desempenho surpreendente só é tão confiável quanto a imparcialidade da comparação. Auditar os dois caminhos para garantir conteúdo de cena e sombreamento idênticos — mapeamento de tons ACES correspondente, Lambert com conservação de energia, luzes lidas dos mesmos objetos e escala idêntica das lâminas — foi o que transformou “o vis-buffer é mais lento” de um provável artefato de medição em uma conclusão defensável, com a única assimetria restante favorecendo o lado conservador da comparação.</p>
<hr>
<p>Parte 21 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion">Parte 20 — Simulando profundidade em um plano</a>
Próxima: <a href="/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets">Parte 22 — Nuvens pelas quais você pode voar e um culling que compensa</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 20: Simulando profundidade em um plano]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion</guid>
            <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 39 portou o mapeamento de oclusão por paralaxe para TSL, esbarrou em duas limitações do fluxo de controle de shaders no WebGPU, descobriu que o bug era uma falha na disciplina de portar literalmente e criou um plano de referência com geometria real para validar os resultados.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 20: Simulando profundidade em um plano</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e contém links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters">Parte 19</a> usou um quad plano para simular uma árvore inteira à distância. Esta parte usa um quad plano para simular profundidade de perto: mapeamento de oclusão por paralaxe, a técnica que faz uma estrada de paralelepípedos parecer ter 5 cm de rejunte rebaixado sem gastar um único vértice adicional. O objetivo era implementá-la na stack de produção (Three.js r184, WebGPU, TSL), para que os materiais detalhados do terreno possam aplicar essa ilusão de profundidade onde ela importa e pagar apenas o custo de uma textura plana em todos os outros lugares.</p>
<h2>Três maneiras de simular profundidade, lado a lado</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/39-parallax-pom/" title="Spike 39: mapeamento de oclusão por paralaxe" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/39-parallax-pom/" target="_blank">Abrir o Spike 39 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=39-parallax-pom">Ver código-fonte</a></p>
<p>O spike coloca três planos de 5×5 m lado a lado, todos com a mesma estrutura de material e diferindo apenas nas UVs que alimentam os samplers. A versão plana amostra a textura diretamente e serve como referência básica. A paralaxe de amostra única desloca a UV uma vez ao longo da direção de visão, usando a altura naquele ponto. É barata e funciona bem com baixa amplitude, mas desliza em ângulos rasantes. O POM faz ray marching no espaço tangente: avança ao longo do raio de visão, encontra a primeira camada em que o raio passa abaixo do campo de alturas e refina o ponto de interseção. O espaço tangente permanece simples porque todos os planos de teste estão alinhados aos eixos; assim, a direção de visão pode ser convertida para o espaço tangente com apenas algumas inversões de sinal, em vez de uma matriz TBN completa por vértice. O conjunto de texturas é carregado diretamente pela API de arquivos do Polyhaven, pelo mesmo caminho usado pela busca de modelos da Parte 17.</p>
<h2>Duas barreiras do WebGPU e um ray marching sem ramificações</h2>
<p>O loop de POM clássico interrompe a busca na primeira interseção. Na r184 isso não funciona, por dois motivos distintos. <code>If(...).and(...)</code> compilava sem erros, mas produzia WGSL no qual o corpo do loop nunca era executado; assim, o refinamento posterior ao loop operava sobre lixo e o plano era renderizado quase branco. Além disso, <code>Break()</code> como nó independente ainda não havia sido incluído no build da r184, portanto, mesmo com um <code>If</code> funcional, não havia como expressar “pare na primeira interseção”. Ambos os problemas remontam a issues conhecidas do three.js relacionadas à otimização excessiva do fluxo de controle do TSL através dos limites de <code>If</code> e <code>Loop</code> nessa faixa de versões.</p>
<p>A reescrita não usa ramificações. Cada iteração amostra a textura incondicionalmente, mantendo o acesso à textura em um fluxo de controle uniforme, como exige a especificação WGSL, e depois incorpora o novo estado por meio de um sinalizador <code>done</code> armazenado como float. Quando <code>done</code> muda para 1, as chamadas de <code>mix</code> de cada iteração passam a equivaler a “manter o estado inalterado”, a versão sem ramificações de um break. O sinalizador <code>done</code> é construído com um helper <code>step</code> implementado como <code>0.5 + 0.5 × sign(x + ε)</code>, porque a coerção de booleano para float tem sido inconsistente na linha r18x, enquanto <code>sign()</code> é universalmente segura. O custo é que cada fragmento executa todas as 64 iterações, independentemente do ponto real de interseção, mas essa é a troca correta na escala de fragmentos: o runtime já limita pelo número máximo de passos, e uma GPU real também executaria especulativamente além de um break “de verdade”. Um fallback limpo é importante aqui: um <code>mix(baseUV, refined, done)</code> final garante que, com amplitude zero — no extremo distante do fade por distância —, nenhum fragmento intercepte a superfície, <code>done</code> permaneça 0 e o material POM seja idêntico bit a bit ao plano. Esse é justamente o objetivo do truque de LOD por distância: reduzir ao custo de uma textura plana onde o efeito já ocupa menos de um pixel.</p>
<h2>O bug era uma falha de disciplina, não uma falha matemática</h2>
<p>A versão sem ramificações funcionava, mas parecia distorcida, com artefatos horizontais estriados em amplitudes moderadas e um resultado sutilmente errado e sem nitidez em amplitudes baixas. A correção veio de um prompt de uma linha: vá ler a referência canônica. O tutorial da LlamAcademy que inspirou isso é apenas um nó do Unity ShaderGraph, portanto a implementação real fica em <code>PerPixelDisplacement.hlsl</code>, da Unity. A leitura linha por linha revelou três diferenças semânticas que eu havia introduzido sem perceber: um erro de um passo na referência inicial da altura do raio (a Unity faz um avanço inicial antes do loop; portanto, meu referencial estava defasado por um passo inteiro, colocando as interseções na camada errada em cerca de metade dos casos), uma convenção de sinal no deslocamento máximo da qual depende a etapa de refinamento e uma escolha entre contabilizar o deslocamento acumulado ou a UV acumulada que tornou minha matemática de refinamento mais difícil e confundiu o sinal.</p>
<p>A causa raiz não foi um erro isolado, mas a mistura de duas referências. Eu havia usado o tutorial de POM do LearnOpenGL como guia, que adota convenções de sinal semelhantes, porém diferentes, e uma fórmula de refinamento distinta. Acabei em um estado híbrido no qual dois terços da matemática correspondiam a uma fonte e um terço à outra. A reescrita é um port quase literal do HLSL da Unity para TSL: mesmos nomes de variáveis, mesmo avanço inicial e mesmo refinamento, com o sinalizador <code>done</code> sem ramificações acrescentado por cima. Vale guardar a lição: ao portar um shader comprovadamente funcional de outra stack, primeiro faça um port linha por linha, mantendo os mesmos nomes, e só depois refatore para o estilo local. Não tente refazer a derivação com base em uma segunda referência no meio do port.</p>
<h2>Um plano de referência que não pode mentir</h2>
<p>A comparação lado a lado não incluía o elemento mais óbvio: um plano com geometria real. Sem ele, dizer que “o POM parece muito bom” não pode ser refutado. Muito bom em comparação com o quê? Por isso, o spike recebeu um quarto plano, com o mesmo mapa de alturas aplicado a posições de vértices reais. O WebGPU não oferece tesselação por hardware — ela simplesmente não faz parte da especificação, tendo sido removida por compatibilidade com o Metal —, portanto o substituto é um plano densamente subdividido (256×256 segmentos, 131.072 triângulos), com deslocamento de vértices no estágio de vértices. O mesmo uniform de amplitude controla tanto o POM quanto o plano geométrico, de modo que os dois desaparecem juntos e a comparação permanece justa em qualquer distância.</p>
<p>Com a referência real na tela, as afirmações qualitativas tornaram-se mensuráveis. Em uma órbita de 16° olhando para baixo, o POM e o plano tesselado concordam quanto ao sombreamento interno. Em ângulos rasantes, divergem exatamente onde precisam: o POM fica limitado à borda retangular perfeitamente reta da geometria, enquanto a malha real exibe no horizonte o perfil irregular de picos e vales reais captando a luz. Assim, o “deslizamento” do POM nas bordas agora é comprovadamente intrínseco ao algoritmo, e não um artefato da textura ou da iluminação. Os dois perfis de custo também ficam claros: o POM é limitado por fragmentos — o custo escala com o número de pixels cobertos —, enquanto o plano tesselado é limitado por vértices — o custo escala com a densidade da malha, independentemente da cobertura. Para um chunk de terreno, que já paga o custo de vértices de um plano controlado por mapa de alturas, o POM é a solução correta para detalhes menores que a malha.</p>
<p>O plano de referência também revelou um bug sutil de UX. O usuário percebeu que a superfície parecia afundar à medida que a amplitude aumentava. A causa era a convenção da Unity, que trata o plano geométrico como o topo do campo de alturas; assim, os picos permanecem alinhados ao plano e todo o resto sofre paralaxe para baixo, arrastando a superfície média para abaixo da referência plana em <code>(1 − mean_h) × amplitude</code>. A correção recentraliza a convenção para que <code>h = 0.5</code> corresponda ao plano: os picos se elevam em direção à câmera e os vales recuam. O algoritmo funciona exatamente como prescrito pela Unity; o spike apenas pós-processa a saída com metade de um deslocamento para corresponder ao que “amplitude” deveria significar para uma pessoa movendo um controle deslizante.</p>
<p>O plano de referência resolveu ainda outra questão. Um controle deslizante de “Passos” parecia não fazer nada, o que dava a impressão de um bug de integração, mas não era. O refinamento secante de três iterações após a busca linear é tão eficaz — o artigo de Tatarchuk sobre POM, de 2006, observa que uma busca de 4 passos seguida de 3 passos secantes é visualmente indistinguível de uma busca de 64 passos — que, em um mapa de alturas suave, qualquer quantidade de passos entre 4 e 64 converge para a mesma UV subpixel. A correção foi um toggle, não uma nova integração: desative o método secante e o controle de passos passa a ser o único responsável pela precisão da interseção; assim, reduzir para 4 produz degraus visíveis nos paralelepípedos, enquanto aumentar para 64 suaviza tudo novamente. O toggle é um uniform 0/1 que usa <code>mix</code> para transformar cada atualização de estado do método secante em uma operação neutra quando está desativado. Assim, alterná-lo nunca recompila o material nem provoca engasgos.</p>
<h2>Tecnologia mencionada neste capítulo</h2>
<p><strong>Mapeamento de oclusão por paralaxe em TSL.</strong> O POM faz ray marching da direção de visão através de um campo de alturas no espaço tangente, encontra a primeira camada em que o raio desce abaixo da superfície e refina a interseção, produzindo a profundidade de rejunte rebaixado em um quad plano sem geometria adicional. Um <code>mix(baseUV, refined, done)</code> final torna o material idêntico bit a bit ao plano quando nenhum fragmento intercepta a superfície, permitindo que a atenuação de amplitude do LOD por distância reduza o custo ao de uma textura plana de longe. Consulte <a href="/pt-BR/guides/landscape-generation-browser#terrain-materials">materiais de terreno</a>.</p>
<p><strong>Loops sem ramificações para o fluxo de controle do WebGPU.</strong> No Three.js r184, o <code>If(...).and(...)</code> do TSL pode compilar para WGSL com um corpo de loop que nunca é executado, e o <code>Break()</code> independente não está disponível. O padrão portátil usa uma amostra de textura incondicional por iteração — mantendo o acesso à textura em fluxo de controle uniforme, conforme a especificação WGSL — e um sinalizador <code>done</code> armazenado como float, que usa <code>mix</code> para transformar cada atualização de estado em uma operação neutra depois de ativado. Um helper <code>step</code> construído com <code>sign(x + ε)</code> evita a coerção pouco confiável de booleano para float. O custo é um número máximo constante de iterações, independentemente do ponto de saída antecipada, a troca correta na escala de fragmentos.</p>
<p><strong>Port literal de shaders.</strong> O port de um shader comprovadamente funcional de outro motor deve primeiro ser feito linha por linha, usando os nomes de variáveis originais, e só depois refatorado para o estilo local. Misturar duas referências — o <code>PerPixelDisplacement.hlsl</code> da Unity e o tutorial do LearnOpenGL — produziu um híbrido com uma referência inicial do raio defasada por um passo, um sinal de deslocamento invertido e uma fórmula de refinamento cujo clamp mascarava pesos fora do intervalo como descontinuidades espaciais. Uma única referência canônica, não uma nova derivação.</p>
<p><strong>Referência real com vértices deslocados.</strong> Sem tesselação por hardware no WebGPU, um plano densamente subdividido (256² segmentos), deslocado no estágio de vértices, funciona como geometria real para validar uma simulação no estágio de fragmentos. Controlar ambos com o mesmo uniform de amplitude mantém a comparação justa em qualquer distância. O POM é limitado por fragmentos — escala com os pixels cobertos —, enquanto o plano geométrico é limitado por vértices — escala com a densidade da malha. Por isso, eles divergem exatamente nas bordas da silhueta, provando que o deslizamento do POM nas bordas é intrínseco, não um artefato.</p>
<hr>
<p>Parte 20 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters">Parte 19 — O impostor que precisa sobreviver a uma floresta</a>
Próxima: <a href="/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer">Parte 21 — Um renderizador mais rápido que não era mais rápido</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 19: O impostor que precisa sobreviver a uma floresta]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters</guid>
            <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 38 reduziu uma árvore a duas texturas em um billboard; depois, uma reescrita KISS e uma correção para N ímpar fizeram tudo se encaixar. Os Spikes 41–42 eliminaram o salto visual com blending hemi-octaédrico e colocaram um milhão de árvores na GPU.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 19: O impostor que precisa sobreviver a uma floresta</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegando agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering">Parte 18</a> deu aos criadores um pincel que preenche uma encosta com árvores. O problema é o custo dessas árvores quando há dezenas de milhares delas na tela. Uma árvore distante não precisa de 2.000 triângulos para contribuir com quatro pixels. Esta parte trata do LOD mais profundo: o impostor, um quad plano que exibe uma imagem da árvore, e do caminho entre “uma árvore que parece correta” e um milhão delas na GPU.</p>
<h2>Uma árvore são duas texturas em um billboard</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/38-imposters/" title="Spike 38: impostores octaédricos" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/38-imposters/" target="_blank">Abrir o Spike 38 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=38-imposters">Ver código-fonte</a></p>
<p>Um impostor pré-renderiza um objeto a partir de uma grade de ângulos de visão em dois atlas de texturas, um para cor e outro para normais no espaço do mundo. Em tempo de execução, ele mostra um único quad voltado para a câmera, amostrando o tile correspondente à visão atual. O bake usa duas passagens por tile: diffuse com um material sem iluminação, para que nenhuma luz seja incorporada à textura, e normais codificadas como <code>normalWorld × 0.5 + 0.5</code>, com o alfa encaminhado da origem para que a silhueta corresponda pixel a pixel. O material em tempo de execução é um <code>MeshStandardNodeMaterial</code> completo, então o impostor continua recebendo o sol e a IBL da cena como qualquer outra superfície. O ganho está na geometria: um quad em vez de milhares de triângulos, com os detalhes armazenados em uma textura de 1 MB.</p>
<p>Separar isso em seu próprio spike foi, por si só, uma lição. O impostor começou como o LOD mais profundo dentro do sistema de dispersão do Spike 37, e cada iteração do bake precisava ser testada por todo o pipeline de dispersão, com a correção do bake misturada à migração das matrizes das instâncias e às trocas de LOD. Isolá-lo em um objeto e um quad, lado a lado com o original, reduziu o tempo de iteração de minutos para segundos.</p>
<h2>Quando a resposta dos livros é a resposta errada</h2>
<p>A primeira implementação usava codificação octaédrica, o mapeamento clássico para compactar direções de uma esfera em um quadrado. Ela passava nos testes numéricos de ida e volta, mas o usuário continuava enviando capturas de tela em que o impostor saltava para um tile que parecia mostrar a árvore vista ligeiramente de cima, em vez de diretamente de frente. Seguiram-se seis rodadas de correções — um uniforme de direção de visão por quad, bake com proporção quadrada, materiais de depuração e billboarding estático —, e todas eram realmente necessárias, mas nenhuma corrigia o bug de fato. A solução só apareceu com a decisão de “repensar tudo do zero, KISS, sem remendos”.</p>
<p>A reescrita descartou a dobra octaédrica em favor de azimute por elevação simples: <code>az = atan2(dir.x, dir.z)</code>, <code>el = asin(dir.y)</code>, <code>uv = (az/2π, el/π + ½)</code>. Essa é toda a codificação, sem normalização L1 nem casos extremos envolvendo o sinal do zero. Ela não é melhor aqui por ser mais precisa — na verdade, amostra a esfera de modo menos uniforme —, mas porque o seletor de células na CPU e o shader na GPU usam as mesmas primitivas. Assim, eles não podem discordar em uma direção de fronteira como o par octaédrico fazia silenciosamente. O atlas parece uma folha de contato: a coluna representa o ângulo ao redor do objeto e a linha representa a elevação, algo imediatamente evidente no overlay.</p>
<p>Mesmo depois disso, a reclamação de que a árvore “parece vista ligeiramente de cima” persistiu, e a causa era uma escolha de quantização, não a codificação. Em uma grade 4×4, os centros das linhas ficam em ±22,5° e ±67,5°, então não há nenhuma linha exatamente em 0° de elevação. Um observador olhando horizontalmente — de longe, o caso mais comum — sempre acaba em uma linha renderizada com inclinação. A solução é usar N ímpar: uma grade 5×5 coloca os centros das linhas em 0°, ±36° e ±72°, então o observador horizontal recebe um tile renderizado exatamente na horizontal. A mesma família de erro de “meia célula de diferença” aparece novamente no trabalho de paralaxe da próxima parte, e a solução vem da mesma pergunta: meu ponto de amostragem discreto realmente cai onde acho que cai para a entrada canônica?</p>
<p>Mais dois elementos foram importantes. O billboard precisa ser estático por intervalos, e não ficar continuamente voltado para a câmera. O impostor é uma imagem plana capturada a partir de uma direção específica de bake, então o plano da imagem do quad em tempo de execução precisa corresponder ao plano dessa câmera de bake. Isso significa que ele mantém sua orientação ao longo do arco em que a mesma célula permanece selecionada e salta apenas na fronteira. Além disso, o orçamento deve acompanhar as direções das quais os jogadores realmente observam: uma etapa posterior eliminou completamente as linhas inclinadas vistas de cima, favorecendo 24 posições em um anel horizontal, separadas por 15°, mais um tile visto diretamente de cima, porque as árvores são observadas da altura dos olhos praticamente o tempo todo.</p>
<h2>O salto visual e como o blending o eliminou</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/42-imposter-flicker/" title="Spike 42: comparação da cintilação dos impostores" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/42-imposter-flicker/" target="_blank">Abrir o Spike 42 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=42-imposter-flicker">Ver código-fonte</a></p>
<p>O billboard estático por intervalos é imperceptível à distância e salta visualmente de perto, o que não é um problema até você começar a orbitar. O Spike 42 colocou quatro variantes lado a lado — o objeto original, a base az/el 5×5 e duas grades hemi-octaédricas — para isolar a cintilação e eliminá-la. Dois artefatos causam o salto. A troca de célula acontece porque o fragment shader quantiza a direção de visão em uma de 25 células; assim, cruzar uma fronteira troca o tile amostrado e reorienta o quad no mesmo frame. A degeneração no polo acontece na visão de cima para baixo, em que todos os azimutes colapsam em um único ponto e a transição entre o anel e o tile superior é a pior do atlas.</p>
<p>O mapeamento hemi-octaédrico corrige os dois problemas. Ele mapeia continuamente o hemisfério superior no quadrado unitário, então direções 3D adjacentes chegam a UVs adjacentes, sem singularidade no polo nem necessidade de um tile especial visto de cima. A solução para a cintilação é o blending bilinear entre células: em vez de saltar para o tile mais próximo, encontra-se o grupo 2×2 de tiles que delimita a direção codificada e mesclam-se os quatro, totalizando 8 amostragens de textura — 4 diffuse e 4 normal. Agora, vistas adjacentes fazem uma transição gradual em vez de saltar. O blending de dois vetores normais unitários não produz por si só um vetor unitário, então o resultado é renormalizado, comportando-se como um slerp para os pequenos ângulos entre tiles vizinhos. O custo é real — um atlas 12×12 ocupa cerca de 9 MB, contra 1,6 MB do az/el, e o bake demora aproximadamente cinco vezes mais, com 288 passagens de render target —, mas o bake acontece uma única vez durante o carregamento, e o blending produz um resultado sem saltos, que é o que torna os impostores utilizáveis enquanto a câmera está realmente em movimento.</p>
<h2>Um milhão de árvores, uma cópia da posição da câmera por frame</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/41-imposter-forest/" title="Spike 41: floresta de impostores" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/41-imposter-forest/" target="_blank">Abrir o Spike 41 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=41-imposter-forest">Ver código-fonte</a></p>
<p>O runtime do Spike 38 executa um <code>lookAt</code> na CPU por quad a cada frame, o que funciona para uma árvore e é fatal para uma floresta. Com um milhão de árvores, as atualizações de matriz e o upload do buffer de instâncias a cada frame dominariam todo o processamento. O Spike 41 transfere todo o pipeline por frame para a GPU. O centro, o yaw e a escala de cada instância são enviados uma única vez durante a construção como atributos instanciados e nunca mudam. O vertex shader constrói a base do billboard a partir da direção de visão no espaço do mundo, <code>camPos − center</code>, e expande um quad unitário compartilhado no espaço do mundo. O fragment shader faz a codificação hemi-octaédrica e o blending bilinear por pixel. O único trabalho da CPU por frame para toda a floresta é um <code>Vector3.copy</code> para atualizar o uniforme da posição da câmera, cujo custo não cresce com a quantidade de árvores.</p>
<p>Um aspecto elegante da matemática é que o yaw de cada instância se anula na decodificação das normais. O bake armazena as normais no sistema de referência da câmera de bake e, como uma rotação yaw ao redor do eixo vertical do mundo preserva +Y e o produto vetorial é equivariante a rotações, a base em tempo de execução construída com uma referência vertical do mundo já equivale à base de bake rotacionada. Assim, o shader decodifica as normais diretamente pelos varyings da base em tempo de execução sem nunca aplicar o yaw da instância. O posicionamento usa uma grade com jitter em vez de uma dispersão puramente aleatória: a área é dividida em células, e uma árvore é colocada em cada célula, no centro mais um deslocamento limitado. Isso garante um espaçamento mínimo — sem duas árvores uma sobre a outra —, mas ainda parece uma floresta natural. Um detalhe fácil de ignorar é a esfera delimitadora. O template da geometria é apenas um quad unitário, então o three.js faria o frustum culling da floresta inteira assim que a câmera deixasse de olhar para a origem. Definir explicitamente uma esfera delimitadora que cubra toda a área, mais a margem de um quad, evita que as árvores nos cantos sejam cortadas em ângulos rasantes.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Atlas de impostores octaédricos e de azimute-elevação.</strong> Um impostor renderiza um objeto a partir de uma grade de direções de visão em um atlas diffuse e um atlas de normais no espaço do mundo. Depois, renderiza um único billboard que amostra o tile correspondente, substituindo milhares de triângulos por duas texturas. O mapeamento octaédrico clássico oferece cobertura uniforme da esfera, mas é propenso a divergências entre CPU e GPU nas fronteiras das dobras; uma grade simples de azimute por elevação amostra a esfera de maneira menos uniforme, mas garante por construção que o seletor de células e o shader concordem. Use N ímpar para que uma linha caia exatamente em 0° de elevação e concentre o orçamento de tiles no anel horizontal, já que os objetos são vistos principalmente da altura dos olhos.</p>
<p><strong>Orientação de billboard estática por intervalos.</strong> Um impostor é uma imagem capturada de uma direção específica de bake, então o plano da imagem do quad em tempo de execução precisa corresponder ao plano da câmera de bake, e não ficar continuamente voltado para a câmera em tempo de execução. O quad mantém sua orientação ao longo do arco em que uma célula permanece selecionada e salta na fronteira, algo imperceptível à distância de uso do impostor e visível apenas de perto, onde impostores não são utilizados.</p>
<p><strong>Atlas hemi-octaédrico com blending bilinear entre células.</strong> Mapear continuamente o hemisfério superior no quadrado unitário elimina a singularidade no polo e o tile especial visto de cima. O salto é eliminado amostrando o grupo 2×2 de tiles que delimita a direção de visão codificada e aplicando blending bilinear aos quatro tiles — 8 amostragens —, para que vistas adjacentes façam uma transição gradual. As normais mescladas são renormalizadas, aproximando um slerp para o pequeno ângulo entre tiles. O custo é um atlas maior — cerca de 9 MB em 12×12 — e um bake único mais demorado, em troca de sombreamento sem saltos durante o movimento da câmera.</p>
<p><strong>Impostores instanciados controlados pela GPU.</strong> O centro, o yaw e a escala de cada instância são enviados uma única vez como atributos instanciados; o vertex shader constrói a base do billboard e expande um quad unitário compartilhado, enquanto o fragment shader faz a codificação e o blending por pixel. O custo da CPU por frame para toda a floresta é uma única cópia do uniforme da posição da câmera, independentemente da quantidade de instâncias. O yaw de cada instância se anula na decodificação das normais porque a construção da base pelo produto vetorial preserva rotações yaw em torno do eixo vertical do mundo. Uma esfera delimitadora explícita para toda a floresta impede que o three.js faça o frustum culling de toda a malha instanciada quando a câmera deixa de olhar para a origem do template do quad unitário. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD controlado pela GPU</a>.</p>
<hr>
<p>Parte 19 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering">Parte 18 — Um pincel de dispersão que parece guiado por IA</a>
Próxima: <a href="/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion">Parte 20 — Simulando profundidade em um plano</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nuvens volumétricas e efeitos climáticos em jogos modernos]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-04-volumetric-clouds-and-weather</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-04-volumetric-clouds-and-weather</guid>
            <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Como jogos modernos renderizam nuvens volumétricas, dispersão atmosférica, neblina, chuva, neve e clima dinâmico, com os artigos fundamentais e as palestras sobre engines por trás de cada técnica.]]></description>
            <content:encoded><![CDATA[<h1>Nuvens volumétricas e efeitos climáticos em jogos modernos</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="/img/blog/clouds_weather_hero.webp" alt="Cena AAA estilizada com imponentes nuvens volumétricas de tempestade, raios, cortinas de chuva e uma estrada de pedra molhada refletindo o céu" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Há algumas semanas, escrevi sobre <a href="/pt-BR/blog/2026-05-03-aaa-rendering-techniques">as técnicas de renderização realmente usadas nos jogos AAA modernos</a>. Uma área que abordei superficialmente foi o céu e o clima, porque ela merece uma lista própria. Nuvens, neblina, chuva e neve são os sistemas que transformam uma demonstração de terreno em um lugar. Eles também compartilham mais código do que pode parecer. Nuvens volumétricas, neblina rasteira e raios crepusculares usam todos o mesmo ray marching. Estradas molhadas, acúmulo de neve e rastros de pegadas usam todos o mesmo truque de deslocamento combinado com PBR. O vento é um único vetor de direção lido por tudo na cena.</p>
<p>Este é um passeio curto e opinativo por como os maiores estúdios criam esses sistemas em 2026, com os artigos e as palestras sobre engines por trás de cada parte.</p>
<h2>1. Céu e atmosfera com base física</h2>
<p>A dispersão atmosférica é a base. A cor do céu, a névoa no horizonte, a forma como montanhas distantes ficam azuis e o laranja do pôr do sol vêm todos da dispersão da luz pelo ar. Engines modernas calculam isso a partir da física: dispersão de Rayleigh para o azul, dispersão de Mie para a névoa ao redor do sol e absorção pelo ozônio para o violeta profundo no zênite.</p>
<p>O método original de Bruneton, de 2008, pré-calculava tudo em tabelas de consulta 4D, o que limitava a variação dinâmica da hora do dia e acrescentava artefatos de LUT quando o sol estava baixo. A atualização de Sébastien Hillaire, de 2020, usada pelo componente Sky Atmosphere da UE5, substitui a LUT de alta dimensionalidade por algumas texturas 2D e uma aproximação de dispersão múltipla atualizada a cada quadro. Ela funciona tanto em celulares quanto em PCs de alto desempenho.</p>
<img src="/img/blog/clouds_weather_atmosphere.webp" alt="Céu com base física durante a hora dourada, com um gradiente suave do laranja ao azul sobre a silhueta de uma montanha distante" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Hillaire, <a href="https://sebh.github.io/publications/egsr2020.pdf">Uma técnica escalável e pronta para produção de renderização de céu e atmosfera</a> (EGSR 2020, o padrão moderno, usado na UE5).</li>
<li>Bruneton e Neyret, <a href="https://hal.inria.fr/inria-00288758/document">Dispersão atmosférica pré-calculada</a> (EGSR 2008, a abordagem original com LUT).</li>
<li>Bruneton, <a href="https://ebruneton.github.io/precomputed_atmospheric_scattering/">Dispersão atmosférica pré-calculada: uma nova implementação</a> (referência de código aberto, com suporte a ozônio e múltiplos planetas).</li>
<li>Epic Games, <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/sky-atmosphere-component-in-unreal-engine">Componente Sky Atmosphere</a> (documentação da UE5).</li>
</ul>
<h2>2. Nuvens volumétricas com ruído Perlin-Worley</h2>
<p>A técnica que serve de base para as nuvens em jogos modernos surgiu na palestra de Andrew Schneider sobre Horizon Zero Dawn, em 2015. Nuvens não são malhas. Elas são uma função de densidade 3D definida por camadas de ruído: uma combinação de Perlin-Worley de baixa frequência produz a forma geral da nuvem, enquanto um ruído Worley de frequência mais alta corrói a silhueta, criando bordas esfiapadas. Um mapa climático — uma textura 2D amostrada pelas coordenadas XZ do mundo — controla a cobertura, o tipo de nuvem e a precipitação por região. Um gradiente baseado na altura combina os perfis de cúmulos, estratos e cirros de acordo com a altitude.</p>
<p>O renderizador percorre um raio da câmera através do volume da nuvem, acumulando densidade e dispersão. A iteração &quot;Nubis&quot;, de 2017, acrescentou criação e animação em escala regional, e a implementação original para PS4 renderizava o céu inteiro em cerca de 2 ms. A maioria dos estúdios que lança jogos com nuvens volumétricas hoje ainda deriva seu trabalho desse artigo.</p>
<img src="/img/blog/clouds_weather_noise.webp" alt="Forma de uma nuvem 3D decomposta em padrões sobrepostos de ruído Perlin e Worley, mostrando como os detalhes corroem a silhueta" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Schneider, <a href="https://www.guerrilla-games.com/read/the-real-time-volumetric-cloudscapes-of-horizon-zero-dawn">As paisagens de nuvens volumétricas em tempo real de Horizon Zero Dawn</a> (SIGGRAPH 2015, a referência canônica).</li>
<li>Schneider, <a href="https://advances.realtimerendering.com/s2017/Nubis%20-%20Authoring%20Realtime%20Volumetric%20Cloudscapes%20with%20the%20Decima%20Engine%20-%20Final%20.pdf">Nubis: criação de paisagens de nuvens volumétricas em tempo real com a Decima Engine</a> (SIGGRAPH 2017, a continuação em escala regional).</li>
<li>Hillaire, <a href="https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf">Renderização de céu, atmosfera e nuvens com base física no Frostbite</a> (SIGGRAPH 2016, a versão do Frostbite).</li>
<li>Häggström, <a href="https://www.diva-portal.org/smash/get/diva2:1223894/FULLTEXT01.pdf">Renderização em tempo real de nuvens volumétricas</a> (uma tese clara e acessível, com o código completo dos shaders).</li>
</ul>
<h2>3. Nuvens de voxels e Nubis³</h2>
<p>A evolução do Nubis, de 2023, abandonou completamente a representação de formas em 2,5D em favor de voxels 3D reais. Cada voxel armazena diretamente a densidade da nuvem, permitindo que artistas esculpam e animem suas formas da mesma maneira que esculpem terrenos. O custo de migrar para uma representação mais densa é compensado pela aceleração do ray marching por meio de campos de distância com sinal compactados e pelo aumento inteligente da resolução de dados esparsos de voxels.</p>
<p>O resultado é o tipo de paisagem de nuvens pela qual você pode voar <em>por dentro</em> sem que os truques subjacentes se desfaçam diante dos seus olhos. É um exagero para a maioria dos estúdios, mas é nessa direção que o segmento de ponta está avançando.</p>
<img src="/img/blog/clouds_weather_voxel_clouds.webp" alt="Uma nuvem cúmulo decomposta em uma grade 3D de voxels, com bordas esfiapadas e suavizadas se mesclando ao interior blocado" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Schneider, <a href="https://www.guerrilla-games.com/read/nubis-cubed">Nubis³: métodos (e loucuras) para modelar e renderizar nuvens imersivas baseadas em voxels em tempo real</a> (SIGGRAPH 2023, a palestra sobre nuvens de voxels).</li>
<li>Schneider, <a href="https://www.guerrilla-games.com/read/nubis-evolved">Nubis, evoluído</a> (SIGGRAPH 2022, a ponte entre 2,5D e 3D completo).</li>
<li>Schneider, <a href="https://www.schneidervfx.com/">Volumetria e efeitos visuais em tempo real</a> (site pessoal de Andrew, com notas de cursos e análises detalhadas).</li>
</ul>
<h2>4. Renderização de nuvens em camadas e fundos 2D</h2>
<p>Nem todo estúdio pode arcar com nuvens totalmente volumétricas, e nem todo ângulo de câmera precisa delas. Muitos jogos combinam técnicas: cirros em grande altitude renderizados como uma camada 2D em movimento, cúmulos em altitude média como volumes percorridos por raios e estratos em baixa altitude como uma camada fina de meio participante. O horizonte costuma usar um cubemap de céu pré-calculado, ao qual a etapa volumétrica se mescla após uma distância de transição.</p>
<p>É essa divisão em camadas que mantém o custo das nuvens sob controle. Um único tipo de nuvem na qualidade máxima pode consumir de 4 a 6 ms; usar qualidades diferentes para camadas em altitudes distintas pode preservar a mesma aparência pela metade do custo.</p>
<img src="/img/blog/clouds_weather_cloud_layers.webp" alt="Céu ao pôr do sol dividido em três camadas de nuvens: mechas altas de cirros, tufos médios de cúmulos e névoa baixa de estratos perto do horizonte" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Vos, <a href="https://www.gdcvault.com/play/1023327/The-Real-Time-Volumetric-Cloudscapes">As paisagens de nuvens volumétricas em tempo real de Horizon Zero Dawn</a> (vídeo da GDC 2016; a análise das camadas está na segunda metade).</li>
<li>Bauer, <a href="https://www.youtube.com/watch?v=IYxtqlD1-Ac">Criando o mundo atmosférico de Red Dead Redemption 2</a> (SIGGRAPH 2019, o pipeline combinado de céu, nuvens e volumetria de RDR2).</li>
<li>Hillaire, <a href="https://www.gdcvault.com/play/1029105/Volumetric-Clouds-and">Nuvens volumétricas e mega partículas na REDengine 4</a> (GDC 2025, o pipeline de nuvens de Cyberpunk 2077).</li>
</ul>
<h2>5. Neblina volumétrica com grades de froxels</h2>
<p>A neblina é um campo 3D, não um efeito de tela 2D. A abordagem moderna padrão é a grade de froxels: uma textura 3D alinhada ao frustum de visão da câmera, em que cada célula (&quot;froxel&quot; = frustum + voxel) armazena a densidade e a cor iluminada. Um compute shader injeta na grade a dispersão proveniente de cada fonte de luz, acumula a extinção ao longo do raio de visão e aplica o resultado em uma passagem de tela cheia.</p>
<p>É isso que produz feixes de luz atravessando janelas, neblina colorida ao redor de luzes pontuais e volumes visíveis ao redor de explosões. Esse também é o mecanismo subjacente da &quot;perspectiva atmosférica&quot;, que faz objetos distantes desaparecerem gradualmente no ar. A técnica foi introduzida por Bart Wronski para Assassin's Creed 4 e padronizada por Sébastien Hillaire no Frostbite.</p>
<img src="/img/blog/clouds_weather_froxel_fog.webp" alt="Frustum da câmera visualizado como uma grade 3D de froxels, com células menores perto da câmera e maiores à distância, contendo partículas de neblina" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Wronski, <a href="https://bartwronski.com/wp-content/uploads/2014/08/bwronski_volumetric_fog_siggraph2014.pdf">Neblina volumétrica: uma solução unificada de dispersão atmosférica baseada em compute shader</a> (SIGGRAPH 2014, o artigo original sobre grades de froxels).</li>
<li>Hillaire, <a href="http://www.frostbite.com/2015/08/physically-based-unified-volumetric-rendering-in-frostbite/">Renderização volumétrica unificada e com base física no Frostbite</a> (SIGGRAPH 2015, a implementação pronta para produção).</li>
<li>Kovalovs, <a href="https://history.siggraph.org/wp-content/uploads/2022/08/2020-Talks-Kovalovs_Volumetric-Effects-of-The-Last-of-Us-Part-Two.pdf">Efeitos volumétricos de The Last of Us Part Two</a> (SIGGRAPH 2020, com observações sobre jitter temporal e composição com correção de profundidade).</li>
<li>Wright et al., <a href="https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-Lumen-Wright%20et%20al.pdf">Lumen: iluminação global em tempo real na Unreal Engine 5</a> (SIGGRAPH 2022, inclui a interação do Lumen com a neblina volumétrica).</li>
</ul>
<h2>6. Raios divinos e feixes crepusculares</h2>
<p>Raios de sol visíveis no ar enevoado não são um efeito separado. Eles surgem do mesmo sistema de neblina, desde que tanto a densidade da neblina quanto o mapa de sombras estejam disponíveis para o mesmo compute shader. Quando o shader injeta luz em um froxel, ele amostra o mapa de sombras na posição desse froxel no mundo. As células na sombra permanecem escuras, enquanto as iluminadas recebem a cor do sol. Ao percorrer o resultado com o raio da câmera, as células claras formam feixes contínuos.</p>
<p>Existem variantes mais baratas em espaço de tela — desfoque radial partindo da posição do sol em direção ao buffer de profundidade —, e elas ainda são a escolha certa para dispositivos móveis ou hardware de baixo desempenho. Elas não funcionam quando o sol está fora da tela, mas custam quase nada.</p>
<img src="/img/blog/clouds_weather_god_rays.webp" alt="Floresta ao amanhecer com raios de sol atravessando troncos de árvores e a neblina rasteira, formando feixes crepusculares bem definidos" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Mitchell, <a href="https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-13-volumetric-light-scattering-post-process">Dispersão volumétrica da luz como pós-processamento</a> (GPU Gems 3, a abordagem de desfoque radial em espaço de tela).</li>
<li>Engelhardt e Dachsbacher, <a href="https://cg.ivd.kit.edu/publications/2010/epipolar/EpipolarSampling.pdf">Amostragem epipolar para sombras e raios crepusculares em meios participantes</a> (I3D 2010, a técnica mais precisa para GPU).</li>
<li>Vos, <a href="https://www.guerrilla-games.com/read/volumetric-light-effects-in-killzone-shadow-fall">Efeitos de luz volumétrica em Killzone: Shadow Fall</a> (SIGGRAPH 2014, a versão de produção com integração de sombras).</li>
</ul>
<h2>7. Raios e eventos climáticos estocásticos</h2>
<p>Um relâmpago é um efeito de um único quadro com duas partes: a malha do raio e a resposta de tonemapping e iluminação de toda a cena. O raio em si costuma ser uma malha procedural de billboards, construída com um algoritmo recursivo de subdivisão de segmentos de linha, com deslocamentos aleatórios para criar irregularidade e afunilamento em direção ao solo. Algumas engines o renderizam como um clarão aditivo em espaço de tela; outras, como uma geometria emissiva totalmente iluminada que projeta luz sobre o mundo por meio da injeção de uma luz pontual durante um único quadro.</p>
<p>A parte interessante é todo o <em>resto</em>: as bases das nuvens são iluminadas por baixo, o chão clareia por dois quadros, a medição da exposição automática leva alguns quadros para se recuperar e um som de trovão é reproduzido com atraso calculado pela distância. Quando bem executado, isso transforma um clarão de 16 ms em uma sequência de cinco segundos que faz o clima parecer algo acontecendo no mundo, e não apenas sobre ele.</p>
<img src="/img/blog/clouds_weather_lightning.webp" alt="Relâmpago ramificado atingindo o solo a partir de uma nuvem de tempestade ao entardecer, iluminando a base das nuvens e a silhueta de um pequeno vilarejo" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Reed e Wyvill, <a href="https://dl.acm.org/doi/10.1145/192161.192256">Simulação visual de relâmpagos</a> (SIGGRAPH 1994, o algoritmo recursivo de raios que todos ainda usam).</li>
<li>Kim e Lin, <a href="https://gamma.cs.unc.edu/POWER/">Animação rápida de relâmpagos usando uma malha adaptativa</a> (IEEE TVCG 2007, com maior embasamento físico).</li>
<li>Bauer, <a href="https://www.youtube.com/watch?v=IYxtqlD1-Ac">Criando o mundo atmosférico de Red Dead Redemption 2</a> (SIGGRAPH 2019, o pipeline de tempestades de RDR2, incluindo a temporização dos relâmpagos).</li>
</ul>
<h2>8. Partículas de chuva, malhas de chuva e gotas em espaço de tela</h2>
<p>A chuva em queda nos jogos modernos raramente é composta apenas por partículas. A solução barata e convincente é usar um pequeno conjunto de texturas em movimento, esticadas sobre quadriláteros verticais ou alinhados à tela e iluminadas pelas mesmas sondas de luz do sol e do céu que todo o resto. Mais perto da câmera, partículas individuais em forma de rastros acrescentam detalhes. Na própria lente da câmera, texturas de gotas, rastros escorrendo e ondulações de impacto transmitem a sensação de que &quot;você está dentro da tempestade&quot;.</p>
<p>O vento afeta a direção da chuva. O mesmo vetor de vento move o mapa climático das nuvens, inclina a grama e inclina os quadriláteros da chuva. Um único vetor para toda a cena, dezenas de sistemas consumidores.
<img src="/img/blog/clouds_weather_rain_storm.webp" alt="Tempestade noturna intensa, com cortinas de chuva iluminadas por faróis distantes e ondulações em uma estrada molhada" style="width:100%;border-radius:8px;margin:1.5rem 0" /></p>
<p>Para se aprofundar:</p>
<ul>
<li>Tatarchuk, <a href="https://gpuopen.com/wp-content/uploads/2018/04/Tatarchuk-Rain-Rendering-EGSR2006.pdf">Renderização de chuva em tempo real direcionável por artistas em ambientes urbanos</a> (EGSR 2006, a referência clássica para chuva em camadas).</li>
<li>Garg e Nayar, <a href="https://www1.cs.columbia.edu/CAVE/projects/rain_ren/rain_ren.php">Renderização fotorrealista de rastros de chuva</a> (SIGGRAPH 2006, a física da luz através das gotas de chuva).</li>
<li>Wojciechowski, <a href="https://www.gdcvault.com/play/1027382/Rain-In-Cyberpunk-2077">Chuva em Cyberpunk 2077</a> (GDC 2021, uma análise detalhada de uma produção moderna).</li>
</ul>
<h2>9. Superfícies molhadas, poças e ondulações</h2>
<p>Uma chuva que não altera o chão parece falsa imediatamente. Superfícies molhadas reagem escurecendo o albedo (a água absorve a luz incidente), suavizando as normais (a película de água alisa a microsuperfície) e reduzindo a rugosidade (a água é um espelho quase perfeito em ângulos rasantes). A mudança no shader é pequena; a mudança visual é enorme.</p>
<p>As poças são controladas por máscaras: uma máscara baseada em altura ou pintada nos vértices define as áreas mais baixas que se enchem de água à medida que um parâmetro de “umidade” aumenta. As ondulações usam texturas de mapa de normais em flipbook, acionadas pelos impactos das gotas de chuva. As implementações realmente boas acumulam o estado de umidade ao longo do tempo, de modo que uma tempestade prolongada encharca o mundo aos poucos, enquanto uma chuva breve apenas escurece as áreas mais altas.</p>
<img src="/img/blog/clouds_weather_wet_surfaces.webp" alt="Rua de paralelepípedos molhada à noite, com poças rasas refletindo letreiros de neon e anéis de ondulação causados pelo impacto das gotas de chuva" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>Lagarde e de Rousiers, <a href="https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/">Migrando o Frostbite para renderização baseada em física 3.0, seção 5.5</a> (SIGGRAPH 2014, os ajustes clássicos de PBR para superfícies molhadas).</li>
<li>Lagarde, <a href="https://seblagarde.wordpress.com/2011/08/17/feeding-a-physical-based-lighting-mode/">Adotando um modelo de sombreamento baseado em física</a> (a série original de posts com a matemática do PBR molhado).</li>
<li>Cyanilux, <a href="https://www.cyanilux.com/tutorials/rain-effects-breakdown/">Análise de efeitos de chuva</a> (um passo a passo acessível no Shader Graph sobre poças, ondulações e gotas).</li>
</ul>
<h2>10. Acúmulo e deformação de neve</h2>
<p>A neve é o problema simétrico ao da chuva: o mundo precisa se lembrar dela, não apenas recebê-la. A abordagem padrão usa uma textura de “acúmulo de neve” vista de cima, que aumenta ao longo do tempo onde quer que o céu esteja visível (calculado em relação a um mapa de profundidade ou de sombras visto de cima). O shader do terreno amostra essa máscara e mescla o material de neve nas regiões sombreadas, além de aplicar deslocamento de neve profunda nas áreas expostas.</p>
<p>Pegadas e marcas de pneus são renderizadas em um mapa de deformação deslizante centralizado no jogador. Conforme a câmera se move, as pegadas antigas saem da área e a textura se repete. O shader do terreno ou da neve amostra esse mapa de deformação e empurra os vértices para baixo onde houver registros. A neve de Battlefield 5 faz isso com tesselação por hardware; abordagens mais baratas usam uma malha de terreno de alta densidade apenas com deslocamento de vértices.</p>
<img src="/img/blog/clouds_weather_snow_deformation.webp" alt="Paisagem nevada estilizada, com pegadas recentes, montes de neve acumulados contra as rochas, flocos caindo e montanhas distantes envoltas em névoa" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>Barré-Brisebois, <a href="https://www.digitalfoundry.net/articles/digitalfoundry-2018-hands-on-with-battlefield-5s-closed-alpha">Experiência prática com Battlefield 5: como os pequenos detalhes importam</a> (o artigo de produção sobre a neve do Frostbite).</li>
<li>St-Amour, <a href="https://is.muni.cz/th/m2v6i/real-time_snow_deformation.txt?lang=cs">Deformação de neve em tempo real</a> (uma tese com todos os detalhes de implementação na GPU).</li>
<li>Andersson, <a href="https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/chapter5-andersson-terrain-rendering-in-frostbite.pdf">Renderização de terrenos no Frostbite usando splatting procedural em shaders</a> (a base do sistema de splatting sobre a qual funciona o acúmulo de neve).</li>
</ul>
<h2>11. Vento como sistema global</h2>
<p>O vento não é um efeito de partículas. Em engines de produção, ele é um único vetor global (às vezes, um campo 3D de baixa resolução) que <em>todos</em> os sistemas dinâmicos leem em seus shaders de vértice. Folhas de grama se curvam, galhos balançam, tecidos tremulam, folhas são levadas, a chuva se inclina, a fumaça é transportada e os mapas meteorológicos das nuvens se deslocam. Um uniforme atualizado por frame, dezenas de consumidores.</p>
<p>A versão mais sofisticada é uma “grade de vento” que armazena direção e intensidade amostradas pela posição no mundo, permitindo tempestades com rajadas localizadas, vales protegidos e rastros de turbulência atrás de edifícios. A vegetação também costuma receber um deslocamento por vértice gravado durante a autoria, para que árvores idênticas não balancem em perfeita sincronia. O resultado é um mundo que respira no mesmo ritmo.</p>
<img src="/img/blog/clouds_weather_wind_foliage.webp" alt="Vento forte inclinando a grama e as árvores para o lado, com folhas rodopiando em primeiro plano sob nuvens de tempestade" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>McAuley, <a href="https://www.gdcvault.com/play/1022235/Rendering-the-World-of-Far">Renderizando o mundo de Far Cry 4</a> (GDC 2015, inclui a grade de vento de Far Cry 4 para a vegetação).</li>
<li>Habel e Wimmer, <a href="https://www.cg.tuwien.ac.at/research/publications/2007/HABEL-2007-IIM/">Renderização realista de paisagens em tempo real usando nuvens de billboards</a> (mais antigo, mas a matemática do vento sobre a vegetação não mudou).</li>
<li>Frostbite, <a href="https://www.guerrilla-games.com/read/the-vegetation-of-horizon-zero-dawn">A vegetação de Horizon Zero Dawn</a> (SIGGRAPH 2017, com a arquitetura do sistema de vento).</li>
</ul>
<h2>12. Tempestades de areia, nevascas e clima denso</h2>
<p>Condições climáticas severas formam uma categoria própria de renderização. Uma tempestade de areia é uma névoa espessa, opaca e alinhada ao solo, com forte viés direcional e névoa de distância agressiva. Uma nevasca acrescenta uma rajada de partículas próxima à câmera e reduz a visibilidade. Cinzas vulcânicas e fumaça usam a mesma arquitetura, mas com cores diferentes.</p>
<p>O que torna esses efeitos convincentes não são as partículas, mas o <em>acoplamento</em>: o sol escurece, o céu muda de tonalidade, a gradação de cores do pós-processamento se altera, o áudio ambiente é trocado, os sons de passos mudam e a voz do jogador, caso exista, fica abafada. O renderizador é o mensageiro; a imersão vem de todos os sistemas do jogo reagindo ao mesmo tempo.</p>
<img src="/img/blog/clouds_weather_sandstorm.webp" alt="Uma parede de poeira e areia alaranjada avançando sobre uma planície desértica, com um céu carregado acima e azul límpido atrás" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>Burley, <a href="https://www.gdcvault.com/play/1024954/The-Real-time-Sky-and">O céu e a atmosfera em tempo real de Uncharted: The Lost Legacy</a> (GDC 2018, com observações sobre a composição de clima denso).</li>
<li>Khalifa, <a href="https://www.gdcvault.com/play/1027587/Atmospheric-Weather-Effects-in-Forza">Efeitos climáticos atmosféricos em Forza Horizon 5</a> (GDC 2022, clima moderno em mundo aberto).</li>
<li>Karis, <a href="https://advances.realtimerendering.com/s2021/">A tecnologia por trás da demonstração “Lumen in the Land of Nanite” da Unreal Engine 5</a> (SIGGRAPH 2021, a pilha volumétrica da cena de poeira na caverna).</li>
</ul>
<h2>13. Hora do dia e céus dinâmicos</h2>
<p>A passagem do tempo em tempo real é o multiplicador que faz valer a pena lançar todos os outros sistemas desta lista. A direção e a cor do sol são atualizadas ao longo de um ciclo de 24 minutos ou 24 horas. A LUT da atmosfera é atualizada conforme o ângulo do sol. A iluminação das nuvens é recalculada a cada frame. As cascatas de sombras são redirecionadas. As sondas de reflexão são atualizadas. A cor ambiente muda. A exposição do pós-processamento se adapta.</p>
<p>Fazer isso sem artefatos visíveis é, em grande parte, uma questão de cache de texturas e estabilidade temporal. Técnicas rápidas pré-calculam o céu em ângulos fixos do sol e fazem a interpolação; técnicas mais lentas recalculam tudo a cada frame. O modelo atmosférico de Hillaire de 2020 é rápido o bastante para ser recalculado, motivo pelo qual faz parte da UE5. O mapa meteorológico das nuvens se desloca com o vento, fazendo a cobertura mudar naturalmente sem que ninguém precise criar keyframes.</p>
<img src="/img/blog/clouds_weather_time_of_day.webp" alt="Três faixas verticais sobre a mesma paisagem: amanhecer rosado, meio-dia azul e pôr do sol vermelho, todas compartilhando a mesma silhueta de colina" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>Hillaire, <a href="https://sebh.github.io/publications/egsr2020.pdf">Uma técnica escalável e pronta para produção de renderização do céu e da atmosfera</a> (EGSR 2020, com a discussão sobre passagem dinâmica do tempo).</li>
<li>Pesce, <a href="https://www.realtimerendering.com/raytracinggems/rtg2/index.html">Renderização do céu em tempo real: técnicas e compromissos</a> (capítulo de Ray Tracing Gems II, uma análise moderna).</li>
<li>Bauer, <a href="https://www.youtube.com/watch?v=IYxtqlD1-Ac">Criando o mundo atmosférico de Red Dead Redemption 2</a> (SIGGRAPH 2019, com o pipeline de 24 horas).</li>
</ul>
<h2>14. A máquina de estados climáticos</h2>
<p>Por baixo de tudo isso existe uma pequena máquina de estados. A maioria dos jogos é lançada com algo entre 4 e 12 estados climáticos (céu limpo, parcialmente nublado, encoberto, chuva leve, chuva forte, tempestade, neblina, neve, nevasca, tempestade de areia), cada um definido por um conjunto de parâmetros: cobertura e tipo de nuvens, velocidade e direção do vento, tipo e intensidade da precipitação, tonalidades da cor ambiente, perfil de áudio e gradação de pós-processamento.</p>
<p>As transições são interpolações lineares entre conjuntos de parâmetros ao longo de 30 a 120 segundos. A transição não é um caso especial: são apenas dois estados sendo interpolados, enquanto cada subsistema de renderização lê os valores atuais dos parâmetros naquele frame. O clima pode ser roteirizado (uma cena precisa de uma tempestade), baseado em seed (determinístico por região e por dia dentro do jogo, para que dois jogadores no mesmo mundo vejam o mesmo clima) ou totalmente criado em uma grade de regiões. Os pipelines mais limpos tratam os três como diferentes agendadores escrevendo no mesmo buffer de parâmetros.</p>
<img src="/img/blog/clouds_weather_state_machine.webp" alt="A mesma cena lado a lado, sob sol e céu limpo à esquerda e chuva de tempestade à direita, com superfícies molhadas e reluzentes" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Para se aprofundar:</p>
<ul>
<li>Bauer, <a href="https://www.youtube.com/watch?v=IYxtqlD1-Ac">Criando o mundo atmosférico de Red Dead Redemption 2</a> (SIGGRAPH 2019, com o pipeline de estados climáticos).</li>
<li>Khalifa, <a href="https://www.gdcvault.com/play/1027587/Atmospheric-Weather-Effects-in-Forza">Efeitos climáticos atmosféricos em Forza Horizon 5</a> (GDC 2022, com a abordagem de mesclagem de parâmetros).</li>
<li>Schneider, <a href="https://www.guerrilla-games.com/read/the-real-time-volumetric-cloudscapes-of-horizon-zero-dawn">As paisagens de nuvens volumétricas em tempo real de Horizon Zero Dawn</a> (SIGGRAPH 2015, com a evolução das nuvens controlada por mapas meteorológicos).</li>
</ul>
<h2>15. Os momentos cinematográficos</h2>
<p>Toda essa pilha existe para criar alguns momentos marcantes. Ficar sobre uma crista enquanto uma frente de tempestade se aproxima. Observar um raio de sol atravessar uma clareira na copa das árvores. Sair de uma caverna e entrar na neve. Voar através de uma nuvem cúmulo e ver a luz em seu interior.</p>
<p>São esses os momentos dos quais os jogadores tiram capturas de tela. Também são os momentos em que todos os sistemas acima precisam funcionar ao mesmo tempo: nuvens volumétricas, dispersão atmosférica, neblina integrada às sombras, PBR molhado, vento sobre a vegetação, gradação de cores conforme a hora do dia e uma transição entre estados climáticos, tudo compondo um único frame. Se qualquer um deles estiver errado, a magia se desfaz.</p>
<img src="/img/blog/clouds_weather_cloud_sea.webp" alt="Vista de cima de um mar de nuvens, com um único pico de montanha emergindo, os topos das nuvens iluminados pelo sol e vales sombreados abaixo" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<h2>O que isso significa para o navegador</h2>
<p>A maioria dessas técnicas pode ser adaptada diretamente para WebGPU. Já lançamos neblina atmosférica básica, mesclagem de skyboxes equiretangulares e névoa de distância em screen space na <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">engine de mundo aberto para navegador</a>. As partes mais difíceis — nuvens volumétricas completas, neblina em grade de froxels, PBR molhado com poças dinâmicas e mapas de deformação de neve — são o próximo passo óbvio, agora que o pipeline de terrenos está estável. A amostragem por fragmento da cor da neblina a partir do skybox no Spike 24 é uma peça desse quebra-cabeça. O próximo passo é um ray marcher de nuvens em compute shader alimentando a mesma LUT atmosférica.</p>
<p>A boa notícia é que o patamar mínimo de hardware para navegadores já é alto o suficiente. Compute no WebGPU, texturas 3D, despacho indireto e consultas de timestamp estão todos disponíveis. A atmosfera de Hillaire de 2020 já foi portada várias vezes para WebGL. O Nubis de Schneider possui implementações de referência de código aberto em GLSL que podem ser traduzidas para WGSL com alterações mecânicas. Já não existe um motivo relacionado à renderização para que um jogo de navegador não possa ter o mesmo céu que um jogo de console. Restam apenas motivos de engenharia — e motivos de engenharia são do tipo que gostamos.</p>
<h2>Leituras adicionais sobre toda a pilha</h2>
<p>Se você quiser uma única fonte que reúna tudo isso, o acervo “Advances in Real-Time Rendering in Games” da SIGGRAPH (<a href="https://advances.realtimerendering.com/">advances.realtimerendering.com</a>) contém as apresentações de referência sobre clima e atmosfera desde 2014. Para análises de produção sobre como jogos específicos renderizam seus céus e efeitos climáticos, os <a href="http://www.adriancourreges.com/blog/">artigos de análise de GPU de Adrian Courrèges</a> incluem descrições detalhadas, frame a frame, de GTA V, Horizon Zero Dawn e Doom Eternal. Especificamente para a matemática do céu e da atmosfera, o <a href="https://www.scratchapixel.com/lessons/3d-basic-rendering/volume-rendering-for-developers/intro-volume-rendering.html">capítulo sobre renderização de volumes do scratchapixel.com</a> é a introdução mais acessível, e o <a href="https://github.com/sebh/UnrealEngineSkyAtmosphere">repositório da implementação</a> de código aberto de Hillaire é a referência com qualidade de produção.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Uma breve lista de técnicas de renderização usadas em jogos AAA modernos]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-03-aaa-rendering-techniques</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-03-aaa-rendering-techniques</guid>
            <pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[As principais técnicas de renderização por trás dos visuais AAA atuais, da geometria virtualizada do Nanite ao path tracing com ReSTIR e ao upscaling baseado em ML, com referências técnicas detalhadas para cada uma.]]></description>
            <content:encoded><![CDATA[<h1>Uma breve lista de técnicas de renderização usadas em jogos AAA modernos</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<img src="/img/blog/aaa_rendering_hero.webp" alt="Cena composta mostrando materiais PBR, geometria virtualizada e iluminação com ray tracing em um jogo AAA moderno" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Se você abrir o pipeline de renderização de um jogo AAA de 2026, verá que a maior parte do que há ali deriva de um pequeno conjunto de técnicas adotadas por diferentes estúdios. Os nomes mudam entre as engines, mas as ideias são as mesmas. Aqui está uma breve lista do que realmente faz o trabalho na tela, com uma figura e algumas referências técnicas detalhadas para cada item.</p>
<h2>1. Renderização baseada em física (PBR)</h2>
<p>Os materiais são descritos por texturas de albedo, rugosidade, metalicidade, normais e oclusão de ambiente, e iluminados por shaders que conservam energia (especular Cook-Torrance e difuso Lambertiano ou Disney). Essa é a base pressuposta por toda engine AAA moderna. Se uma superfície parece consistente sob a luz do sol, de uma lâmpada e de uma lanterna, é graças ao PBR.</p>
<img src="/img/blog/aaa_pbr_materials.webp" alt="Capacete de ferro renderizado com materiais PBR, com amostras flutuantes de texturas mostrando mapas de albedo, rugosidade, metalicidade e normais" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Burley, <a href="https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf">Sombreamento baseado em física na Disney</a> (as notas originais do curso sobre a &quot;BRDF Disney&quot;, SIGGRAPH 2012).</li>
<li>Karis, <a href="https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf">Sombreamento real na Unreal Engine 4</a> (SIGGRAPH 2013, a apresentação de referência sobre PBR na UE4).</li>
<li>Lagarde e de Rousiers, <a href="https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/">Migrando a Frostbite para renderização baseada em física 3.0</a> (SIGGRAPH 2014, o pipeline completo da Frostbite).</li>
<li><a href="https://www.realtimerendering.com/">Real-Time Rendering, 4ª edição, capítulo 9</a> (a referência bibliográfica).</li>
</ul>
<h2>2. Sombreamento diferido e com buffer de visibilidade</h2>
<p>Primeiro, a geometria grava atributos (normais, IDs de materiais e profundidade) em um G-buffer ou buffer de visibilidade. A iluminação é executada como um passe em tela cheia que lê esses buffers e sombreia cada pixel uma única vez. Os buffers de visibilidade (usados pelo Nanite e por sistemas semelhantes) levam isso ainda mais longe ao armazenar apenas IDs de triângulos e resolver posteriormente os parâmetros de material por pixel, o que mantém baixo o custo de overdraw em geometrias densas.</p>
<img src="/img/blog/aaa_deferred_gbuffer.webp" alt="G-buffer de sombreamento diferido dividido em painéis de cor final, normais no espaço do mundo, profundidade e ID de material" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Engel, <a href="https://download.nvidia.com/developer/presentations/2004/6800_Leagues/6800_Leagues_Deferred_Shading.pdf">Sombreamento diferido</a> (NVIDIA, a apresentação que estabeleceu os fundamentos).</li>
<li>Burns e Hunt, <a href="http://jcgt.org/published/0002/02/04/">O buffer de visibilidade: uma abordagem amigável ao cache para sombreamento diferido</a> (JCGT 2013, o artigo original sobre buffers de visibilidade).</li>
<li>Wihlidal, <a href="http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/">&quot;Otimizando o pipeline gráfico com computação&quot;</a> (GDC 2016, o caminho diferido da Frostbite baseado em computação).</li>
<li>Karis, Stubbe, Wihlidal, <a href="https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf">Uma análise aprofundada da geometria virtualizada do Nanite</a> (SIGGRAPH 2021, incluindo uma explicação detalhada do buffer de visibilidade do Nanite).</li>
</ul>
<h2>3. Geometria virtualizada (ao estilo Nanite)</h2>
<p>As malhas são pré-processadas em uma hierarquia de clusters. Em tempo de execução, a GPU transmite e seleciona clusters na resolução correspondente a cada pixel, oferecendo detalhes precisos em escala subpixel sem LODs manuais. O Nanite da Unreal é o exemplo mais conhecido. Outras engines agora incluem suas próprias variantes. O resultado prático são assets com qualidade cinematográfica em tempo real, sem a necessidade de criar LODs.</p>
<img src="/img/blog/aaa_virtualized_geometry.webp" alt="Renderização suave à esquerda e clusters de meshlets em escala subpixel, com cores vivas, à direita" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Karis, Stubbe, Wihlidal, <a href="https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf">Uma análise aprofundada da geometria virtualizada do Nanite</a> (SIGGRAPH 2021).</li>
<li>Brian Karis, <a href="https://www.youtube.com/watch?v=eviSykqSUUw">Apresentação sobre o Nanite na GDC 2021</a> (uma explicação acessível em vídeo).</li>
<li>Liktor, <a href="https://research.activision.com/publications/2021/09/geometry-rendering-pipeline-architecture-at-activision">Arquitetura do pipeline de renderização de geometria na Activision</a> (renderização baseada em clusters, 2021).</li>
<li>Schied et al., <a href="https://research.nvidia.com/publication/2017-07_spatiotemporal-variance-guided-filtering-real-time-reconstruction-path-traced">Filtragem guiada por variância espaço-temporal</a> (técnicas relacionadas de descarte de clusters).</li>
</ul>
<h2>4. Ray tracing em tempo real para sombras, reflexos e AO</h2>
<p>O ray tracing por hardware (DXR, Vulkan RT) traça raios de sombra, raios de reflexos espelhados e brilhantes e raios de oclusão de ambiente contra uma BVH construída a cada quadro. Mesmo poucos raios por pixel superam o que as técnicas em espaço de tela conseguem fazer, especialmente para reflexos de elementos fora da tela e sombras de contato. A maioria dos jogos o utiliza de forma cirúrgica, não para tudo.</p>
<img src="/img/blog/aaa_hardware_raytracing.webp" alt="Carro esportivo vermelho e brilhante em um showroom de luxo, com reflexos precisos gerados por ray tracing e sombras de contato" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Microsoft, <a href="https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html">Especificação funcional do DirectX Raytracing (DXR)</a> (a referência da API).</li>
<li>Wyman, <a href="http://intro-to-dxr.cwyman.org/">Introdução ao DirectX Raytracing</a> (as notas do curso da SIGGRAPH, bastante acessíveis).</li>
<li>Boksansky e Marrs, <a href="https://www.realtimerendering.com/raytracinggems/rtg2/index.html">Ray Tracing Gems II, capítulos 17–19</a> (PDF gratuito, técnicas modernas de DXR).</li>
<li>Stachowiak, <a href="https://www.ea.com/frostbite/news/stochastic-screen-space-reflections">Reflexos estocásticos em espaço de tela</a> (Frostbite, a ponte entre SSR e RT).</li>
</ul>
<h2>5. Ray tracing por software (ao estilo Lumen)</h2>
<p>Nem todo jogador tem uma placa RTX, então as engines também oferecem alternativas baseadas em campos de distância ou caches de superfície. O Lumen da Unreal, por exemplo, traça raios contra campos de distância com sinal e caches de superfície para obter GI difusa com baixo custo, recorrendo a raios por hardware apenas quando necessário. É assim que jogos AAA conseguem um &quot;visual de ray tracing&quot; nos consoles.</p>
<img src="/img/blog/aaa_software_rt_lumen.webp" alt="Interior de uma catedral com vazamento das cores dos vitrais e uma sobreposição em wireframe de SDF revelando a geometria aproximada" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Wright et al., <a href="https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-Lumen-Wright%20et%20al.pdf">Lumen: iluminação global em tempo real na Unreal Engine 5</a> (SIGGRAPH 2022, o artigo sobre o Lumen).</li>
<li>Epic Games, <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/lumen-technical-details-in-unreal-engine">Detalhes técnicos do Lumen</a> (documentação oficial da engine).</li>
<li>Wright, <a href="https://advances.realtimerendering.com/s2021/Wright%20et%20al%20-%20Radiance%20Caching%20for%20Real-Time%20Global%20Illumination%20-%20SIGGRAPH%202021.pdf">Cache de radiância para iluminação global em tempo real</a> (SIGGRAPH 2021).</li>
<li>Wright, <a href="https://www.youtube.com/watch?v=2GYXuM10riw">Apresentação sobre o Lumen na GDC 2022</a> (versão em vídeo).</li>
</ul>
<h2>6. ReSTIR e amostragem por reservatórios</h2>
<p>Para iluminação direta e indireta com milhares de fontes de luz, o ReSTIR (reamostragem de importância espaço-temporal por reservatórios) reutiliza amostras de luz entre pixels e quadros. É assim que jogos como Cyberpunk 2077 com Path Tracing mantêm baixo o nível de ruído usando um ou dois raios por pixel. A tendência é que apareça em mais engines à medida que o path tracing se torna o objetivo para sistemas de alto desempenho.</p>
<img src="/img/blog/aaa_ray_tracing_restir.webp" alt="Rua cyberpunk com reflexos gerados por ray tracing no pavimento molhado e trajetórias visíveis de luz rebatida" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Bitterli et al., <a href="https://research.nvidia.com/sites/default/files/pubs/2020-07_Spatiotemporal-reservoir-resampling/ReSTIR.pdf">Reamostragem por reservatórios espaço-temporais para ray tracing em tempo real com iluminação direta dinâmica</a> (SIGGRAPH 2020, o artigo original sobre o ReSTIR).</li>
<li>Ouyang et al., <a href="https://research.nvidia.com/publication/2021-06_restir-gi-path-resampling-real-time-path-tracing">ReSTIR GI: reamostragem de trajetórias para path tracing em tempo real</a> (HPG 2021, ReSTIR para iluminação indireta).</li>
<li>Lin et al., <a href="https://research.nvidia.com/publication/2022-07_generalized-resampled-importance-sampling-foundations-restir">Amostragem de importância reamostrada generalizada</a> (SIGGRAPH 2022, os fundamentos matemáticos).</li>
<li>NVIDIA, <a href="https://www.nvidia.com/en-us/geforce/news/cyberpunk-2077-ray-tracing-overdrive-technology-preview-on-rtx-4090/">Análise técnica aprofundada do path tracing de Cyberpunk 2077</a> (blog de engenharia).</li>
</ul>
<h2>7. Nuvens volumétricas, neblina e atmosfera</h2>
<p>Os céus são percorridos por ray marching através de ruído 3D e volumes de densidade. A atmosfera usa tabelas de espalhamento pré-calculadas (ao estilo Bruneton) para as transições do sol e da lua. A neblina usa uma grade de froxels — imagine uma textura 3D alinhada ao frustum de visão — que captura a iluminação local. Juntas, essas técnicas proporcionam um &quot;clima como sistema&quot; em vez de uma skybox.</p>
<img src="/img/blog/aaa_volumetrics.webp" alt="Vale montanhoso ao pôr do sol, com nuvens volumétricas e raios crepusculares atravessando o ar enevoado" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Schneider, <a href="https://www.guerrilla-games.com/read/the-real-time-volumetric-cloudscapes-of-horizon-zero-dawn">As paisagens de nuvens volumétricas em tempo real de Horizon Zero Dawn</a> (SIGGRAPH 2015, a referência de nuvens).</li>
<li>Hillaire, <a href="https://sebh.github.io/publications/egsr2020.pdf">Uma técnica escalável e pronta para produção de renderização de céu e atmosfera</a> (EGSR 2020, a sucessora moderna da técnica de Bruneton usada na UE5).</li>
<li>Wronski, <a href="https://bartwronski.com/2014/08/22/volumetric-fog-siggraph-2014/">Neblina volumétrica: uma solução unificada baseada em compute shaders para espalhamento atmosférico</a> (SIGGRAPH 2014, a neblina de froxels de Assassin's Creed 4).</li>
<li>Hillaire, <a href="https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite">Renderização volumétrica unificada e baseada em física na Frostbite</a> (SIGGRAPH 2015).</li>
</ul>
<h2>8. Mapas de sombras em cascata e mapas de sombras virtuais</h2>
<p>Para as sombras do sol, mapas de sombras em cascata dividem o frustum em intervalos e renderizam cada um na resolução apropriada. Mapas de sombras virtuais vão além: um único mapa de sombras enorme é dividido em páginas, e apenas as páginas visíveis pela câmera são renderizadas. É assim que jogos AAA mantêm sombras nítidas perto do jogador sem um consumo gigantesco de memória.</p>
<img src="/img/blog/aaa_cascaded_shadows.webp" alt="Cena externa com três frustums coloridos de cascata sobrepostos, mostrando os detalhes das sombras perto da câmera" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Dimitrov, <a href="https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded_shadow_maps.pdf">Mapas de sombras em cascata</a> (whitepaper da NVIDIA, a referência padrão).</li>
<li>Microsoft, <a href="https://learn.microsoft.com/en-us/windows/win32/dxtecharts/cascaded-shadow-maps">Técnicas comuns para melhorar mapas de profundidade de sombras</a> (documentação do DirectX).</li>
<li>Wright, <a href="https://advances.realtimerendering.com/s2023/index.html#VirtualShadowMaps">Mapas de sombras virtuais no Capítulo 4 de Fortnite Battle Royale</a> (SIGGRAPH 2023, a apresentação sobre VSM da UE5).</li>
<li>Epic Games, <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/virtual-shadow-maps-in-unreal-engine">Documentação sobre mapas de sombras virtuais</a>.</li>
</ul>
<h2>9. Efeitos em espaço de tela (SSAO, SSR, SSGI, SSSSS)</h2>
<p>Ler os buffers de profundidade e normais com baixo custo permite obter oclusão de ambiente (SSAO), reflexos (SSR), iluminação global com um rebatimento (SSGI) e espalhamento subsuperficial para a pele (SSSSS). Essas técnicas não capturam detalhes fora da tela, motivo pelo qual o ray tracing vem ganhando espaço, mas continuam presentes em toda parte como uma base rápida.</p>
<img src="/img/blog/aaa_screen_space_effects.webp" alt="Cena de cozinha lado a lado comparando iluminação plana à esquerda com SSAO, SSR e SSGI à direita" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Aprofundamentos:</p>
<ul>
<li>Mittring, <a href="https://www.crytek.com/wp-content/uploads/Finding%20Next%20Gen%20-%20CryEngine%202.pdf">Encontrando a próxima geração: CryENGINE 2</a> (SIGGRAPH 2007, o artigo original sobre SSAO).</li>
<li>McGuire et al., <a href="https://research.nvidia.com/publication/scalable-ambient-obscurance">Obscurância de ambiente escalável</a> (HPG 2012, SSAO moderno).</li>
<li>Stachowiak e Uludag, <a href="https://www.ea.com/frostbite/news/stochastic-screen-space-reflections">Reflexos estocásticos em espaço de tela</a> (Frostbite, a referência sobre SSR).</li>
<li>Jimenez, <a href="https://www.iryoku.com/separable-sss/">Espalhamento subsuperficial separável</a> (a técnica usada para pele na maioria das engines AAA).</li>
<li>Mara et al., <a href="https://research.activision.com/publications/2016/09/deep-g-buffers-for-stable-global-illumination-approximation">Espaço de tela profundo</a> (Activision, parte da linhagem do SSGI).</li>
</ul>
<h2>10. Antisserrilhamento temporal e upscaling por ML (DLSS, FSR, XeSS)</h2>
<p>O quadro é renderizado em uma resolução interna mais baixa e reconstruído usando vetores de movimento, profundidade e histórico. Os upscalers baseados em ML (DLSS 3/4, FSR 3, XeSS) também acrescentam geração de quadros, interpolando quadros intermediários a partir do fluxo óptico. Hoje, a maioria dos títulos AAA é lançada pressupondo que algum upscaler estará ativado, o que muda a forma de distribuir o orçamento de processamento do restante do quadro.</p>
<img src="/img/blog/aaa_ml_upscaling.webp" alt="Comparação lado a lado entre uma entrada de baixa resolução e uma saída nítida do mesmo personagem, reconstruída por ML" style="width:100%;border-radius:8px;margin:1.5rem 0" />
Leituras aprofundadas:
- Karis, [Superamostragem temporal de alta qualidade](http://advances.realtimerendering.com/s2014/index.html#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING) (SIGGRAPH 2014, a palestra de referência sobre TAA).
- Salvi, [Uma incursão pela superamostragem temporal](https://research.nvidia.com/sites/default/files/pubs/2016-03_An-Excursion-in/dlss_DTAA-2-2.pdf) (NVIDIA, no caminho até o DLSS).
- Edelsten, [Verdadeiramente de nova geração: adicionando aprendizado profundo a jogos e gráficos](https://www.gdcvault.com/play/1026184/) (GDC 2019, arquitetura do DLSS).
- AMD, [Detalhes técnicos do FidelityFX Super Resolution 3](https://gpuopen.com/fidelityfx-super-resolution-3/) e Intel, [Artigo técnico do XeSS](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-xess-technical-paper.html).
<h2>11. Renderização orientada pela GPU e mesh shaders</h2>
<p>O descarte, a seleção de LOD e o envio de chamadas de desenho são executados integralmente na GPU. Mesh shaders substituem o pipeline de vértices/geometria/tesselação por um estágio mais flexível, semelhante a computação, que emite meshlets. Em conjunto com multi-draw indirect, isso mantém a CPU completamente fora do loop crítico por objeto.</p>
<img src="/img/blog/aaa_gpu_driven_meshlets.webp" alt="Cena de um espaçoporto alienígena com sobreposição colorida de clusters de meshlets mostrando a renderização orientada pela GPU" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Haar e Aaltonen, <a href="https://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf">Pipelines de renderização orientados pela GPU</a> (SIGGRAPH 2015, a palestra fundamental sobre Assassin's Creed Unity).</li>
<li>Wihlidal, <a href="https://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/">Otimizando o pipeline gráfico com computação</a> (GDC 2016, descarte orientado pela GPU da Frostbite).</li>
<li>Kubisch, <a href="https://developer.nvidia.com/blog/introduction-turing-mesh-shaders/">Introdução aos mesh shaders de Turing</a> (NVIDIA, uma introdução aos mesh shaders).</li>
<li>Pesce, <a href="https://www.bartwronski.com/">Um passeio relâmpago pelos mesh shaders</a> (e blogs de engenharia semelhantes reunidos nas <a href="http://www.adriancourreges.com/blog/">análises com RenderDoc de Adrian Courrèges</a>).</li>
</ul>
<h2>12. Renderização de cabelo, tecido e pele</h2>
<p>O cabelo usa sombreamento anisotrópico no estilo Marschner com geometria baseada em fios (NVIDIA HairWorks, AMD TressFX ou sistemas nativos do motor). O tecido é simulado na GPU com dinâmica baseada em posições e renderizado com reflexão especular anisotrópica. A pele usa espalhamento subsuperficial em espaço de tela, além de iluminação envolvente pré-integrada. Normalmente, é nesses três elementos que se percebe a diferença de orçamento entre produções AAA e independentes.</p>
<img src="/img/blog/aaa_hair_cloth_skin.webp" alt="Close de uma guerreira com cabelo baseado em fios, capa de tecido e pele com espalhamento subsuperficial" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Marschner et al., <a href="https://www.cs.cornell.edu/~srm/publications/SG03-hair.pdf">Espalhamento da luz em fibras de cabelo humano</a> (SIGGRAPH 2003, o modelo fundamental de cabelo).</li>
<li>Chiang et al., <a href="https://benedikt-bitterli.me/pchfm/pchfm.pdf">Um modelo prático e controlável de cabelo e pelos para path tracing em produção</a> (Disney 2016, amplamente usado em aproximações em tempo real).</li>
<li>Müller et al., <a href="https://matthias-research.github.io/pages/publications/posBasedDyn.pdf">Dinâmica baseada em posições</a> (a referência padrão para simulação de tecidos).</li>
<li>Jimenez et al., <a href="https://www.iryoku.com/separable-sss/">Espalhamento subsuperficial separável</a> e <a href="https://www.iryoku.com/translucency/">Translucidez de pele realista em tempo real</a>.</li>
</ul>
<h2>13. Decalques, texturização virtual e camadas de materiais</h2>
<p>A variação das superfícies vem de decalques em camadas (marcas de bala, sujeira, sangue, encardido) projetados no buffer de profundidade, além de texturas virtuais que transmitem detalhes em alta resolução no momento exato em que são necessários. As camadas de materiais combinam vários conjuntos PBR por pixel usando máscaras e projeção triplanar, fazendo com que uma única rocha pareça cinco rochas diferentes.</p>
<img src="/img/blog/aaa_decals_virtual_textures.webp" alt="Parede desgastada de um bunker de concreto com decalques de balas, grafite e um detalhe do atlas de páginas de uma textura virtual" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Pranckevičius, <a href="https://aras-p.info/blog/2009/02/27/deferred-decals/">Decalques diferidos</a> e continuações (a clássica série de artigos de Aras).</li>
<li>Mittring, <a href="http://advances.realtimerendering.com/s2012/Epic/UnrealEngine4_Mittring_SIGGRAPH2012_Final_Cleaned.pdf">A tecnologia por trás da demonstração &quot;Unreal Engine 4 Elemental&quot;</a> (SIGGRAPH 2012, inclui detalhes sobre texturização virtual).</li>
<li>van Waveren, <a href="https://www.realtimerendering.com/blog/id-tech-5-challenges-from-texture-virtualization-to-massive-parallelization/">Desafios da id Tech 5: da virtualização de texturas à paralelização em massa</a> (SIGGRAPH 2009, a palestra sobre MegaTexture).</li>
<li>Williams, <a href="https://www.gdcvault.com/play/1022144/Crafting-the-World-of-The">Camadas de materiais em The Order: 1886</a> (GDC 2014, materiais PBR em camadas).</li>
</ul>
<h2>14. Transparência independente de ordem</h2>
<p>Cabelos, folhagens, partículas e vidros não podem ser ordenados de forma simples. Motores AAA usam técnicas como OIT com combinação ponderada, descascamento de profundidade ou listas encadeadas por pixel para renderizá-los corretamente sem uma etapa de ordenação na CPU. Discretamente, essa é uma das partes mais caras do quadro em cenas com muita folhagem.</p>
<img src="/img/blog/aaa_oit_transparency.webp" alt="Cena de floresta com folhagem translúcida sobreposta, fumaça, gotas de vidro e cabelo translúcido" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>McGuire e Bavoil, <a href="http://jcgt.org/published/0002/02/09/">Transparência independente de ordem com combinação ponderada</a> (JCGT 2013, o artigo sobre WBOIT).</li>
<li>Bavoil e Myers, <a href="https://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf">Transparência independente de ordem com descascamento duplo de profundidade</a> (white paper da NVIDIA).</li>
<li>Yang et al., <a href="https://www.cse.chalmers.se/edu/year/2017/course/TDA362/Per-Pixel%20Linked%20List.pdf">Construção simultânea de listas encadeadas em tempo real na GPU</a> (a referência sobre listas encadeadas por pixel).</li>
<li>Wyman, <a href="https://research.nvidia.com/publication/2016-06_exploring-and-expanding-continuum-oit-algorithms">Explorando e expandindo o contínuo de algoritmos de OIT</a> (HPG 2016, estudo comparativo).</li>
</ul>
<h2>15. Cache neural de radiância e removedores de ruído com ML</h2>
<p>A camada mais recente. O cache neural de radiância da NVIDIA aprende a iluminação indireta de cada cena e a consulta em vez de traçar mais raios. Removedores de ruído com ML (OptiX, Intel Open Image Denoise e soluções internas personalizadas) limpam sinais esparsos de ray tracing em milissegundos. Esta categoria deve crescer rapidamente ao longo dos próximos dois anos.</p>
<img src="/img/blog/aaa_neural_radiance_denoise.webp" alt="Catedral renderizada por ray tracing com ruído a 1 amostra por pixel à esquerda e resultado limpo por um removedor de ruído com ML à direita" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Leituras aprofundadas:</p>
<ul>
<li>Müller et al., <a href="https://research.nvidia.com/publication/2021-06_real-time-neural-radiance-caching-path-tracing">Cache neural de radiância em tempo real para path tracing</a> (SIGGRAPH 2021, o artigo sobre NRC).</li>
<li>Schied et al., <a href="https://research.nvidia.com/publication/2017-07_spatiotemporal-variance-guided-filtering-real-time-reconstruction-path-traced">Filtragem espaçotemporal guiada por variância: reconstrução em tempo real para iluminação global com path tracing</a> (HPG 2017, SVGF).</li>
<li>Chaitanya et al., <a href="https://research.nvidia.com/publication/interactive-reconstruction-monte-carlo-image-sequences-using-recurrent-denoising">Reconstrução interativa de sequências de imagens Monte Carlo usando um autoencoder recorrente de remoção de ruído</a> (SIGGRAPH 2017, o primeiro removedor de ruído recorrente com ML).</li>
<li>Intel, <a href="https://www.openimagedenoise.org/documentation.html">Documentação do Open Image Denoise</a> (removedor de ruído de código aberto para produção).</li>
</ul>
<h2>O que isso significa para o navegador</h2>
<p>Já lançamos várias dessas técnicas em WebGPU no nosso <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">motor de mundo aberto para navegadores</a>. Mapas de sombras em cascata, instanciação orientada pela GPU, PBR triplanar, neblina em espaço de tela e terreno virtualizado baseado em clipmaps rodam a 120 FPS em uma aba. O restante (ray tracing por hardware, mesh shaders e aumento de resolução com ML) está chegando à web à medida que a especificação WebGPU avança. Para saber mais, consulte nossos guias sobre <a href="/pt-BR/guides/browser-3d-open-world-tech">tecnologia de mundo aberto no navegador</a> e <a href="/pt-BR/guides/landscape-generation-browser">geração de paisagens</a>.</p>
<h2>Leituras adicionais sobre toda a pilha</h2>
<p>Se você quiser ler apenas um livro, <a href="https://www.realtimerendering.com/">Real-Time Rendering, 4ª edição</a> é a referência padrão que aborda a maioria dos tópicos acima. Para acompanhar as pesquisas mais recentes, o arquivo do curso “Advances in Real-Time Rendering in Games” da SIGGRAPH (<a href="https://advances.realtimerendering.com/">advances.realtimerendering.com</a>) oferece PDFs gratuitos de análises aprofundadas de motores AAA desde 2006. Para análises de produção que detalham como jogos específicos renderizam cada quadro, os <a href="http://www.adriancourreges.com/blog/">artigos de Adrian Courrèges sobre criação de perfis de GPU</a> são leitura obrigatória.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 18: um pincel de dispersão que parece usar IA]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering</guid>
            <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 37 criou um pincel de dispersão heurístico que mantém árvores longe de penhascos e pedrinhas junto à linha d'água sem usar LLM; depois, nós o levamos por instanciamento, LOD por distância e uma pilha de bugs até chegar a uma pincelada de 4 ms.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 18: um pincel de dispersão que parece usar IA</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search">Parte 17</a> deu ao jogador um conjunto de animações pronto para combate e uma maneira de inserir qualquer modelo CC0 no mundo. Esta parte volta ao lado do criador. A paleta do Spike 34 posiciona um objeto por clique, o que funciona bem para compor um objeto de destaque e é inútil para uma floresta. O Spike 37 é o pincel: arraste-o pelo terreno e as árvores preenchem os lugares onde deveriam estar.</p>
<h2>Posicionamento por IA sem uma IA</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/37-ai-scattering/" title="Spike 37: dispersão de objetos assistida por IA" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/37-ai-scattering/" target="_blank">Abrir o Spike 37 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=37-ai-scattering">Ver código-fonte</a></p>
<p>A pergunta que este spike responde é se um pincel puramente heurístico parece inteligente o bastante para dispensar o LLM. O teste de “posicionamento por IA” é concreto: as árvores ficam longe dos penhascos, as rochas se inclinam acompanhando a encosta e as pedrinhas da praia param na linha d'água, tudo já na primeira pincelada. Chegamos lá com predicados de inclinação e altitude, sorteios ponderados e espaçamento por família, sem uma única chamada a modelo.</p>
<p>O pincel trabalha sobre um mapa de altura de CPU de 257×257, com elementos ajustados manualmente para que cada predefinição tenha onde ser aplicada: montanhas ao norte para as seleções de inclinação mista, uma faixa de penhascos a leste para cascalheiras, uma planície costeira ao sul para praia e prado e uma depressão de lago no sudoeste. O terreno gera cores de vértice a partir de um classificador de biomas baseado em <code>(altitude, slope)</code>, portanto, antes de pintar uma única árvore, você consegue ver onde uma predefinição será aplicada. Cinco predefinições são fornecidas como dados simples, cada uma com uma lista de seleções como <code>{ category, weight, slopeMin, slopeMax, altMin, altMax, minSpacing, alignToSlope }</code>. Cliff and Scree define <code>slopeMin: 0.3</code> para que as rochas sejam posicionadas apenas em encostas reais e <code>alignToSlope: true</code> para que o vetor vertical de cada rochedo acompanhe a normal da superfície.</p>
<p>A cada pincelada, o mecanismo de dispersão amostra <code>densityPerM2 × area</code> pontos candidatos dentro do disco do pincel, lê a altura e a inclinação de cada candidato, filtra as seleções da predefinição para manter aquelas cujos predicados são satisfeitos, sorteia uma delas com pesos e então verifica o espaçamento usando um hash espacial dentro do raio. Tudo é determinístico: um gerador de números aleatórios Mulberry32 com semente controla cada sorteio, portanto <code>(seed, brush events)</code> reproduz qualquer sessão com exatidão. No terreno inicial, uma pincelada de Mixed Forest em um prado plano posicionou 139 dos 158 candidatos em 5 ms, enquanto a mesma predefinição em um penhasco posicionou apenas 106 de 226, e o HUD informou que 81 deles foram rejeitados por inclinação. Esse detalhamento das rejeições é a essência da experiência do usuário: você consegue ver <em>por que</em> o penhasco recebeu apenas algumas árvores, em vez de precisar adivinhar.</p>
<p>A razão para manter as predefinições como dados simples é que a versão com LLM, quando chegar, será uma troca de JSON, não uma reescrita. <code>paint({ preset })</code> não se importa se <code>preset.picks</code> veio de uma receita ajustada manualmente ou de um worker que transformou “floresta decídua com rochedos cobertos de musgo” em pesos. O mecanismo também nunca fixa um ID de objeto no código, portanto é possível trocar o catálogo sem alterar o mecanismo.</p>
<h2>De 300 draw calls para 49</h2>
<p>A primeira versão renderizava cada posicionamento como um <code>clone(true)</code> de um grupo com várias malhas, o que funciona bem com algumas centenas de objetos, mas encontra uma barreira no limite de 2.500, quando as draw calls chegam aos milhares. Antes de atingir esse ponto, migramos para <code>InstancedMesh</code>, com um bucket por <code>(propId, partIndex)</code>. Cada bucket cresce dobrando de tamanho: alocamos um <code>InstancedMesh</code> maior, copiamos as matrizes ativas, trocamos o pai na cena e descartamos o atributo antigo. O apagamento usa swap-remove, portanto remover uma instância é O(1), independentemente do tamanho do bucket. O determinismo, o espaçamento e o HUD de rejeições permanecem inalterados, pois a troca acontece inteiramente abaixo do registro de posicionamento.</p>
<p>Um diagnóstico no pacote MegaKit resolveu uma questão real de arquitetura. Uma malha glTF com várias primitivas (tronco e folhas) pode chegar ao three.js como uma única malha com um array de materiais e <code>geometry.groups</code>, ou como malhas irmãs separadas, cada uma com um material. Para esse pacote, o carregador segue o segundo caminho: cada parte é uma malha com um único material e grupos vazios. Esse é o melhor formato para dispersão, porque buckets separados por primitiva permitem que o bucket do tronco cresça independentemente do bucket das folhas caso suas contagens divirjam. A quantidade de draw calls é a mesma nos dois casos, mas a divisão oferece uma organização melhor da memória. O ganho medido se confirmou: uma pincelada de floresta que gerava cerca de 300 draw calls passou a gerar 49, e uma sessão completa com várias pinceladas chegou a 3.221 instâncias a 75 FPS em 51 draw calls — um limite que a abordagem com clones nunca conseguiria alcançar antes de estourar o orçamento de frame.</p>
<h2>LOD por distância e quatro bugs escondidos nele</h2>
<p>O instanciamento reduziu as draw calls, mas cada instância ainda desenhava sua contagem total de triângulos, mesmo as árvores a 90 m de distância que contribuíam com apenas dois pixels de detalhes de folhas. Por isso, geramos três níveis de LOD por parte do objeto com o meshoptimizer (completo, 50%, 15%), ampliamos a chave do bucket para <code>(propId, partIndex, lod)</code> e adicionamos um <code>move()</code> que transfere um posicionamento entre buckets irmãos sem fazer alocações. As faixas de distância vão de 0 a 30 m, de 30 a 90 m e além, com ±4 m de histerese ao redor de cada limite para que uma câmera parada próxima à borda de uma faixa não fique transferindo um posicionamento de um lado para o outro e reenviando sua matriz a cada frame. A reavaliação é limitada a 4 Hz e só acontece se a câmera realmente tiver se movido, portanto uma câmera parada custa apenas uma comparação de distância ao quadrado por frame.</p>
<p>Foi nesse caminho de LOD que surgiram os bugs mais instrutivos. O primeiro se manifestava como posicionamentos desaparecendo ou sendo duplicados enquanto a câmera orbitava, e piorava conforme a cena ficava mais cheia. A causa era uma matriz temporária compartilhada: <code>move()</code> lia a transformação de um posicionamento para <code>_tmpMat</code>, definida no escopo do módulo, mas o swap-remove do bucket de origem usava essa mesma <code>_tmpMat</code> em sua reorganização interna, sobrescrevendo a matriz transportada antes que o destino pudesse gravá-la. O bug só não ocorria quando o slot movido já era o último do bucket, uma chance aproximada de <code>1/count</code>, que corresponde exatamente ao “piscar raro que piora conforme a cena cresce” observado no teste. A correção foi reservar uma <code>_carryMat</code> exclusiva para <code>move()</code>. Em um teste de estresse com 1.274 movimentações acumuladas, o agrupamento permaneceu idêntico em cada pixel.</p>
<p>O segundo bug era mais sutil: todas as transições de LOD pareciam suaves, <em>exceto</em> a primeira. As árvores que passavam para o LOD1 apresentavam uma mudança visível de sombreamento, embora sua silhueta quase não mudasse, enquanto reduções maiores de triângulos mais adiante na sequência pareciam normais. O simplificador com <code>LockBorder</code> nunca move nem cria vértices, portanto os vértices preservados mantêm exatamente suas normais, mas mesmo assim estávamos chamando <code>computeVertexNormals()</code> após cada simplificação. O LOD0 retorna as normais originais criadas pelo artista sem alterações; já o LOD1 e os seguintes recebiam o recálculo genérico do three.js por média das faces. O limite entre 0 e 1 era o único ponto da sequência em que o regime de normais mudava, portanto era ali que acontecia o salto visual. Remover essa única linha defensiva corrigiu o sombreamento e, como bônus, reduziu aproximadamente pela metade o tempo de geração por objeto, pois deixamos de recalcular normais em quatro LODs por parte.</p>
<p>Uma auditoria do que o simplificador produzia revelou um terceiro ganho. Cada LOD era um <code>original.clone()</code> com um índice novo, e <code>BufferGeometry.clone()</code> faz uma cópia profunda de cada atributo, portanto cinco LODs mantinham cinco cópias independentes dos buffers de posição, normal, UV e cor, embora seus valores fossem idênticos bit a bit. Refatoramos o sistema para compartilhar referências de atributos e manter apenas um buffer de índices privado por LOD, reduzindo uma parte típica de árvore de 20 identidades distintas de atributos para 9 e enviando cada buffer de vértices à GPU apenas uma vez. O armazenamento compartilhado traz dois contratos: não altere os dados dos atributos por meio de nenhum LOD individual e não chame <code>dispose()</code> na geometria de um único LOD, pois ambas as ações afetariam todos os irmãos que compartilham o buffer.</p>
<p>O quarto bug não tinha nenhuma relação com a pintura. Apenas mover o cursor sobre o terreno derrubava a taxa de quadros, mesmo sem nenhum botão pressionado. O manipulador de <code>pointermove</code> fazia raycast contra a malha do terreno, um plano com 131.072 triângulos e sem estrutura espacial, portanto o three.js percorria o buffer de índices inteiro por evento, em uma frequência que podia chegar a 1.000 eventos por segundo. Não precisávamos da malha para essa consulta, pois o terreno é um mapa de altura paramétrico. Uma marcha de raio adaptativa contra <code>sampleHeight</code> — passos grandes bem acima da superfície, um mínimo de 0,4 m perto dela e depois 12 bisseções na inversão de sinal — custa aproximadamente de 8 a 30 amostras por raio, em vez de 131.072 testes de triângulos, cerca de três ordens de grandeza menos, e o simples movimento do cursor voltou a manter o limite de frames.</p>
<h2>O custo apenas muda de lugar; garanta que ele saia do clique</h2>
<p>Depois de migrar o spike para <code>WebGPURenderer</code> no three r184, nosso alvo de produção, um perfil do DevTools mostrou que a primeira pintura bloqueava por 265 ms, sendo 79% desse tempo dentro do WASM do meshoptimizer. A geração realizava trabalho de verdade, cerca de 180 chamadas de simplificação para uma predefinição fria, mas estava sendo executada dentro do manipulador de clique porque <code>preloadProps</code> apenas buscava e analisava as cenas, sem iniciar a geração dos LODs. A correção foi fazer com que a seleção da predefinição realizasse toda a geração em segundo plano: agora <code>preloadProps</code> chama o caminho de resolução das partes, armazena em cache a promise em andamento para que um clique rápido se junte a ela em vez de iniciar uma duplicata e memoriza o pré-processamento por geometria que o simplificador repetia quatro vezes por parte. O tempo da primeira pintura caiu de 209 ms para 4 ms no HUD. O tempo de WASM não desapareceu; apenas saiu do caminho crítico do usuário e passou a ocorrer enquanto ele observa o terreno e decide onde pintar.</p>
<p>Essa é a lição recorrente deste spike. Quase nenhuma dessas correções alterou o que o pincel <em>faz</em>. Elas alteraram <em>quando</em> o custo é pago: fora do clique, fora do movimento do cursor, fora do limite perto do qual a câmera está parada. Uma ferramenta de dispersão que parece instantânea não necessariamente realiza menos trabalho; ela realiza o trabalho onde o usuário não precisa esperar por ele.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Dispersão heurística por adequação.</strong> Um pincel amostra pontos candidatos em um disco, lê <code>(height, slope)</code> de cada ponto em um mapa de altura da CPU, filtra as seleções de uma predefinição por predicados de inclinação e altitude, sorteia uma delas com pesos e a rejeita se violar o espaçamento mínimo por família, acompanhado em um hash espacial. Seleções alinhadas à inclinação giram seu vetor vertical para acompanhar a normal da superfície. Isso produz posicionamentos que parecem intencionais — árvores longe dos penhascos, rochas inclinadas acompanhando as encostas e pedrinhas parando na linha d'água — sem pesos aprendidos, e mantém a predefinição como dados simples para que uma lista de seleções gerada por LLM possa substituí-la diretamente.</p>
<p><strong>Posicionamento determinístico com carregamentos assíncronos.</strong> Um gerador de números aleatórios Mulberry32 com semente controla cada sorteio, portanto <code>(seed, brush events)</code> reproduz uma sessão com exatidão. Os sorteios do RNG acontecem antes de qualquer <code>await</code>, e as reservas de espaçamento são inseridas no índice espacial antes que o clone glTF seja resolvido, portanto candidatos concorrentes respeitam uns aos outros e o carregamento assíncrono de recursos não consegue alterar a sequência.</p>
<p><strong>InstancedMesh organizado em buckets com edições O(1).</strong> Um <code>InstancedMesh</code> por <code>(propId, partIndex, lod)</code>, com a capacidade duplicada sob demanda por meio da cópia das matrizes ativas para um buffer maior. O apagamento e a remoção FIFO usam swap-remove com um array de referências inversas que corrige o índice da instância movida, portanto uma remoção é O(1), independentemente do tamanho do bucket. Um diagnóstico confirmou que as partes glTF chegam como malhas de material único, tornando um bucket por primitiva o caminho ativo e fornecendo a cada primitiva um bucket que pode crescer de forma independente.</p>
<p><strong>LOD por distância com histerese e buffers de atributos compartilhados.</strong> Três níveis simplificados pelo meshopt por parte, selecionados por faixas de distância com ±4 m de histerese para que uma câmera próxima a um limite não fique alternando continuamente, reavaliados em uma frequência limitada e condicionados ao movimento real da câmera. Como a simplificação com <code>LockBorder</code> nunca move os vértices, todos os LODs compartilham um único conjunto de buffers de posição, normal, UV e cor, diferenciando-se apenas pelo buffer de índices privado, o que reduz aproximadamente pela metade o número de buffers distintos de vértices na GPU. Ignorar um <code>computeVertexNormals</code> defensivo mantém as normais do artista idênticas entre os LODs e remove a única descontinuidade de sombreamento da sequência. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD e meshoptimizer</a>.</p>
<p><strong>Raycast analítico no mapa de altura para consultas de alta frequência.</strong> Consultar o cursor na frequência de <code>pointermove</code> contra uma malha plana de 131 mil triângulos percorre o buffer de índices inteiro por evento. Substituir isso por uma marcha de raio adaptativa contra a função analítica de altura — passos grandes longe da superfície, um mínimo pequeno próximo a ela e bisseção na inversão de sinal de $(\text{ray}_y - \text{terrain}_y)$ — custa dezenas de amostras em vez de dezenas de milhares de testes de triângulos, cerca de três ordens de grandeza menos, e um vetor de saída pré-alocado mantém o caminho crítico livre de alocações.</p>
<p><strong>Remova o trabalho do caminho crítico da interação.</strong> Trabalhos únicos e caros — geração de LODs pelo meshopt e compilação de pipelines WGSL — devem ser executados durante intervalos ociosos, não dentro do manipulador de clique. Pré-carregar a geração completa da predefinição ativa quando ela é selecionada, armazenar em cache a promise em andamento para que um clique rápido se junte a ela em vez de iniciar outra e memorizar o pré-processamento por geometria reduziu a latência da primeira pintura de 209 ms para 4 ms sem reduzir o trabalho total realizado.</p>
<hr>
<p>Parte 18 de 29.
Anterior: <a href="/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search">Parte 17 — Animações que não precisaram de redirecionamento e uma busca de assets em tempo real</a>
Próxima: <a href="/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters">Parte 19 — O impostor que precisa sobreviver a uma floresta</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 17: Animações que não precisaram de retargeting e uma busca de assets em tempo real]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search</guid>
            <pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 35 fez o retargeting de 262 clipes de combate e locomoção para o rig do jogador e criou um teste offline de paridade para validar a precisão. O Spike 36 integrou uma busca de modelos do Polyhaven em tempo real, sem etapa de build.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 17: Animações que não precisaram de retargeting e uma busca de assets em tempo real</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>A <a href="/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring">Parte 16</a> nos deu um mundo no qual é possível posicionar objetos. Esta parte oferece ao jogador algo melhor para fazer nele: um conjunto de animações com qualidade para combate e uma forma de adicionar ao mundo qualquer um dos milhares de modelos CC0 simplesmente digitando uma busca.</p>
<h2>262 clipes, um esqueleto, três correções de retargeting</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/35-combat-animations/" title="Spike 35: Animações de combate" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/35-combat-animations/" target="_blank">Abrir o Spike 35 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=35-combat-animations">Ver código-fonte</a></p>
<p>O rig do jogador é um esqueleto <code>3MIKE</code> no estilo CC4, com 213 ossos, incluindo um rig facial com mais de 80 ossos e articulações completas dos dedos. As fontes das animações não correspondem a ele. A primeira versão fez o retargeting de clipes de combate do Mixamo selecionados manualmente, mas o conjunto de origem era limitado, e a locomoção estava dividida de forma pouco prática entre o Mixamo e algumas animações ociosas BVH do Kimodo. O verdadeiro avanço veio quando migramos a biblioteca de origem para os dois pacotes Universal Animation Library da Quaternius: 262 clipes no total, todos em um único manequim consistente no estilo UE5, com 65 ossos. Há combos de espada, tiro com arco, escaladas, corridas em paredes, esquivas, reações a golpes, emotes e um conjunto de locomoção muito mais limpo.</p>
<p>Isso significa levar uma origem de 65 ossos para um destino de 213 ossos, sem nomes de ossos, orientações de pose T ou proporções de membros em comum. Cada clipe é remapeado em três etapas: um mapa de nomes de ossos, um alinhamento de bind pose para que os dois rigs compartilhem uma orientação de referência e o escalonamento das trilhas de posição para que o rig de origem, mais baixo, não deixe o personagem enterrado até os joelhos no chão. Fazer isso corretamente exigiu investigar uma sequência de bugs, cada um trazendo um aprendizado específico.</p>
<p>O personagem ficava excessivamente torcido, com todos os ombros e cotovelos girados cerca de 30° além do necessário. A causa era uma incompatibilidade de poses: a origem UAL vem em uma pose T verdadeira, o bind do CC4 está em pose A, e o retargeter pressupunha que os dois rigs estavam em poses de referência correspondentes. Assim, a diferença entre A e T era somada a cada delta por frame. A correção força a cadeia dos braços do CC4 a assumir uma pose T verdadeira ao capturar o bind e, em seguida, faz o retargeting, mantendo os deltas pequenos.</p>
<p>Depois, o corpo parou de se deslocar. Os knockbacks faziam a parte superior do corpo recuar, mas deixavam os pés plantados. A UAL coloca o deslocamento no osso <code>root</code>, não na pelve, portanto ler a posição da pelve resultava em movimento quase nulo. A correção soma <code>root.position</code> e <code>pelvis.position</code>, escala a parte horizontal e grava uma única trilha de posição do quadril. Durante esse trabalho, descobrimos que os deslocamentos verticais estavam errados em cerca de 12%, porque havíamos usado a proporção global dos membros para o eixo Y, quando a altura da pelve exige a proporção entre o quadril e o chão. Duas proporções, dois eixos: $\text{verticalRatio} = |{\text{targetHipY}}/{\text{sourceHipY}}|$ para Y e a proporção geral para a horizontal. O excesso de movimento desapareceu. E uma sequência de avisos de NaN em <code>RL_BoneRoot.position</code> foi rastreada até o mapeamento do <code>root</code> da UAL, que fica em $Y=0$, para um osso de destino cuja trilha de posição é normalizada dividindo-a pelo Y do bind. Divisão por zero, NaN e uma trilha descartada silenciosamente. A correção foi remover completamente o mapeamento da raiz, pois sua translação já está presente na trilha do quadril.</p>
<p>A menor correção foi a mais satisfatória de observar. O personagem segurava a espada com os dedos frouxos da bind pose porque o mapa de ossos não continha entradas para os dedos, e o retargeter só grava trilhas para ossos mapeados. Adicionar 30 ossos dos dedos — cinco dedos, três segmentos, duas mãos, descartando o quarto osso auxiliar da ponta usado pela UAL, que não deforma a malha — fez a mão se fechar no cabo e se abrir ao soltá-lo.</p>
<h2>Validando 262 clipes sem assistir a 262 clipes</h2>
<p>Não é possível verificar visualmente uma biblioteca com 262 clipes, então criamos um teste offline de paridade de trajetórias: um script Node headless que carrega cada pacote, amostra o esqueleto de origem a 60 Hz, executa o pipeline de retargeting e compara as posições e rotações globais de cada osso com a origem após o escalonamento. O desvio máximo no Y da pelve ficou em 0,003 m. As mãos apresentaram um deslocamento constante de 2,5°, que inicialmente pareceu indicar que “os dedos não estão acompanhando”, mas um deslocamento constante é o delta do bind entre a mão plana da UAL e o bind ligeiramente curvado do CC4, e permanece invariável ao longo do clipe. Erros reais de animação aparecem como desvios que variam de um frame para outro. Depois que isso ficou claro, o teste se tornou uma verificação de regressão executada de uma só vez: se o desvio de um clipe deixar de manter essa linha de base constante, alguma alteração recente quebrou o retargeting.</p>
<p>Um manequim de referência exibido lado a lado tornou decisiva a parte visual da depuração. Pressionar a barra invertida mostra, ao lado do jogador, o rig de origem responsável pelo clipe atual. Assim, a pergunta “o ombro está torcido?” passa a ser “a torção já existe na origem ou foi adicionada pelo retargeting?”. Os testes numéricos detectam regressões, a referência visual identifica erros de bind pose que os números não revelam e, juntos, eles eliminaram as tentativas no escuro.</p>
<p>Com o pipeline sólido, trocar a base de WASD, salto e natação do Mixamo para a UAL exigiu apenas um pequeno mapa de aliases: a máquina de estados finitos continua usando nomes genéricos de estados, como <code>idle</code> e <code>walk</code>, que são resolvidos para nomes de clipes da UAL durante a reprodução. A natação exigiu offsets de rig específicos para cada clipe, porque as poses de nado livre e de sustentação vertical posicionam a pelve em alturas anatômicas diferentes. Por isso, submergimos o rig em meio metro durante a natação ativa e ainda mais durante a sustentação, fazendo a transição entre as duas a uma frequência suave de 5 Hz.</p>
<h2>Digite uma palavra, obtenha um modelo</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/36-polyhaven-models/" title="Spike 36: Modelos do Polyhaven" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/36-polyhaven-models/" target="_blank">Abrir o Spike 36 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=36-polyhaven-models">Ver código-fonte</a></p>
<p>O Spike 34 consumiu um dia inteiro na seleção manual de um pacote CC0. A solução de longo prazo é uma caixa de busca. O Polyhaven publica cerca de 1.100 modelos CC0 por meio de uma API JSON permissiva e de uma CDN determinística, e este spike conecta todo o fluxo — consulta, miniaturas, carregamento e renderização — a partir de uma página estática sem etapa de build, usando aproximadamente 300 linhas de JavaScript puro mais three.js.</p>
<p>Na inicialização, ele busca o catálogo completo uma única vez, cerca de 600 KB. A busca usa pontuação inteiramente no cliente — nome tem prioridade sobre ID, que tem prioridade sobre categoria, que tem prioridade sobre tag — com debounce de 120 ms, renderizando os 60 primeiros cards. As miniaturas são carregadas de forma lazy por meio de um <code>IntersectionObserver</code>, para que digitar não dispare 60 solicitações de uma só vez. A parte interessante é o carregamento. O endpoint de arquivos do Polyhaven expõe glTF com vários arquivos, não GLB, com texturas compartilhadas entre resoluções e divididas em arquivos separados, além de retornar um mapa <code>include</code> de caminhos relativos para URLs absolutas da CDN. Em vez de baixar e modificar o JSON por conta própria, passamos esse mapa pelo <code>LoadingManager.setURLModifier</code>, que é acionado para cada dependência necessária ao loader — o <code>.bin</code> e cada textura — e a resolve por meio da CDN. Um clique, um único arquivo aparente. Tanto a API quanto a CDN definem CORS permissivo, verificado com <code>curl</code> antes de escrever qualquer código cliente, portanto não é necessário um proxy. Os materiais PBR são renderizados corretamente com <code>RoomEnvironment</code> e os padrões de tone mapping ACES, sem ajustes específicos para cada asset, enquanto as texturas 1k mantêm um modelo típico entre 2 e 5 MB, em vez dos 20 a 40 MB das versões 4k.</p>
<p>Uma etapa de meshoptimizer em WASM completa o conjunto com um redutor não destrutivo: cada malha armazena um clone de sua geometria original, e alterar a proporção reconstrói um buffer de índices a partir desse clone, em vez de simplificar cumulativamente. Geometrias com vários materiais são simplificadas por grupo e têm seus <code>geometry.groups</code> reconstruídos, para que os slots de materiais não sejam mesclados. Uma poltrona passa de 5.626 triângulos na qualidade máxima para 2.812 na metade.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Retargeting de esqueletos com alinhamento de bind pose.</strong> Mapear uma animação de um esqueleto para outro com nomes de ossos, proporções e poses de referência diferentes exige três correções: um mapa de nomes de ossos, um alinhamento de bind pose para que os dois rigs compartilhem uma orientação de referência — forçando a cadeia dos braços da pose A do destino a assumir a pose T da origem — e o escalonamento das trilhas de posição. Uma incompatibilidade de poses adiciona a rotação de A para T a cada delta por frame, duplicando a rotação das articulações. Ossos auxiliares não mapeados precisam ser descartados, pois um osso raiz em $Y=0$ causa uma divisão silenciosa por zero na normalização da trilha de posição.</p>
<p><strong>Duas proporções de escala para um único rig.</strong> O deslocamento horizontal usa a proporção global dos membros — o tamanho geral do esqueleto —, mas o deslocamento vertical da pelve usa a proporção entre quadril e chão $|{\text{targetHipY}}/{\text{sourceHipY}}|$, pois a proporção entre pernas e torso difere entre os rigs. Usar uma única proporção para os dois eixos faz as escaladas ultrapassarem o destino e as recuperações afundarem abaixo do chão. O deslocamento da UAL também fica no osso <code>root</code>, não na pelve, portanto os dois precisam ser somados em uma única trilha de posição do quadril para preservar o movimento em clipes de knockback, escalada e locomoção.</p>
<p><strong>Teste offline de paridade de trajetórias.</strong> Um script headless amostra o esqueleto de origem a 60 Hz, executa o pipeline de retargeting e compara as transformações globais de cada osso com a origem. Um deslocamento constante a cada frame é o delta inofensivo do bind, enquanto um desvio que varia de frame para frame é um erro real. Assim, o teste se torna uma verificação de regressão acionada quando uma alteração remove ou distorce uma trilha. O isolamento dos GLBs por pacote — carregando um novo e liberando-o antes do próximo — evita contenção de cache durante toda a varredura.</p>
<p><strong>LoadingManager.setURLModifier para grafos glTF em CDN.</strong> Quando uma CDN distribui glTF como um grafo de URIs relativas acompanhado por um mapa de inclusão — caminho relativo para URL absoluta —, <code>setURLModifier</code> resolve pela CDN cada dependência solicitada pelo loader sem reescrever o JSON. Isso transforma uma distribuição com vários arquivos e várias resoluções em um carregamento de um único clique. Descartar a geometria, os materiais e os mapas de textura de cada modelo anterior antes de carregar o próximo evita o acúmulo de centenas de MB de memória da GPU durante uma sessão de navegação.</p>
<p><strong>Simplificação não destrutiva de malhas.</strong> Armazenar um clone da geometria original de cada malha e reconstruir apenas o buffer de índices para cada proporção de simplificação mantém as alterações rápidas e evita os danos cumulativos causados por simplificações repetidas. Executar o processo por fatia de <code>geometry.groups</code> e reconstruir os grupos preserva as atribuições de múltiplos materiais. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">LOD e meshoptimizer</a> para entender como isso alimenta o LOD baseado em distância.</p>
<hr>
<p>Parte 17 de 29.
Anterior: <a href="/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring">Parte 16 — Estrutura para um mundo que continua crescendo</a>
Próxima: <a href="/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering">Parte 18 — Um pincel de dispersão que parece usar posicionamento por IA</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Anatomia de um motor de jogos com IA: o que realmente existe por trás do prompt]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-30-anatomy-of-an-ai-game-engine</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-30-anatomy-of-an-ai-game-engine</guid>
            <pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A Cinevva parece uma única caixa de texto. Por trás dela, há um orquestrador, 26 ferramentas tipadas, onze provedores de ativos, um iframe com o jogo ao vivo que o agente pode jogar e uma vitrine integrada a reels de gameplay. Veja a máquina inteira, peça por peça.]]></description>
            <content:encoded><![CDATA[<h1>Anatomia de um motor de jogos com IA: o que realmente existe por trás do prompt</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<p>O comentário mais comum que ouvimos de quem não conhece a Cinevva é que ela não passa de uma camada sobre IA. A pessoa entra na página inicial, vê uma única caixa de texto com a frase &quot;Descreva seu jogo&quot; e acha que já entendeu o produto. Ela entendeu a porta de entrada. O produto é tudo o que você não vê quando digita essa frase e, desde esta semana, também um mundo aberto no qual seus jogadores entram para encontrar seu jogo.</p>
<p>Por trás dessa caixa, há um motor de jogos completo, um orquestrador com 26 ferramentas tipadas, uma frota de modelos generativos, uma busca agregada de ativos em onze provedores, um iframe com o jogo ao vivo que o agente pode ler e modificar e uma vitrine na qual o mesmo agente publica seu jogo finalizado como um reel de gameplay que pode ser navegado com gestos. Nada disso é exótico. É tudo infraestrutura. O trabalho interessante foi decidir o que conectar a quê.</p>
<p>Este post percorre a máquina de cima a baixo. É a explicação que damos a investidores técnicos e a desenvolvedores que pensam em contribuir. Todos querem a mesma resposta.</p>
<h2>1. O prompt é a superfície, não o sistema</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-prompt.png" alt="O campo de prompt da Cinevva na página inicial. Um único campo de texto diz 'Crie um jogo de tiro espacial neon com asteroides, power-ups e uma trilha sonora synth.' Abaixo, há três chips de sugestões e o logotipo da Cinevva." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>A primeira decisão de design foi esconder tudo até que o usuário tivesse algo em jogo. A página inicial tem uma única função: receber uma frase e transformá-la em um projeto. Dividimos a experiência em um lado público (o prompt, as demonstrações, o feed da vitrine) e um lado para criadores (a IDE, as ferramentas de ativos, o fluxo de publicação), para que quem só quer jogar não precise olhar para a cabine de comando.</p>
<p>É por isso que o texto de exemplo alterna entre frases como &quot;Crie um jogo da cobrinha com gráficos neon&quot; e &quot;Jogo de tiro contra zumbis com visão de cima&quot;. Não são tutoriais. São uma autorização. O usuário percebe que uma &quot;ideia maluca de fliperama&quot; é uma entrada válida.</p>
<p>No momento em que você envia o prompt, três coisas acontecem. O sistema cria um novo projeto de jogo com um ID estável. Ele inicializa uma árvore de arquivos vazia (<code>index.html</code>, <code>game.js</code>, <code>style.css</code>, <code>assets/</code>, <code>GDD.md</code>). E encaminha sua frase ao orquestrador com uma janela de contexto do projeto que aponta para esses arquivos vazios. Daqui em diante, você está conversando com um agente que tem mãos.</p>
<h2>2. Por trás do campo, uma IDE completa no navegador</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-ide.png" alt="A IDE de criação da Cinevva no modo escuro. Três painéis: uma árvore de arquivos à esquerda com GDD.md, index.html, game.js, style.css e uma pasta de ativos; um editor de código com realce de sintaxe no centro, mostrando a configuração de um renderizador Three.js; e uma prévia ao vivo do jogo à direita, exibindo um jogo de tiro espacial neon com visão de cima e uma interface com PONTUAÇÃO e VIDAS." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O espaço de trabalho do criador é uma IDE de três painéis que roda inteiramente no navegador. Árvore de arquivos à esquerda, editor de código no centro, iframe com o jogo ao vivo à direita e um campo de chat ao longo da parte inferior. Escolhemos esse layout de propósito. Quem já escreve código o reconhece de imediato. Quem não escreve pode ignorar o painel central e interagir apenas pelo chat.</p>
<p>A árvore de arquivos não é uma metáfora. Cada projeto realmente é um pequeno site estático composto por HTML, JS, CSS, imagens geradas, modelos GLB e arquivos de áudio, servido diretamente pelo R2 por meio de um Cloudflare Worker. Não há etapa de build. E o motivo é importante. Isso significa que o agente pode ler e modificar os arquivos que você realmente publicaria, e que o mesmo artefato roda no iframe, em um celular encapsulado pelo Capacitor, na Steam encapsulado pelo Tauri e dentro do Discord como uma Embedded Activity. Um sistema de arquivos, seis destinos de distribuição.</p>
<p>Todo jogo que geramos precisa expor duas funções em <code>window</code>. <code>getGameState()</code> retorna um objeto simples que descreve o jogo em execução (posição do jogador, pontuação, vidas, inimigos, temporizadores). <code>setGameState(patch)</code> aplica uma atualização parcial ao jogo ao vivo. Essa pequena API é o contrato que permite ao agente inspecionar e modificar um jogo em execução sem recarregá-lo. Voltaremos ao motivo disso na seção 6.</p>
<h2>3. O orquestrador, onde o motor realmente funciona</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-orchestrator.png" alt="Um diagrama do orquestrador da Cinevva. Um nó central chamado 'Orquestrador (LLM)' está conectado a oito nós de ferramentas dispostos ao redor dele: list_game_files, read_files, edit_files, write_files, generate_image (Flux), generate_skybox (Blockade), generate_music (ElevenLabs) e rig_model (Tripo). Uma linha pontilhada conecta o orquestrador a um nó chamado 'Iframe do jogo', identificado como 'capturas de tela + console'." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O que as pessoas não percebem quando chamam isso de &quot;uma camada sobre IA&quot; é que a IA não escreve código tanto quanto rege uma pequena orquestra. O orquestrador é um loop de chamadas de ferramentas executado sobre um modelo de fronteira, com um prompt de sistema de cerca de mil linhas e uma superfície tipada de 26 funções. As definições das ferramentas são o motor. O modelo é o maestro.</p>
<p>As 26 ferramentas se dividem em cinco famílias. As ferramentas de sistema de arquivos (<code>list_game_files</code>, <code>read_files</code>, <code>search_game_file</code>, <code>edit_files</code>, <code>write_files</code>, <code>delete_files</code>, <code>rename_files</code>) permitem que o agente trate seu jogo como qualquer outra base de código. As ferramentas de projeto (<code>create_new_game</code>, <code>list_games</code>, <code>open_game</code>, <code>ask_user</code>, <code>game_ready</code>) cuidam do ciclo de vida do projeto e da devolução do controle ao humano. As ferramentas generativas (<code>generate_image</code>, <code>generate_skybox</code>, <code>generate_music</code>, <code>generate_sfx</code>, <code>rig_model</code>, <code>apply_animation</code>, <code>list_animations</code>, <code>list_skybox_styles</code>) acionam modelos especializados que abordaremos a seguir. As ferramentas de conhecimento (<code>search_fonts</code>, <code>list_library_docs</code>, <code>search_library_docs</code>, <code>list_generations</code>) fornecem ao agente fontes confiáveis, indexadas e verdadeiras, em vez de APIs inventadas. E as ferramentas de runtime (<code>read_console_logs</code>, <code>execute_js</code>) permitem que o agente veja e interaja com o jogo ao vivo.</p>
<p>Um primeiro turno típico funciona mais ou menos assim. O usuário digita &quot;Crie um jogo de tiro espacial neon&quot;. O modelo emite uma chamada a <code>write_files</code> com um <code>GDD.md</code> que descreve o design e depois chama <code>write_files</code> novamente com <code>index.html</code>, <code>game.js</code> e <code>style.css</code>. Ele chama <code>generate_music</code> com <code>prompt=&quot;jogo de tiro espacial synthwave, 120 BPM, linha de baixo pulsante&quot;</code>, iniciando uma tarefa no ElevenLabs Music e retornando uma URL de MP3 dentro do R2. Ele chama <code>generate_skybox</code> com um estilo do modelo 3 da Blockade Labs para criar o campo estelar. Chama <code>game_ready(title, message)</code> para atualizar o iframe com a nova build e, por fim, <code>read_console_logs</code> para confirmar que o jogo iniciou sem erros. Se encontrar um stack trace, volta a <code>edit_files</code> e corrige o problema antes mesmo que o usuário o reporte.</p>
<p>A propriedade interessante desse design é que o prompt de sistema e os esquemas das ferramentas são as únicas partes específicas do produto. Troque o modelo e tudo continuará funcionando. Migramos entre três modelos de fronteira no último ano sem alterar a interface, porque ela é nossa.</p>
<h2>4. O lado dos ativos: distribuição entre modelos</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-assets.png" alt="Um painel de geração de ativos com três seções. O painel esquerdo é um gerador de música com o prompt 'jogo de tiro espacial synthwave, 120 BPM, linha de baixo pulsante', um controle de duração de 60 segundos e um botão Gerar, além de um cartão de forma de onda em reprodução chamado 'Tema da Deriva de Asteroides'. O painel central é um gerador de skybox com o prompt 'espaço profundo, nebulosa distante, duas luas' e uma grade com quatro miniaturas de ambientes em 360 graus. O painel direito mostra uma prévia rotativa de um modelo 3D de uma nave espacial roxa low-poly, com um botão 'Rig + Caminhar' e um indicador de status que diz 'Criando rig... faltam 1 min 42 s'." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O agente não gera imagens, músicas, sons ou modelos 3D. Ele chama especialistas. O lado de ativos da Cinevva distribui o trabalho entre os melhores modelos de cada categoria, conectados de modo que a saída de um seja uma entrada válida para o seguinte.</p>
<p>Para imagens e sprites, <code>generate_image</code> chama o Flux Pro 1.1 com largura e altura sempre múltiplas de 32 e um formato de saída que muda para PNG quando o prompt pede transparência. Para músicas completas, o agente chama o ElevenLabs Music com um prompt estilístico e uma duração em segundos. Para efeitos isolados, o ElevenLabs SFX produz um clipe a partir de um texto como &quot;disparo de arma laser, blaster de ficção científica&quot; em cinco a quinze segundos. Para ambientes em 360 graus, <code>generate_skybox</code> chama a Blockade Labs com um de aproximadamente noventa estilos, usando por padrão o modelo 3 Digital Painting. Para personagens 3D, <code>rig_model</code> envia um GLB à Tripo para receber esqueleto e rig como criatura bípede, quadrúpede, hexápode, octópode, serpentina ou aquática; depois, <code>apply_animation</code> reproduz qualquer uma das quinze predefinições (parado, andar, correr, pular, escalar, mergulhar, golpear, atirar, sofrer dano, cair, virar, além das marchas para não bípedes).</p>
<p>Cada uma dessas operações é uma tarefa, não uma chamada síncrona. Elas levam de segundos a minutos. O orquestrador as inicia, retorna imediatamente com um <code>jobId</code>, e um canal separado envia o resultado de volta à conversa quando o ativo está pronto. Isso muda completamente a experiência. O agente pode continuar construindo a lógica do jogo enquanto o rig de um modelo 3D é finalizado em segundo plano. O usuário nunca precisa esperar por um único caminho crítico.</p>
<p>Todos os artefatos vão para o mesmo lugar. O R2, por trás de <code>cdn.cinevva.com</code>, acessível por URLs simples. Isso significa que a próxima chamada <code>write_files</code> do agente pode simplesmente incorporar <code>&lt;audio src=&quot;https://cdn.cinevva.com/audio/abc.mp3&quot;&gt;</code>, e o jogo funciona. Sem SDK, sem empacotador de ativos, sem manifesto.</p>
<h2>5. Busca de ativos em onze provedores</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-asset-search.png" alt="A página de busca de ativos da Cinevva no modo escuro. Uma barra de busca na parte superior contém a consulta 'rocha de asteroide', e uma fileira de filtros de provedores mostra Todos, PolyHaven, Sketchfab, Kenney, Quaternius, AmbientCG, OpenGameArt, Freesound, Smithsonian, Synty e Jamendo, com Sketchfab destacado. Abaixo, há uma grade de quatro colunas com miniaturas de modelos de asteroides; cada cartão mostra o selo do provedor, uma etiqueta de licença CC0 ou CC-BY e um botão 'Usar no jogo'. Uma pequena legenda no canto superior direito mostra um balão de conversa que diz 'ChatGPT: uma das melhores ferramentas gratuitas de ativos para desenvolvedores de jogos'." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>A geração é ótima quando você precisa de algo específico. A busca é mais rápida quando aquilo já existe. A <a href="/pt-BR/assets">Biblioteca de Ativos</a> é uma busca federada em onze provedores gratuitos e licenciados, apresentada em um único feed ordenado por relevância.</p>
<p>O registro de provedores fica no worker e executa cada consulta em paralelo na PolyHaven, Sketchfab, Freesound, Kenney, AmbientCG, Quaternius, OpenGameArt, coleção 3D do Smithsonian, TurboSquid, Synty e Jamendo. Cada provedor implementa uma interface comum (<code>search</code>, <code>getMetadata</code>, <code>isBrowsable</code>, <code>getSupportedTypes</code>), portanto adicionar um novo exige apenas um arquivo TypeScript. A camada de agregação cuida da paginação baseada em cursor entre provedores que paginam de formas diferentes, elimina licenças duplicadas, normaliza os tipos de ativos em uma taxonomia comum (modelo, textura, áudio, HDRI, sprite) e marca cada ativo com sua licença, para que o agente nunca escolha algo que não possa redistribuir.</p>
<p>Quando o ChatGPT recomendou essa ferramenta no trimestre passado como um dos melhores buscadores gratuitos de ativos para desenvolvedores de jogos, fomos ler a recomendação. Ela estava certa sobre o que importa. A questão não é termos uma barra de busca sobre uma única biblioteca. A questão é que um desenvolvedor independente procurando por &quot;textura de parede de pedra&quot; recebe resultados da PolyHaven e da AmbientCG lado a lado com a versão estilizada da Kenney e uma opção de fotogrametria do Smithsonian, com uma única consulta e metadados de licença consistentes. O agente pode acessar o mesmo endpoint e escolher ativos da mesma forma que uma pessoa.</p>
<p>Essa é a parte do motor que costuma receber menos destaque do que merece. A maioria das demonstrações de IA para jogos gera tudo do zero e acaba com uma aparência uniforme, brilhante e ligeiramente estranha. Os jogos interessantes na Cinevva combinam ativos gerados com fotogrametria CC0 real do Smithsonian e uma árvore low-poly feita à mão pela Quaternius. O orquestrador pode fazer tudo isso em um único turno.</p>
<h2>6. O agente joga o jogo</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-debug.png" alt="Uma visualização de depuração ao vivo. À esquerda, um jogo de tiro espacial neon em execução mostra a nave do jogador desviando de asteroides, com PONTUAÇÃO de 4820 e 2 vidas restantes. À direita, dois painéis empilhados: um painel execute_js mostra a chamada JSON.stringify(getGameState()) e um objeto JSON retornado com posição, velocidade, saúde, pontuação e vidas do jogador, além da contagem de asteroides; e um painel read_console_logs mostra as linhas mais recentes do log, incluindo surgimento de asteroide, power-up coletado, um aviso de carregamento de textura e uma leitura de 58 FPS." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Essa é a parte que mais surpreende os engenheiros quando a demonstramos. O agente não se limita a escrever código em arquivos. Ele executa o jogo em um iframe isolado, lê o console, faz capturas de tela em camadas separadas para a interface e a cena 3D e executa JavaScript dentro do runtime ao vivo para inspecionar ou alterar o estado.
<code>read_console_logs</code> retorna as últimas cinquenta linhas de <code>console.log</code>, <code>console.warn</code>, <code>console.error</code> e exceções não capturadas do iframe. Após cada chamada de <code>game_ready</code>, o prompt do sistema exige que o agente chame <code>read_console_logs</code> e corrija todos os erros antes de devolver o projeto ao usuário. Essa única regra elimina a maioria das falhas do tipo &quot;a IA disse que estava pronto, mas a tela está preta&quot; que outras ferramentas apresentam.</p>
<p><code>execute_js</code> é a ferramenta mais poderosa. Ela executa JavaScript arbitrário dentro do escopo global do jogo. Como todo jogo precisa implementar <code>getGameState</code> e <code>setGameState</code>, o agente pode executar <code>JSON.stringify(getGameState())</code> para ler todo o estado do jogo em execução e, depois, <code>setGameState({player: {x: 500, y: 100}})</code> para teletransportar o jogador, <code>setGameState({lives: 99})</code> para conceder invencibilidade ou <code>setGameState({level: 3})</code> para avançar de fase. Ele também pode investigar níveis mais baixos, usando algo como <code>scene.children.map(c =&gt; c.type)</code> para inspecionar o grafo de cena do Three.js ou <code>document.querySelectorAll('canvas').length</code> para confirmar que o renderizador foi montado.</p>
<p>É fácil entender por que isso importa. Quando um usuário diz &quot;o jogador fica preso na parede na segunda fase&quot;, o agente não precisa adivinhar. Ele abre a segunda fase, executa <code>getGameState()</code>, examina os dados de colisão, testa uma correção com <code>execute_js</code> e só então grava a correção no disco. Ele depura como você depura. Tratamos o iframe como um par, não como um alvo.</p>
<h2>7. Documentação das bibliotecas, a entediante fonte da verdade</h2>
<p>Indexamos a documentação oficial das bibliotecas que os jogos realmente usam (Three.js, Tone.js, Cannon e uma lista cada vez maior) em um repositório JSON estruturado e pesquisável. O agente recorre a <code>search_library_docs(&quot;threejs&quot;, &quot;Mesh&quot;)</code> antes de buscar na web aberta. É mais rápido, tem controle de versão e nunca alucina um método que foi renomeado duas versões menores atrás.</p>
<p>Isso não é glamouroso. É a diferença entre uma IA que produz código funcional e uma IA que produz código com aparência convincente, mas que trava na linha quarenta. Grande parte da qualidade visível dos jogos criados com a Cinevva vem dessa única decisão.</p>
<h2>8. De jogável a encontrável: reels e a vitrine</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-reels.png" alt="Uma vitrine de reels curtos de gameplay. No centro, um cartão em formato de celular reproduz um vídeo vertical de um jogo de tiro espacial neon durante um combate, com ícones de interface social empilhados à direita (um coração com 14 mil, um balão de conversa com 3,2 mil, uma seta de compartilhamento com 1,8 mil e um botão 'Jogar agora'). O rótulo do reel na parte inferior diz 'Asteroid Drift • por @indiedev • 3,1 mi de visualizações'. À esquerda, um pequeno trecho de conversa intitulado 'Do chat do criador' mostra a mensagem do assistente 'O jogo está no ar. O reel foi publicado automaticamente na sua vitrine'. À direita, um painel da Vitrine lista três jogos em alta: Asteroid Drift, Roadhawk Runner e Birb Clicker, cada um com uma contagem de visualizações e um selo 'Em alta'." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Um jogo que ninguém joga é um hobby particular. O problema não resolvido mais difícil do desenvolvimento independente já não é criar jogos. É a distribuição. Por isso, o mecanismo não para em &quot;jogável&quot;. Ele para em &quot;diante dos jogadores&quot;.</p>
<p>Todo projeto na Cinevva tem uma URL de compartilhamento estável, como <code>play.cinevva.com/{game-id}</code>, que abre a versão mais recente do jogo em um iframe de página inteira com uma pequena sobreposição para curtidas e compartilhamentos. Quando o agente chama <code>game_ready</code>, ele não apenas atualiza a prévia no ambiente de desenvolvimento. Ele também oferece a publicação de um reel de gameplay de 15 segundos na <a href="/pt-BR/play">vitrine da Cinevva</a>. O reel é uma gravação real do jogo sendo jogado, capturada diretamente do iframe (fornecemos um pequeno script de gravação que o agente injeta no ambiente de execução do jogo), então o que o espectador vê ao rolar a tela é gameplay de verdade, não uma edição de marketing.</p>
<p>Isso muda o ciclo de trabalho de quem desenvolve. O ciclo antigo era programar, compilar, empacotar, enviar, escrever uma página na Steam, enfrentar um algoritmo e observar um contador de listas de desejos. O novo ciclo é digitar uma frase, ver o agente criar, apertar publicar e assistir ao seu jogo aparecer no mesmo feed vertical junto de todos os outros. A descoberta acontece no mesmo produto em que a criação acontece, na mesma URL e com o mesmo login.</p>
<p>Para quem acompanha a comunidade de desenvolvimento, esta é a parte que vale examinar. A distribuição sempre foi a vantagem competitiva que os mecanismos de jogos não ofereciam. A Unity fornece um editor; você publica o jogo na Steam. A Unreal fornece um editor; você publica na Epic Store. A Cinevva fornece as duas metades. O mecanismo e a vitrine se comunicam por meio do mesmo orquestrador que criou o jogo.</p>
<h2>9. Mais jogados: o ciclo virtuoso da descoberta</h2>
<img src="/img/blog/engine-anatomy/cinevva-engine-charts.png" alt="O ranking Mais jogados da Cinevva. Três abas na parte superior dizem Em alta, Recentes e Mais jogados, com Mais jogados selecionada. Abaixo, uma fileira em formato de pódio com três jogos em destaque: em primeiro lugar, The Breaker Belt, com 42,1 mil partidas; em segundo, Roadhawk Runner, com 27,7 mil partidas; e em terceiro, Asteroid Drift, com 37,1 mil partidas. Abaixo do pódio, uma grade mais densa com seis cartões menores de jogos, incluindo Pixel Platformer, Zombie Shooter, Fruit Ninja Clone, Tower Defense, Card Battler e Zombie Builder, cada um com miniaturas, nomes de usuário dos criadores e contagens de partidas." style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O feed de reels é uma metade do ciclo virtuoso. O <a href="/pt-BR/charts">ranking Mais jogados</a> é a outra. Ele classifica os jogos por popularidade, recência e desempenho histórico, com peso baseado na taxa de conclusão (o jogador terminou uma sessão?) em vez de cliques brutos. É mais difícil manipular conclusões do que instalações. Um jogo que prende a atenção por sessenta segundos supera um jogo que é aberto e fechado.</p>
<p>Cada sinal retroalimenta o próximo turno do orquestrador. Quando o agente diz &quot;vamos adicionar um power-up&quot;, ele não está se baseando em uma biblioteca fixa de padrões. Ele usa um modelo que observou quais mecânicas realmente retêm jogadores na Cinevva neste mês. O ciclo virtuoso não é apenas um ranking no fim da página. É o sinal de treinamento de todo o mecanismo.</p>
<p>Para investidores, essa é a nossa vantagem competitiva baseada em dados. Não temos apenas jogos gerados. Temos jogos gerados com métricas reais de engajamento em nível de sessão, vinculadas aos prompts que os produziram, às escolhas de recursos feitas pelo agente e aos jogadores que gostaram deles. Cada ciclo aprimora o seguinte.</p>
<h2>10. O mecanismo se torna um jogo</h2>
<p>Esta é a parte que venho insinuando nas redes sociais há meses. Tudo o que aparece nas seções 1 a 9 descreve um sistema que cria jogos. O mais difícil de descrever, até você ver, é o que acontece quando o próprio sistema se torna um lugar.</p>
<p>Criar jogos é um trabalho árduo, mesmo com tudo o que já lançamos. Se você nunca criou um jogo, de repente passa a ser responsável pelo enredo, pelos personagens, pelo mundo, pelas mecânicas, por quem faz o quê e quando, pelo comportamento da iluminação, pelas camadas de efeitos especiais, pela música, pelos efeitos sonoros e pelos movimentos de câmera. É muita coisa. E, além de tudo isso, você quer que o resultado pareça realmente seu, não um modelo pronto cuspido pela ferramenta de outra pessoa. O prompt e o orquestrador fazem a maior parte do trabalho mecânico por você. O que eles não conseguem fazer sozinhos é dar à sua criação final um palco onde possa se apresentar com outras pessoas assistindo.</p>
<p>Então construímos o palco. Durante a maior parte deste ano, <a href="/pt-BR/about">Oleg</a> e a equipe do mecanismo vêm publicando o trabalho em uma <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">série de 14 partes</a> sobre como colocar um mundo aberto no navegador. Terreno esculpido que você pode modificar em tempo real. Biomas que se pintam como rocha ou grama de acordo com a inclinação e a altitude. 80.000 folhas de grama que se curvam com o vento sobre um terreno gerado por computação. Um personagem que caminha, corre, pula e plana sobre terreno volumétrico e baseado em mapas de altura no mesmo movimento. Nada disso era uma demonstração técnica feita por si só. Era a base para isto.</p>
<p>Hoje, o mundo aberto está disponível em uma versão inicial. Você escolhe um avatar e entra. Seus jogos publicados aparecem como espaços visitáveis ao seu redor, assim como os <a href="/pt-BR/play">reels da vitrine</a> e o <a href="/pt-BR/charts">ranking Mais jogados</a> os exibem agora, mas você chega a pé em vez de deslizar pela tela. Você pode entrar no jogo de uma pessoa desconhecida a partir do mundo sem sair dele. Pode ficar ao lado da pessoa que criou aquilo que acabou de jogar e dizer o que adicionaria em seguida. Pode esculpir um pedaço de terreno com alguém que acabou de conhecer e transformá-lo na semente de um novo projeto. A descoberta deixa de ser um feed. Ela se torna um passeio.</p>
<p>Nosso mecanismo sempre foi um mecanismo de jogos no sentido técnico. Hoje, ele também se torna um jogo no sentido literal. Aquilo que cria seu jogo agora é um jogo em si, e o mesmo orquestrador da seção 3, os mesmos fluxos de recursos da seção 4, o mesmo contrato <code>getGameState</code> da seção 6, os mesmos reels da seção 8 e o mesmo ranking da seção 9 se combinam no único lugar pelo qual seus jogadores circulam. A fronteira entre criar e jogar sempre foi artificial. Agora ela desapareceu.</p>
<h2>O que achamos que fizemos certo</h2>
<p>A primeira coisa que fizemos certo foi tratar o prompt como um recurso de interface, não como um produto. O produto é o orquestrador, o conjunto de ferramentas, os fluxos de recursos e a vitrine. O prompt é a maneira mais simples possível de colocar um usuário dentro do sistema.</p>
<p>A segunda foi insistir em um sistema de arquivos plano e nativo do navegador, sem etapa de compilação. É por isso que um jogo da Cinevva pode ser distribuído para dispositivos móveis, computadores, Steam e Discord a partir de uma única fonte da verdade. É por isso que o agente consegue ler o que escreveu.</p>
<p>A terceira foi o contrato segundo o qual todo jogo gerado implementa <code>getGameState</code> e <code>setGameState</code>. Essa única linha no prompt do sistema é o que faz o agente parecer um colaborador em vez de um gerador de código. O agente consegue jogar, e um sistema que consegue jogar consegue depurar.</p>
<p>A quarta foi conectar a vitrine ao mesmo ciclo do mecanismo. Não venceremos apenas pela qualidade bruta do modelo. Não há vantagem defensável em estar dois meses à frente de um gerador com pesos abertos. Vencemos quando o mesmo agente que cria seu jogo também o publica, mede seu desempenho e aprende com a maneira como pessoas desconhecidas o jogaram.</p>
<p>Se quiser ver toda a máquina em movimento, o prompt da página inicial continua sendo a porta de entrada. Digite uma frase. Veja o que acontece nos bastidores. Depois, volte aqui e conte qual parte do mecanismo devemos abordar em seguida.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 16: Estrutura para um mundo que não para de crescer]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring</guid>
            <pubDate>Tue, 28 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O spike 33 dividiu um monólito de 6.285 linhas em módulos e revelou três bugs ocultos de ordem de inicialização. O spike 34 usou essa estrutura para adicionar mais de 100 objetos posicionáveis sem inflar nada.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 16: Estrutura para um mundo que não para de crescer</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegando agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Todos os spikes desde a <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting">parte 13</a> seguiram a mesma receita: copiar o monólito anterior e adicionar um recurso. Ao final do <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water">spike 32</a>, esse monólito tinha 6.285 linhas de <code>index.html</code> dentro de um único <code>&lt;script type=&quot;module&quot;&gt;</code>. As buscas no código geravam muito ruído, descobrir onde adicionar um recurso levava mais tempo do que implementá-lo, e qualquer mudança arquitetural afetava um arquivo grande demais para visualizar mentalmente o diff. Antes de adicionar outro recurso, resolvemos essa dívida estrutural.</p>
<h2>Dividindo o monólito sem alterar o comportamento</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/33-code-structure/" title="Spike 33 — Refatoração da estrutura do código" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/33-code-structure/" target="_blank">Abrir o spike 33 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=33-code-structure">Ver código-fonte</a></p>
<p>A restrição era rigorosa: cada divisão precisava ser uma refatoração pura, não uma reformulação. O monólito se transformou em 19 arquivos <code>.mjs</code> mais um shell hospedeiro de 151 linhas. Módulos de nível superior para cena, água, grama, personagem, física, multijogador e interface; um <code>wgsl.mjs</code> que concentra todas as strings de código-fonte WGSL como fonte única da verdade para a GPU; e uma subárvore <code>terrain/</code> para mapa de altura, SDF, chunks, wrappers de buffers da GPU, pincel, LOD e persistência. O código total ficou em 6.555 linhas, basicamente o monólito acrescido do boilerplate de imports. Nenhuma mudança líquida significativa no volume, mas uma grande melhoria na navegabilidade.</p>
<p>Então a página carregou com uma tela preta. Duas mensagens de erro, com duas causas-raiz sem relação entre si. A primeira era uma reclamação do WebGPU sobre o vínculo de um buffer de zero bytes. No monólito, o buffer do pincel SDF era alocado de forma preguiçosa quando o primeiro chunk de marching cubes aparecia, e a factory de bind groups por acaso era executada mais tarde, depois que o buffer já existia. Separar <code>terrain/gpu.mjs</code> de <code>terrain/brush.mjs</code> reordenou a avaliação dos módulos, fazendo a factory ser executada primeiro e tentar vincular um placeholder <code>null</code>. A correção foi adiar a criação do bind group até o primeiro dispatch, usando um helper <code>getOrCreateBindGroup(chunk)</code>. O padrão de &quot;criar tudo antecipadamente&quot; era um artefato do caminho único de inicialização do monólito.</p>
<p>A segunda mensagem era um aviso assustador sobre o esqueleto FBX, mas acabou sendo uma pista falsa. Ela aparecia desde o spike 25 e era inofensiva. O personagem só estava ausente porque o primeiro bug causava um efeito em cascata: todos os dispatches de computação lançavam erros, o mapa de altura nunca era gravado, as amostras de altura retornavam 0, e o personagem surgia na origem e caía através do mundo. Corrigido o buffer, o personagem passou a ser animado normalmente, aviso e tudo mais.</p>
<p>Essa é a verdadeira lição da refatoração. O monólito ocultava todas as relações do tipo &quot;X precisa existir antes que Y seja construído&quot; dentro da ordem do script, de cima para baixo. A modularização embaralhou essa ordem e revelou mais três bugs latentes de ordenação: a distribuição da grama antes do upload do mapa de altura, a adição do plano de água antes da conclusão da decodificação do mapa de ambiente e o carregamento da persistência concluído depois do primeiro frame. Os três foram corrigidos com uma linha cada, e nenhum deles teria sido detectado sem a divisão.</p>
<h2>Cem objetos sem aumentar nada</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/34-world-authoring/" title="Spike 34 — Criação do mundo" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/34-world-authoring/" target="_blank">Abrir o spike 34 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=34-world-authoring">Ver código-fonte</a></p>
<p>O spike 34 foi o teste para saber se a estrutura valeria a pena. O objetivo era criar uma paleta em primeira pessoa para posicionar árvores, pedras, arbustos, cogumelos e caminhos de um pacote de modelos CC0, com alinhamento ao terreno, persistência, sincronização multijogador e colisores físicos, tudo sem sair dos controles. Cada linha de código nova foi adicionada em cinco arquivos novos dentro de <code>src/props/</code>, e nenhum módulo existente cresceu mais do que dez linhas de integração.</p>
<p>A trajetória dos assets teve um desvio que vale registrar, porque é o tipo de coisa que consome um dia inteiro. Começamos com o pacote Ultimate Nature, da Quaternius, uma biblioteca FBX sem texturas incorporadas. Os materiais FBX vinham como <code>MeshPhong</code> sem mapa, então criamos manualmente uma tabela que associava nomes de materiais a PNGs, convertemos Phong para Standard e definimos os espaços de cor à mão. Cerca de 30% dos materiais não tinham um PNG correspondente, e vários nomes eram ambíguos entre árvores semelhantes. Um segundo pacote FBX tinha a mesma lacuna. A solução não era criar mais tabelas de mapeamento, e sim usar um pacote mais bem preparado: o Stylized Nature MegaKit, da Quaternius, traz 116 glTFs completos com materiais PBR incorporados e normais pré-calculadas. Trocar <code>FBXLoader</code> por <code>GLTFLoader</code> eliminou a conversão de centímetros para metros, a tabela de texturas e a conversão de Phong, além de reduzir <code>library.mjs</code> em cerca de 80 linhas. A conclusão: glTF com PBR é o pipeline certo para pacotes CC0 que oferecem esse formato, enquanto FBX com mapeamento manual de texturas exigiu o dobro de código para entregar metade da qualidade.</p>
<p>O caminho do glTF também trouxe algumas arestas. A paleta renderiza 116 miniaturas, e <code>canvas.toDataURL()</code> do WebGPU retorna uma imagem vazia para uma superfície <code>GPUCanvasContext</code>. Por isso, as miniaturas são renderizadas em um <code>RenderTarget</code>, lidas com <code>readRenderTargetPixelsAsync</code> e copiadas para um canvas 2D, respeitando o alinhamento de 256 bytes por linha do WebGPU. As prévias fantasma clonam cada material para tingi-lo de verde, o que falhava em malhas cujo <code>material</code> era um array; corrigimos isso com uma ramificação <code>Array.isArray</code>. E os mapas de normais de 16 bits, que ocupavam cerca de 200 MB, passaram uma vez por <code>mogrify -depth 8</code> e caíram para aproximadamente 32 MB, sem diferença visual, já que os navegadores reduzem a profundidade durante o upload de qualquer forma.</p>
<h2>Quando a geometria renderizada só existe na GPU</h2>
<p>O bug mais instrutivo fazia a prévia fantasma saltar em incrementos de 1 a 2 metros conforme o cursor se movia. As malhas do terreno armazenam as posições dos vértices em um <code>StorageBufferAttribute</code>, pois o pipeline de computação as grava diretamente na GPU. Por isso, o <code>Raycaster</code> da CPU do three.js não consegue enxergá-las e não retorna nada. O fallback era um ray marching grosseiro, com passos de 1,5 m sobre o mapa de altura analítico, e esse intervalo fixo era a grade que o usuário enxergava. Substituímos isso por um marching adaptativo: passos de 2,5 m quando o raio está muito acima da superfície, reduzidos para 0,4 m quando está a menos de 5 m dela, seguidos por 14 bisseções quando o sinal de $(\text{ray}_y - \text{terrain}_y)$ se inverte. Isso alcança precisão inferior a um milímetro com cerca de 30 passos largos mais 14 bisseções por consulta. Quando a geometria renderizada só existe na GPU, não lute contra o raycaster: faça o marching sobre a fonte analítica.</p>
<h2>Colisores que permanecem corretos após edições</h2>
<p>Escolhemos proxies primitivos em vez de envoltórias convexas ou colisores de malha. Os objetos da Quaternius são arredondados, low-poly e sem concavidades significativas, então as envoltórias exigiriam aproximadamente 50 vezes mais código e teriam 10 vezes mais custo em tempo de execução para oferecer a mesma jogabilidade. Cada objeto é reduzido a uma forma derivada de sua caixa delimitadora: árvores e cactos viram uma cápsula vertical, pedras viram uma esfera, troncos viram uma cápsula horizontal ao longo do eixo maior, e arbustos e flores decorativos não recebem nenhuma forma. Pedras e troncos são caminháveis — aplicam um empurrão apenas vertical para que seja possível ficar em cima deles — enquanto árvores e cactos bloqueiam o jogador — aplicam um empurrão 3D completo para impedir que ele escale o tronco. Um hash espacial de 8 m limita o teste por frame à vizinhança 3×3 do jogador, normalmente de zero a seis objetos.</p>
<p>Duas decisões de design mantiveram o sistema coerente. O alinhamento ao terreno é uma flag no manifesto, não um enum de categoria fixado no código. Assim, a prévia fantasma e o posicionamento confirmado leem o mesmo valor <code>placement.alignToTerrain</code> e não podem divergir. Além disso, os objetos posicionados reagem às edições do terreno por meio de um único helper: depois de uma pincelada — local ou reproduzida a partir de um peer —, <code>refreshPlacementsInRadius</code> reamostra o solo sob cada objeto no disco afetado, reaplica o alinhamento e recalcula as extremidades do colisor. Esculpa uma colina sob uma árvore e ela sobe junto. A persistência e o multijogador reutilizam exatamente o padrão do spike 31, armazenando uma lista plana de <code>{uid, propId, x, y, z, rotY, scale}</code> e espelhando eventos de posicionamento, remoção e ajuste pelo BroadcastChannel.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Decomposição em módulos ES com WebGPU.</strong> Dividir um <code>&lt;script type=&quot;module&quot;&gt;</code> monolítico em imports <code>.mjs</code> com caminhos diretos não exige um bundler quando os módulos são servidos como assets estáticos, e o TSL do three.js funciona bem entre os limites dos módulos. O custo oculto é a ordem de inicialização: um monólito codifica &quot;construa X antes de Y&quot; na ordem do script, de cima para baixo, enquanto os módulos são avaliados na ordem dos imports, o que pode executar uma factory de bind groups da GPU antes que o buffer exista. O padrão de correção é a inicialização preguiçosa (<code>getOrCreate...</code> no primeiro uso) e aguardar a promise correta, em vez de depender da ordem das declarações.</p>
<p><strong>glTF com PBR incorporado versus FBX com mapeamento manual.</strong> O glTF usa metros, referencia suas próprias texturas e fornece <code>MeshStandardMaterial</code> diretamente, portanto um pacote CC0 criado como glTF entra direto em um pipeline PBR. Pacotes FBX sem metadados de associação de texturas exigem uma tabela de nomes de materiais para PNGs mantida manualmente, que fica desatualizada a cada atualização do pacote, além de uma conversão de Phong para Standard e da marcação manual do espaço de cor. Como proteção para folhagens, materiais <code>transparent</code> sem <code>alphaTest</code> são promovidos a cartões recortados com <code>alphaTest: 0.5</code>, para que sejam ordenados corretamente atrás da geometria opaca.</p>
<p><strong>Miniaturas offscreen com WebGPU.</strong> <code>canvas.toDataURL()</code> retorna uma imagem vazia em um canvas baseado em <code>GPUCanvasContext</code>, pois não há um caminho de uma superfície de apresentação de volta para um contexto 2D. Renderizar em um <code>RenderTarget</code>, ler os pixels com <code>readRenderTargetPixelsAsync</code> e copiá-los para um canvas 2D funciona, desde que a cópia avance de acordo com o stride de leitura alinhado a 256 bytes do WebGPU. Os resultados ficam em cache no <code>localStorage</code> sob uma chave cuja versão é incrementada, para que mudanças no pacote invalidem renderizações antigas.</p>
<p><strong>Ray marching adaptativo sobre um mapa de altura analítico.</strong> Quando os vértices do terreno ficam em um <code>StorageBufferAttribute</code> da GPU, o raycaster da CPU não consegue enxergá-los. Percorrer a função analítica de altura com passos grandes longe da superfície, passos pequenos perto dela e um refinamento por busca binária quando o sinal de $(\text{ray}_y - \text{terrain}_y)$ se inverte oferece precisão inferior a um milímetro para o cursor com um número limitado de amostras. A mesma primitiva alimenta o cursor do pincel e a prévia fantasma do objeto.</p>
<p><strong>Colisores de cápsula primitivos com hash espacial.</strong> Cada objeto é reduzido a uma cápsula ou esfera derivada de sua categoria e de sua caixa delimitadora, registrada como <code>{kind, walkable, radius, p1, p2}</code> em cada célula de 8 m do hash que ela sobrepõe. A cada frame, o jogador testa apenas os objetos em sua vizinhança de células 3×3, com uma resolução cápsula contra cápsula para cada um. Proxies caminháveis — pedras e troncos — recebem um empurrão apenas vertical; proxies bloqueadores — árvores — recebem o empurrão 3D completo. Consulte <a href="/pt-BR/guides/landscape-generation-browser#physics-on-sdf-terrain">colisões em terreno SDF</a> para ver a matemática de cápsulas na qual isso se baseia.</p>
<hr>
<p>Parte 16 de 29.
Anterior: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water">Parte 15 — Substitua a base e depois sincronize-a</a>
Próxima: <a href="/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search">Parte 17 — Animações que não precisaram de retargeting e uma busca de assets em tempo real</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Quatro apostas que fiz sobre games em 2022. Como elas envelheceram.]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-24-2022-predictions-aging-well</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-24-2022-predictions-aging-well</guid>
            <pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Em uma entrevista de 2022, apostei em engines de jogos WebGL, GPT-3 para diálogos, colaboração criativa em tempo real e na ascensão dos indies. Quatro anos depois, veja onde cada aposta chegou.]]></description>
            <content:encoded><![CDATA[<h1>Quatro apostas que fiz sobre games em 2022. Como elas envelheceram.</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<img src="/img/blog/tech-done-different.jpg" alt="Entrevista para o podcast Tech Done Different, maio de 2022" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Em maio de 2022, sentei para uma entrevista com Ted Harrington no podcast <em>Tech Done Different</em>. O texto original <a href="https://medium.com/@vio-202020/interview-with-tech-done-different-3b9bf74d99c2">ainda está disponível no Medium</a>. Eu administrava a Cinevva havia cerca de dois anos. Éramos cinco pessoas, tínhamos captado US$ 200 mil, um prêmio Mozilla Builders na parede e muitas opiniões sobre os rumos do desenvolvimento de jogos.</p>
<p>A maioria dessas opiniões não estava em alta em 2022. A Web3 fazia muito barulho, os estúdios AAA dominavam a conversa, o GPT-3 era um brinquedo de pesquisa e WebGL era algo que entusiastas usavam para criar jogos em game jams. Dizer em voz alta que “qualquer pessoa criativa será desenvolvedora de jogos” rendia acenos educados e nenhum cheque.</p>
<p>Na semana passada, voltei a ler aquela entrevista. Algumas coisas que eu disse parecem pensamento positivo. Outras fazem parecer que eu tinha uma máquina do tempo. Estas são as quatro apostas que melhor envelheceram, acompanhadas do que realmente existe em 2026 em cada uma dessas áreas.</p>
<h2>Aposta 1: engines de jogos devem estar no navegador</h2>
<p>O que eu disse em 2022:</p>
<blockquote>
<p>A tecnologia WebGL pode ser usada em várias plataformas, incluindo dispositivos móveis, ao contrário de outras APIs que podem estar restritas apenas a PCs. Assim, esteja você no conforto da sua casa ou em movimento, ainda pode criar e publicar. Jogos WebGL usam apenas uma fração do poder de CPU e GPU que um jogo tradicional para PC exigiria.</p>
</blockquote>
<p>Em 2022, essa era uma aposta que ia contra o consenso. Todas as grandes engines pressupunham downloads, instalações e ciclos de atualizações. Jogos de navegador remetiam à nostalgia do Flash e ao Friv.</p>
<p>Em 2026, nossa <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">engine de mundo aberto roda a 120 FPS em uma aba do navegador</a>. Terreno, árvores, física e sincronização multiplayer, tudo em uma única URL. Sem download, sem loja de aplicativos, sem instalação.</p>
<img src="/img/blog/open-world-browser-120fps.png" alt="O mundo aberto da Cinevva rodando a 120 FPS em uma aba do navegador, com terreno, árvores, física e uma cápsula de jogador renderizados em uma única aba" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O navegador alcançou esse nível porque três coisas chegaram ao mesmo tempo. O WebGPU foi disponibilizado em todos os principais navegadores. SharedArrayBuffer e o isolamento entre origens se tornaram confiáveis. O WebAssembly ficou rápido o bastante para executar engines de física reais compiladas a partir de Rust. Nossa equipe <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">publicou um diário de campo em 12 partes</a> sobre o custo real de cada uma dessas etapas. Em resumo, o navegador agora é um verdadeiro ambiente de execução para jogos, não um lugar onde você coloca a demonstração do jogo de verdade.</p>
<p>O que não previ dentro dessa aposta: subestimei quanto do trabalho estaria relacionado à cultura de engenharia. As engines de jogos para navegador não são mais limitadas pelos navegadores. Elas são limitadas pela suposição de que um jogo de verdade precisa ter um launcher.</p>
<h2>Aposta 2: a IA faz parte do pipeline, não é um recurso secundário</h2>
<p>O que eu disse em 2022:</p>
<blockquote>
<p>Para que nossa plataforma funcione perfeitamente, usamos ferramentas de jogos de código aberto, mas também desenvolvemos nossa própria tecnologia usando Processamento de Linguagem Natural, Visão Computacional e soluções da OpenAI, como GPT3 para diálogos e DALL:E para geração de imagens a partir de texto.</p>
</blockquote>
<p>Em maio de 2022, o GPT-3 era uma API paga que a maioria das pessoas nunca havia usado. O DALL·E mal tinha completado um ano e possuía uma lista de espera. Dizer que iríamos usá-los como ferramentas de produção para desenvolvimento de jogos era, na melhor das hipóteses, um cosplay ambicioso.</p>
<p>Em 2026, todo pipeline criativo sério inclui modelos de linguagem e geração de imagens, inclusive o nosso. Hoje, dentro da Cinevva, temos ElevenLabs Music para trilhas sonoras, ElevenLabs SFX para efeitos, Hunyuan3D e SAM3D para assets, Flux para elementos visuais, Mubert para áudio adaptativo e AceStep para instrumentação. Prompts entram, assets utilizáveis saem, e tudo acontece dentro do editor.</p>
<img src="/img/blog/prompting-3d-games.png" alt="Geração de assets 3D orientada por prompts dentro do editor da Cinevva" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>As provas dessa aposta estão no relatório <em>State of the Game Industry</em> da GDC 2026. <a href="/pt-BR/signals/2026-03-28-the-52-52-split">52% das empresas de jogos afirmam que a IA generativa está sendo usada em produção</a>. Os mesmos 52% dos desenvolvedores afirmam que o uso da IA está causando um impacto negativo no setor. Os dois números são verdadeiros. A questão é que a tecnologia subjacente se tornou um requisito básico mais rápido do que qualquer um queria admitir.</p>
<p>Onde errei: achei que a resistência viria de engenheiros preocupados com a qualidade. A maior parte veio de artistas preocupados com seus empregos. Essa conversa é <a href="/pt-BR/signals/2026-03-16-when-ai-overrides-the-artist">muito mais complicada do que eu esperava</a>, e quem trata isso como uma vitória incontestável não está prestando atenção.</p>
<h2>Aposta 3: o desenvolvimento de jogos precisa de colaboração ao estilo Google Docs</h2>
<p>O que eu disse em 2022:</p>
<blockquote>
<p>O controle de código-fonte e o trabalho com versões de aplicativos costumam estar ligados à colaboração remota. Hoje, para fazer isso, você precisa saber onde ir, qual versão do aplicativo precisa baixar, estar em sincronia com todos — e Deus nos livre se um membro da equipe estiver usando a versão errada do aplicativo, pois isso vira um problema. Buscamos uma experiência quase em tempo real, como a de um documento do Google: simples, sem esforço, mas divertida.</p>
</blockquote>
<p>A colaboração em tempo real em ferramentas de programação era uma ideia de nicho em 2022. O Replit oferecia isso. O VS Code Live Share existia. A maioria das engines profissionais exigia Perforce, uma VPN e um engenheiro sênior tomando conta da sua branch.</p>
<p>Em 2026, a colaboração em tempo real é o padrão para qualquer ferramenta criativa que queira ser levada a sério. O Figma tornou isso normal para design. Cursor e Bolt tornaram isso normal para código. Lovable tornou isso normal para prototipagem de aplicativos. As engines de jogos ficaram para trás, em vez de liderarem.</p>
<img src="/img/blog/open-world-chunk-streaming.png" alt="Transmissão de chunks de um jogo de mundo aberto entre jogadores em tempo real" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Em nossa própria engine, agora temos a sincronização multiplayer funcionando como um <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">experimento de primeira classe</a>, com streaming de chunks, presença e estado compartilhado no navegador. A experiência está mais próxima de “abra o link e entre” do que de “configure o Perforce, peça no Slack a versão correta e instale o launcher”.</p>
<p>O que subestimei: a parte mais difícil da colaboração em tempo real em jogos não é a rede. É resolver conflitos de intenção criativa. Duas pessoas editando a mesma fase ao mesmo tempo é um problema de UX antes de ser um problema de sincronização, e a maioria das ferramentas ainda não resolveu isso.</p>
<h2>Aposta 4: o futuro é indie</h2>
<p>O que eu disse em 2022:</p>
<blockquote>
<p>Pessoas criativas que não conseguiam produzir conteúdo para jogos agora poderão fazer isso. Descobriremos muitas novas formas de como as pessoas percebem e expressam a criatividade. Desta vez, o processo não será controlado apenas por corporações ou grandes estúdios AAA. O futuro é indie.</p>
</blockquote>
<p>Essa foi a frase mais romântica da entrevista e a mais fácil de descartar em 2022. Os orçamentos AAA nunca tinham sido tão altos. Projetos indies nunca haviam enfrentado tanta dificuldade para conseguir visibilidade. Os números pareciam apontar na direção oposta.</p>
<p>Em 2026, os números se inverteram. Criamos <a href="/pt-BR/blog/2026-02-18-a-breaker-belt">A Breaker Belt</a> em três dias, com duas pessoas, para web, dispositivos móveis e PC a partir de uma única base de código. Antes, isso levaria um trimestre para uma equipe pequena. <a href="/pt-BR/signals/2026-03-23-solo-devs-shipping-not-vibing">Desenvolvedores solo estão lançando jogos de verdade, não apenas fazendo “vibe coding”</a>, e o Steam Next Fest 2026 teve um número recorde de inscrições de equipes formadas por uma única pessoa.</p>
<img src="/img/social/industry-solo-dev-shipping.png" alt="Desenvolvedores solo lançando jogos com qualidade de produção usando ferramentas de IA e engines de navegador" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>O custo de descobrir se uma ideia estranha funciona despencou. Essa é a verdadeira mudança, e todo o resto — a IA no pipeline, o navegador como ambiente de execução e a colaboração em tempo real — está a serviço dela. Um desenvolvedor solo com as ferramentas certas agora tem a capacidade de produção que um estúdio de 20 pessoas tinha em 2018.</p>
<p>O que eu não enxerguei com clareza suficiente em 2022: o gargalo passa da produção para a descoberta. Quando todos conseguem lançar, ser encontrado se torna o problema mais difícil. É aí que está nosso próximo conjunto de apostas, e estamos no meio do processo de fazê-las.</p>
<h2>Onde errei de propósito</h2>
<p>Algumas das apostas de 2022 ainda estão pendentes ou precisam ser revistas.</p>
<p>O tema da Web3 ganhou mais destaque naquela entrevista do que deveria. A parte de criptografia e provas de conhecimento zero continua válida como uma infraestrutura básica para direitos sobre conteúdo. O enquadramento em torno de tokens e marketplaces envelheceu mal, e boa parte da energia que dediquei a isso em 2022 teria sido mais bem empregada em ferramentas de IA, onde estava o verdadeiro potencial.</p>
<p>Também subestimei quanto da ideia de que “qualquer pessoa pode criar um jogo” é um problema de descoberta e distribuição, não de ferramentas. Criar agora é fácil. Ser encontrado e receber pelo trabalho ainda são tarefas difíceis, e é nelas que concentramos a maior parte do nosso trabalho atualmente.</p>
<p>Essa é a aposta que estamos fazendo agora. Acabamos de lançar um feed de reels no estilo TikTok em <a href="/pt-BR/play">cinevva.com/play</a>. Clipes de gameplay real com navegação vertical, quinze segundos cada, sem trailers e sem imagens de marketing. Se algo chamar sua atenção, basta um toque para jogar no navegador. Sem download, sem instalação, sem tutorial entre você e o jogo.</p>
<p>O motivo de isso ser importante para desenvolvedores indies é uma matemática brutal. O Steam Next Fest 2026 teve um número recorde de inscrições, o que parece ótimo até você calcular o orçamento de atenção disponível por jogo. A maioria das demos recebeu menos de mil impressões. O funil das páginas da loja já estava quebrado antes de a IA piorar a situação. Uma interface de rolagem em que a própria gameplay é o anúncio reduz o ciclo de descoberta a um único gesto. Ela coloca desenvolvedores solo diante dos jogadores da mesma maneira que o TikTok apresenta músicos desconhecidos aos ouvintes. Essa é a peça que faltava entre “qualquer pessoa pode lançar” e “qualquer pessoa pode ter sucesso”.</p>
<p>Se quiser ler a entrevista original na íntegra, <a href="https://medium.com/@vio-202020/interview-with-tech-done-different-3b9bf74d99c2">ela está aqui</a>. Reler tudo foi útil. A maior parte do que dissemos em voz alta em 2022 acabou se tornando um roteiro viável, apenas em um ritmo mais acelerado do que imaginávamos.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 13: Escultura de terreno e a morte da função matemática]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting</guid>
            <pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Três tentativas fracassadas, uma malha que explodiu e um bug nas emendas que nos obrigou a repensar como os dados do terreno funcionam.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 13: Escultura de terreno e a morte da função matemática</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>Durante doze partes, construímos um motor de terreno que você podia observar. Sobrevoar. Admirar as emendas sem rachaduras. Desta vez, queríamos tocá-lo.</p>
<p>O objetivo parecia simples: permitir que o jogador esculpisse o terreno com um pincel, em tempo real, sem quebrar nenhum dos sistemas que levamos 24 spikes para construir. Foram necessárias três tentativas, dois bugs que pareciam falhas de renderização, mas eram falhas do modelo de dados, e uma reformulação fundamental de como os dados do terreno deveriam funcionar.</p>
<h2>Três tentativas fracassadas</h2>
<p>O Spike 25 deveria ser o mais fácil. A base de código de produção já tinha um raycaster que detecta colisões com as malhas do terreno. A ferramenta de posicionamento o utiliza para inserir objetos. Uma ferramenta de pincel segue o mesmo padrão, apenas modifica os valores do mapa de altura em vez de instanciar um prefab. Simples.</p>
<p>Primeira tentativa: construí tudo diretamente no código TypeScript de produção em <code>world/client/</code>. Novo <code>terrain-brush.ts</code>, alterações em <code>chunk.ts</code>, mudanças de protocolo e atualizações em componentes Vue. Em menos de uma hora, eu tinha um pincel que mais ou menos funcionava, mas os limites dos chunks exibiam descontinuidades visíveis nas normais. Não conseguia saber se o bug estava no código do pincel, na costura existente entre chunks ou em alguma interação com o loop de renderização completo. Essa é exatamente a situação que a metodologia de spikes existe para evitar. Ignorei a regra e paguei por isso imediatamente. Reverti tudo.</p>
<p>Segunda tentativa: um spike independente, mas recorri ao Three.js 0.170.0 e ao WebGL. O código de produção usa WebGL, então parecia natural. Mas todos os Spikes 13 a 24 já tinham migrado para WebGPU. Criar um spike de pincel em WebGL provaria que ele funciona no renderizador legado, não naquele para o qual estamos migrando. Direção errada. Comecei de novo.</p>
<p>Terceira tentativa: WebGPU, <code>WebGPURenderer</code>, shader de computação para geração de vértices, seguindo a mesma stack do Spike 22. Desta vez, a arquitetura estava certa. Cinco operações de pincel funcionando: elevar, rebaixar, suavizar, achatar e adicionar ruído. P95 do ciclo do pincel abaixo de 4 ms no M1.</p>
<p>E o bug nas emendas continuava lá.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/25-heightmap-brush/" title="Spike 25: Pincel de mapa de altura" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/25-heightmap-brush/" target="_blank">Abrir o Spike 25 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=25-heightmap-brush">Ver código-fonte</a></p>
<h2>O bug nas emendas que não queria morrer</h2>
<p>A solução padrão para calcular normais entre chunks é a sobreposição das bordas: cada chunk armazena um anel adicional de dados de seus vizinhos, para que o cálculo da normal no limite possa amostrar os dois lados. Implementei isso. Copiei os dados das bordas vizinhas para um buffer expandido. As emendas continuavam rachando.</p>
<p>Investiguei a matemática. No limite entre o chunk A (cx=-1) e o chunk B (cx=0), ambos precisam calcular a mesma normal no vértice compartilhado. O shader do chunk A amostrava <code>mix(own_col31, own_col32, 0.85)</code>. O chunk B amostrava <code>mix(neighbor_edge, own_col0, 0.85)</code>. Esses são caminhos diferentes de interpolação bilinear por dados diferentes. Mesmo com dados de borda corretos, os dois chunks calculam normais diferentes para o mesmo ponto.</p>
<p>Foi nesse momento que percebi que a cópia da borda não era o verdadeiro bug. O verdadeiro bug era o modelo de dados.</p>
<p>Todos os spikes do 1 ao 24 usavam uma função matemática procedural chamada <code>height_at()</code>. Você fornecia coordenadas do mundo e recebia uma altura. Limpa, global e sem estado. O pincel não podia modificar uma função matemática, então eu havia adicionado um buffer de <code>displacement</code> por cima. O terreno agora era <code>height_at(x,z) + displacement[i]</code>. O shader da GPU incorporava 30 linhas de funções de ruído para o terreno-base, além de código de interpolação bilinear para a camada de deslocamento. O pincel de achatamento precisava subtrair <code>height_at()</code> para descobrir qual valor de deslocamento produziria a altura-alvo. Dois sistemas sobrepostos, calculando coisas diferentes com estratégias de amostragem diferentes.</p>
<p>Nada disso corresponde à forma como um jogo de verdade funciona. Em produção, terrenos autorais são dados amostrados e armazenados em buffers. A função procedural era um substituto conveniente desde os primeiros spikes. Ela havia cumprido seu papel. Agora, estava causando bugs ativamente.</p>
<p>Eu a eliminei.</p>
<p>Agora, cada chunk possui um <code>heightmap</code> Float32Array com valores reais de altura. Na criação, o ruído procedural o preenche. Depois disso, a função de ruído nunca mais é chamada. O pincel modifica diretamente as alturas armazenadas. O shader da GPU lê de um único buffer usando uma única função: <code>hm_at(i,j)</code>. As normais usam diferenças centrais alinhadas à grade sobre os mesmos dados. Sem ambiguidade de interpolação bilinear. Sem incompatibilidade entre dois sistemas. O shader caiu de 90 linhas para 40.</p>
<p>As emendas se corrigiram sozinhas. Agora, os dois chunks em uma borda compartilhada leem os mesmos valores discretos de altura de seus respectivos buffers, com os pontos internos corretos do vizinho na sobreposição da borda. Mesmos dados de entrada, mesmas normais de saída.</p>
<p>Essa não foi uma lição sobre pincéis. Foi uma lição de arquitetura de dados que o pincel revelou.</p>
<h2>A malha que explodiu</h2>
<p>O Spike 26 era o equivalente volumétrico. Modificar com um pincel um volume SDF de 64 ao cubo e, em seguida, gerar novamente sua malha com marching cubes. A mesma pergunta do Spike 25, mas em 3D.</p>
<p>Na primeira vez que executei, a malha explodiu. Espinhos longos disparavam em todas as direções, como um ouriço-do-mar em um dia ruim.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/26-sdf-brush/" title="Spike 26: Pincel SDF" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/26-sdf-brush/" target="_blank">Abrir o Spike 26 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=26-sdf-brush">Ver código-fonte</a></p>
<p>A tabela de casos de MC que eu havia gerado tinha 3.840 entradas em vez de 4.096. A tabela completa tem $256\ \text{casos} \times 16\ \text{posições} = 4096$, mas eu tinha $256 \times 15 = 3840$, com dezesseis linhas ausentes a partir do caso 112. Todas as consultas depois desse índice estavam deslocadas, então o número do caso não correspondia mais aos dados de triangulação. Quando o marching cubes lê a entrada errada, ele cria arestas cujas duas extremidades estão do mesmo lado da superfície. Em uma aresta com cruzamento real, o parâmetro de interpolação</p>
<p>$$
t = \frac{-v_a}{v_b - v_a}
$$</p>
<p>fica em $[0, 1]$ porque $v_a$ e $v_b$ têm sinais opostos. Em uma aresta falsa, eles têm o mesmo sinal, então $v_b - v_a$ fica próximo de zero ou troca de sinal, e $t$ dispara para fora de $[0, 1]$, posicionando o vértice muito além do volume. Multiplique isso por algumas centenas de células erradas e você terá um ouriço.</p>
<p>A correção foi ridiculamente simples: copiar, byte por byte, a tabela comprovada do Spike 12. Lição aprendida. Nunca gere novamente uma tabela de consulta quando já houver uma cópia comprovada.</p>
<p>O segundo bug era mais sutil. O pincel de suavização deveria suavizar as formas do terreno. Em vez disso, criava vincos acentuados. O problema: eu estava puxando cada valor SDF em direção a zero, a isosuperfície. Parece que isso deveria suavizar as coisas, mas na verdade colapsa o campo de distância. Os voxels acima e abaixo da superfície avançam em direção a zero, achatando tudo dentro do raio do pincel. No limite, voxels suavizados encontram voxels não suavizados com um degrau abrupto. O pincel de &quot;suavização&quot; era um gerador de vincos.</p>
<p>A solução foi aplicar uma suavização laplaciana adequada. Em vez de puxar cada valor em direção a zero, puxe-o em direção à média de seus 6 vizinhos diretos:</p>
<p>$$
\phi_i \leftarrow \phi_i + \lambda\left(\frac{1}{6}\sum_{j \in N(i)} \phi_j - \phi_i\right)
$$</p>
<p>O termo entre parênteses é um laplaciano discreto, e $\lambda \in (0, 1]$ é a intensidade da suavização. Isso calcula a média da geometria próxima, suavizando a forma da superfície e preservando o gradiente do campo de distância em vez de colapsá-lo.</p>
<h2>Tudo ao mesmo tempo</h2>
<p>O Spike 27 foi a etapa de validação da integração. Pegar o pipeline completo do Spike 24 — patches de mapa de altura, chunks de MC, emendas Transvoxel e LOD com geomorph — e combiná-lo com o modelo de dados amostrados do Spike 25 e com os dois tipos de pincel.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/27-hm-mc-brush/" title="Spike 27: Integração de HM + MC + pincel" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/27-hm-mc-brush/" target="_blank">Abrir o Spike 27 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=27-hm-mc-brush">Ver código-fonte</a></p>
<p>A primeira coisa que fiz foi remover <code>height_at()</code> de todos os shaders. Agora, os três shaders de computação — preenchimento do SDF, patch do mapa de altura e emenda Transvoxel — vinculam o mesmo buffer de GPU do mapa de altura de 129x129 e usam a mesma função de interpolação bilinear <code>hm_sample()</code> por meio de um preâmbulo WGSL compartilhado. Uma fonte de dados, vários consumidores. As funções de ruído procedural que existiam em todos os shaders desde o Spike 1 desapareceram.</p>
<p>Então começaram os problemas interessantes.</p>
<p>Quando um pincel SDF bloqueia um chunk no modo MC, a emenda Transvoxel entre esse chunk e seu vizinho de mapa de altura precisa amostrar o volume SDF, não o mapa de altura. Ampliei o shader de emenda com bindings adicionais de buffers de armazenamento e flags de MC por chunk. Quatro combinações de limites a tratar: HM-HM, HM-MC, MC-HM e MC-MC.</p>
<p>O LOD foi outro quebra-cabeça. Nos spikes anteriores, mudar um chunk de MC para um LOD mais baixo significava preencher novamente o SDF em uma resolução mais grosseira. Substituí isso por amostragem baseada em stride: os dados SDF permanecem na resolução máxima, com 65 pontos de grade. O shader de MC calcula um stride a partir da proporção entre o tamanho da grade e o número de células. No LOD0, o stride é 1. No LOD1, é 2, amostrando um voxel a cada dois. Os chunks podem mudar livremente de LOD sem alterar seus dados SDF.</p>
<p>A correção mais satisfatória foi a criação dinâmica de chunks verticais. Esculpa para cima além do topo de um chunk, e um novo chunk exclusivo de MC aparece acima dele, com o SDF inicializado a partir da face limítrofe do chunk inferior. Esculpa para baixo, e acontece o mesmo. O mundo cresce para acomodar as edições.</p>
<p>A última armadilha era o pincel de mapa de altura não fazer nada, silenciosamente, nos chunks bloqueados em MC. O pincel HM modifica <code>heightmapCPU</code> e o envia novamente. Os chunks de MC não leem mais o mapa de altura porque seu SDF foi preenchido a partir dele e, depois, divergiu. Adicionei <code>syncHeightmapToSdf()</code>: depois que o mapa de altura é alterado, as colunas SDF são recalculadas para todos os chunks de MC dentro do raio do pincel, e os novos valores são enviados. Agora, os dois tipos de pincel funcionam nos dois tipos de chunk.</p>
<h2>O que realmente aprendemos</h2>
<p>Os spikes de pincel deveriam responder a uma pergunta de desempenho: a escultura pode ser executada dentro do orçamento de frame? Pode. Essa foi a parte fácil.</p>
<p>A parte difícil foi descobrir que 24 spikes usando <code>height_at()</code> como a fonte da verdade do terreno haviam criado uma dependência invisível que quebrou no momento em que tentamos editar qualquer coisa. A função procedural era limpa, global e sem estado — até deixar de representar o terreno.</p>
<p>Regras que anotamos e não esqueceremos:</p>
<ol>
<li>A altura do terreno vem de dados amostrados. Os chunks são proprietários de seus buffers.</li>
<li>A geração procedural preenche os dados iniciais. Ela não é a fonte da verdade em tempo de execução.</li>
<li>O pincel modifica diretamente os dados dos chunks. Nada de camadas de deslocamento.</li>
<li>As normais vêm dos mesmos dados, por meio de diferenças centrais alinhadas à grade.</li>
<li>A sobreposição da borda, com 1 célula do interior do vizinho, resolve as normais entre chunks.</li>
<li>Nunca gere novamente uma tabela de consulta quando já houver uma cópia comprovada.</li>
</ol>
<p>Na <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive">parte 14</a>, paramos de esculpir geometria de depuração e começamos a fazer com que ela pareça e transmita a sensação de um lugar de verdade.</p>
<h2>Tecnologia mencionada neste capítulo</h2>
<p><strong>Arquitetura de mapa de altura amostrado.</strong> O terreno é armazenado como dados pertencentes a cada chunk, em vez de ser avaliado por uma função procedural em tempo de execução. Cada chunk mantém um <code>Float32Array</code> com valores reais de altura. O ruído procedural preenche os dados iniciais durante a criação; depois disso, a função nunca mais é chamada. Isso elimina a incompatibilidade entre o terreno baseado em funções matemáticas e as camadas de edição, simplifica as operações do pincel — que edita diretamente os valores armazenados — e torna o shader da GPU extremamente simples: lê do buffer e calcula as normais por meio de diferenças centrais alinhadas à grade. Para mundos abertos com streaming, o padrão é a propriedade por chunk, com sobreposição de 1 célula das bordas dos vizinhos. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#terrain-data-representation">guia de geração de paisagens</a>.</p>
<p><strong>Operações de pincel SDF.</strong> Modificação de um campo de distância com sinal para esculpir o terreno. Adicionar, ou inflar, usa uma atenuação smooth-step ao redor de uma esfera. Subtrair, ou escavar, usa a mesma forma com sinal invertido. Suavizar usa uma média laplaciana: lê os 6 vizinhos diretos, calcula a média deles e aproxima o valor dessa média. A abordagem ingênua de puxar os valores em direção a zero colapsa o campo de distância e cria arestas acentuadas. A suavização laplaciana preserva o gradiente do campo enquanto suaviza as formas. Consulte <a href="/pt-BR/guides/landscape-generation-browser#signed-distance-fields-sdfs">representação de terreno com SDF</a>.</p>
<p><strong>Transvoxel com fontes de dados mistas.</strong> As células de transição no limite entre um chunk de MC e um chunk de mapa de altura precisam amostrar dados diferentes em cada lado. O shader de emenda usa flags por chunk e bindings de buffers para tratar as quatro combinações: HM-HM, HM-MC, MC-HM e MC-MC. Quando um dos lados está bloqueado em MC, o shader interpola trilineramente o buffer SDF em vez de amostrar o mapa de altura.
<strong>LOD baseado em stride para marching cubes.</strong> Os dados de SDF são armazenados em resolução máxima, independentemente do nível de LOD atual do chunk. O shader de MC calcula um stride de amostragem com base na proporção entre os pontos da grade de SDF e as células de MC. Na resolução máxima, o stride é 1; na metade da resolução, o stride é 2. Isso desacopla os dados de SDF das mudanças de LOD, permitindo que os chunks alternem livremente entre níveis de LOD sem reconstruir o SDF.</p>
<hr>
<p>Parte 13 de 14.
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons">Parte 12 — Anéis, névoa do céu e o que faríamos novamente</a>
Próxima: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive">Parte 14 — O mundo ganha vida</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 14: O mundo ganha vida]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive</guid>
            <pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Materiais que reagem à sua escultura, grama que cresce no teto de cavernas e um planador que faz você esquecer que está em uma aba do navegador.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 14: O mundo ganha vida</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Depois da <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting">parte 13</a>, já tínhamos a escultura. Elevar o terreno, escavar cavernas, suavizar penhascos, tudo em tempo real e com as emendas intactas. Mas o mundo parecia uma demonstração técnica. Sombreamento plano com cores de depuração, sobreposições de wireframe, geometria cinza com cores de LOD para diferenciar os chunks. Dava para editá-lo. Não dava para senti-lo.</p>
<p>Três spikes mudaram isso. Não a arquitetura. Exatamente o mesmo pipeline, os mesmos buffers e a mesma costura de emendas da Parte 13. Apenas adicionamos as camadas que fazem o terreno parecer um lugar: superfícies que respondem à forma, vida crescendo sobre essas superfícies e um corpo que caminha por elas.</p>
<p>A diferença entre &quot;tecnicamente funciona&quot; e &quot;quero ficar aqui&quot; revelou-se surpreendentemente pequena.</p>
<h2>Esculpa um penhasco e veja-o virar rocha</h2>
<p>O Spike 28 fez uma pergunta específica: um material de quatro camadas com mapeamento triplanar consegue rodar em terreno gerado por compute sem estourar o orçamento de frame? A resposta foi sim, mas o interessante foi o que aconteceu depois.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/28-multi-material/" title="Spike 28: Texturização com múltiplos materiais" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/28-multi-material/" target="_blank">Abrir o Spike 28 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=28-multi-material">Ver código-fonte</a></p>
<p>Quatro texturas procedurais (grama, rocha, areia e neve) geradas a partir de ruído FBM na inicialização. Sem arquivos externos, sem pipeline de assets, apenas matemática e uma <code>DataTexture</code>. Os pesos dos materiais vêm da própria superfície: inclinação e altitude. Áreas planas abaixo da linha das árvores recebem grama. Faces íngremes recebem rocha. Terrenos baixos recebem areia. Picos altos recebem neve. Cada peso bruto vem de um <code>smoothstep</code> aplicado à inclinação e à altitude; depois, eles são normalizados por fragmento para que $\sum_i w_i = 1$, e a cor final seja simplesmente a mistura ponderada $\sum_i w_i, c_i$. A normalização é o que impede a superfície de ficar escura ou estourada onde os biomas se sobrepõem. O mapeamento triplanar cuida da projeção UV nos chunks de MC, cujos triângulos não têm coordenadas UV significativas.</p>
<p>O momento que nos convenceu: eleve o terreno com o pincel para criar um penhasco íngreme, e a textura de rocha aparece na nova face no mesmo frame. Achate-o novamente, e a grama recupera a superfície. O material não sabe nada sobre o pincel. Ele apenas lê a posição no mundo e a normal da superfície, os mesmos dados usados para construir a geometria. O ciclo de feedback entre escultura e visual é instantâneo e não depende de scripts específicos.</p>
<p>O Spike 8 testou o custo dos materiais de terreno há um mês e meio, usando uma malha estática no WebGL. O Spike 28 comprova que isso funciona em terreno dinâmico gerado por compute, com todo o pipeline de escultura rodando por baixo. Já havíamos reservado orçamento para isso, mas ainda assim foi um alívio ver tudo se manter a 60 fps.</p>
<h2>80.000 tufos de grama e o teto de uma caverna</h2>
<p>O Spike 29 foi quando os instintos de desenvolvimento de jogos assumiram o controle.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/29-vegetation/" title="Spike 29: Vegetação / Grama por shader" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/29-vegetation/" target="_blank">Abrir o Spike 29 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=29-vegetation">Ver código-fonte</a></p>
<p>Queríamos a grama de Breath of the Wild. Não a contagem de polígonos, mas a sensação. Grama que cobre os lugares certos, move-se com o vento e faz você querer correr por ela.</p>
<p>Cada tufo de grama é composto por três quads que se cruzam em ângulos de 60 graus, com quatro segmentos verticais para permitir a curvatura. É um formato de cruz que parece volumétrico de qualquer ângulo, sem truques de billboard. Um <code>InstancedMesh</code> por chunk, 80.000 tufos espalhados pelo mundo, aproximadamente 2,4 milhões de vértices de grama no total.</p>
<p>O posicionamento é a parte que importa. A CPU percorre uma grade com jitter em cada chunk e avalia a mesma lógica de inclinação e altitude usada pelo shader de materiais na GPU. Onde o sistema de materiais diz &quot;grama&quot;, a grama cresce. Onde rocha ou areia predominam, a densidade cai para zero. A transição é suave porque os pesos de <code>smoothstep</code> subjacentes produzem gradientes contínuos nas bordas dos biomas. Você não percebe uma fronteira porque ela não existe.</p>
<p>Então fizemos algo que eu não tinha certeza de que funcionaria. Grama em superfícies SDF. A função de dispersão percorre cada coluna do volume SDF e encontra cruzamentos por zero entre voxels adjacentes. Onde o SDF passa de negativo para positivo, há uma superfície. A normal da superfície é simplesmente o gradiente normalizado do campo, $\hat{n} = \frac{\nabla\phi}{\lVert\nabla\phi\rVert}$, calculado a partir das diferenças entre voxels, e a inclinação é $\arccos(\hat{n} \cdot \hat{y})$, o ângulo em relação à vertical. Se for suave o suficiente, colocamos grama.</p>
<p>Esculpa uma caverna com o pincel SDF no Spike 27. Volte ao Spike 29 e haverá grama crescendo sobre o teto da caverna. O código de dispersão não sabe o que é uma caverna. Ele apenas enxerga uma superfície com a inclinação certa, na altitude certa. É esse tipo de comportamento emergente que torna tão gratificante criar sistemas de mundo aberto.</p>
<p>O vento é uma onda senoidal no vertex shader TSL, modulada pela coordenada V da lâmina para que as pontas balancem enquanto as raízes permanecem fixas. Pressione V para alternar entre suave, forte e desligado. O vento atua simultaneamente sobre todos os 80 mil tufos, sem nenhum custo de CPU, porque ocorre inteiramente no vertex shader.</p>
<p>O Spike 7 testou 50 mil instâncias de grama, e estávamos preocupados em atingir o limite. O Spike 29 roda 80 mil sobre terreno gerado por compute, costura de emendas, texturização com múltiplos materiais e pincéis. Agrupar tudo em menos chamadas de desenho de <code>InstancedMesh</code> ainda importa mais do que reduzir a quantidade de vértices por lâmina. A lição do Spike 7 se confirmou.</p>
<p>Uma coisa que deixamos para depois: a grama não é atualizada quando você esculpe o terreno sob ela. As matrizes das instâncias são definidas no momento da dispersão. Esculpa uma colina até transformá-la em vale, e a grama ficará flutuando no ar até você pressionar G para dispersá-la novamente. Bom o suficiente para um spike. Em produção, precisaremos refazer a dispersão nos chunks marcados como alterados.</p>
<h2>O primeiro passo</h2>
<p>O Spike 30 é aquele ao qual continuo voltando.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/30-physics/" title="Spike 30: Física do terreno" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/30-physics/" target="_blank">Abrir o Spike 30 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=30-physics">Ver código-fonte</a></p>
<p>Nenhuma biblioteca de física. Uma cápsula personalizada com timestep fixo de 120 Hz. A base de código de produção usa Rapier em um web worker (o Spike 2 comprovou que a latência é aceitável), mas este spike precisava validar as próprias consultas de colisão. Um personagem consegue caminhar por terreno de heightmap, passar para terreno SDF esculpido e não cair através dele?</p>
<p>O núcleo é uma função chamada <code>terrainQuery(x, y, z)</code>. Ela verifica se a posição está dentro de um chunk travado em MC. Se estiver, interpola trilinearmente o espelho do SDF na CPU e retorna o gradiente como normal da superfície. Caso contrário, faz uma consulta no heightmap com uma normal calculada por diferença central. O personagem não sabe sobre qual sistema de terreno está pisando. Ele apenas pergunta onde está o chão e recebe uma resposta.</p>
<p>A colisão com SDF era a parte que eu esperava ser difícil. Sete pontos de sondagem ao redor da cápsula (base, centro, topo e quatro deslocamentos cardinais). Uma sonda na posição $\mathbf{x}$ está penetrando sempre que $\phi(\mathbf{x}) &lt; r$, com profundidade de penetração $p = r - \phi(\mathbf{x})$, e a direção de expulsão é o gradiente do campo $\hat{n} = \frac{\nabla\phi}{\lVert\nabla\phi\rVert}$. Para impedir que a cápsula volte a entrar na superfície, a velocidade é projetada para cancelar seu componente voltado para dentro:</p>
<p>$$
\mathbf{v}' = \mathbf{v} - (\mathbf{v} \cdot \hat{n}),\hat{n}
$$</p>
<p>o que deixa apenas o componente que desliza ao longo da superfície. Não é uma engine de física. São consultas geométricas e uma resposta simples. Ainda assim, isso lida com cavernas, saliências e túneis esculpidos sem nenhum código de caso especial para cada formato. Você entra em uma caverna que escavou dez segundos antes, e a cápsula acompanha exatamente o contorno do teto.</p>
<p>O modelo de movimentação começou prático e acabou ficando divertido. Caminhar, correr, dar pique, pular e deslizar em inclinações acima de 45 graus. Então adicionei um parapente ao estilo BotW, e o spike virou algo que eu não queria fechar.</p>
<p>Pressione Espaço enquanto estiver no ar. A gravidade cai de -30 para -4. A velocidade de queda fica limitada a -3. Uma malha em forma de asa delta se abre a partir da cápsula com interpolação de escala. Incline para a esquerda, e a asa se inclina. A orientação da cápsula se alinha automaticamente ao vetor de velocidade, para que você sempre olhe na direção do movimento. Solte Espaço para cair. Toque o chão e você volta a correr.</p>
<p>A câmera recua para uma visão em terceira pessoa (pressione P para alternar). Ela segue o jogador por trás, com suavização da guinada. Um ray marching do jogador até a câmera testa o terreno em 20 passos. Voe para dentro de uma caverna, e o braço da câmera encurta suavemente em vez de atravessar a rocha. Saia pelo outro lado, e ele volta a se estender.</p>
<p>Este foi o momento que fez o spike valer a pena: esculpa um penhasco alto com o pincel de heightmap. Mude para a câmera em terceira pessoa. Corra até a borda. Pule. Abra o parapente. Incline para a esquerda sobre o terreno que você acabou de esculpir, com a grama cultivada no Spike 29 balançando lá embaixo, a textura de rocha do Spike 28 na face do penhasco e as emendas Transvoxel mantendo-se intactas em todas as bordas de chunks. Pouse do outro lado. Tudo funcionando junto em uma única aba do navegador.</p>
<h2>Esculpindo sob os próprios pés</h2>
<p>Uma coisa sobre a qual eu não tinha certeza: o que acontece quando você esculpe o terreno sobre o qual o personagem está parado? As alterações do heightmap propagam-se instantaneamente por <code>terrainQuery()</code>, pois ela lê diretamente o buffer da CPU. As alterações de SDF propagam-se pelo espelho do SDF na CPU. A cápsula resolve a penetração no próximo tick de física, o que, a 120 Hz, ocorre em até $\frac{1}{120} \approx 8.3$ ms.</p>
<p>Simplesmente funciona. Eleve o chão sob o jogador, e ele sobe junto. Escave o chão, e ele cai. Nenhum tratamento especial. A física roda rápido o suficiente para que alterações de terreno em um único frame nunca produzam grandes penetrações. Esse foi um feliz acaso da escolha de 120 Hz para o timestep. Escolhemos essa frequência para ter movimentos suaves, e ela tornou a edição de terreno segura sem nenhum custo adicional.</p>
<h2>30 spikes depois</h2>
<p>Começamos esta série com um heightmap plano e 500 cubos. Agora temos terreno esculpido com cavernas volumétricas, costura de emendas com Transvoxel, texturização com múltiplos materiais que responde ao formato da superfície, 80.000 tufos de grama balançando ao vento e um personagem que caminha, corre, dá piques, pula e plana sobre tudo isso.</p>
<p>Nada disso está em produção ainda. A base de código em <code>world/client/</code> ainda roda WebGL com chunks simples de heightmap. Tudo que veio desses 30 spikes está em páginas HTML independentes. O trabalho de integração vem a seguir: migrar para <code>WebGPURenderer</code>, conectar a política híbrida de HM/MC ao gerenciador de chunks e integrar os sistemas de pincéis e materiais ao multiplayer.</p>
<p>Mas o sistema de renderização não é mais o risco. As questões em aberto agora são sobre fluxo de dados: persistência das edições, sincronização em rede das pinceladas e escultura colaborativa. O que acontece entre jogadores, não entre triângulos.</p>
<p>Se você acompanha esta série desde a Parte 1, obrigado por continuar conosco durante as partes confusas. Se acabou de encontrá-la, volte à <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first">Parte 1</a>. É nos caminhos errados que estão as lições.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>TSL (Three Shading Language).</strong> O sistema de shaders baseado em nós do Three.js para o renderizador WebGPU. Os materiais são compostos a partir de nós (<code>positionWorld</code>, <code>normalWorld</code>, <code>smoothstep</code>, <code>triplanarTexture</code>) usando composição de funções em JavaScript. O grafo de shaders é compilado para WGSL em tempo de execução. A TSL substitui o <code>ShaderMaterial</code> em GLSL puro para alvos WebGPU e fornece interoperabilidade entre os recursos padrão de materiais do Three.js (luzes, sombras e neblina) e lógica personalizada por fragmento.</p>
<p><strong>Mapeamento triplanar.</strong> Uma técnica de projeção de texturas que amostra uma textura três vezes (nos planos XY, XZ e YZ) e faz a mistura com base na direção da normal da superfície. Isso elimina o estiramento de UV em geometrias de malha arbitrárias, o que é essencial para a saída de marching cubes, na qual os triângulos não têm coordenadas UV significativas. A TSL fornece <code>triplanarTexture()</code> como um nó integrado.</p>
<p><strong>Vegetação instanciada com lâminas de quads cruzados.</strong> Cada tufo de grama é composto por três quads que se cruzam em ângulos de 60 graus, criando uma aparência volumétrica de qualquer direção de visualização. Quatro segmentos verticais por quad permitem uma curvatura suave na animação do vento. Todo o campo é renderizado como um único <code>InstancedMesh</code> por chunk. A capacidade é superalocada em 25% para que novas instâncias possam ocupar os espaços reservados quando o terreno for editado, sem realocar o buffer da GPU. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#vegetation-and-foliage">guia de geração de paisagens sobre vegetação</a>.
<strong>Colisão de cápsula vs. SDF.</strong> Colisão do personagem contra terreno volumétrico sem um motor de física. A cápsula é testada em vários pontos contra o SDF. Quando o valor do campo é menor que o raio da cápsula, o gradiente fornece a normal voltada para fora, e a diferença fornece a profundidade da penetração. Isso lida com cavernas, saliências e túneis sem nenhum código específico para cada forma. Consulte <a href="/pt-BR/guides/landscape-generation-browser#physics-on-sdf-terrain">colisões em terrenos SDF</a>.</p>
<p><strong>Controlador de personagem com passo de tempo fixo.</strong> A física avança a 120 Hz independentemente da taxa de quadros, acumulando o tempo real e consumindo-o em passos de tamanho fixo. Um número máximo de subpassos evita uma espiral da morte em quadros lentos. O ajuste ao solo mantém a cápsula em contato com o terreno ao percorrer inclinações. O passo de tempo fixo garante um comportamento determinístico para futuras repetições multijogador.</p>
<hr>
<p>Parte 14 de 29.
Anterior: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting">Parte 13 — Esculpindo o terreno e a morte da função matemática</a>
Próxima: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water">Parte 15 — Substitua a linha de base e depois sincronize-a</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 15: Substitua a base e depois sincronize]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water</guid>
            <pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Os spikes 31 e 32 levaram à decisão de substituir o mundo legado pelo melhor spike, comprovaram o multiplayer por repetição dos parâmetros do pincel e adicionaram um oceano com natação.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 15: Substitua a base e depois sincronize</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>As primeiras catorze partes abordaram os spikes 1 a 30. Essa sequência terminou com um sistema de terreno que podia ser esculpido em tempo real, além de um personagem capaz de caminhar, planar e cair sobre ele. Esta parte retoma a série no spike 31, e a primeira decisão que tivemos que tomar não foi técnica. Foi o que fazer com todo aquele código dos spikes.</p>
<h2>A decisão de “substituir, não fazer backport”</h2>
<p>Tínhamos 30 arquivos HTML independentes, cada um comprovando um conceito isolado, e nenhuma integração. O <code>world/client/</code> de produção ainda era a stack antiga: WebGL, um mapa de altura simples, um controlador de personagem com 75 linhas e um protocolo MessagePack com nove tipos de mensagem. Sem edição, sem WebGPU, sem materiais, sem vegetação.</p>
<p>O plano óbvio era fazer o backport dos resultados dos spikes para aquela base de código de produção, um por um. Descartamos esse plano. O spike 30 já tinha terreno, física, materiais, vegetação e câmera melhores do que o <code>world/client/</code> jamais teve. Fazer backport para o código WebGL antigo significaria lutar contra ele o tempo todo. Então decidimos substituir a implementação do mundo pelo spike mais bem-sucedido e seguir construindo a partir dele. O spike 30 se tornou a nova base, e o <code>world/client/</code> virou código morto.</p>
<p>Isso reformulou o trabalho restante. Para passar de uma “ótima demonstração técnica para um jogador” a um “produto”, precisávamos de multiplayer, persistência, streaming de mundo infinito e posicionamento de objetos. A sincronização do terreno multiplayer veio primeiro, porque é ela que força as decisões de arquitetura. A pergunta que ela responde é simples de fazer e cara de responder errado: quando o Jogador A esculpe, o que realmente trafega pela rede?</p>
<h2>Repetição dos parâmetros do pincel, não sincronização de pixels</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/31-multiplayer-sync/" title="Spike 31: sincronização de terreno multiplayer" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/31-multiplayer-sync/" target="_blank">Abrir o spike 31 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=31-multiplayer-sync">Ver código-fonte</a></p>
<p>Antes de escrever uma única linha de código de rede, rastreamos exatamente o que uma pincelada faz. O pincel do mapa de altura percorre um raio ao redor do cursor em um <code>Float32Array</code> na CPU, aplica uma atenuação smoothstep e então adiciona, subtrai, suaviza ou achata. O pincel SDF faz a mesma coisa em 3D sobre uma esfera de voxels. Ambos os caminhos são pura matemática de arrays na CPU. Não há computação na GPU nesse ciclo, aleatoriedade nem ponto flutuante não determinístico. O mesmo array de entrada mais os mesmos parâmetros resulta na mesma saída em todas as máquinas.</p>
<p>Esse é o truque todo. Não enviamos o terreno editado. Enviamos os parâmetros do pincel, 56 bytes por tick da pincelada, e cada cliente repete a mesma função determinística. O protocolo de sincronização tem quatro tipos de mensagem: descoberta de pares, a mensagem do pincel <code>{op, wx, wy, wz, radius, strength, flattenTarget}</code> e uma mensagem de posição do jogador a 20 Hz.</p>
<p>No spike, ignoramos completamente o servidor e usamos <code>BroadcastChannel</code>, a API do navegador para troca de mensagens entre abas da mesma origem. Abra duas abas e elas se comunicam, sem nenhuma infraestrutura. Isso isola a questão da sincronização da latência, autenticação e integração com Durable Objects. Se a repetição dos parâmetros converge entre abas, também convergirá por WebSocket.</p>
<p>O único ponto em que a repetição pode divergir são as operações que dependem da ordem. Elevar e rebaixar são comutativas, então <code>val + strength * falloff</code> chega ao mesmo resultado independentemente de quem aplicou primeiro. Suavizar e achatar leem valores vizinhos, portanto dois clientes suavizando exatamente o mesmo ponto no mesmo instante podem divergir em frações de milímetro por tick. Na prática, isso nunca acontece, e a correção para produção já é óbvia: encaminhar as edições pelo DO, deixar que ele atribua um número de sequência monotônico, aplicar de forma otimista no cliente e corrigir a ordem caso a sequência autoritativa seja diferente. É a clássica concorrência otimista, e o DO já é um ponto natural de serialização.</p>
<h2>A cápsula do outro jogador que vivia desaparecendo</h2>
<p>As edições foram sincronizadas na primeira tentativa. A cápsula do jogador remoto, não. Ela aparecia e desaparecia na outra aba, e foram necessários três bugs diferentes para fazê-la permanecer sólida.</p>
<p>A cápsula surgia na origem do mundo, que fica enterrada sob o terreno, porque a mensagem <code>join</code> chega antes de qualquer dado de posição. Correção: iniciá-la oculta e revelá-la na primeira atualização de posição. A transmissão da posição ficava dentro do loop de renderização, e o Chrome limita o <code>requestAnimationFrame</code> em abas sem foco, então a verificação de inatividade da outra aba removia o par e a mensagem seguinte o recriava. Correção: mover a transmissão para um <code>setInterval</code>, que não é limitado em abas visíveis. E o tempo limite de inatividade de 5 segundos era agressivo demais, sendo acionado por qualquer pausa do GC. Correção: aumentá-lo para 30 segundos e contar com a mensagem <code>leave</code> limpa nos fechamentos normais.</p>
<h2>Persistência e entrada tardia, mesmo formato</h2>
<p>Incorporamos a persistência ao mesmo spike em vez de criar outro, porque o formato de serialização é idêntico, seja o destino o IndexedDB ou outra aba. Um snapshot contém o mapa de altura completo (um <code>Float32Array</code> de 129×129, cerca de 66 KB), somente os chunks SDF editados (cada um com $65^3$, cerca de 1,1 MB) e a lista de IDs dos chunks bloqueados no modo marching cubes. Um salvamento com debounce grava no IndexedDB dois segundos após a última edição. Durante o carregamento, o terreno procedural é gerado de forma síncrona e, em seguida, o estado salvo o sobrescreve antes do primeiro frame significativo. A entrada tardia reutiliza exatamente os mesmos bytes: quando uma nova aba entra, uma aba existente com edições serializa seu estado e o envia diretamente à recém-chegada, e a montanha que você esculpiu cinco minutos antes aparece na tela dela.</p>
<p>O primeiro teste de persistência revelou um belo bug de ordenação. A grama é distribuída de forma síncrona durante a inicialização usando as alturas procedurais, mas a restauração do IndexedDB é assíncrona e sobrescreve o mapa de altura depois, deixando cada folha flutuando ou afundada. A correção é uma passagem de <code>refreshAllGrass()</code> que obtém novamente a altura sob cada instância e oculta qualquer folha que agora esteja em uma inclinação ou altitude inadequada. A mesma função atende tanto ao carregamento quanto à entrada tardia.</p>
<h2>A saga das inclinações</h2>
<p>O terreno esculpido é mais irregular do que a base procedural suave, e expôs três bugs de física que o terreno antigo jamais conseguiria revelar. Caminhar diretamente morro acima fazia a cápsula deslizar para o lado. A causa era uma projeção de velocidade feita para manter o movimento tangente ao chão, mas implementada usando apenas os componentes horizontais da normal. Em uma inclinação diagonal com normal $(-0.3, 0.9, -0.3)$, caminhar para o norte introduzia do nada uma velocidade lateral. O ajuste ao chão já mantém o jogador sobre a superfície, então simplesmente removemos a projeção.</p>
<p>O desvio persistiu por causa de uma segunda fonte. As sondas de colisão SDF empurram o corpo para fora ao longo do gradiente, de acordo com a profundidade de penetração. Em qualquer inclinação, o gradiente tem componentes horizontais, então uma penetração de 0,1 m em uma inclinação de 15° empurra cerca de 0,026 m para o lado por passo e, a 120 Hz, isso equivale a aproximadamente 3 m/s de desvio invisível. Correção: dividir a resposta de acordo com a inclinação. Em terreno caminhável ($\text{grad}_y$ acima do limite de caminhada), o empurrão é somente vertical. Em paredes e penhascos, o empurrão 3D completo permanece, porque é exatamente nesses pontos que você quer ser desviado. Também aumentamos o limite caminhável de $\cos 45°$ para $\cos 60°$, para que colinas feitas com o pincel pareçam escaláveis como em Breath of the Wild.</p>
<p>O terceiro bug congelava a cápsula nos limites dos chunks, porque as sondas de colisão amostravam o SDF de um único chunk e recebiam o sentinela de “muito fundo no ar” quando uma sonda atravessava para o vizinho. A correção foi <code>sdfSampleWorld(wx, wy, wz)</code> e <code>sdfGradientWorld(...)</code>, que encontram o chunk correto para qualquer posição no mundo e usam como fallback uma estimativa de distância do mapa de altura onde não existe SDF. Agora, a transição da colisão de SDF para mapa de altura é contínua.</p>
<h2>A água completa o mundo</h2>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/32-water-swimming/" title="Spike 32: água e natação" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/32-water-swimming/" target="_blank">Abrir o spike 32 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=32-water-swimming">Ver código-fonte</a></p>
<p>Até aqui, todos os spikes eram “terra acima da água”. O spike 32 adicionou um oceano e, com ele, uma nova ação de movimento. Definimos o nível da água como 22 em um terreno que varia aproximadamente de 8 a 58, inundando os vales baixos, criando praias na linha costeira e mantendo bastante terra seca para jogar.</p>
<p>A superfície é um <code>MeshStandardNodeMaterial</code> construído em TSL, com a mesma abordagem baseada em nós usada no terreno. Três ondas senoidais sobrepostas em frequências diferentes deslocam os vértices, e a normal da superfície vem das derivadas analíticas de cosseno dessas ondas, em vez das normais da malha. A cor varia de turquesa nas águas rasas a verde-petróleo escuro nas profundezas, usando uma estimativa de profundidade $(\text{level} - y + \text{wave}) \times 0.12$ limitada a $[0,1]$; a espuma surge quando essa estimativa se aproxima de zero na linha costeira; e o alfa acompanha a profundidade, fazendo a água rasa parecer transparente e a água profunda, quase opaca.</p>
<p>A natação usa uma mola de flutuabilidade. O jogador entra no modo de natação quando os pés ficam abaixo do nível da água e o centro do corpo está a até meia altura de cápsula da superfície. Uma mola puxa o corpo em direção a um alvo logo abaixo da superfície e, com uma constante de flutuabilidade de 12 contra um amortecimento da água de 4, o jogador boia de forma estável, com a cabeça para fora e sem oscilar. A velocidade de natação é menor do que a de caminhada, com aceleração flutuante e arrasto; saltar perto da superfície lança o jogador para fora a 60% da velocidade normal de salto; e a entrada na água limita a velocidade descendente a -5 m/s para evitar um mergulho profundo. A colisão com o terreno continua funcionando debaixo d'água, permitindo caminhar pelo leito do lago onde ele se eleva acima do alvo de natação. O indicador de natação acompanha a transmissão de posição para que os outros jogadores vejam você nadar, e uma sobreposição HTML com gradiente colore a visão quando a câmera mergulha abaixo da superfície.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Repetição determinística dos parâmetros do pincel.</strong> Em vez de transmitir o terreno editado, cada cliente envia apenas os parâmetros do pincel e repete a mesma função na CPU. Isso funciona porque tanto os pincéis do mapa de altura quanto os de SDF usam pura matemática de <code>Float32Array</code>, sem aleatoriedade nem não determinismo da GPU, portanto entradas idênticas produzem saídas idênticas bit a bit em todos os lugares. O payload é de 56 bytes por tick da pincelada. Operações comutativas (elevar, rebaixar) convergem independentemente da ordem, enquanto operações que leem os vizinhos (suavizar, achatar) precisam de um ponto de serialização para garantir a convergência, fornecido pelo Durable Object de produção por meio de números de sequência monotônicos.</p>
<p><strong>BroadcastChannel como substituto do WebSocket.</strong> Uma API do navegador para troca de mensagens entre abas da mesma origem, sem servidor. Foi usada aqui para testar o protocolo de sincronização isoladamente da latência da rede e da autenticação. O formato de serialização (mapa de altura <code>Float32Array</code> bruto, mais chunks SDF editados, mais IDs dos chunks bloqueados em MC) usa os mesmos bytes empregados na persistência com IndexedDB e na transferência de estado para entradas tardias, de modo que um formato atende a três funções.</p>
<p><strong>Resposta de colisão SDF dividida por inclinação.</strong> Quando uma sonda de cápsula penetra o terreno volumétrico, a correção ingênua empurra o corpo para fora ao longo do gradiente do SDF, de acordo com a profundidade de penetração. Em inclinações, esse gradiente tem componentes horizontais, introduzindo desvio lateral. Dividir a resposta para que superfícies caminháveis ($\text{grad}_y$ acima do limite) recebam um empurrão apenas vertical, enquanto superfícies íngremes mantêm o empurrão 3D completo mais a projeção da velocidade, elimina o desvio sem perder a colisão com paredes. Consulte <a href="/pt-BR/guides/landscape-generation-browser#physics-on-sdf-terrain">colisões em terrenos SDF</a>.</p>
<p><strong>Água em TSL com normais analíticas das ondas.</strong> O oceano é um material de nós cujos vértices são deslocados pela soma de três ondas senoidais. Em vez de recalcular as normais da malha após o deslocamento, a normal da superfície é derivada analiticamente das derivadas de cosseno das funções de onda, o que é mais barato e evita os artefatos das normais por diferenças finitas em uma grade de baixa resolução. A cor baseada em profundidade, a espuma na linha costeira e a transparência baseada em profundidade usam uma única estimativa de profundidade.</p>
<p><strong>Natação com mola de flutuabilidade.</strong> A física de natação modela o corpo como uma mola amortecida puxada em direção a um alvo logo abaixo da superfície. Com uma constante de flutuabilidade de 12 e amortecimento de 4, o jogador se estabiliza na superfície sem oscilar. Constantes de movimento distintas — velocidade menor, aceleração flutuante e arrasto intenso — fazem a natação ter uma sensação diferente da caminhada, e a colisão existente entre cápsula e terreno continua funcionando debaixo d'água.</p>
<hr>
<p>Parte 15 de 29.
Anterior: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive">Parte 14 — O mundo ganha vida</a>
Próxima: <a href="/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring">Parte 16 — Estrutura para um mundo que não para de crescer</a>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cubcoats × Cinevva: Resumo de uma página dos conceitos de jogos]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-03-19-cubcoats-game-concepts-one-pager</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-03-19-cubcoats-game-concepts-one-pager</guid>
            <pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Três conceitos de jogos educativos (Árvore Dourada, Ilha das Palavras da Kali, Histórias da Ilha) baseados no fluxo de esboço para vídeo e 3D e na gatinha Kali. Para licenciamento ou apresentação a parceiros.]]></description>
            <content:encoded><![CDATA[<h1>Cubcoats × Cinevva: Resumo de uma página dos conceitos de jogos</h1>
<p><em>Baseado no fluxo de esboço para vídeo e 3D e na gatinha Kali (Cubcoats). Para licenciamento, blog ou apresentação a parceiros.</em></p>
<p><strong>Para compartilhar como PDF:</strong> Abra esta página e use <strong>Arquivo → Imprimir → Salvar como PDF</strong> no navegador.</p>
<hr>
<h2>Árvore Dourada — Resumo completo de uma página</h2>
<p><strong>Slogan:</strong> <em>Um mundo que cresce com você. Visite, regue e observe. Sem fases. Sem pressão.</em></p>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Idades</strong></td>
<td>3–13 anos (público amplo)</td>
</tr>
<tr>
<td><strong>Formato</strong></td>
<td>Mundo 3D compartilhado e episódico + &quot;momentos&quot; curtos semanais</td>
</tr>
<tr>
<td><strong>Plataforma</strong></td>
<td>Web (e dispositivos móveis posteriormente); acesso fácil, sem necessidade de instalação</td>
</tr>
<tr>
<td><strong>Gancho viral</strong></td>
<td>&quot;Envie uma semente para um amigo.&quot; Crescimento compartilhado e clipes tranquilos que podem ser compartilhados.</td>
</tr>
</tbody>
</table>
<h3>O que é</h3>
<p>Um mundo tranquilo e colecionável que evolui em <strong>tempo real</strong>. Uma &quot;temporada&quot; compartilhada (por exemplo, Ilha da Primavera): uma única ilha 3D pela qual as crianças podem caminhar (como na demo jogável da Kali). Não há fases nem pontuações tradicionais. O progresso é representado por <strong>sua árvore ganhou um novo galho</strong>, <strong>você viu 12 momentos</strong>, <strong>alguém regou sua semente</strong>.</p>
<p>Em cada visita, a criança pode: <strong>regar uma planta</strong>, <strong>deixar um desejo</strong> (texto ou emoji), <strong>encontrar um personagem escondido de Cubcoats</strong> ou <strong>assistir a um &quot;momento&quot; de 30 segundos</strong> (por exemplo, Kali sob a árvore dourada). Novos momentos e pequenos trechos da história são lançados <strong>semanalmente</strong> (em episódios). Idioma ambiente opcional: &quot;Hoje Kali disse: [uma frase em inglês e chinês].&quot;</p>
<p>O aprendizado e o desenvolvimento são <strong>sutis</strong>: os temas (gentileza, paciência, tentar novamente) estão presentes no texto e na arte, não em lições. A experiência parece um brinquedo ou um lugar compartilhado, não uma escola.</p>
<h3>Ciclo principal</h3>
<ol>
<li><strong>Visite</strong> a ilha (pelo navegador ou aplicativo).</li>
<li><strong>Faça uma ação tranquila</strong> por visita: regue, deixe um desejo, encontre um personagem ou assista a um momento.</li>
<li><strong>Veja o mundo mudar</strong> ao longo dos dias e semanas (novas flores, novas poses de personagens e novos clipes curtos).</li>
<li><strong>Compartilhe</strong> uma semente com um amigo ou &quot;visite a ilha de um amigo&quot; para dar ao crescimento um aspecto social.</li>
</ol>
<h3>Como utiliza o fluxo e os personagens do artigo</h3>
<ul>
<li><strong>Mundo 3D jogável:</strong> O mesmo fluxo da demo da Kali: pose em T → 3D Model Generator → rigging → Cinevva Engine. A ilha e os personagens podem ser explorados em terceira pessoa, sem pressão.</li>
<li><strong>Árvore dourada:</strong> Vem diretamente do artigo (o momento em que Kali passa &quot;do medo à esperança&quot; e a árvore dourada mágica). A árvore é o ponto central emocional e visual da temporada.</li>
<li><strong>&quot;Momentos&quot; curtos:</strong> Produzidos como o vídeo curto vertical do artigo: quadro inicial + quadro final + prompt de movimento → Video Generator (Kling 3.0 Pro), 9:16, ~15 s. Cada lançamento semanal apresenta um pequeno trecho da história.</li>
<li><strong>Propriedade intelectual de Cubcoats:</strong> Kali e os outros sete personagens, o mundo da ilha e o estilo artístico acolhedor e arredondado. Adequado para um relançamento voltado primeiro ao licenciamento (2026), com um produto digital e episódico.</li>
</ul>
<h3>Por que pode viralizar</h3>
<ul>
<li><strong>Acesso fácil:</strong> Um único link, sem uma introdução demorada. &quot;Envie uma semente para um amigo&quot; é uma forma simples de compartilhar.</li>
<li><strong>Clipes tranquilos e bonitos:</strong> Os momentos curtos podem ser compartilhados de forma independente (nas redes sociais, como em &quot;assista a estes 15 segundos&quot;). Têm potencial para memes de maneira positiva.</li>
<li><strong>Episódico:</strong> &quot;O que há de novo nesta semana?&quot; incentiva novas visitas e conversas.</li>
<li><strong>Sutil:</strong> Os pais veem uma experiência &quot;tranquila, criativa, sem anúncios e sem estresse&quot;; as crianças desenvolvem rotina e paciência e recebem surpresas suaves, sem que isso seja ensinado explicitamente.</li>
</ul>
<h3>Possível abordagem para o blog da Cinevva</h3>
<p><em>&quot;Transformamos Kali de um esboço em vídeo e depois em uma personagem com a qual você pode caminhar. Árvore Dourada é o próximo passo: um mundo que cresce em tempo real, onde as crianças visitam, regam e assistem a momentos curtos — sem fases, sem pressão. O mesmo fluxo. Um novo formato.&quot;</em></p>
<hr>
<h2>Ilha das Palavras da Kali — Visão geral</h2>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Slogan</strong></td>
<td>Ajude Kali e seus amigos a desenvolver a ilha. Aprenda inglês e chinês ao longo do caminho.</td>
</tr>
<tr>
<td><strong>Idades</strong></td>
<td>5–11 anos</td>
</tr>
<tr>
<td><strong>Foco</strong></td>
<td>Inglês ↔ chinês para crianças; forte gamificação e progressão.</td>
</tr>
</tbody>
</table>
<p><strong>Ideia central:</strong> A ilha de Cubcoats é dividida em áreas dos personagens (a floresta da Kali, a praia de outro personagem). As crianças ajudam os personagens realizando tarefas curtas que exigem compreender e produzir conteúdo em inglês e chinês. Progressão = &quot;Sementes → Mudas → Árvores → Árvore Dourada&quot; (a próxima área é desbloqueada quando a árvore da área atual está completa). Corrida diária das &quot;lanternas&quot;, cartões de frases colecionáveis, barras de &quot;humor&quot; dos personagens e painel opcional para os pais. Momentos curtos da história (no estilo de quadros inicial e final) são desbloqueados quando uma área é concluída. <strong>Fluxo:</strong> O mesmo fluxo de 3D + vídeo para os personagens e momentos emocionais.</p>
<hr>
<h2>Cubcoats: Histórias da Ilha — Visão geral</h2>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Slogan</strong></td>
<td>Crie seu próprio episódio de Cubcoats. Remixe a história. Compartilhe sua versão.</td>
</tr>
<tr>
<td><strong>Idades</strong></td>
<td>3–13 anos (faixas: 3–6, 7–10, 11–13)</td>
</tr>
<tr>
<td><strong>Foco</strong></td>
<td>Criação e remixagem de histórias; a educação está integrada, mas não é o principal atrativo.</td>
</tr>
</tbody>
</table>
<p><strong>Ideia central:</strong> Narrativas episódicas nas quais as crianças escolhem diferentes caminhos (o que Kali diz? aonde ela vai?). A atividade principal é <strong>remixar</strong>: escolha uma personagem, um lugar e 2–3 momentos → gere um &quot;episódio&quot; curto (roteiro + imagens simples ou, futuramente, um vídeo curto gerado por IA). O aprendizado é sutil (vocabulário e padrões presentes nos diálogos e nas escolhas). Aspecto social: compartilhe seu episódio, jogue a versão de outra pessoa e avalie como &quot;gentil / engraçada / surpreendente&quot;. <strong>Fluxo:</strong> O mesmo visual dos personagens + fluxo de vídeo com dois quadros para os &quot;momentos&quot; gerados pelos usuários; 3D para manter a consistência dos personagens e do mundo.</p>
<hr>
<p><em>Ferramentas da Cinevva mencionadas: Image Generator, Video Generator (Kling 3.0 Pro), 3D Model Generator, auto-rigging, Cinevva Engine. Reflete o fluxo descrito em &quot;Do esboço ao vídeo e ao 3D jogável&quot; (2026-03-04).</em></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[O que realmente é necessário para criar um mundo aberto em um navegador]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-03-14-open-world-browser-medium-article</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-03-14-open-world-browser-medium-article</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Um relato jornalístico da série de engenharia da Cinevva em 12 partes: 24 experimentos exploratórios, desde referências iniciais de terreno pouco atraentes até marching cubes na GPU, LOD com streaming e depuração de emendas com Transvoxel.]]></description>
            <content:encoded><![CDATA[<h1>O que realmente é necessário para criar um mundo aberto em um navegador</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<img src="/img/blog/open-world-browser-120fps.png" alt="O mundo aberto rodando em um navegador a 120 FPS — terreno, árvores, física e uma cápsula de jogador, tudo renderizado em uma única aba" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>A equipe da Cinevva acaba de publicar um dos diários de engenharia mais transparentes da história recente do desenvolvimento de jogos: uma <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">série em 12 partes</a> que documenta nossa tentativa de criar um mundo aberto multijogador que roda inteiramente no navegador. Sem download. Sem loja de aplicativos. Apenas uma URL.</p>
<p>O projeto abrangeu 24 experimentos técnicos que chamamos de &quot;spikes&quot; — protótipos curtos e focados, cada um projetado para responder a uma única questão de alto risco. Cada spike foi publicado com código-fonte funcional que você pode abrir e executar em seu navegador agora mesmo. A série foi escrita por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva, e parece menos uma peça de marketing e mais um diário de campo vindo da linha de frente do que os navegadores realmente conseguem fazer em 2026.</p>
<p>O que torna a série interessante — mesmo que você nunca pretenda criar sistemas de terreno — é o método por trás dela. É um estudo de caso sobre como reduzir os riscos de um projeto ambicioso antes de assumir qualquer compromisso caro.</p>
<h2>Começando pela pergunta mais difícil</h2>
<p>A maioria dos projetos de mundo aberto morre seguindo uma sequência previsível. Primeiro, surge um conceito bonito. Depois, uma cena atraente. Então você descobre que o orçamento de frame já foi totalmente consumido antes mesmo de existir jogabilidade.</p>
<p>Nossa equipe inverteu a ordem. O <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first">primeiro spike</a> era deliberadamente feio: uma malha de terreno de 512 metros, 500 objetos instanciados, ruído procedural de altura, um plano de água e neblina. Sem sombras, sem etapa de embelezamento. A única pergunta era se um navegador conseguiria manter uma taxa de quadros estável enquanto a câmera se movia pelo cenário.</p>
<p>Conseguiu. E esse &quot;sim&quot; estabeleceu algo que Oleg chama de &quot;contrato de referência&quot; — um custo de referência medido para uma cena mínima, em relação ao qual todos os recursos posteriores precisariam se justificar. Se um novo efeito ficasse ótimo, mas estourasse o orçamento de frame, ele não seria lançado. Pelo menos, ainda não.</p>
<p>Esse tipo de disciplina parece óbvio. Na prática, é raro em ambientes de prototipagem acelerada, nos quais todos estão empolgados com o próximo avanço visual.</p>
<h2>A aposta na física</h2>
<p>O <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics">segundo experimento</a> abordou um debate arquitetural que divide os desenvolvedores de jogos para navegador: a física deve rodar na thread principal, onde é mais simples, ou em um Web Worker, onde não pode bloquear a renderização?</p>
<p>No papel, executar a física em um worker é mais organizado. Na prática, o receio é a latência. Cada evento de entrada precisa atravessar duas vezes uma fronteira de mensagens: uma para chegar ao worker e outra para trazer o resultado de volta. Se esse percurso de ida e volta for lento demais, pressionar uma tecla e ver o personagem se mover dará a sensação de falta de resposta.</p>
<p>A equipe integrou o motor de física Rapier (compilado de Rust para WebAssembly) em um worker dedicado, conectou o pipeline de mensagens e fez as medições. A sobrecarga foi insignificante. Os controles continuaram respondendo imediatamente. Mas tivemos o cuidado de observar que havíamos validado um cenário específico, e não uma regra universal. Quando a pressão sobre a GPU e a complexidade do streaming mudassem mais tarde, as premissas precisariam ser verificadas novamente.</p>
<h2>Os spikes sem graça que salvaram o projeto</h2>
<p>A <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes">terceira parte</a> da série não tem capturas de tela. Ela aborda três experimentos que pareciam pouco interessantes, mas tiveram consequências para o produto como um todo.</p>
<p>O primeiro testou se os Durable Objects da Cloudflare conseguiriam lidar com transmissões de posição em tempo real nas taxas de atualização típicas de jogos — a base do modo multijogador. Se isso tivesse falhado, toda a arquitetura de rede precisaria adotar fragmentação antecipada, em vez de atribuir cada ilha a uma única instância.</p>
<p>O segundo validou um perfil de qualidade para dispositivos móveis: não uma predefinição para desktop rebatizada, mas um caminho explícito de renderização de baixo custo a partir da mesma referência de terreno. A questão era se o mundo poderia continuar legível e responsivo sob as limitações das GPUs de dispositivos móveis sem reescrever o renderizador.</p>
<p>O terceiro avaliou se scripts de comportamento gerados por IA para os fluxos de trabalho dos criadores seriam confiáveis o suficiente para uso em produção.</p>
<p>Nenhum deles produziu vídeos de demonstração. Os três estabeleceram limites rígidos que moldaram todas as decisões arquiteturais posteriores. Oleg escreve que esses &quot;spikes pouco chamativos mudaram a arquitetura mais rapidamente do que os spikes visuais&quot;.</p>
<h2>Streaming: onde projetos bonitos desmoronam</h2>
<img src="/img/blog/open-world-chunk-streaming.png" alt="Spike 6: streaming de chunks em ação — cada área colorida é um chunk de terreno carregado e descarregado dinamicamente conforme a câmera se move" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>É possível esconder muita coisa em uma imagem estática. Não é possível esconder uma travada de 40 milissegundos ao atravessar a fronteira de um chunk correndo.</p>
<p>A equipe testou o <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">streaming antes de criar o terreno avançado</a>, separando deliberadamente as questões envolvidas. O Spike 6 validou o carregamento de chunks vizinhos com conteúdo simples. Somente depois desse resultado claro o Spike 11 introduziu o streaming de mapas de altura comprimidos com refinamento progressivo — carregando primeiro o terreno com resolução de 17 amostras, depois 33 e, por fim, a grade completa de 65 amostras.</p>
<p>A ordem importou mais do que esperávamos. Se tivéssemos começado diretamente com chunks de altura comprimidos, cada travada teria uma causa ambígua. Era um problema de decodificação, uma interrupção no upload de texturas ou um problema de atualização da geometria? Testar primeiro o streaming simples eliminou uma categoria inteira de incerteza.</p>
<p>Surgiu uma lição prática: meça diretamente as interrupções de upload, não por meio do FPS médio. As médias escondem picos no tempo de frame, e são esses picos que os jogadores realmente sentem.</p>
<h2>As guerras pelo orçamento visual</h2>
<p>Três experimentos separados atacaram os custos de renderização de forma isolada, em vez de agrupá-los. Densidade da <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty">vegetação</a> e animação pelo vento. Materiais de terreno com múltiplas camadas e mapeamento triplanar para faces de penhascos. Mapas de sombras em cascata sob uma carga realista de terreno.</p>
<p>O spike de vegetação revelou que agrupar instâncias em menos malhas era mais importante do que reduzir a quantidade de polígonos de cada folha de grama. O spike de materiais constatou que a projeção triplanar em superfícies verticais justificava o custo de GPU, mas adicionar uma quinta camada de mistura de texturas não. O spike de sombras determinou que três cascatas com resolução de 1024 produziam sombras de contato aceitáveis sem ultrapassar 2 milissegundos de tempo de GPU.</p>
<p>A equipe adotou uma regra direta: um recurso só avança se conseguir justificar seu custo com dados medidos de tempo de frame. Essa restrição, estabelecida desde cedo, tornou significativamente mais claras as decisões arquiteturais posteriores relacionadas a terreno volumétrico e clipmaps.</p>
<h2>A mudança de rumo que alterou a trajetória do projeto</h2>
<p>Antes do Spike 10, nosso modelo mental era &quot;um mundo maior significa mais geometria&quot;. Depois do <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps">Spike 10</a>, passou a ser &quot;orçamento de geometria constante, com atualizações de anéis centralizados na câmera&quot;.</p>
<p>Clipmaps de geometria — anéis concêntricos de terreno centralizados na câmera, cada um progressivamente menos detalhado — permitiram que a contagem de triângulos permanecesse aproximadamente constante, independentemente da distância de renderização. O truque prático foi aplicar geomorphing nas fronteiras dos anéis: combinar suavemente as alturas dos vértices no shader para que a mudança entre os níveis de resolução se tornasse invisível durante o movimento.</p>
<p>Uma lição sutil surgiu da metodologia de testes. Clipmaps parecem bons em capturas de tela. Seus artefatos só aparecem durante o movimento contínuo da câmera pelas fronteiras dos anéis. A equipe passou um tempo realizando percursos em velocidade constante e procurando ruído temporal. &quot;As capturas de tela mentiram&quot;, escreve Oleg. &quot;O movimento revelou a verdade.&quot;</p>
<h2>Indo para o subsolo</h2>
<p>Mapas de altura não conseguem representar cavernas. Eles armazenam um único valor de elevação para cada ponto de uma grade. No momento em que você precisa de túneis, saliências ou faces rochosas esculpidas, precisa de terreno volumétrico.</p>
<p>O <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Spike 12</a> implementou marching cubes na GPU usando compute shaders do WebGPU, extraindo malhas triangulares de um campo de distância com sinal 3D. Quatro chunks de 64 ao cubo rodaram simultaneamente, com atualizações da malha a cada frame a partir de edições animadas do SDF. O compute shader cuidou de tudo — avaliar o campo, classificar as células e emitir os vértices — sem nenhuma leitura de volta pela CPU.</p>
<p>O desafio não era fazê-lo funcionar. Era fazê-lo funcionar junto com todo o resto. Integração com o grafo de cena do Three.js, gerenciamento do ciclo de vida dos buffers (buffers WebGPU não podem ser redimensionados), tratamento de fences para evitar destruir recursos da GPU ainda em uso — a série dedica <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration">duas</a> partes completas ao que chamamos de &quot;fortalecimento incremental&quot;, o processo pouco glamouroso de adicionar uma capacidade de cada vez e verificar se a camada anterior continua funcionando após cada adição.</p>
<h2>O pesadelo das emendas</h2>
<p>A seção tecnicamente mais angustiante da série abrange as <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut">Partes 9 a 11</a> e trata do que acontece quando chunks de terreno com resoluções diferentes se encontram.</p>
<p>Quando um chunk de alto nível de detalhe fica ao lado de um chunk de baixo nível de detalhe, as malhas geradas de forma independente não se alinham na fronteira. O resultado são rachaduras visíveis, bordas tremeluzentes e junções em T pelas quais a luz vaza. O algoritmo Transvoxel resolve isso com células especiais de transição que conectam as diferenças de resolução — mas implementá-lo corretamente em todas as configurações de chunks, com uma ordem de enrolamento consistente, gerenciamento adequado de buffers e intervalos de desenho precisos, consumiu seis experimentos separados.</p>
<p>A história de depuração mais memorável da equipe: dois dias perseguindo um artefato de emenda que atribuímos à lógica de transição. O verdadeiro culpado eram dados obsoletos. O compute shader da GPU escrevia N vértices em um buffer, mas a chamada de desenho ainda estava configurada para renderizar N+M vértices do frame anterior. Esses vértices extras continham lixo que produzia triângulos finíssimos e tremeluzentes. A correção foi uma única linha: limitar o intervalo de desenho à contagem de vértices ativos do contador atômico.</p>
<p>&quot;Bugs de renderização muitas vezes se disfarçam de bugs de geração de malha&quot;, observa Oleg. &quot;A geometria esteve correta o tempo todo.&quot;</p>
<h2>Do caos à governança</h2>
<p>Depois da batalha das emendas, a equipe substituiu o comportamento improvisado dos chunks por um <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes">sistema explícito de políticas</a>. Uma função central passou a decidir o nível de LOD de cada chunk, o modo de renderização (mapa de altura ou marching cubes) e quais faces precisavam de células de transição. Anéis de distância determinavam o LOD básico. Uma restrição de adjacência garantia que dois chunks vizinhos não diferissem em mais de um nível de resolução. Um bitmap de edições mantinha chunks volumétricos no modo marching cubes independentemente da distância caso contivessem modificações feitas por criadores.</p>
<p>Sobreposições de depuração com código de cores — verde para chunks de mapa de altura, azul para marching cubes e laranja para faces de transição — transformaram &quot;vi um bug em algum lugar perto daquela elevação&quot; em &quot;o bug aparece na posição (142, 12, -67), voltado para noroeste&quot;.</p>
<p>&quot;A política não reduziu a complexidade&quot;, escreve Oleg. &quot;Ela organizou a complexidade.&quot;</p>
<h2>O resultado de tudo isso</h2>
<p>O spike final combinou anéis de clipmap, neblina do céu por fragmento (amostrando a cor real do skybox na direção de cada fragmento do terreno) e a conexão de módulos do Three.js em uma demonstração unificada. O resultado é um sistema de terreno que combina edição volumétrica no campo próximo, chunks de mapas de altura a média distância e anéis de clipmap ao longe, sob uma camada de políticas que governa modo, LOD e transições.</p>
<p>A série termina com <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons">lições</a> que Oleg diz que repetiria em qualquer projeto futuro:</p>
<ul>
<li><strong>Comece com spikes de risco antes do trabalho em recursos.</strong> Elimine as dúvidas do tipo &quot;será que conseguimos mesmo fazer isso?&quot; antes de investir em pipelines de conteúdo.</li>
<li><strong>Congele referências comprovadamente funcionais antes de grandes saltos de integração.</strong> O dia gasto estabelecendo um ponto de controle limpo economiza vários dias investigando regressões mais tarde.</li>
<li><strong>Imponha políticas e observabilidade antes de maratonas de otimização.</strong> Condições nomeadas com regras de acionamento sempre superam bugs misteriosos.</li>
<li><strong>Teste em movimento, não com capturas de tela.</strong> Mudanças bruscas, tremulação e travadas de streaming se escondem em imagens estáticas.</li>
<li><strong>Meça o tempo de frame de cada recurso, não o FPS médio.</strong> As médias escondem os picos que os usuários realmente sentem.</li>
<li><strong>Publique as partes complicadas.</strong> Os caminhos errados, as caçadas a fantasmas, os dois dias culpando o sistema errado. Essas são as partes com as quais as pessoas realmente podem aprender.</li>
</ul>
<h2>Por que isso importa para além da Cinevva</h2>
<p>A série é significativa por três motivos que vão além do pipeline de terreno de uma única empresa.</p>
<p>Primeiro, ela demonstra que compute shaders do WebGPU, física em WebAssembly e Durable Objects implantados na borda ultrapassaram um limiar. Um mundo aberto multijogador com terreno volumétrico, edição em tempo real e LOD com streaming é arquiteturalmente viável em uma aba do navegador em 2026. Isso não era verdade dois anos atrás.</p>
<p>Segundo, a metodologia de spikes — experimentos pequenos e focados, cada um respondendo a uma única questão de alto risco com resultados funcionais e mensuráveis — oferece um modelo para qualquer equipe que esteja tentando algo que talvez não funcione. A disciplina de medir antes de assumir compromissos, de estabelecer referências antes de integrar e de identificar casos extremos antes de otimizar se aplica a muito mais do que sistemas de terreno.</p>
<p>Terceiro, a transparência radical é o ponto central. Publicar o código-fonte de todos os 24 experimentos, incluindo os becos sem saída e os desvios de dois dias para depuração, transforma isso em algo maior do que um blog técnico. É um caderno público de engenharia que trata o leitor como colega, e não como cliente.
A série completa está disponível em nosso <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>, com todos os protótipos exploratórios rodando ao vivo no navegador.</p>
<hr>
<p><em>Este artigo foi publicado originalmente no <a href="https://vio-202020.medium.com/de-risking-an-ambitious-project-before-committing-to-anything-big-like-an-online-open-world-c94a7c05ee06">Medium</a>.</em></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[2026-03-04-sketch-to-animated-3d-character-factcheck-note]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-03-04-sketch-to-animated-3d-character-factcheck-note</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-03-04-sketch-to-animated-3d-character-factcheck-note</guid>
            <pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[]]></description>
            <content:encoded><![CDATA[<h1>Nota de verificação de fatos: &quot;Do esboço ao vídeo e ao 3D jogável&quot;</h1>
<p><strong>Para:</strong> Oleg Sidorkin<br>
<strong>Assunto:</strong> <a href="https://app.cinevva.com/blog/2026-03-04-sketch-to-animated-3d-character">app.cinevva.com/blog/2026-03-04-sketch-to-animated-3d-character</a><br>
<strong>Verificado com base em:</strong> repositório cinevva-site (site de marketing + worker)</p>
<hr>
<h2>Verificado ✓</h2>
<ul>
<li><strong>Nomes das ferramentas</strong> (Image Generator, Video Generator, 3D Model Generator) e <strong>link</strong> <code>/tools/hunyuan3d</code> correspondem ao código-fonte.</li>
<li><strong>Autor</strong> (Oleg Sidorkin, CTO) está correto.</li>
<li><strong>Fluxo de trabalho de vídeo:</strong> interpolação entre quadro inicial e final, formato 9:16, duração de 3 a 15 s e modo storyboard são todos compatíveis.</li>
<li><strong>Pipeline 3D:</strong> Hunyuan3D e &quot;3D Model Generator&quot; estão corretos; rigging automático (Tripo) e &quot;enviar GLB → modelo com rig + biblioteca de animações&quot; correspondem à implementação.</li>
<li><strong>Cinevva Engine</strong> e &quot;Reflete o estado no início de 2026&quot; estão corretos.</li>
</ul>
<hr>
<h2>Correção necessária</h2>
<p><strong>&quot;Seedance 1.5 Pro&quot;</strong> — Neste repositório, o vídeo é implementado com o <strong>Kling 3.0 Pro</strong> (<a href="http://fal.ai">fal.ai</a>), e não com o Seedance 1.5 Pro:</p>
<ul>
<li><code>worker/src/genai/fal-video.ts</code> usa <code>fal-ai/kling-video/v3/pro/...</code></li>
<li><code>worker/src/index.ts</code> registra o custo como <code>model: 'kling-3.0-pro'</code></li>
<li><code>tools/video.md</code> diz: &quot;Gere vídeos curtos com IA a partir de texto ou imagens usando o <strong>Kling 3.0 Pro</strong>&quot;</li>
</ul>
<p>O nome da API/do produto é &quot;seedance&quot; (por exemplo, <code>/genai/seedance/generate</code>), mas o modelo subjacente é o Kling 3.0 Pro. O Seedance 1.5 Pro é um modelo diferente (ByteDance). Fontes públicas costumam limitar o Seedance 1.5 Pro a cerca de 10 s; aqui, o Kling oferece suporte a 15 s.</p>
<p><strong>Recomendação:</strong> No artigo, mencione o modelo de vídeo como <strong>&quot;Kling 3.0 Pro&quot;</strong> (ou, por exemplo, &quot;Video Generator (Kling 3.0 Pro)&quot;) para que corresponda ao site de marketing e à documentação. Se o backend do aplicativo realmente usa o Seedance 1.5 Pro, alinhe o texto do aplicativo e o <code>tools/video.md</code> para que descrevam o mesmo modelo.</p>
<hr>
<h2>Não verificável no repositório</h2>
<p>Cubcoats, Kali the Kitty, Mimi Chao e as alegações comerciais relacionadas são externos e não foram verificados.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Do esboço ao vídeo e ao 3D jogável: personagem, cenas da história e rig]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-03-04-sketch-to-animated-3d-character</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-03-04-sketch-to-animated-3d-character</guid>
            <pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Como usamos o Gerador de Imagens, o Gerador de Vídeos e o Gerador de Modelos 3D da Cinevva para transformar um esboço simples de personagem em um curta vertical gerado por IA e um modelo 3D com rig que você pode controlar em um navegador.]]></description>
            <content:encoded><![CDATA[<h1>Do esboço ao vídeo e ao 3D jogável</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO da Cinevva</em></p>
<p>Levar uma personagem de um esboço plano a um jogo 3D jogável costumava exigir um artista conceitual, um modelador 3D, um especialista em rigging, um animador e alguém para integrar tudo em uma engine. Queríamos descobrir quanto desse pipeline as <a href="/pt-BR/tools/">ferramentas criativas da Cinevva</a> conseguiriam fazer por conta própria. Veja o que descobrimos.</p>
<p>A personagem é <strong>Kali the Kitty</strong>, da <a href="https://thecubcoats.com">Cubcoats</a>, uma marca infantil que vendeu mais de um milhão de moletons de pelúcia com capuz por meio da Nordstrom, Amazon e Disney Store. A Cubcoats tem oito personagens originais com personalidades definidas, um mundo fictício em uma ilha e 14 patentes. A marca será relançada em 2026 como uma plataforma focada em licenciamento, e queríamos mostrar como essa propriedade intelectual fica quando vai além dos produtos físicos: um curta em vídeo vertical e uma personagem 3D com rig que você pode controlar em um navegador. Tudo abaixo é resultado real dessa exploração.</p>
<p>Arraste os visualizadores 3D para girar ao redor dos modelos. Role a tira de filme para ver como o curta vertical avança do primeiro ao último quadro.</p>
<h2>Da ilustração plana de um livro a uma personagem que você pode girar</h2>
<p>A Cubcoats já tinha lindas ilustrações 2D. A direção de arte de Mimi Chao deu a cada personagem uma aparência calorosa, arredondada e desenhada à mão, que funciona perfeitamente em moletons e livros infantis. Mas ilustrações planas não alimentam um pipeline 3D. Precisávamos de uma personagem em alta fidelidade que pudesse servir como referência para geração de vídeo, criação de malha e rigging, sempre mantendo o mesmo rosto.</p>
<p>Começamos enviando as ilustrações originais do livro ao nosso <a href="/pt-BR/tools/flux">Gerador de Imagens</a> e pedindo que ele produzisse uma versão renderizada em 3D da mesma personagem.</p>
<figure class="pipeline-media pipeline-media--single">
<img src="/blog/sketch-to-3d-pipeline/kali-flat-book-art.png" alt="Ilustrações planas originais de Kali the Kitty para os livros da Cubcoats" loading="lazy">
<figcaption><strong>O ponto de partida.</strong> As ilustrações 2D originais de Kali para os livros. Calorosas, expressivas e completamente planas.</figcaption>
</figure>
<p>As proporções foram mantidas, a personalidade ficou evidente e o visual suave de desenho animado em 3D deu a todas as ferramentas das etapas seguintes uma referência consistente. A partir daí, refinamos a pose e o enquadramento exatos até termos uma referência principal definitiva.</p>
<div class="pipeline-media pipeline-media--two">
<figure>
<img src="/blog/sketch-to-3d-pipeline/kali-3d-chatgpt.png" alt="Referência definitiva de Kali como personagem em estilo 3D" loading="lazy">
<figcaption><strong>Referência principal definitiva.</strong> Visual de desenho animado 3D de corpo inteiro usado no vídeo e como referência visual para a malha.</figcaption>
</figure>
<figure>
<img src="/blog/sketch-to-3d-pipeline/kali-3d-tpose.png" alt="Referência em pose T para geração 3D e rigging" loading="lazy">
<figcaption><strong>Folha de pose T.</strong> A mesma personagem, com os braços abertos. Ela alimentou tanto o [Gerador de Modelos 3D](/pt-BR/tools/hunyuan3d) quanto o [sistema automático de rigging](/pt-BR/tools/rigger).</figcaption>
</figure>
</div>
<p>A pose T é importante porque o rigging exige que os braços fiquem afastados do tronco. Ignore essa etapa e o sistema automático de rigging fundirá os braços ao corpo e desistirá do processo. Geramos a pose T a partir do mesmo visual definitivo da personagem para que a silhueta e as proporções permanecessem consistentes em todas as etapas.</p>
<h2>Escreva a história antes de gerar qualquer coisa</h2>
<p>Antes de usar as ferramentas de vídeo, escrevemos uma sequência narrativa em linguagem simples. O traço de personalidade de Kali no universo da Cubcoats é “Positiva”. É ela quem faz todos se sentirem incluídos e mantém o otimismo quando as coisas ficam difíceis. Por isso, criamos um arco de dez segundos em torno dessa ideia:</p>
<blockquote>
<p>Kali entra em uma floresta escura e enevoada à noite, segurando uma pequena lanterna brilhante. Ela se assusta, senta-se sozinha e quase desiste. Então descobre uma árvore dourada mágica que se ilumina atrás dela. Encantamento, alegria e partículas douradas caindo como chuva. Do medo à esperança em um só instante.</p>
</blockquote>
<p>Esse é o enredo inteiro. Ele não precisa ser complexo. Precisa encaixar uma virada emocional em uma única cena contínua, para que o quadro inicial e o quadro final realmente possam se conectar.</p>
<h2>Dois quadros que delimitam toda a história</h2>
<p>Geramos duas imagens-chave a partir desse enredo usando nosso <a href="/pt-BR/tools/flux">Gerador de Imagens</a>. Usamos a orientação vertical para corresponder ao formato do curta. Os prompts descrevem exatamente a mesma personagem em dois momentos diferentes.</p>
<p><strong>Prompt do quadro inicial:</strong></p>
<blockquote>
<p>Renderização animada em 3D no estilo Pixar de Kali, uma pequena gatinha rosa com grandes olhos escuros e redondos, orelhas pontudas cor-de-rosa, barriga rosa-clara e um rosto alegre e arredondado. Ela está parada na entrada de uma floresta escura e enevoada à noite, segurando uma pequena lanterna brilhante com as duas patas. Suas orelhas estão levemente abaixadas e sua expressão é nervosa, mas determinada. Iluminação cinematográfica com a luz azul e fria da lua vindo de cima e o brilho laranja e quente da lanterna. Névoa densa entre troncos de árvores escuras ao fundo. Ângulo da câmera: plano médio, levemente baixo, olhando para cima em direção a ela. Sem texto, sem marca-d'água.</p>
</blockquote>
<p><strong>Prompt do quadro final:</strong></p>
<blockquote>
<p>Renderização animada em 3D no estilo Pixar de Kali, uma pequena gatinha rosa com grandes olhos escuros e redondos, orelhas pontudas cor-de-rosa e barriga rosa-clara. Ela está parada diante de uma enorme árvore mágica coberta por flores douradas brilhantes, com os braços bem abertos e um enorme sorriso radiante de alegria. Pétalas douradas flutuam no ar ao redor dela. A árvore irradia uma luz dourada e quente que ilumina toda a clareira da floresta. O céu noturno estrelado está visível acima. Ângulo da câmera: plano aberto visto levemente de baixo, com uma composição de revelação épica. Sem texto, sem marca-d'água.</p>
</blockquote>
<div class="pipeline-media pipeline-media--two">
<figure>
<img src="/blog/sketch-to-3d-pipeline/kali-start-frame.png" alt="Quadro inicial do vídeo vertical gerado por IA" loading="lazy">
<figcaption><strong>Quadro inicial.</strong> Kali nervosa na entrada da floresta, segurando sua lanterna.</figcaption>
</figure>
<figure>
<img src="/blog/sketch-to-3d-pipeline/kali-end-frame.png" alt="Quadro final do vídeo vertical gerado por IA" loading="lazy">
<figcaption><strong>Quadro final.</strong> Kali alegre diante da árvore dourada.</figcaption>
</figure>
</div>
<p>O Seedance interpola entre esses dois extremos. A descrição da personagem permanece idêntica nos dois prompts para que o modelo saiba que se trata da mesma pessoa. Apenas a cena, a emoção e a câmera mudam.</p>
<h2>O vídeo ficou melhor do que esperávamos</h2>
<p>Carregamos os dois quadros no <a href="/pt-BR/tools/video">Gerador de Vídeos</a> da Cinevva, escolhemos o Seedance 1.5 Pro, definimos a proporção 9:16 e a duração de 15 segundos, inserimos o enredo no prompt de movimento e clicamos em Gerar.</p>
<figure class="pipeline-media pipeline-media--single">
<img src="/blog/sketch-to-3d-pipeline/video-generator-setup.png" alt="Gerador de Vídeos da Cinevva com os quadros inicial e final carregados" loading="lazy">
<figcaption><strong>A configuração usada.</strong> Quadro inicial, quadro final, prompt de movimento, formato vertical 9:16, 15 segundos e áudio ativado.</figcaption>
</figure>
<p>O clipe interpola entre os dois extremos. O áudio nativo foi gerado na mesma execução. Estes são alguns quadros capturados ao longo da linha do tempo.</p>
<div class="pipeline-filmstrip" aria-label="Quadros do vídeo vertical gerado">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_01.jpg" alt="Quadro 1 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_02.jpg" alt="Quadro 2 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_03.jpg" alt="Quadro 3 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_04.jpg" alt="Quadro 4 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_05.jpg" alt="Quadro 5 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_06.jpg" alt="Quadro 6 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_07.jpg" alt="Quadro 7 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_08.jpg" alt="Quadro 8 do vídeo" loading="lazy">
<img src="/blog/sketch-to-3d-pipeline/seedance-frames/frame_09.jpg" alt="Quadro 9 do vídeo" loading="lazy">
</div>
<p class="pipeline-caption">Da esquerda para a direita: progressão do curta vertical gerado.</p>
<p>Usar os quadros inicial e final foi a opção certa quando precisávamos chegar a uma pose de encerramento exata. Para arcos emocionais mais longos, uma única referência acompanhada de um prompt funcionou bem com o modo storyboard Kling 3.0 Pro do <a href="/pt-BR/tools/video">Gerador de Vídeos</a>. Configurações diferentes para trabalhos diferentes.</p>
<h2>A mesma personagem, agora em 3D</h2>
<p>É aqui que as coisas ficam interessantes. A imagem em pose T, pertencente à mesma família visual das referências do vídeo, foi enviada ao nosso <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a> para a geração de um modelo 3D a partir da imagem. O objetivo não é fazer a malha do jogo corresponder ao vídeo pixel por pixel. O objetivo é corresponder à memória do jogador. A personagem no jogo deve transmitir a mesma personalidade que ela transmite no vídeo. E, como definimos uma única identidade visual desde o início, foi exatamente isso que aconteceu.</p>
<figure class="pipeline-media pipeline-media--single">
<img src="/blog/sketch-to-3d-pipeline/kali-preview.png" alt="Prévia renderizada da malha do Hunyuan3D" loading="lazy">
<figcaption>Renderização de prévia da malha gerada. Texturas e proporções antes do rigging.</figcaption>
</figure>
<p>O <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a> produz um arquivo GLB texturizado. Ele ainda não está pronto para um jogo, mas é claramente a Kali. A silhueta ficou correta. Os materiais ficaram próximos do esperado. Bom o bastante para seguir em frente.</p>
<h2>Arraste estes modelos</h2>
<p>Resultado bruto do <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a>. Sem rig, apenas uma malha texturizada. Ao lado, a mesma personagem após o rigging automático, com uma animação de caminhada incorporada.</p>
<ClientOnly>
  <SketchPipelineModels />
</ClientOnly>
<h2>Rigging e animação</h2>
<p>O <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a> fornece uma malha. Para que essa malha se mova, ela precisa de um esqueleto e de animações. Nossa plataforma cuida do rigging automaticamente: envie o GLB e receba um modelo com rig e uma biblioteca completa de animações. Ciclos de caminhada, respiração em repouso, saltos e tudo mais de que uma personagem de jogo precisa.</p>
<p>A animação de caminhada padrão parecia quase certa, mas não totalmente. Os braços de Kali ficavam muito próximos do corpo, e sua cabeça se inclinava em um ângulo ligeiramente errado. Ambas as características faziam sentido para um humanoide genérico, mas não combinavam com uma gatinha arredondada de desenho animado. Por isso, adicionamos correções de ossos em tempo de execução no código do jogo: um deslocamento nos ombros para empurrar os braços para fora e uma correção na rotação da cabeça para eliminar a inclinação. Pequenos ajustes, grande diferença. Sem eles, ela parecia rígida e robótica. Com eles, ela voltou a parecer a Kali.</p>
<h2>Controlando a personagem em um navegador</h2>
<p>Colocamos o modelo com rig na <a href="/pt-BR/engine">Cinevva Engine</a>. Câmera em terceira pessoa, movimentação com WASD, itens colecionáveis espalhados pelo cenário e câmera orbital quando a personagem está parada. Ajustar a transição entre as animações exigiu algumas iterações: crossfade entre repouso e caminhada, normalização da altura, correção do eixo frontal e ajustes nos deslocamentos dos ossos. A câmera em terceira pessoa atrás da personagem completa a jornada do esboço até algo que você pode controlar.</p>
<p>Experimente. Use WASD para se mover.</p>
<div class="pipeline-game-embed">
<iframe src="https://api.cinevva.com/games/game-mmbijtzp-d63d/" allow="fullscreen" loading="lazy"></iframe>
</div>
<h2>O que eu diria a quem fosse fazer isso amanhã</h2>
<p>Defina um único visual para a personagem antes de usar qualquer ferramenta de geração. Cada minuto dedicado a acertar essa referência economiza uma hora tentando recuperar a consistência depois.</p>
<p>Primeiro, escreva um enredo curto. Depois, gere os quadros inicial e final com o <a href="/pt-BR/tools/flux">Gerador de Imagens</a>. Mantenha as imagens de referência dentro dos limites de tamanho da API. Use uma pose T criada a partir do mesmo sistema visual ao alimentar o <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a>.</p>
<p>Envie os dois quadros ao <a href="/pt-BR/tools/video">Gerador de Vídeos</a> e deixe-o fazer a interpolação. Para a malha do jogo, aplique o rig ao modelo pela plataforma e ajuste os deslocamentos dos ossos em tempo de execução caso as animações padrão não se encaixem perfeitamente nas proporções da sua personagem.</p>
<p>Há dois anos, esse pipeline não existia. Você precisaria de uma equipe e de um orçamento. Agora, precisa de um esboço e das <a href="/pt-BR/tools/">ferramentas criativas</a> da Cinevva. Acredito que essa seja uma mudança significativa em quem pode dar vida a uma personagem.</p>
<hr>
<p><em>Ferramentas da Cinevva utilizadas: <a href="/pt-BR/tools/flux">Gerador de Imagens</a>, <a href="/pt-BR/tools/video">Gerador de Vídeos</a>, <a href="/pt-BR/tools/hunyuan3d">Gerador de Modelos 3D</a>, rigging automático e <a href="/pt-BR/engine">Cinevva Engine</a>. Conteúdo referente ao início de 2026.</em></p>
<style>
.pipeline-media { margin: 1.25rem 0; }
.pipeline-media figure { margin: 0; }
.pipeline-media img {
  width: 100%;
  height: auto;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.08);
  display: block;
}
.pipeline-media figcaption {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  color: var(--vp-c-text-2);
  line-height: 1.45;
}
.pipeline-media--two {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
}
@media (min-width: 768px) {
  .pipeline-media--two {
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
  }
}
.pipeline-media--single { margin: 1.25rem 0; }
.pipeline-filmstrip {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 12px 4px;
  margin: 1rem 0;
  border-radius: 10px;
  background: var(--vp-c-bg-soft);
  border: 1px solid rgba(255,255,255,0.06);
  -webkit-overflow-scrolling: touch;
}
.pipeline-filmstrip img {
  flex: 0 0 auto;
  height: 180px;
  width: auto;
  border-radius: 6px;
  object-fit: cover;
}
@media (min-width: 900px) {
  .pipeline-filmstrip img { height: 220px; }
}
.pipeline-caption {
  font-size: 0.85rem;
  color: var(--vp-c-text-2);
  margin: 0.35rem 0 1rem;
}
.pipeline-game-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16/9;
  margin: 1.25rem 0;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.08);
}
.pipeline-game-embed iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
}
</style>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 1: Começamos tentando quebrá-lo]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Antes de construir cavernas ou shaders sofisticados, precisávamos responder a uma pergunta incômoda: isso sequer consegue rodar em um navegador?]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 1: Começamos tentando quebrá-lo</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegando agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>Estamos construindo um mundo aberto multiplayer que roda inteiramente no navegador. Sem instalação, sem loja de aplicativos, apenas uma URL. O maior risco inicial era óbvio: será que um navegador consegue renderizar um mundo 3D persistente com taxas de quadros jogáveis e ainda deixar margem para jogabilidade, física e rede?</p>
<p>A maioria dos projetos de mundo aberto fracassa em uma ordem previsível. Primeiro surge um bom conceito. Depois, uma cena bonita. Então você percebe que seu orçamento de frame já acabou antes mesmo de existir jogabilidade.</p>
<p>Queríamos responder à questão do orçamento de renderização antes de investir em qualquer outra coisa. Por isso, o Spike 1 pulou o trailer bonito e foi direto para as medições.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/spikes/01-terrain/" title="Spike 1: Terreno e instanciamento" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/01-terrain/" target="_blank">Abrir o Spike 1 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=01-terrain">Ver código-fonte</a></p>
<p>A configuração era simples de propósito. Uma malha de terreno de 512 metros, altura procedural gerada por ruído senoidal em camadas com atenuação nas bordas da ilha, um plano de água, névoa atmosférica e 500 objetos instanciados. Usamos WebGL puro com Three.js, mapeamento de tons ACES e nenhuma sombra.</p>
<p>Não nos importávamos com a aparência. Queríamos saber se a cena permanecia estável enquanto a câmera se movia por ela.</p>
<p>Duas conclusões desse spike moldaram todo o projeto.</p>
<p>Primeiro, confirmamos que tínhamos uma margem real de desempenho em desktops, desde que mantivéssemos a primeira versão disciplinada. Isso nos deu confiança para tentar uma arquitetura de terreno mais complexa depois.</p>
<p>Segundo, criamos um contrato de referência. Cada spike seguinte precisava justificar seu custo em relação a essa cena. Se um novo recurso tivesse boa aparência, mas custasse caro demais, ele não era promovido.</p>
<p>Essa disciplina de manter uma referência se tornou crucial mais tarde, quando encontramos artefatos nas emendas, transições entre LODs mistos e geração de malha orientada por compute shaders. Sem uma referência estável, todo bug parece maior do que realmente é.</p>
<p>Na parte 2, passamos da renderização para a sensação dos controles. Física em um Worker parece ótima nos documentos de arquitetura. Mas isso só importa se o personagem ainda responder imediatamente quando você pressionar uma tecla.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Terreno com mapa de altura.</strong> Uma grade 2D na qual cada célula armazena um único valor de elevação. A GPU desloca uma malha plana no vertex shader para criar a superfície do terreno. Mapas de altura são compactos (um chunk de 65x65 ocupa cerca de 8 KB em 16 bits), adequados à GPU e rápidos de renderizar. A limitação é que eles não conseguem representar cavernas, saliências ou qualquer superfície que se dobre sobre si mesma. Para saber mais sobre as limitações dos mapas de altura e o que vem depois deles, consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#why-heightmaps-arent-enough">guia de geração de paisagens</a>.</p>
<p><strong>Three.js.</strong> A biblioteca de renderização que usamos durante todo este projeto. A Three.js abstrai o WebGL 2 (e, posteriormente, o WebGPU) em um grafo de cena com câmeras, luzes, materiais e objetos geométricos. Ela fornece <code>InstancedMesh</code> para renderizar muitas cópias da mesma geometria em uma única draw call, além de frustum culling, materiais PBR e pós-processamento. Consulte a <a href="https://github.com/mrdoob/three.js">Three.js no GitHub</a>. Para entender como a Three.js se encaixa em uma stack de mundo aberto para navegador, consulte nosso <a href="/pt-BR/guides/browser-3d-open-world-tech#three-js">guia de tecnologias 3D para navegador</a>.</p>
<p><strong>InstancedMesh.</strong> Um recurso da Three.js que renderiza N cópias da mesma geometria com uma única draw call, cada uma com posição, rotação e escala diferentes. As transformações por instância são armazenadas em um buffer de atributos de matrizes. Foi assim que renderizamos 500 objetos no Spike 1 sem fazer 500 draw calls separadas. Para vegetação em grande escala, o culling de instâncias orientado pela GPU leva essa técnica ainda mais longe. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-vegetation-culling">guia de paisagens sobre culling de vegetação na GPU</a>.</p>
<p><strong>Orçamento de frame.</strong> A 60 fps, cada frame tem $\frac{1000\ \text{ms}}{60} \approx 16.7$ ms para tudo: lógica em JavaScript, física, renderização e composição. Um spike de “verificação de orçamento” mede quanto desse tempo uma cena de referência consome, de modo que a margem restante para tudo o que você adicionar depois seja $t_\text{features} = 16.7 - t_\text{baseline}$. Se a cena de referência já consumir 12 ms, restarão apenas cerca de 4,7 ms para jogabilidade, física e rede combinadas. Essa abordagem vem do desenvolvimento de mundos abertos AAA, em que <a href="/pt-BR/guides/browser-3d-open-world-tech#what-we-can-learn-from-skyrim-and-the-witcher">GTA V, Skyrim e Elden Ring</a> usam LOD e streaming agressivos para permanecer dentro de orçamentos de frame fixos.</p>
<hr>
<p>Parte 1 de 12.<br>
A seguir: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics">Parte 2 — Física em Worker e o receio do atraso nos controles</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 2: Física em Worker e o receio do atraso nos controles]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Transferimos o Rapier para um Web Worker e medimos o que preocupa todo mundo primeiro: o movimento parece atrasado?]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 2: Física em Worker e o receio do atraso nos controles</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Se você trabalha com jogos multiplayer no navegador por tempo suficiente, acaba se deparando com esta discussão.</p>
<p>&quot;Física em um worker é uma arquitetura limpa. Física na thread principal parece mais segura.&quot;</p>
<p>As duas coisas podem ser verdade. O que importa é a resposta dos controles e a latência com entradas reais.</p>
<p>O Spike 2 foi criado para responder a essa questão com medições, não opiniões.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/02-rapier-worker/" title="Spike 2 com Rapier em Worker" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/02-rapier-worker/" target="_blank">Abrir o Spike 2 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=02-rapier-worker">Ver código-fonte</a></p>
<p>Reutilizamos o terreno do Spike 1 e integramos o Rapier em um module worker dedicado. O estado das entradas era enviado ao worker a cada quadro, a simulação era executada nele e a posição autoritativa retornava ao renderizador.</p>
<p>As principais métricas foram a latência entre a entrada e o movimento visível, além do tempo de cada etapa da física. Também monitoramos possíveis oscilações durante o movimento normal, arrancadas em velocidade e sequências de saltos.</p>
<p>O resultado foi melhor do que o esperado. Com o formato e a cadência das nossas mensagens, a comunicação com o worker não foi o principal fator de latência. Os controles continuaram parecendo imediatos, que era a única coisa com a qual os jogadores se importariam.</p>
<p>Um desafio dessa fase foi o risco de interpretação. Após um resultado bem-sucedido, as equipes muitas vezes generalizam demais e presumem que a questão de arquitetura está resolvida para sempre. Não está. Validamos apenas um cenário concreto e um perfil de hardware. Os spikes posteriores ainda precisaram reavaliar as premissas quando a carga da GPU e do streaming mudou.</p>
<p>Este spike também aprimorou nosso processo. Passamos a exibir por padrão a telemetria de tempo na HUD dos spikes interativos. Isso mudou as conversas da equipe de &quot;parece estranho&quot; para &quot;este caminho acrescentou 1,2 ms&quot;.</p>
<p>Na parte 3, abordamos os experimentos menos glamourosos que evitaram surpresas caras mais adiante: carga de transmissão, limitações de dispositivos móveis e confiabilidade da geração de comportamentos.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Rapier.</strong> Um motor de física escrito em Rust que é compilado para WebAssembly e usado no navegador. Ele processa corpos rígidos, colisores, articulações, controladores de personagens e raycasting com desempenho de 2 a 3 vezes superior ao nativo. Para mundos abertos, o Rapier oferece controladores de personagem do jogador (caminhar pelo terreno, subir degraus e deslizar em encostas), colisão de objetos, raycasting para interações e volumes de gatilho. Consulte a <a href="https://rapier.rs/">documentação do Rapier</a> e nosso <a href="/pt-BR/guides/browser-3d-open-world-tech#rapier-rust-wasm">guia de tecnologias 3D para navegador sobre física</a>.</p>
<p><strong>Web Workers.</strong> Threads do navegador que executam JavaScript (ou Wasm) fora da thread principal. Executar a simulação de física em um worker significa que uma chamada pesada a <code>world.step()</code> não bloqueia a renderização. A thread principal envia o estado das entradas ao worker a cada quadro por meio de <code>postMessage</code> e recebe de volta as posições autoritativas. A penalidade de latência corresponde aos dois percursos das mensagens (cerca de 0,1 a 0,5 ms cada em computadores). O benefício é que a thread de renderização nunca fica paralisada pela detecção de colisões. Objetos transferíveis (transferência de <code>ArrayBuffer</code>) eliminam o custo de cópia de grandes arrays de posições.</p>
<p><strong>WebAssembly (Wasm).</strong> Um formato binário de instruções executado nos navegadores com velocidade próxima à nativa. Rapier, Havok e Recast são todos compilados para Wasm. A etapa de física no Rapier-Wasm normalmente leva de 0,5 a 2 ms para algumas centenas de corpos, em comparação com 5 a 15 ms em uma implementação JavaScript equivalente. Os módulos Wasm são carregados como arquivos <code>.wasm</code>, obtidos junto com o código JavaScript de integração. Consulte a <a href="https://webassembly.org/">especificação do WebAssembly</a>.</p>
<p><strong>Latência entre entrada e resposta visual.</strong> O tempo entre o pressionamento de uma tecla e a alteração visual correspondente na tela. Para que o movimento pareça &quot;imediato&quot;, esse valor precisa permanecer abaixo de aproximadamente 80 ms. Em uma configuração com física em um worker, a cadeia é aditiva:</p>
<p>$$
L = t_\text{input} + 2,t_\text{msg} + t_\text{step} + t_\text{render} + t_\text{vsync}
$$</p>
<p>evento keydown (thread principal), <code>postMessage</code> até o worker e de volta ($2,t_\text{msg}$), a etapa de física, a aplicação da nova posição pelo renderizador e, por fim, o próximo vsync. Cada termo é pequeno por si só (cerca de 0,1 a 0,5 ms por percurso da mensagem em computadores), mas eles se acumulam. Por isso, medimos $L$ diretamente em vez de confiar no diagrama da arquitetura.</p>
<hr>
<p>Parte 2 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first">Parte 1 — Começamos tentando quebrar tudo</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes">Parte 3 — Os spikes pouco glamourosos que nos salvaram</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 3: Os spikes discretos que nos salvaram]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A carga de broadcast com Durable Objects, as restrições de qualidade em dispositivos móveis e a confiabilidade da geração de comportamentos não eram temas glamorosos, mas evitaram surpresas caras.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 3: Os spikes discretos que nos salvaram</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, Diretor de Tecnologia e Cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>Esta parte tem menos capturas de tela chamativas e mais segurança arquitetural.</p>
<p>Depois dos Spikes 1 e 2, fizemos três verificações de risco que pareciam pequenas, mas tiveram impacto no produto.</p>
<p>A primeira foi a distribuição de broadcasts com Durable Objects. Testamos a distribuição das posições de vários clientes em frequências de atualização semelhantes às de um jogo e monitoramos a distribuição de latência, o uso de CPU por atualização e a integridade das entregas. Se isso tivesse falhado, teríamos adotado o particionamento antecipado em vez de manter uma única instância responsável por cada ilha.</p>
<p>A segunda foi a validação das restrições de dispositivos móveis. Não um preset para desktop renomeado como mobile, mas um perfil explicitamente de baixo custo, baseado no mesmo terreno de referência.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/01-terrain/?quality=mobile" title="Perfil de qualidade para dispositivos móveis baseado no Spike 1" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/01-terrain/?quality=mobile" target="_blank">Abrir o perfil para dispositivos móveis em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=01-terrain">Ver código-fonte</a></p>
<p>Reduzimos a densidade de segmentos, a quantidade de objetos, a pressão sobre a resolução de renderização e o alcance da névoa. A pergunta era simples: este mundo pode continuar legível e responsivo sob as restrições típicas de dispositivos móveis sem que seja necessário reescrever todo o renderizador?</p>
<p>A terceira foi a confiabilidade da geração de comportamentos para os fluxos de trabalho dos criadores. Avaliamos a taxa de JSON válido, a correção semântica em relação às primitivas esperadas e a latência de resposta. Se isso tivesse falhado, teríamos adotado uma criação de comportamentos estritamente baseada em formulários.</p>
<p>A principal conclusão deste capítulo é que esses spikes discretos mudaram a arquitetura mais rapidamente do que os spikes visuais. Eles estabeleceram limites rígidos para a topologia de rede, as promessas para dispositivos móveis e a UX das ferramentas.</p>
<p>Na parte 4, voltamos ao trabalho visível com o terreno e testamos o comportamento do streaming durante o movimento, não apenas capturas de tela do carregamento estático.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Cloudflare Durable Objects.</strong> Instâncias serverless com estado, implantadas na borda, com persistência integrada e suporte a WebSocket. Cada Durable Object mantém o estado autoritativo de um shard (ou chunk) do mundo. Os jogadores se conectam via WebSocket e recebem broadcasts das posições dos outros jogadores no mesmo shard. Quando o jogador se move para um chunk adjacente, ele se conecta ao Durable Object desse chunk. Os Durable Objects persistem automaticamente o estado em disco e podem escalar para milhares de instâncias simultâneas. Consulte a <a href="https://developers.cloudflare.com/durable-objects/">documentação dos Cloudflare Durable Objects</a> e nosso <a href="/pt-BR/guides/browser-3d-open-world-tech#multiplayer-networking">guia de tecnologias 3D para navegadores sobre redes multijogador</a>.</p>
<p><strong>Particionamento espacial.</strong> Divisão do mundo entre instâncias de servidor por região geográfica. Cada shard controla uma área retangular da grade do mundo. Conforme a densidade de jogadores muda, os shards podem ser divididos ou combinados. Jogadores próximos ao limite de um shard veem o conteúdo de ambos os shards por meio de consultas de visibilidade entre shards. É assim que o <a href="/pt-BR/guides/browser-3d-open-world-tech#server-architecture">EVE Online comporta milhares de jogadores</a> em um único universo.</p>
<p><strong>Distribuição de broadcasts via WebSocket.</strong> Distribuição de atualizações de posição em tempo real de um servidor para vários clientes conectados. A largura de banda downstream por cliente aumenta linearmente de acordo com o número de jogadores visíveis:</p>
<p>$$
B_\text{client} = N_\text{visible} \times b_\text{player}
$$</p>
<p>Em frequências de atualização semelhantes às de um jogo (20–30 Hz), cada jogador gera $b_\text{player} \approx 800$ bytes/segundo de dados de posição. Portanto, 200 jogadores visíveis custam $200 \times 800 = 160$ KB/s por cliente. O custo da abordagem ingênua de todos para todos no servidor é pior, crescendo como $N^2$. É exatamente por isso que a filtragem por relevância (gerenciamento espacial de interesse) limita $N_\text{visible}$ antes de enviar os dados para cada conexão dentro do orçamento de cada atualização. Consulte nosso <a href="/pt-BR/guides/browser-3d-open-world-tech#client-server-communication">guia de redes</a> para conhecer as diferenças entre compressão delta e frequência de atualização.</p>
<p><strong>Restrições de renderização em dispositivos móveis.</strong> As GPUs de dispositivos móveis têm de 1/5 a 1/10 do desempenho das GPUs de desktop, um limite de memória de aproximadamente 1 GB (em comparação com 2–4 GB no desktop) e redução térmica de desempenho sob carga prolongada. Um perfil de qualidade para dispositivos móveis reduz a densidade de segmentos, a quantidade de objetos, a resolução de renderização, a distância de visualização e a qualidade das sombras. O objetivo não é alcançar paridade com o desktop, mas manter a legibilidade e a responsividade. Consulte os <a href="/pt-BR/guides/browser-3d-open-world-tech#browser-3d-performance-real-numbers">números de desempenho 3D em navegadores</a> para ver benchmarks reais de GPU.</p>
<hr>
<p>Parte 3 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics">Parte 2 — Física em Worker e o receio de atraso nos comandos</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">Parte 4 — Streaming antes de terrenos sofisticados</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 4: Streaming antes de terrenos sofisticados]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Primeiro testamos o carregamento e a troca de chunks com conteúdo simples e, depois, passamos ao refinamento progressivo de mapas de altura. Essa ordem valeu a pena.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 4: Streaming antes de terrenos sofisticados</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegando agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>É no streaming que projetos com “boa aparência” geralmente desmoronam.</p>
<p>É possível esconder muita coisa em um quadro estático. Mas não dá para esconder uma travada de 40 ms ao cruzar o limite de um chunk.</p>
<p>Testamos o streaming de propósito antes de criar uma representação avançada do terreno. Isso nos deu dados claros sobre o comportamento de carregamento e descarregamento.</p>
<p>O Spike 6 validou a rotatividade dos chunks vizinhos usando conteúdo simples.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/06-chunk-streaming/" title="Spike 6: carregamento e troca de chunks" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/06-chunk-streaming/" target="_blank">Abrir o Spike 6 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=06-chunk-streaming">Ver código-fonte</a></p>
<p>Depois, no Spike 11, passamos para o pipeline real do terreno: streaming de chunks de altura, com decodificação em worker e refinamento progressivo de grades com 17, 33 e 65 amostras.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/11-chunk-streaming/" title="Spike 11: streaming de chunks de mapa de altura" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/11-chunk-streaming/" target="_blank">Abrir o Spike 11 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=11-chunk-streaming">Ver código-fonte</a></p>
<p>A sequência importou mais do que esperávamos. Se tivéssemos começado diretamente com chunks de altura comprimidos, a causa de cada travada seria ambígua: problema de decodificação, de upload de textura ou de atualização da geometria? O Spike 6 eliminou uma camada de incerteza antes que o Spike 11 adicionasse complexidade.</p>
<p>Uma lição prática deste capítulo foi aplicada nos spikes posteriores: as pausas causadas por uploads precisam ser medidas diretamente, não deduzidas a partir do FPS médio. O FPS médio esconde picos no tempo de quadro, e são esses picos que os usuários realmente sentem.</p>
<p>Na parte 5, entramos no capítulo sobre custos visuais, em que vegetação, shaders de terreno e sombras em cascata disputam o mesmo orçamento por quadro.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Streaming baseado em chunks.</strong> O mundo é dividido em uma grade de chunks independentes (normalmente de 64 × 64 metros). À medida que o jogador se move, os chunks da borda traseira são descarregados, enquanto os da borda dianteira entram por streaming. É assim que funciona o <a href="/pt-BR/guides/browser-3d-open-world-tech#skyrim-s-cell-system">sistema de células de Skyrim</a>: uma grade de 5 × 5 células carregada ao redor do jogador, com trocas conforme ele se move. A versão para navegador acrescenta latência de rede à equação, tornando essencial o pré-carregamento preditivo com base na velocidade do jogador. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#streaming-architecture-for-terrain">guia de arquitetura de streaming</a>.</p>
<p><strong>Refinamento progressivo de mapas de altura.</strong> Primeiro, o terreno é enviado em baixa resolução e depois refinado. Os tamanhos das grades não são arbitrários: cada nível é uma grade de $(2^k + 1) \times (2^k + 1)$, portanto $17 = 2^4 + 1$, $33 = 2^5 + 1$ e $65 = 2^6 + 1$. O $+1$ mantém uma amostra compartilhada em cada limite para que os chunks vizinhos se alinhem, e cada etapa praticamente quadruplica o número de amostras ($n^2$ cresce à medida que o lado dobra). Uma grade de 17 × 17 (o mínimo para um chunk de 64 m com espaçamento de 4 m) tem cerca de 200 bytes quando comprimida e renderiza uma superfície visível instantaneamente. Depois, transmitimos o refinamento de 33 × 33 e, por fim, a resolução completa de 65 × 65. Cada nível adiciona amostras sem substituir os dados anteriores. Isso corresponde diretamente aos anéis de LOD de clipmaps de geometria, nos quais terrenos distantes usam dados de baixa resolução e terrenos próximos usam a resolução completa. Consulte <a href="/pt-BR/guides/landscape-generation-browser#progressive-chunk-loading">carregamento progressivo de chunks</a>.</p>
<p><strong>Codificação delta e compressão.</strong> Dados de mapas de altura são bem comprimidos porque células adjacentes têm valores semelhantes. A codificação delta armazena a diferença entre cada célula e seu valor previsto (a média das vizinhas), concentrando os valores perto de zero. Combinado com zlib ou brotli, um chunk de 65 × 65 cai de 8,4 KB sem compressão para 1–2 KB comprimido. Com precisão reduzida para chunks distantes (8 bits em vez de 16 bits), o tamanho fica entre 0,5 e 1 KB. Consulte <a href="/pt-BR/guides/landscape-generation-browser#terrain-data-compression-for-streaming">compressão de dados de terreno</a>.</p>
<p><strong>Pré-carregamento preditivo.</strong> Consiste em carregar chunks antes que o jogador chegue até eles. A distância de antecipação precisa cobrir o quanto o jogador percorre durante o carregamento de um chunk, $d_\text{prefetch} = v \cdot t_\text{load}$, portanto ela varia de acordo com a velocidade: ao caminhar (5 km/h), pré-carregue 2 chunks à frente (128 m); ao correr (15 km/h), pré-carregue 4. O anel de carregamento se desloca de acordo com a direção da velocidade. Uma fila de prioridade ordena as solicitações pendentes por urgência e cancela as de chunks dos quais o jogador se afastou. Consulte <a href="/pt-BR/guides/landscape-generation-browser#predictive-pre-fetching">pré-carregamento preditivo</a>.</p>
<hr>
<p>Parte 4 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes">Parte 3 — Os spikes pouco chamativos que nos salvaram</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty">Parte 5 — Definindo o orçamento dos elementos visuais</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 5: Orçando os recursos visuais]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Vegetação, materiais de terreno e sombras em cascata ficaram ótimos. O verdadeiro trabalho foi provar que cabiam no orçamento de frame.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 5: Orçando os recursos visuais</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, Diretor de Tecnologia e Cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Este foi o capítulo em que a ambição visual encontrou a aritmética.</p>
<p>Dividimos o custo de renderização em spikes separados porque resultados agrupados são difíceis de diagnosticar. Se você ativa tudo de uma vez, só descobre que o frame está lento. Não descobre qual recurso consumiu o orçamento.</p>
<p>O Spike 7 teve como foco a densidade da vegetação e o custo da animação. A abordagem foi distribuir a vegetação em tempo de execução usando mapas de densidade de 32x32 para cada chunk do terreno, alimentando grandes conjuntos de <code>InstancedMesh</code>. Cada lâmina de grama e agrupamento de arbustos recebeu animação de vento no vertex shader, controlada por uma textura de ruído em movimento. O principal número que monitoramos não foi a quantidade de triângulos, mas o overhead das draw calls e o throughput de vértices em GPUs intermediárias. Descobrimos que agrupar as instâncias em menos meshes era mais importante do que reduzir a quantidade de polígonos de cada lâmina.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/07-gpu-vegetation/" title="Spike 7: Vegetação na GPU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/07-gpu-vegetation/" target="_blank">Abrir o Spike 7 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=07-gpu-vegetation">Ver código-fonte</a></p>
<p>O Spike 8 aumentou a complexidade dos materiais do terreno. Mistura de múltiplas camadas ponderada pelo ângulo de inclinação e pela altitude, projeção triplanar opcional para faces de penhascos e normal maps por camada. O shader fazia splatting com base na inclinação usando quatro camadas de textura, cada uma exigindo uma amostra difusa e uma amostra de normal. Isso representa $4 \times 2 = 8$ leituras de textura por fragmento antes mesmo de acrescentar qualquer iluminação, e a quantidade cresce linearmente com o número de camadas, portanto uma quinta camada significa 10 leituras para cada pixel. Fizemos o profiling especificamente em GPUs Intel integradas para encontrar o limite mínimo. A conclusão foi que a projeção triplanar em superfícies verticais valia o custo, mas adicionar uma quinta camada de splat não.</p>
<p><a href="/pt-BR/spikes/08-terrain-material/" target="_blank">Abrir o Spike 8 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=08-terrain-material">Ver código-fonte</a></p>
<p>O Spike 9 concentrou-se no custo dos mapas de sombras em cascata sob uma carga realista de terreno e objetos. CSM com três cascatas era a configuração de referência. Testamos especificamente com o sol em ângulos baixos, pois é quando a pressão sobre as cascatas é maior. A cascata distante cobre uma enorme fatia do frustum, e o detalhamento das sombras é determinado pela densidade de texels, $\rho \approx \frac{R^2}{A}$ (um mapa de sombras com resolução $R$ estendido sobre uma área de terreno $A$). Usar um único mapa para toda a distância de visão torna $A$ enorme e faz $\rho$ despencar. As cascatas resolvem isso dividindo o frustum para que cada fatia receba seu próprio mapa de $R \times R$ sobre um $A$ pequeno, mantendo $\rho$ aproximadamente constante do plano mais próximo ao mais distante. Medimos a diferença no tempo de GPU entre duas e quatro cascatas e, depois, entre resoluções de mapa de sombras de 1024 e 2048. O resultado foi que três cascatas em 1024 nos proporcionaram sombras de contato aceitáveis perto da câmera sem ultrapassar 2 ms de tempo de GPU no nosso hardware-alvo.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/09-csm-shadows/" title="Spike 9: Sombras CSM" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/09-csm-shadows/" target="_blank">Abrir o Spike 9 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=09-csm-shadows">Ver código-fonte</a></p>
<p>A parte difícil dessa fase foi manter a disciplina de produto. Alguns efeitos ficaram excelentes e, mesmo assim, precisaram ser limitados porque consumiam uma parcela grande demais do orçamento de frame em relação ao seu impacto visual.</p>
<p>Nossa regra ficou simples. Um recurso só avança se puder justificar seu custo com dados medidos de tempo de frame.</p>
<p>Parece óbvio. Não é algo comum em ciclos rápidos de prototipagem, nos quais todos estão empolgados com o próximo ganho visual. Manter essa regra desde o início tornou muito mais claras as decisões de arquitetura sobre clipmaps e zonas volumétricas mais adiante, pois já conhecíamos o custo individual de cada recurso que disputava os mesmos 16 ms.</p>
<p>Na parte 6, chegamos à primeira grande mudança de arquitetura do terreno com os geometry clipmaps.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>InstancedMesh e vegetação na GPU.</strong> O <code>InstancedMesh</code> do Three.js renderiza N cópias da mesma geometria com uma única draw call. Para a vegetação, um mapa de densidade (32x32 por chunk) controla a distribuição, em tempo de execução, de lâminas de grama e agrupamentos de arbustos em buffers de instâncias. A animação do vento é executada no vertex shader usando uma textura de ruído em movimento. Em grande escala, o <code>ComputeInstanceCulling</code> do WebGPU elimina instâncias fora da tela e distantes antes da rasterização, enquanto o <code>IndirectBatchedMesh</code> reúne vários tipos de vegetação em um único buffer, desenhado com multi-draw indirect. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-vegetation-culling">guia de paisagens sobre culling de vegetação na GPU</a>.</p>
<p><strong>Mapeamento triplanar.</strong> Texturas com mapeamento UV convencional ficam esticadas em encostas íngremes porque as coordenadas UV são comprimidas. O mapeamento triplanar projeta texturas ao longo dos três eixos (X, Y, Z) e faz a mistura com base na normal da superfície. Faces de penhascos recebem a projeção X ou Z (sem esticar), enquanto terrenos planos recebem a projeção Y. A mistura é suave e automática, sem exigir abertura de UV. Para terrenos PBR, os mesmos pesos de mistura são aplicados aos canais de albedo, normal, rugosidade e oclusão de ambiente. Consulte os <a href="/pt-BR/guides/landscape-generation-browser#triplanar-mapping">detalhes do mapeamento triplanar</a>.</p>
<p><strong>Splatting de materiais baseado em inclinação e altitude.</strong> Em vez de splat maps pintados à mão, os materiais são atribuídos proceduralmente no fragment shader com base nas propriedades do terreno. Áreas planas em baixa altitude recebem grama, encostas íngremes recebem rocha, áreas de grande altitude recebem neve (apenas em superfícies planas o suficiente para permitir o acúmulo) e áreas próximas ao nível do mar recebem areia. As transições usam <code>smoothstep</code> para produzir uma mistura suave. Em nossa implementação, cada chunk do terreno avalia quatro camadas de textura, com amostras difusas e de normal por camada, totalizando oito leituras de textura por fragmento antes da iluminação. Consulte a <a href="/pt-BR/guides/landscape-generation-browser#slope-and-altitude-based-material-assignment">atribuição de materiais por inclinação e altitude</a>.</p>
<p><strong>Mapas de Sombras em Cascata (CSM).</strong> O CSM divide o frustum de visão da câmera em 3 a 4 faixas de distância (cascatas). Cada cascata renderiza um mapa de sombras da perspectiva do sol com uma resolução adequada à sua distância. Cascatas próximas recebem sombras em alta resolução (sombras de contato detalhadas sob árvores e edifícios), enquanto cascatas distantes recebem resolução mais baixa (sombras amplas de montanhas). O shader do terreno amostra todas as cascatas e seleciona a adequada para cada fragmento. Custo de desempenho: 3 a 4 cascatas em 1024x1024 acrescentam cerca de 0,5 a 1 ms à renderização dos mapas de sombras, além de aproximadamente 0,2 a 0,3 ms para a amostragem. Consulte <a href="/pt-BR/guides/landscape-generation-browser#shadows-for-terrain">sombras para terrenos</a>.</p>
<hr>
<p>Parte 5 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">Parte 4 — Streaming antes de terrenos sofisticados</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps">Parte 6 — Os clipmaps mudaram o rumo da história</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 6: Clipmaps mudaram o rumo da história]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Os clipmaps de geometria nos deram uma maneira de manter o custo do terreno previsível enquanto nos deslocávamos por um mundo muito maior.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 6: Clipmaps mudaram o rumo da história</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, Diretor de Tecnologia e Cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Antes do Spike 10, nosso modelo mental ainda era: &quot;um mundo maior significa mais geometria&quot;. Depois do Spike 10, o modelo passou a ser: &quot;orçamento constante de geometria, com atualizações de anéis centralizados na câmera&quot;. Essa mudança alterou a trajetória do projeto.</p>
<p>A ideia por trás dos clipmaps de geometria é simples. O terreno é renderizado como um conjunto de anéis concêntricos centralizados na câmera. O anel mais interno tem a maior densidade de vértices. Cada anel $k$ em direção ao exterior dobra o espaçamento entre vértices, $s_k = s_0 \cdot 2^k$, cobrindo assim uma área de terreno $4\times$ maior que a do anel imediatamente anterior.</p>
<p>Essa duplicação é todo o segredo. A distância de visão cresce geometricamente com o número de anéis, $d_\text{view} \approx s_0 \cdot 2^{L}$, enquanto o custo de vértices cresce apenas linearmente:</p>
<p>$$
V_\text{total} \approx N^2 \cdot L
$$</p>
<p>para $L$ anéis com $N \times N$ vértices cada. Dobrar a distância de visão custa apenas mais um anel, não quatro vezes mais geometria. A contagem de triângulos permanece praticamente constante, independentemente do tamanho do mundo, porque você sempre renderiza os mesmos $L$ anéis com a mesma resolução.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/10-clipmap-geomorph/" title="Geomorfização com clipmaps de geometria do Spike 10" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/10-clipmap-geomorph/" target="_blank">Abrir o Spike 10 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=10-clipmap-geomorph">Ver código-fonte</a></p>
<p>O truque prático foi usar geomorfização nos limites dos anéis. Quando um vértice passa de um anel de LOD para o seguinte, sua altura precisa fazer uma transição suave entre a amostra de alta resolução e a de baixa resolução. Sem isso, surgem saltos visíveis sempre que a câmera se move e os anéis se deslocam. Resolvemos isso com um fator de mistura baseado na distância do vértice até a borda do anel, interpolando a altura no shader de vértice:</p>
<p>$$
h = \operatorname{lerp}(h_\text{fine},, h_\text{coarse},, \alpha), \qquad \alpha = \operatorname{smoothstep}(d_\text{near},, d_\text{far},, d)
$$</p>
<p>onde $d$ é a distância do vértice em relação à câmera. Dentro do anel ($d \le d_\text{near}$), o vértice usa sua altura em resolução máxima; quando alcança o anel seguinte ($d \ge d_\text{far}$), ele já migrou para a altura de baixa resolução que esse anel usará, portanto não resta nenhum salto a ocorrer.</p>
<p>Uma lição sutil veio dos testes de movimentação da câmera. É fácil avaliar clipmaps em capturas de tela estáticas e não perceber artefatos de transição. Passamos bastante tempo percorrendo os limites dos anéis em velocidade constante e observando a presença de ruído temporal. As capturas de tela mentiam. O movimento revelava a verdade.</p>
<p>Esse spike também nos proporcionou uma separação arquitetural bem definida. Com o tempo, o terreno próximo poderia se tornar dinâmico e caro, com edição volumétrica, materiais mais complexos e interação com a física. O terreno distante poderia permanecer estável, previsível e barato. Essa separação se tornou a espinha dorsal de todas as decisões arquiteturais tomadas dali em diante.</p>
<p>Se você está avaliando clipmaps para seu próprio projeto, teste ciclos de estresse, não imagens bonitas. Percursos longos, mudanças na altitude da câmera e travessias repetidas dos limites são o que expõe os problemas reais.</p>
<p>Na parte 7, adicionamos geração volumétrica de malhas e passamos de &quot;terreno como superfície&quot; para &quot;terreno como volume editável&quot;. Foi nesse ponto que o projeto deixou de ser apenas um renderizador e começou a se tornar um editor de mundos.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Clipmaps de geometria.</strong> Apresentados por Losasso e Hoppe na SIGGRAPH 2004 (<a href="https://hhoppe.com/geomclipmap.pdf">artigo</a>), os clipmaps de geometria renderizam o terreno como anéis quadrados concêntricos centralizados na câmera. Cada anel tem o dobro da área do anterior, com metade da resolução de vértices. A contagem total de vértices é constante: aproximadamente $N^2 \cdot L$. Com $N = 256$ e $L = 8$ níveis, isso representa $256^2 \times 8 \approx 524{,}000$ vértices, independentemente do tamanho do mundo. A CPU atualiza os dados do mapa de alturas de cada anel conforme a câmera se move. O shader de vértice lê a altura de uma textura e desloca a grade plana. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#geometry-clipmaps">guia de paisagens sobre clipmaps de geometria</a> e o <a href="https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry">GPU Gems 2, Capítulo 2</a>.</p>
<p><strong>Geomorfização.</strong> O maior artefato visual no LOD de terrenos é o surgimento de saltos: os vértices mudam de posição repentinamente quando um trecho troca de nível de LOD. A geomorfização elimina esse problema ao mesclar as posições dos vértices entre os níveis de LOD ao longo de uma zona de transição. Cada vértice armazena tanto sua altura no LOD atual quanto sua altura no LOD de menor resolução. Um fator de transformação baseado na distância da câmera interpola suavemente entre elas: <code>morphedHeight = mix(fineLodHeight, coarseLodHeight, smoothstep(lodNear, lodFar, distance))</code>. A zona de transição normalmente corresponde aos 20% externos de cada anel. Em velocidades normais da câmera, a transição é invisível. Consulte os <a href="/pt-BR/guides/landscape-generation-browser#geomorphing-pop-free-lod-transitions">detalhes sobre geomorfização</a>.</p>
<p><strong>CDLOD (clipmaps adaptativos por quadtree).</strong> Uma evolução dos clipmaps de anéis fixos, proposta por Strugar (2014, <a href="https://www.vertexasylum.com/CDLOD/cdlod_latest.pdf">artigo</a>). Em vez de anéis concêntricos com resolução uniforme, o CDLOD usa uma quadtree que se adapta à complexidade do terreno. Áreas planas usam nós de baixa resolução, enquanto áreas com muitos detalhes, como penhascos e cristas, recebem subdivisões mais refinadas. Isso é importante para mundos criados por usuários, nos quais diferentes chunks podem ter níveis de complexidade muito distintos. Consulte a seção sobre <a href="/pt-BR/guides/landscape-generation-browser#cdlod-quadtree-adaptive-clipmaps">CDLOD em nosso guia de paisagens</a>.</p>
<hr>
<p>Parte 6 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty">Parte 5 — Definindo o orçamento dos elementos visuais</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Parte 7 — Marching cubes e as primeiras cavernas de verdade</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 7: Marching cubes e as primeiras cavernas de verdade]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 12 trouxe marching cubes em tempo real com WebGPU para o projeto e mudou o que o terreno podia ser.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 7: Marching cubes e as primeiras cavernas de verdade</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e traz links para todas as partes.</p>
<p>Mapas de altura são ótimos até você precisar de saliências.</p>
<p>No momento em que você quer túneis escavados, bordas rochosas suspensas ou tetos de cavernas, um pipeline baseado apenas em campos de altura começa a limitar você. Um mapa de altura armazena um valor Y por coordenada XZ. Ele é fisicamente incapaz de representar qualquer superfície que se dobre sobre si mesma. Precisávamos de uma representação volumétrica.</p>
<p>O Spike 12 implementou marching cubes na GPU usando shaders de computação WebGPU. O algoritmo avalia um campo de distância com sinal (SDF) em uma grade 3D e extrai uma malha de triângulos na superfície onde ocorre o cruzamento por zero. Cada célula tem 8 vértices, cada um dentro ou fora da superfície, o que resulta em $2^8 = 256$ padrões de sinal possíveis. Uma tabela de consulta mapeia cada padrão para um conjunto de triângulos. Os vértices ficam nas arestas da célula, no ponto em que o campo cruza zero. Para uma aresta entre os vértices $a$ e $b$, com valores de campo $f_a$ e $f_b$, a interpolação linear posiciona o vértice em</p>
<p>$$
\mathbf{v} = \mathbf{p}_a + t,(\mathbf{p}_b - \mathbf{p}_a), \qquad t = \frac{-f_a}{f_b - f_a}
$$</p>
<p>que corresponde à fração ao longo da aresta em que o campo chega a zero. Como $f_a$ e $f_b$ têm sinais opostos em uma aresta de cruzamento, $t$ sempre fica em $[0, 1]$. Executamos isso simultaneamente em quatro chunks ativos de $64^3$ e testamos edições animadas do SDF com reconstrução da malha a cada quadro.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/12-webgpu-marching-cubes/" title="Spike 12: Marching Cubes com WebGPU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/12-webgpu-marching-cubes/" target="_blank">Abrir o Spike 12 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=12-webgpu-marching-cubes">Ver código-fonte</a></p>
<p>O primeiro ganho foi a confiança no próprio pipeline de computação. Um único dispatch podia avaliar o SDF, classificar as células e emitir vértices em um buffer da GPU sem nenhuma leitura de volta pela CPU. O segundo ganho foi descobrir a rapidez com que “funciona” se transforma em uma busca por artefatos. Triângulos ausentes raramente eram um problema da teoria de marching cubes. Eram incompatibilidades nos índices da tabela, intervalos de desenho incorretos que liam além da contagem de vértices ativos ou interações em casos extremos próximos às bordas dos chunks, onde as amostras vizinhas do SDF não estavam disponíveis.</p>
<p>Esse spike nos obrigou a pensar em zonas. Perto da câmera, você quer liberdade volumétrica para que os jogadores possam esculpir, cavar e ver cavernas. Longe da câmera, você quer a eficiência dos clipmaps, onde um mapa de altura plano é mais barato e perfeitamente adequado. Essa dualidade se tornou a espinha dorsal da arquitetura que continuamos refinando a partir do Spike 13.</p>
<p>Um dos meus momentos favoritos de depuração foi usar a opção de visualização em wireframe enquanto as edições estavam em andamento. Ver a topologia se formar e se dissolver em tempo real tornou os compromissos de qualidade imediatamente visíveis. Era possível ver onde a densidade de vértices era suficiente, onde ficava grosseira demais e exatamente onde as transições de LOD acabariam precisando de suporte ao Transvoxel para evitar fissuras.</p>
<p>Na parte 8, abordamos o desafio da integração. Manter malhas brutas geradas por computação e a lógica do grafo de cena do Three.js em um único pipeline de renderização estável foi mais difícil do que a demonstração isolada sugeria.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Marching cubes.</strong> Um algoritmo para extrair uma malha de triângulos de um campo escalar 3D (Lorensen e Cline, 1987). Cada célula de uma grade 3D regular é classificada por meio da amostragem do campo em seus 8 vértices. O padrão de sinais produz um índice de caso (0–255), e uma tabela de consulta mapeia cada caso para um conjunto de triângulos. Os vértices são posicionados nas arestas da grade por meio da interpolação entre os dois vértices. O algoritmo é altamente paralelizável, pois cada célula é processada de forma independente, o que o torna ideal para computação na GPU. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#signed-distance-fields-sdfs">guia de criação de paisagens sobre SDFs e marching cubes</a>.</p>
<p><strong>Campos de distância com sinal (SDFs).</strong> Uma representação volumétrica que armazena, em cada ponto do espaço 3D, a distância com sinal até a superfície mais próxima. Valores positivos ficam do lado de fora, valores negativos ficam do lado de dentro, e o cruzamento por zero define a superfície. SDFs podem representar formas 3D arbitrárias: cavernas, arcos, saliências e geometria flutuante que mapas de altura não conseguem expressar. A edição funciona naturalmente como álgebra de conjuntos aplicada ao campo. Adicionar material (união de duas formas) é $d = \min(d_1, d_2)$; remover material (cavar) é $d = \max(d_1, -d_2)$, com a forma de escavação negada; e uma mesclagem suave usa um mínimo suavizado, como</p>
<p>$$
\operatorname{smin}(d_1, d_2, k) = \min(d_1, d_2) - \frac{h^2}{4k}, \qquad h = \max\bigl(k - |d_1 - d_2|,, 0\bigr)
$$</p>
<p>em que $k$ controla o raio da mesclagem. Quando $k \to 0$, isso volta a ser um $\min$ rígido. Consulte <a href="/pt-BR/guides/landscape-generation-browser#signed-distance-fields-sdfs">representação de terreno com SDF</a>.</p>
<p><strong>Shaders de computação WebGPU.</strong> Programas de GPU que executam computação de uso geral, sem estarem vinculados ao pipeline de rasterização. Um shader de computação despacha grupos de trabalho compostos por threads que são executadas em paralelo. Para marching cubes, cada thread processa uma célula da grade: amostra o SDF, classifica a célula, consulta a triangulação, interpola os vértices das arestas e os adiciona a um buffer de malha usando contadores atômicos. Nenhuma leitura de volta pela CPU é necessária, pois o buffer de saída é usado diretamente como dados de vértices para a renderização. O <a href="https://www.willusher.io/webgpu-marching-cubes/">webgpu-marching-cubes</a>, de Will Usher, demonstra o processamento em tempo real de uma grade $256^3$ no navegador. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">guia de criação de paisagens sobre LOD controlado por WebGPU</a>.</p>
<p><strong>Arquitetura híbrida de mapa de altura + SDF.</strong> A abordagem prática para terrenos no navegador: mapas de altura cobrem o mundo inteiro — de forma econômica e compacta —, enquanto volumes SDF existem apenas nos chunks que precisam de cavernas, saliências ou elementos esculpidos pelos criadores (5–10% dos chunks). Perto da câmera, a liberdade volumétrica permite escavações e cavernas. À distância, mapas de altura fornecem terrenos planos com eficiência. Consulte <a href="/pt-BR/guides/landscape-generation-browser#hybrid-heightmap-base--volumetric-overlays">representação híbrida de terreno</a>.</p>
<hr>
<p>Parte 7 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps">Parte 6 — Os clipmaps mudaram o rumo da história</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration">Parte 8 — Integração sem perder nossa base de referência</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 8: Integração sem perder nossa base de referência]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Os spikes 13 e 14 tiveram menos a ver com funcionalidades e mais com disciplina de processo. Congelamos uma base limpa e, então, fortalecemos uma camada de cada vez.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 8: Integração sem perder nossa base de referência</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>É na integração que os projetos ficam complicados. Você tem componentes que funcionam isoladamente. Então, conecta tudo e, de repente, parece que qualquer bug pode estar em qualquer lugar.</p>
<p>Os spikes 13 e 14 foram nossa resposta a essa armadilha. O spike 13 estabeleceu uma base limpa de Three.js com WebGPU. Apenas um renderizador, uma cena, uma câmera e uma malha simples. Sem terreno, computação ou efeitos. Confirmamos que o backend WebGPU do Three.js era inicializado corretamente, que o loop de renderização estava estável e que os materiais baseados em nós da TSL (Three.js Shading Language) funcionavam como esperado. Só depois que esse ponto de verificação foi aprovado começamos a adicionar camadas.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/13-threejs-webgpu/" title="Spike 13 — Base de Three.js com WebGPU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/13-threejs-webgpu/" target="_blank">Abrir o spike 13 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=13-threejs-webgpu">Ver código-fonte</a></p>
<p>O spike 14 foi um processo de fortalecimento incremental. Adicionamos um recurso de cada vez: primeiro os controles da câmera, depois a iluminação, em seguida a malha gerada por computação pelo pipeline de marching cubes e, por fim, a infraestrutura de buffers para enviar a saída da GPU diretamente aos atributos de geometria do Three.js. Após cada adição, verificamos se a camada anterior continuava funcionando corretamente.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/14-threejs-webgpu-incremental/" title="Spike 14 — Fortalecimento incremental" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/14-threejs-webgpu-incremental/" target="_blank">Abrir o spike 14 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=14-threejs-webgpu-incremental">Ver código-fonte</a></p>
<p>Isso parece lento. E foi lento por exatamente um dia, mas logo depois nos poupou vários dias quando a lógica de emendas e a troca de políticas ficaram complicadas.</p>
<p>A categoria específica de bug que justificou essa disciplina foram os artefatos finíssimos. Pequenas lascas que pareciam corrupção de geometria, mas que, na verdade, eram dados obsoletos. O shader de computação gravava N vértices em um buffer, mas a chamada de desenho continuava configurada para renderizar N+M vértices do quadro anterior. Esses vértices adicionais continham lixo da execução anterior. O resultado visual eram triângulos oscilantes e extremamente finos, que apareciam e desapareciam de forma imprevisível.</p>
<p>Você não vence esse tipo de bug com intuição. Você o vence com alterações controladas, sabendo exatamente o que mudou entre o último estado funcional e o estado atual com defeito.</p>
<p>A integração com WebGPU também nos ensinou sobre o ciclo de vida dos buffers. Os buffers de GPU no WebGPU são imutáveis depois de mapeados para um uso específico. Se for necessário redimensionar um buffer de vértices porque a saída do marching cubes cresceu, é preciso criar um novo buffer e atualizar o vínculo. Não existe <code>realloc</code>. Acertar esse ciclo de vida e destruir buffers antigos sem entrar em conflito com operações da GPU ainda em andamento exigiu um gerenciamento explícito de fences que não existe no WebGL.</p>
<p>Na parte 9, avançamos para o trabalho nas emendas com Transvoxel. Esse capítulo começa propositalmente com uma estrutura básica. A essa altura, já tínhamos assimilado por completo a lição de que apressar a integração produz mistérios, enquanto uma preparação controlada produz problemas que podem ser depurados.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>WebGPU.</strong> O sucessor do WebGL, que oferece acesso de baixo nível à GPU no navegador, com shaders de computação e renderização indireta. Os dois recursos essenciais do WebGPU para mundos abertos são: os shaders de computação permitem gerar terrenos, posicionar vegetação e fazer culling na GPU; a renderização indireta permite que a GPU decida o que desenhar com base na saída da computação, eliminando gargalos de CPU em cenas densas. Disponível no Chrome, Edge e Firefox em computadores. Consulte <a href="/pt-BR/guides/browser-3d-open-world-tech#webgpu-the-performance-unlock">WebGPU como um salto de desempenho</a>.</p>
<p><strong>Three.js Shading Language (TSL).</strong> O sistema de shaders baseado em nós do Three.js, que substitui GLSL/WGSL puros por expressões JavaScript combináveis. Nós da TSL como <code>texture()</code>, <code>positionWorld</code>, <code>smoothstep()</code> e <code>fog()</code> criam, em tempo de execução, um grafo de shader que é compilado para o backend apropriado (GLSL do WebGL ou WGSL do WebGPU). A TSL permite escrever a lógica de materiais uma única vez e usá-la nos dois renderizadores. O grafo de nós é avaliado a cada quadro, portanto uniforms dinâmicos e ramificações condicionais funcionam de forma natural.</p>
<p><strong>Ciclo de vida dos buffers de GPU no WebGPU.</strong> Os buffers do WebGPU são criados com sinalizadores de uso específicos (<code>VERTEX</code>, <code>STORAGE</code>, <code>COPY_DST</code> etc.) e não podem ser redimensionados após a criação. Se uma execução de marching cubes produzir mais vértices do que o buffer comporta, será necessário criar um novo buffer, atualizar o vínculo e destruir o antigo. Destruir um buffer que ainda esteja referenciado por um comando da GPU em andamento causa erros. O gerenciamento explícito de fences (por meio de <code>device.queue.onSubmittedWorkDone()</code>) garante que o buffer antigo não seja destruído antes que a GPU termine de usá-lo. Essa disciplina de ciclo de vida não existe no WebGL, em que o driver gerencia a memória implicitamente.</p>
<p><strong>Fortalecimento incremental.</strong> Uma disciplina de processo para integração: estabelecer uma base comprovadamente funcional, adicionar um recurso de cada vez e verificar se a camada anterior continua funcionando após cada adição. Essa abordagem é mais lenta por um dia e economiza dias durante a depuração posterior, pois cada regressão pode ser rastreada até uma alteração específica e controlada. O padrão de primeiro criar uma base e depois avançar incrementalmente é comum no <a href="/pt-BR/guides/browser-3d-open-world-tech#what-we-d-build-first">desenvolvimento de mundos abertos AAA</a>, em que os sistemas são integrados em uma ordem específica para gerenciar riscos.</p>
<hr>
<p>Parte 8 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Parte 7 — Marching cubes e as primeiras cavernas de verdade</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut">Parte 9 — O Transvoxel começou com uma estrutura básica</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 9: Transvoxel começou com uma estrutura básica]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Não partimos direto para a cobertura completa das junções. Primeiro construímos uma bancada de testes para junções e depois validamos uma face com dados de referência.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 9: Transvoxel começou com uma estrutura básica</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e contém links para todas as partes.</p>
<p>É nas junções que a confiança vai por água abaixo.</p>
<p>Tudo pode parecer estável até duas resoluções se encontrarem. Um chunk com LOD 0 fica ao lado de outro com LOD 1. Suas malhas são geradas de forma independente. No limite compartilhado, as posições dos vértices não coincidem porque o chunk de menor resolução tem metade da densidade da grade. O resultado são rachaduras visíveis, junções em T e bordas tremeluzentes.</p>
<p>O algoritmo Transvoxel resolve isso gerando células de transição especiais ao longo da face de limite entre dois chunks com resoluções diferentes. Essas células coletam amostras simultaneamente das grades de alta e baixa resolução e produzem triângulos que unem as duas superfícies. O algoritmo usa suas próprias tabelas de consulta, separadas das tabelas convencionais de marching cubes, com 512 casos de células de transição.</p>
<p>A essa altura, já tínhamos cicatrizes de integração suficientes para saber que não devíamos apressar a implementação.</p>
<p>O Spike 15 tinha uma única tarefa: construir uma bancada de testes de junções na qual pudéssemos confiar antes de mexer no algoritmo completo. Configuramos um ambiente controlado no qual dois chunks com dados SDF conhecidos ficavam lado a lado em resoluções diferentes, com controles de visualização para alternar de forma independente a malha principal, a malha da junção, o wireframe e as normais.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/15-transvoxel-seam/" title="Estrutura básica de junção Transvoxel do Spike 15" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/15-transvoxel-seam/" target="_blank">Abrir o Spike 15 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=15-transvoxel-seam">Ver código-fonte</a></p>
<p>Quando a bancada de testes ficou estável, o Spike 16 validou a geração de células de transição orientada por tabelas em uma única face. Escolhemos uma face alinhada a um eixo (limite +X), implementamos a avaliação das células de transição apenas para essa face e comparamos o resultado com os dados de referência do artigo sobre Transvoxel.</p>
<p><a href="/pt-BR/spikes/16-transvoxel-face/" target="_blank">Abrir o Spike 16 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=16-transvoxel-face">Ver código-fonte</a></p>
<p>Testamos intencionalmente uma face de cada vez, porque a integração das tabelas de transição apresenta muitos modos de falha independentes. O cálculo do índice de caso depende da coleta dos vértices corretos das duas grades. A indexação dos vértices dentro de uma célula de transição usa um esquema de numeração diferente do usado pelas células convencionais de marching cubes. A ordem de orientação dos vértices precisa ser consistente com a malha principal, ou o backface culling eliminará os triângulos da junção. Se você testar as seis faces ao mesmo tempo, todos os sintomas parecerão aleatórios. Se testar uma face minuciosamente, obterá falhas significativas e depuráveis.</p>
<p>Outro ganho sutil dessa fase foi o investimento em ferramentas. Criamos cedo controles de visibilidade, renderização apenas das junções e indicadores de LOD codificados por cores. Na época, esses controles pareciam trabalho adicional. Mais tarde, quando os casos extremos ficaram complicados, eles compensaram o investimento repetidas vezes, pois conseguíamos isolar exatamente quais células de junção estavam se comportando mal.</p>
<p>Ao final deste capítulo, não tínhamos &quot;terminado as junções&quot;. Estávamos em uma posição na qual era possível analisar os bugs de junção em vez de temê-los.</p>
<p>Na parte 10, começa a verdadeira montanha-russa. Cantos com LODs mistos, inversões de orientação, fantasmas causados por sobredesenho parcial e aqueles momentos em que você tem certeza de que o algoritmo está errado, mas então descobre que o bug é um intervalo de desenho lendo além da contagem de vértices ativos.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>O algoritmo Transvoxel.</strong> Projetado por Eric Lengyel (<a href="https://transvoxel.org/">transvoxel.org</a>), o Transvoxel resolve o problema mais difícil do LOD em terrenos volumétricos: as junções entre chunks com resoluções diferentes. Quando um chunk de alta resolução fica ao lado de outro de baixa resolução, as malhas de marching cubes não se alinham no limite, produzindo rachaduras visíveis. O Transvoxel insere células de transição especiais ao longo das faces de limite, superando a diferença de resolução com triângulos adicionais que correspondem aos dois lados. O algoritmo usa suas próprias tabelas de consulta (separadas das tabelas convencionais de marching cubes), com 512 casos de células de transição reduzidos a 73 classes de equivalência. Ele não é protegido por patentes e já foi usado em jogos lançados (Space Engineers, Astroneer). Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#the-transvoxel-algorithm">guia de paisagens sobre Transvoxel</a>.</p>
<p><strong>Células de transição.</strong> Células especiais geradas na face entre dois níveis de LOD. Ao contrário das células convencionais de marching cubes, que coletam amostras de 8 cantos de uma grade, as células de transição coletam amostras simultaneamente das grades de alta e baixa resolução. A face de alta resolução tem $3 \times 3 = 9$ pontos de amostragem, enquanto a face de baixa resolução tem $2 \times 2 = 4$. Essas 9 amostras são o que dá ao algoritmo seus $2^9 = 512$ casos brutos, que se reduzem a 73 triangulações distintas após eliminar rotações e reflexões equivalentes. A classificação e a triangulação das células usam tabelas dedicadas que produzem triângulos conectando vértices nas duas resoluções. O esquema de numeração dos vértices é diferente daquele usado pelas células convencionais de MC, o que é uma fonte comum de bugs de implementação.</p>
<p><strong>Junções de transição de LOD.</strong> O limite entre duas resoluções de malha no qual a incompatibilidade topológica causa artefatos visuais. Sem a união, um chunk com LOD 0 (grade de 1 m) ao lado de um chunk com LOD 1 (grade de 2 m) produz junções em T: a malha fina tem vértices ao longo do limite que não são compartilhados pela malha grossa, causando rachaduras pelas quais a luz vaza. Transvoxel, geomorphing e geometria de saias são três abordagens para corrigir essas junções. Para terrenos volumétricos, o Transvoxel é a solução padrão porque o geomorphing funciona apenas com mapas de altura. Consulte <a href="/pt-BR/guides/landscape-generation-browser#lod-for-volumetric-terrain">LOD para terrenos volumétricos</a>.</p>
<hr>
<p>Parte 9 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration">Parte 8 — Integração sem perder nossa referência</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos">Parte 10 — Caos nas junções e a batalha contra o chefão dos cantos</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 10: O caos das emendas e a batalha final dos cantos]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Os spikes 17 a 22 foram onde a teoria encontrou os casos extremos. Investigamos bugs de orientação, triângulos ausentes, fantasmas de sobredesenho e lógica híbrida de fallback.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 10: O caos das emendas e a batalha final dos cantos</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e contém links para todas as partes.</p>
<p>Se as partes anteriores pareceram metódicas, este capítulo pareceu um combate.</p>
<p>Os spikes 17 a 22 marcaram nossa era dos casos extremos. Marching cubes com LOD duplo, emendas nas fronteiras entre mapas de altura e MC, chunks de canto com resoluções mistas onde três ou quatro níveis de LOD se encontram, geração de emendas na GPU e comportamento do modo de fallback. Cada spike tratou de um cenário de falha específico que havíamos encontrado ou previsto.</p>
<p>O Spike 17 testou marching cubes duplo com dois níveis de LOD ativos simultaneamente. O desafio era que os vizinhos de um único chunk podiam ter resoluções diferentes em faces distintas. A lógica das células de transição do Spike 16 funcionava em uma face por vez, mas, quando um chunk precisava de células de transição em várias faces, o gerenciamento do buffer de vértices ficava complicado. As células de transição de cada face precisavam ser geradas e anexadas sem sobrescrever as demais.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/19-transvoxel-corner-grid/" title="Spike 19: grade de cantos com Transvoxel" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/19-transvoxel-corner-grid/" target="_blank">Abrir o Spike 19 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=19-transvoxel-corner-grid">Ver código-fonte</a></p>
<p>O primeiro vilão recorrente foi a ordem de orientação dos vértices. Várias vezes achamos que tínhamos problemas de topologia, mas acabamos descobrindo problemas de orientação. O descarte de faces traseiras estava eliminando triângulos válidos das emendas porque a orientação estava invertida em relação à malha principal. A mesma causa-raiz produzia sintomas visuais diferentes dependendo do ângulo da câmera. A correção foi impor uma convenção de orientação consistente no código de emissão das células de transição e verificá-la alternando para um material de duas faces.</p>
<p>O segundo vilão foi a falsa confiança trazida por algo parcialmente correto. Uma emenda podia parecer perfeita de um ângulo de câmera e quebrar quando os papéis de LOD eram trocados entre chunks de maior e menor resolução. A célula de transição é assimétrica. Ela coleta amostras de maneiras diferentes nos lados de alta e baixa resolução. Se a lógica que determina &quot;qual lado tem alta resolução&quot; estiver invertida em uma configuração, o bug só aparece quando a câmera se move para uma posição específica.</p>
<p>Então veio uma de nossas recuperações favoritas. Estávamos investigando um artefato de corte na emenda dos tiles de mapa de altura e culpando a lógica de transição. Perdemos dois dias nisso. O verdadeiro culpado era um sobredesenho residual. A geometria de maior resolução de um frame anterior continuava na cauda do buffer depois que o chunk era reduzido para um LOD mais baixo. O intervalo de desenho ainda estava definido com a contagem de vértices antiga e maior. Assim que limitamos o intervalo de desenho à contagem de vértices ativos informada pelo contador atômico do compute shader, o &quot;misterioso problema de emenda&quot; desapareceu.</p>
<p>Foi um ótimo lembrete de que bugs de renderização muitas vezes se disfarçam de bugs de geração de malha. A geometria estava correta o tempo todo. A chamada de desenho apenas estava lendo além do fim dos dados válidos.</p>
<p>No Spike 22, já estávamos testando um fallback híbrido no qual os chunks podiam alternar de marching cubes para o modo de mapa de altura sob condições específicas, como quando o chunk não contém edições volumétricas e está suficientemente distante da câmera. Isso nos deu um caminho mais prático do que uma política de tudo ou nada. Chunks editados no campo próximo usam MC para oferecer liberdade volumétrica. Chunks não editados no campo distante usam mapas de altura para ganhar eficiência.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/22-gpu-mc-heightmap-fallback/" title="Spike 22: fallback de MC para mapa de altura" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/22-gpu-mc-heightmap-fallback/" target="_blank">Abrir o Spike 22 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=22-gpu-mc-heightmap-fallback">Ver código-fonte</a></p>
<p>Este capítulo foi a queda acentuada da montanha-russa. Frustrante e produtivo ao mesmo tempo. Muitas das correções individuais eram pequenas, às vezes uma única linha alterando um operador de comparação ou um deslocamento. Mas o entendimento que elas proporcionaram sobre como as transições de LOD, o gerenciamento de buffers e os intervalos de desenho interagem não foi nada pequeno.</p>
<p>Na parte 11, abordamos a camada de estabilização que veio depois do caos: modos de chunk baseados em políticas e a transição da correção reativa de bugs para regras explícitas do sistema.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Marching cubes com LOD duplo.</strong> Execução de marching cubes em dois níveis de resolução simultaneamente, com células de transição unindo a fronteira. O desafio é que os vizinhos de um único chunk podem ter resoluções diferentes em faces distintas, exigindo a geração independente de células de transição para cada face. As células de transição de cada face são anexadas ao buffer de vértices sem sobrescrever as demais. Contadores atômicos acompanham a contagem total de vértices ativos em todas as faces.</p>
<p><strong>Ordem de orientação dos vértices.</strong> A ordem dos vértices em cada triângulo determina qual lado é a face &quot;frontal&quot;. Uma orientação consistente — geralmente no sentido anti-horário quando vista de fora — é necessária para o descarte de faces traseiras. Quando as células de transição emitem triângulos, a orientação deve corresponder à convenção da malha principal. Invertê-la faz com que o descarte de faces traseiras elimine triângulos válidos das emendas, o que parece com superfícies ausentes em determinados ângulos de câmera. Uma técnica comum de depuração é alternar <code>side: THREE.DoubleSide</code> no material para confirmar se os artefatos são problemas de orientação ou lacunas reais na topologia.</p>
<p><strong>Fallback de mapa de altura para MC.</strong> Um modo híbrido de chunks no qual chunks distantes ou não editados usam terreno baseado em mapa de altura — barato e com superfície plana — enquanto chunks próximos ou editados usam marching cubes — volumétrico e compatível com cavernas. A decisão de fallback depende da distância até a câmera e de o chunk conter ou não edições de SDF. A emenda entre um chunk de mapa de altura e um chunk de MC exige sua própria geometria de transição, semelhante ao Transvoxel, mas conectando duas representações diferentes em vez de dois níveis de LOD. Consulte <a href="/pt-BR/guides/landscape-generation-browser#hybrid-heightmap-base--volumetric-overlays">mapa de altura híbrido + sobreposições volumétricas</a>.</p>
<p><strong>Intervalo de desenho e contadores atômicos.</strong> Na geração de malha orientada pela GPU, o compute shader grava vértices em um buffer e incrementa um contador atômico para acompanhar quantos vértices foram emitidos. A chamada de desenho deve usar esse contador como a contagem de vértices, e não a capacidade do buffer. Se o intervalo de desenho não for limitado à contagem ativa, vértices residuais de frames anteriores — ainda presentes na cauda do buffer — produzem geometria fantasma: lascas finas e triângulos tremeluzentes que parecem erros de topologia, mas na verdade são artefatos de renderização causados pela leitura além dos dados válidos.</p>
<hr>
<p>Parte 10 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut">Parte 9 — O Transvoxel começou com uma estrutura básica</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes">Parte 11 — Modo por política, não modo codificado diretamente</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 11: Modo baseado em políticas, não modo hardcoded]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O Spike 23 nos levou de correções reativas a políticas explícitas. LOD orientado por distância, alternância entre HM/MC orientada por edições e controles melhores para depurar emendas.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 11: Modo baseado em políticas, não modo hardcoded</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>Depois do capítulo sobre o caos das emendas, precisávamos parar de reagir e começar a governar.</p>
<p>O Spike 23 substituiu comportamentos improvisados por regras explícitas de política. Em vez de cada chunk fazer o que seu estado local sugeria, um sistema central de políticas passou a tomar as decisões. Qual nível de LOD este chunk recebe? Ele é renderizado como heightmap ou marching cubes? Precisa de células de transição e, se sim, em quais faces? As respostas vinham de uma função de política que avaliava a distância até a câmera, o histórico de edições e os estados de resolução dos vizinhos.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/23-policy-chunk-modes/" title="Spike 23: Políticas para os modos dos chunks" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/23-policy-chunk-modes/" target="_blank">Abrir o Spike 23 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=23-policy-chunk-modes">Ver código-fonte</a></p>
<p>A atribuição de LOD baseada em distância usava anéis concêntricos ao redor da câmera, de maneira semelhante ao conceito de clipmap, mas aplicada à grade de chunks. Os chunks do anel 0 recebem MC em resolução máxima. O anel 1 recebe MC em meia resolução. O anel 2 e os seguintes recebem o modo heightmap. A restrição de adjacência era crucial: para quaisquer dois chunks vizinhos, exigimos $|L_i - L_j| \le 1$. A proporção de resolução entre as duas faces é $2^{|L_i - L_j|}$, e as tabelas de transição do Transvoxel são definidas apenas para o caso 2:1 ($|L_i - L_j| = 1$). Um salto de 2 níveis exigiria uma transição 4:1 que as tabelas não conseguem representar, então a política força o chunk com menos detalhes a aumentar sua resolução até que a restrição seja atendida.</p>
<p>A lógica de alternância entre HM/MC verificava o bitmap de edições de cada chunk. Se um chunk tivesse qualquer edição volumétrica (cavernas, túneis ou terreno esculpido), ele permanecia no modo MC independentemente da distância. Chunks sem edições podiam passar para o modo heightmap quando se afastavam o suficiente. Essa abordagem híbrida nos deu liberdade volumétrica onde ela importava e eficiência onde não importava.</p>
<p>Visto de fora, este spike parecia menor do que alguns dos anteriores. Na prática, foi uma grande melhoria na qualidade de vida durante o desenvolvimento.</p>
<p>Quando seu sistema consegue explicar por que um chunk mudou de modo, você passa menos tempo tentando adivinhar. Adicionamos sobreposições com códigos de cores: verde para chunks de heightmap, azul para chunks de MC e laranja para faces com transições ativas. Quando a visibilidade das emendas tem controles de renderização dedicados, a ambiguidade da depuração visual diminui. Quando os intervalos de desenho são vinculados explicitamente às contagens de vértices ativos fornecidas pelo sistema de políticas, os fantasmas de geometria obsoleta deixam de desperdiçar sua tarde.</p>
<p>Também reformulamos o comportamento da câmera neste spike. Os spikes anteriores tinham controles simples de órbita, adequados para capturas de tela, mas inúteis para reproduzir bugs. O Spike 23 adicionou uma câmera de voo com WASD, velocidade configurável, opção de travar a altitude e exibição da posição. Isso parece algo pequeno. Mas representou a diferença entre &quot;vi um bug em algum lugar perto daquela cordilheira&quot; e &quot;o bug aparece na posição (142, 12, -67), com a câmera voltada para noroeste&quot;.</p>
<p>O principal aprendizado deste capítulo é que a política não reduziu a complexidade. Ela organizou a complexidade. A mesma quantidade de casos extremos continuava existindo. Mas agora cada caso extremo tinha um nome, uma condição de disparo e um ponto no código onde era possível definir um breakpoint. Esse é outro tipo de vitória, e é isso que determina se um sistema pode continuar evoluindo ou se entra em colapso sob o próprio peso.</p>
<p>Ao final do Spike 23, tínhamos uma camada previsível de comportamento de campo próximo, pronta para ser conectada a uma estratégia de anéis de clipmap para o campo distante sem o medo constante de bugs de interação.</p>
<p>Na parte 12, abordamos o Spike 24, em que as transições entre anéis, a névoa do skybox e a integração de shaders no nível de versão do Three.js encerram este capítulo do projeto.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Política de LOD baseada em distância.</strong> Uma função central que atribui o nível de LOD e o modo de renderização a cada chunk com base na distância até a câmera, no histórico de edições e nos estados dos vizinhos. Anéis concêntricos de distância determinam o LOD base: anel 0 = MC em resolução máxima, anel 1 = MC em meia resolução, anel 2+ = modo heightmap. A função de política é executada a cada quadro conforme a câmera se move e aciona as transições dos chunks. Isso substitui decisões improvisadas por chunk por um sistema de regras previsível e fácil de depurar. Consulte <a href="/pt-BR/guides/landscape-generation-browser#gpu-driven-lod-with-webgpu">Seleção de LOD orientada pela GPU</a> para ver o equivalente em compute shader.</p>
<p><strong>Restrições de adjacência.</strong> O algoritmo Transvoxel lida apenas com proporções de resolução de 2:1. Se dois chunks vizinhos diferirem em mais de um nível de LOD (por exemplo, LOD 0 ao lado de LOD 2), as tabelas de transição não conseguem produzir uma geometria de emenda válida. O sistema de políticas aplica essa regra aumentando a resolução do chunk com menos detalhes quando a diferença de LOD ultrapassa 1. Essa propagação de restrições pode ocorrer em cascata: aumentar a resolução de um chunk pode forçar seus vizinhos a fazer o mesmo. A implementação é uma passagem iterativa simples que converge em 2 a 3 iterações nas configurações de grade mais comuns.</p>
<p><strong>Bitmap de edições para seleção de modo.</strong> Cada chunk mantém um bitmap que registra se ele contém edições volumétricas de SDF (cavernas, túneis ou partes esculpidas). Chunks com qualquer edição permanecem no modo marching cubes independentemente da distância, preservando as modificações do criador. Chunks sem edições passam para o modo heightmap quando estão suficientemente distantes da câmera, economizando processamento e memória. O bitmap consiste em um único sinalizador por chunk, mas pode ser ampliado para registrar a densidade de edições e permitir decisões de modo mais granulares.</p>
<p><strong>Sobreposições para depuração visual.</strong> Renderização de chunks com códigos de cores, em que verde = modo heightmap, azul = modo MC e laranja = faces com transições ativas. Sobreposições por chunk com números de nível de LOD, rótulos de modo e opções para ativar a visualização em wireframe. Essas são ferramentas de desenvolvimento, não recursos entregues no produto, mas compensam o investimento repetidamente durante a depuração de transições de LOD e artefatos nas emendas. Combinadas a uma câmera de voo com WASD que informa a posição exata no mundo, elas transformam &quot;vi um bug em algum lugar&quot; em &quot;o bug aparece em (142, 12, -67) com esta configuração de LOD&quot;.</p>
<hr>
<p>Parte 11 de 12.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos">Parte 10 — O caos das emendas e a batalha contra o chefe dos cantos</a><br>
Próxima: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons">Parte 12 — Anéis, névoa no céu e o que faríamos novamente</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um mundo aberto no navegador, parte 12: Anéis, névoa do céu e o que faríamos novamente]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[O spike 24 encerrou este capítulo com transições entre anéis de clipmap, integração da névoa com o skybox e uma longa lista de lições aprendidas a duras penas.]]></description>
            <content:encoded><![CDATA[<h1>Construindo um mundo aberto no navegador, parte 12: Anéis, névoa do céu e o que faríamos novamente</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Chegou agora? Consulte o <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">guia da série</a>. Ele explica o que é um spike e reúne links para todas as partes.</p>
<p>O spike 24 deveria ser apenas “adicionar anéis de clipmap ao terreno”. Acabou se tornando um grande final que envolveu renderização, shaders, infraestrutura de módulos e integração visual, tudo ao mesmo tempo.</p>
<p>A principal tarefa relacionada ao terreno era gerar anéis concêntricos de clipmap no vertex shader. Cada anel é uma malha de grade plana centralizada na câmera, com os vértices deslocados por amostras do mapa de altura. O anel interno usa a resolução máxima. Cada anel subsequente dobra o espaçamento entre vértices e cobre uma área maior. A parte complicada é o limite entre os anéis: onde um anel de alta resolução encontra um de baixa resolução, os vértices da borda da malha mais detalhada precisam se ajustar ao ponto médio da borda da malha mais grosseira. Fizemos um morphing de borda 2:1 detectando os vértices de limite — aqueles cuja coordenada na grade é ímpar ao longo da borda do anel — e ajustando sua altura ao ponto médio dos dois vizinhos pares, $h = \tfrac{1}{2}(h_\text{left} + h_\text{right})$. Esse é exatamente o ponto em que ficaria a única aresta do anel mais grosseiro, de modo que as bordas detalhada e grosseira descrevem a mesma linha. Isso produz emendas estanques sem geometria de transição.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0;border:1px solid rgba(255,255,255,0.12)">
<iframe src="/pt-BR/spikes/24-gpu-clipmap-rings/" title="Spike 24: anéis de clipmap e névoa do céu" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000" loading="lazy" allowfullscreen></iframe>
</div>
<p><a href="/pt-BR/spikes/24-gpu-clipmap-rings/" target="_blank">Abrir o spike 24 em uma nova aba ↗</a> · <a href="/pt-BR/blog/spike-source?spike=24-gpu-clipmap-rings">Ver código-fonte</a></p>
<p>Depois veio a integração da névoa com o céu. Queríamos que o terreno distante se fundisse com a cor real do céu, e não com uma constante uniforme. Isso significava que o shader de névoa precisava saber qual seria a cor do céu na direção de cada fragmento. Carregamos uma textura HDR equiretangular de skybox e a amostramos no fragment shader usando a direção de visão da câmera até o fragmento, convertida em coordenadas UV equiretangulares por meio do nó <code>equirectUV</code> do TSL. O fator de névoa era baseado na distância, usando <code>positionView.z.negate()</code> para a profundidade no espaço da câmera, com uma interpolação via <code>smoothstep</code> entre uma distância próxima e outra distante.</p>
<p>A conexão dos módulos acabou sendo mais irritante do que qualquer parte da geometria. Atualizamos para o Three.js 0.183.1, que reorganizou as saídas do build. O import <code>three/tsl</code> precisava ser resolvido para <code>three.tsl.js</code>, e o TSL importava internamente <code>three/webgpu</code> como um especificador simples. Ambos os mapeamentos precisavam estar explícitos no mapa de importação do HTML. A ausência de qualquer um deles gerava erros obscuros como “does not provide an export” ou “failed to resolve module specifier”, sem indicar qual mapeamento estava errado. Assim que ambos foram adicionados ao mapa de importação, o grafo de shaders carregou corretamente.</p>
<p>Também tivemos um problema de orientação do skybox, em que a textura era renderizada de cabeça para baixo. A correção foi definir <code>flipY = true</code> na textura equiretangular, que é o padrão do Three.js para texturas carregadas, mas estava definido como <code>false</code> em nosso código inicial.</p>
<p>A implementação original da névoa amostrava o céu em uma direção quase constante, produzindo uma faixa estreita da cor do horizonte em vez de um gradiente natural. A correção foi calcular, para cada pixel, a direção real no espaço do mundo entre a câmera e o fragmento usando <code>positionWorld.sub(cameraPosition).normalize()</code> e passá-la para <code>equirectUV</code> na consulta da cor da névoa. Isso fez com que os fragmentos do terreno se fundissem com a cor do céu que realmente está atrás deles, o que parece correto de qualquer ângulo da câmera.</p>
<p>Por baixo de todas as correções individuais, o resultado principal se manteve. Agora temos um sistema de terreno que combina edição volumétrica de curta distância — marching cubes com emendas Transvoxel —, chunks de mapa de altura a média distância e anéis de clipmap a longa distância, tudo governado por uma camada de políticas que decide o modo, o LOD e o comportamento das transições.</p>
<p>Se eu tivesse que citar os padrões que repetiria no próximo projeto, seriam estes:</p>
<p>Comece com spikes de risco antes de desenvolver funcionalidades. O spike 1 respondeu à pergunta “será que conseguimos renderizar rápido o suficiente?” antes de investirmos em pipelines de conteúdo.</p>
<p>Congele baselines estáveis antes de grandes saltos de integração. Os spikes 13 e 14 nos pouparam dias procurando regressões por bisseção.</p>
<p>Imponha políticas e observabilidade antes de maratonas de otimização. O spike 23 transformou bugs misteriosos em condições nomeadas com regras de acionamento.</p>
<p>Teste em movimento, não com capturas de tela. Estalos de clipmap, cintilação nas emendas e travamentos de streaming ficam ocultos em quadros estáticos.</p>
<p>Meça o custo de tempo de quadro por funcionalidade, não o FPS médio. As médias escondem os picos que os usuários realmente sentem.</p>
<p>E publique as partes bagunçadas. Os caminhos errados, as caçadas a fantasmas em buffers desatualizados, os dois dias culpando a lógica de transição quando o problema estava no intervalo de desenho. Essas são as partes com as quais as pessoas realmente podem aprender.</p>
<h2>Verificação com o mundo real: devlogs de Vuntra City</h2>
<p>Depois de concluir esta série, analisamos os devlogs de <code>@VuntraCity</code> como uma verificação externa da implementação em relação às nossas próprias premissas sobre mundos abertos. É um projeto nativo em UE5, não uma stack de navegador, mas os padrões de sistema são semelhantes o bastante para tornar a comparação útil.</p>
<p>O primeiro sinal é que a velocidade de deslocamento precisa ser tratada como um controle de streaming, e não apenas como parte da jogabilidade. Em Vuntra City, o transporte de alta velocidade é deliberadamente direcionado para acima da maioria dos interiores, e o alcance dos detalhes varia de acordo com a velocidade de movimento para evitar recriações constantes e interrupções (<a href="https://www.youtube.com/watch?v=KKeBElJS6-M">sistema de transporte</a>, <a href="https://www.youtube.com/watch?v=aPuYXyJet38">técnicas de desempenho</a>). Isso corresponde à direção da nossa camada de políticas: o modo de movimento deve influenciar diretamente o raio dos chunks, a ativação de interiores e a quantidade de trabalho permitida por quadro.</p>
<p>O segundo sinal é a arquitetura. Os mapas e o sistema de endereços deles exigiram separar a topologia do mundo dos objetos renderizados para que consultas globais pudessem ser executadas em regiões descarregadas (<a href="https://www.youtube.com/watch?v=6dLn1GQpu2c">mapas e endereços</a>). É a mesma separação de que precisamos no navegador para busca no mundo, roteamento de missões, verificações de moderação e indexação de pontos de interesse, sem forçar caminhos de dados vinculados à renderização.</p>
<p>O terceiro sinal é a divisão da simulação em níveis. O projeto para um milhão de NPCs mantém o estado geral dos cronogramas barato e global, reservando o custoso orçamento de comportamento apenas para as proximidades do jogador (<a href="https://www.youtube.com/watch?v=nBV0yAAJUf0">visão geral de um milhão de NPCs</a>, <a href="https://www.youtube.com/watch?v=eUi7DB1ar3s">análise detalhada do sistema</a>). Isso reforça nosso próprio modelo de simulação centrado em AOI, no qual a fidelidade do campo próximo e o determinismo do campo distante são aspectos separados, com orçamentos distintos.</p>
<p>E o quarto sinal é a qualidade do design, não a escala bruta. Os melhores momentos de exploração surgem de distribuições ponderadas, exceções raras e pistas diegéticas de navegação, em vez de sobreposições constantes da interface (<a href="https://www.youtube.com/watch?v=4MZ5-KQW3pc">observações sobre ambientes procedurais</a>, <a href="https://www.youtube.com/watch?v=ixR1hqZJlv4">ciclo sem minimapa</a>). Para nós, isso serve de lembrete de que os sistemas técnicos devem ser ajustados para produzir variações que possam ser descobertas, e não apenas a maior vazão possível.</p>
<h2>Tecnologias mencionadas neste capítulo</h2>
<p><strong>Geometria de anéis de clipmap.</strong> Cada anel é uma malha de grade plana centralizada na câmera, com os vértices deslocados por amostras do mapa de altura. O anel interno usa a resolução máxima. Cada anel subsequente dobra o espaçamento entre vértices e cobre uma área maior. A parte complicada é o limite: onde um anel de alta resolução encontra um de baixa resolução, os vértices da borda da malha mais detalhada se ajustam ao ponto médio da borda da malha mais grosseira. A técnica tem origem no artigo de Losasso e Hoppe para a SIGGRAPH 2004 (<a href="https://hhoppe.com/geomclipmap.pdf">PDF</a>) e é detalhada em <a href="https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry">GPU Gems 2, capítulo 2</a>. Consulte nosso <a href="/pt-BR/guides/landscape-generation-browser#geometry-clipmaps">guia de paisagens sobre clipmaps de geometria</a>.</p>
<p><strong>Morphing de borda 2:1.</strong> No limite entre dois anéis de clipmap, o anel mais detalhado tem vértices em posições que não existem no anel mais grosseiro. Os vértices de limite cuja coordenada na grade é ímpar ao longo da borda do anel são detectados, e sua altura é interpolada entre os dois vértices pares vizinhos. Isso produz emendas estanques sem uma geometria de transição dedicada. A interpolação é executada no vertex shader: <code>morphedHeight = mix(heightLeft, heightRight, 0.5)</code> para os vértices de limite, usando a mesma estrutura de geomorphing descrita em <a href="/pt-BR/guides/landscape-generation-browser#geomorphing-pop-free-lod-transitions">nosso guia</a>.</p>
<p><strong>Mapeamento de skybox equiretangular.</strong> Uma única imagem 2D que mapeia a esfera completa das direções do céu usando uma projeção de longitude e latitude. O eixo horizontal cobre de 0 a 360 graus, e o eixo vertical cobre de 0 a 180 graus. Uma direção de visão normalizada $\hat{d} = (d_x, d_y, d_z)$ é convertida em coordenadas UV a partir de sua longitude e latitude:</p>
<p>$$
u = \frac{1}{2} + \frac{\operatorname{atan2}(d_z,, d_x)}{2\pi}, \qquad v = \frac{1}{2} - \frac{\arcsin(d_y)}{\pi}
$$</p>
<p>No Three.js, definir <code>texture.mapping = EquirectangularReflectionMapping</code> com <code>SRGBColorSpace</code> habilita esse recurso como plano de fundo da cena. No TSL, <code>equirectUV(direction)</code> aplica a mesma conversão, transformando uma direção de visão 3D nas coordenadas UV 2D usadas para amostrar a textura.</p>
<p><strong>Cor da névoa por fragmento obtida do céu.</strong> A névoa padrão mistura os fragmentos com uma única cor constante. Em uma cena com um skybox detalhado, isso parece errado porque a cor do céu varia conforme a direção. A solução é calcular, para cada pixel, a direção no espaço do mundo entre a câmera e o fragmento (<code>positionWorld.sub(cameraPosition).normalize()</code>) e amostrar o skybox nessa direção para obter a cor da névoa. Cada fragmento se funde com a cor do céu que realmente está atrás dele, produzindo uma mistura correta de qualquer ângulo da câmera. O fator de névoa usa <code>smoothstep(nearDist, farDist, viewDepth)</code> com <code>positionView.z.negate()</code> para a profundidade no espaço da câmera.</p>
<p><strong>Mapas de importação para módulos ES.</strong> Um mecanismo nativo do navegador (<code>&lt;script type=&quot;importmap&quot;&gt;</code>) que mapeia especificadores simples de módulos — como <code>three/tsl</code> — para URLs reais. Quando o Three.js 0.183.1 reorganizou as saídas do build, <code>three/tsl</code> precisou ser resolvido para <code>three.tsl.js</code>, enquanto o TSL importava internamente <code>three/webgpu</code> como um especificador simples. Ambos os mapeamentos precisavam estar explícitos no mapa de importação, ou o navegador gerava erros como “does not provide an export” ou “failed to resolve module specifier”.</p>
<h2>Leitura complementar</h2>
<p>Para uma cobertura mais aprofundada das tecnologias usadas ao longo desta série, consulte nossos guias complementares:</p>
<ul>
<li><a href="/pt-BR/guides/landscape-generation-browser">Geração de paisagens com LOD dinâmico e streaming para mundos abertos no navegador</a> aborda mapas de altura, SDFs, marching cubes, Transvoxel, clipmaps de geometria, geomorphing, arquitetura de streaming, materiais de terreno e renderização de vegetação.</li>
<li><a href="/pt-BR/guides/browser-3d-open-world-tech">Tecnologia 3D para mundos abertos multijogador criados por usuários no navegador</a> aborda stacks de renderização, WebGPU, física, redes, arquitetura multijogador e lições de Skyrim, The Witcher 3, Breath of the Wild e GTA V.</li>
</ul>
<p>Obrigado por acompanhar esta jornada em doze partes.</p>
<p>Parte 1: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first">Começamos tentando quebrar tudo</a><br>
Parte 2: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics">Física em worker e o medo do atraso de entrada</a><br>
Parte 3: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes">Os spikes pouco chamativos que nos salvaram</a><br>
Parte 4: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">Streaming antes de terrenos sofisticados</a><br>
Parte 5: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty">Definindo o orçamento dos efeitos visuais</a><br>
Parte 6: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps">Os clipmaps mudaram o rumo da história</a><br>
Parte 7: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Marching cubes e as primeiras cavernas de verdade</a><br>
Parte 8: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration">Integração sem perder nosso baseline</a><br>
Parte 9: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut">O Transvoxel começou com uma estrutura básica</a><br>
Parte 10: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos">O caos das emendas e a batalha contra o chefão dos cantos</a><br>
Parte 11: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes">Modo definido por políticas, não fixo no código</a></p>
<hr>
<p>Parte 12 de 14.<br>
Anterior: <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes">Parte 11 — Modo definido por políticas, não fixo no código</a><br>
Próxima: <a href="/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting">Parte 13 — Escultura de terreno e a morte da função matemática</a><br>
Guia da série: <a href="/pt-BR/blog/2026-02-25-open-world-browser-series-guide">/pt-BR/blog/2026-02-25-open-world-browser-series-guide</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Guia da série sobre mundo aberto no navegador]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-series-guide</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-25-open-world-browser-series-guide</guid>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Ordem de leitura da série em 29 partes sobre mundo aberto no navegador, além de um glossário de spikes em linguagem simples e um mapa de demonstrações ao vivo.]]></description>
            <content:encoded><![CDATA[<h1>Guia da série sobre mundo aberto no navegador</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO e cofundador da Cinevva</em></p>
<p>Esta página é o ponto central de navegação da série completa.</p>
<p>Se você chegou por uma parte aleatória, comece aqui.</p>
<p>Atualização de março de 2026: adicionamos uma verificação externa da implementação com base nos diários de desenvolvimento de Vuntra City na <a href="/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons">Parte 12</a> e ampliamos o <a href="/pt-BR/guides/browser-3d-open-world-tech">guia técnico complementar sobre mundos abertos no navegador</a> com as mesmas descobertas.</p>
<p>Atualização de abril de 2026: as Partes 13-14 abordam a escultura de terreno (Spikes 25-27) e a camada visual e de jogabilidade: texturização com múltiplos materiais, vegetação instanciada e um controlador de personagem com colisão HM+SDF (Spikes 28-30).</p>
<p>Atualização de maio de 2026: a série chegou a 29 partes. As Partes 15-29 cobrem o longo trecho intermediário da implementação, terreno e água multijogador, uma refatoração da estrutura do código, animações e distribuição com IA, impostores e paralaxe, o buffer de visibilidade que não compensou, nuvens e culling de meshlets, avatares em lote e voz por proximidade, persistência do mundo e vento na GPU, personagens universais, três abordagens para água, uma ilha procedural, grama e oclusão, além de um controlador de personagem modular capaz de controlar qualquer corpo (Spikes 31-60).</p>
<p>Atualização de junho de 2026: a Parte 30 adiciona uma câmera ciente de colisões, um braço de câmera com mola, um contrato modular de colisão e uma sonda de distância com sinal que impede a câmera de atravessar saliências e tetos de cavernas, não apenas colinas.</p>
<h2>O que &quot;spike&quot; significa nesta série</h2>
<p>Um spike é um experimento curto e focado.</p>
<p>Cada spike testa uma questão arriscada. Mantemos o escopo restrito, medimos o que importa e decidimos o próximo passo. Pense nele como um instrumento de aprendizado, não como um recurso finalizado.</p>
<p>Neste projeto, os spikes são, em sua maioria, páginas independentes em <code>/spikes/*</code> que você pode executar ao vivo.</p>
<h2>Leia a série em ordem</h2>
<ol>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-01-risk-first">Parte 1: Começamos tentando quebrar tudo</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-02-worker-physics">Parte 2: Física em worker e o medo do atraso de entrada</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-03-the-unflashy-spikes">Parte 3: Os spikes pouco chamativos que nos salvaram</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-04-streaming-before-fancy">Parte 4: Streaming antes de um terreno sofisticado</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-05-budgeting-the-pretty">Parte 5: Definindo o orçamento dos recursos visuais</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-06-clipmaps">Parte 6: Os clipmaps mudaram o rumo da história</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-07-marching-cubes">Parte 7: Marching cubes e as primeiras cavernas de verdade</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-08-webgpu-integration">Parte 8: Integração sem perder nossa linha de base</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-09-transvoxel-first-cut">Parte 9: O Transvoxel começou com uma estrutura básica</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-10-seam-chaos">Parte 10: O caos das emendas e a batalha contra o chefão dos cantos</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-11-policy-modes">Parte 11: Modos definidos por políticas, não codificados diretamente</a></li>
<li><a href="/pt-BR/blog/2026-02-25-open-world-browser-part-12-lessons">Parte 12: Anéis, névoa no céu e o que faríamos novamente</a></li>
<li><a href="/pt-BR/blog/2026-04-13-open-world-browser-part-13-terrain-sculpting">Parte 13: Escultura de terreno e o fim da função matemática</a></li>
<li><a href="/pt-BR/blog/2026-04-13-open-world-browser-part-14-world-comes-alive">Parte 14: O mundo ganha vida</a></li>
<li><a href="/pt-BR/blog/2026-04-13-open-world-browser-part-15-multiplayer-and-water">Parte 15: Substitua a linha de base e depois sincronize-a</a></li>
<li><a href="/pt-BR/blog/2026-04-28-open-world-browser-part-16-structure-and-authoring">Parte 16: Estrutura para um mundo que não para de crescer</a></li>
<li><a href="/pt-BR/blog/2026-05-01-open-world-browser-part-17-animations-and-search">Parte 17: Animações que não precisaram de retargeting e uma busca de assets ao vivo</a></li>
<li><a href="/pt-BR/blog/2026-05-02-open-world-browser-part-18-ai-scattering">Parte 18: Um pincel de distribuição que parece usar posicionamento por IA</a></li>
<li><a href="/pt-BR/blog/2026-05-04-open-world-browser-part-19-imposters">Parte 19: O impostor que precisa sobreviver a uma floresta</a></li>
<li><a href="/pt-BR/blog/2026-05-05-open-world-browser-part-20-parallax-occlusion">Parte 20: Simulando profundidade em um plano</a></li>
<li><a href="/pt-BR/blog/2026-05-06-open-world-browser-part-21-visibility-buffer">Parte 21: Um renderizador mais rápido que não era mais rápido</a></li>
<li><a href="/pt-BR/blog/2026-05-07-open-world-browser-part-22-clouds-and-meshlets">Parte 22: Nuvens que podem ser iluminadas e um culling que precisa ser alimentado</a></li>
<li><a href="/pt-BR/blog/2026-05-08-open-world-browser-part-23-avatars-and-voice">Parte 23: Cinquenta avatares e uma voz na sala</a></li>
<li><a href="/pt-BR/blog/2026-05-09-open-world-browser-part-24-persistence-and-wind">Parte 24: Salvando um mundo e um vento que você pode ver</a></li>
<li><a href="/pt-BR/blog/2026-05-10-open-world-browser-part-25-universal-characters">Parte 25: Um esqueleto para todos os trajes</a></li>
<li><a href="/pt-BR/blog/2026-05-12-open-world-browser-part-26-water">Parte 26: Três maneiras de criar água</a></li>
<li><a href="/pt-BR/blog/2026-05-13-open-world-browser-part-27-island-and-terrain">Parte 27: Uma ilha gerada por ruído e um solo que realmente parece solo</a></li>
<li><a href="/pt-BR/blog/2026-05-14-open-world-browser-part-28-grass-and-occlusion">Parte 28: Grama até o horizonte e um terreno que se oculta</a></li>
<li><a href="/pt-BR/blog/2026-05-15-open-world-browser-part-29-pluggable-character">Parte 29: Um controlador para qualquer corpo</a></li>
<li><a href="/pt-BR/blog/2026-06-04-open-world-browser-part-30-collision-aware-camera">Parte 30: Uma câmera que respeita as paredes</a></li>
</ol>
<h2>Mapa de spikes ao vivo</h2>
<p>Comece por estes se quiser percorrer diretamente a linha do tempo técnica.</p>
<p><a href="/pt-BR/spikes/01-terrain/" target="_blank">Spike 1</a> terreno e instanciamento · <a href="/pt-BR/blog/spike-source?spike=01-terrain">código-fonte</a><br>
<a href="/pt-BR/spikes/02-rapier-worker/" target="_blank">Spike 2</a> física em worker · <a href="/pt-BR/blog/spike-source?spike=02-rapier-worker">código-fonte</a><br>
O Spike 3, um teste de carga de transmissão com Durable Objects, foi executado como script de serviço, não como página independente<br>
O Spike 4 testou restrições de qualidade em dispositivos móveis pelo parâmetro de consulta do Spike 1<br>
O Spike 5 testou a confiabilidade do comportamento de LLMs e foi executado como script, não como página independente<br>
<a href="/pt-BR/spikes/06-chunk-streaming/" target="_blank">Spike 6</a> carregamento e troca de chunks · <a href="/pt-BR/blog/spike-source?spike=06-chunk-streaming">código-fonte</a><br>
<a href="/pt-BR/spikes/07-gpu-vegetation/" target="_blank">Spike 7</a> vegetação gerada com mapas de densidade · <a href="/pt-BR/blog/spike-source?spike=07-gpu-vegetation">código-fonte</a><br>
<a href="/pt-BR/spikes/08-terrain-material/" target="_blank">Spike 8</a> custo do material do terreno · <a href="/pt-BR/blog/spike-source?spike=08-terrain-material">código-fonte</a><br>
<a href="/pt-BR/spikes/09-csm-shadows/" target="_blank">Spike 9</a> orçamento para sombras em cascata · <a href="/pt-BR/blog/spike-source?spike=09-csm-shadows">código-fonte</a><br>
<a href="/pt-BR/spikes/10-clipmap-geomorph/" target="_blank">Spike 10</a> clipmaps e geomorphing · <a href="/pt-BR/blog/spike-source?spike=10-clipmap-geomorph">código-fonte</a><br>
<a href="/pt-BR/spikes/11-chunk-streaming/" target="_blank">Spike 11</a> streaming de chunks de mapa de altura · <a href="/pt-BR/blog/spike-source?spike=11-chunk-streaming">código-fonte</a><br>
<a href="/pt-BR/spikes/12-webgpu-marching-cubes/" target="_blank">Spike 12</a> marching cubes com WebGPU · <a href="/pt-BR/blog/spike-source?spike=12-webgpu-marching-cubes">código-fonte</a><br>
<a href="/pt-BR/spikes/13-threejs-webgpu/" target="_blank">Spike 13</a> linha de base da integração com WebGPU · <a href="/pt-BR/blog/spike-source?spike=13-threejs-webgpu">código-fonte</a><br>
<a href="/pt-BR/spikes/14-threejs-webgpu-incremental/" target="_blank">Spike 14</a> robustecimento incremental · <a href="/pt-BR/blog/spike-source?spike=14-threejs-webgpu-incremental">código-fonte</a><br>
<a href="/pt-BR/spikes/15-transvoxel-seam/" target="_blank">Spike 15</a> estrutura básica das emendas · <a href="/pt-BR/blog/spike-source?spike=15-transvoxel-seam">código-fonte</a><br>
<a href="/pt-BR/spikes/16-transvoxel-face/" target="_blank">Spike 16</a> primeira face Transvoxel · <a href="/pt-BR/blog/spike-source?spike=16-transvoxel-face">código-fonte</a><br>
<a href="/pt-BR/spikes/17-dual-mc-lod/" target="_blank">Spike 17</a> LOD duplo de MC · <a href="/pt-BR/blog/spike-source?spike=17-dual-mc-lod">código-fonte</a><br>
<a href="/pt-BR/spikes/18-transvoxel-heightmap-seam/" target="_blank">Spike 18</a> emenda de mapa de altura · <a href="/pt-BR/blog/spike-source?spike=18-transvoxel-heightmap-seam">código-fonte</a><br>
<a href="/pt-BR/spikes/19-transvoxel-corner-grid/" target="_blank">Spike 19</a> canto com resoluções mistas · <a href="/pt-BR/blog/spike-source?spike=19-transvoxel-corner-grid">código-fonte</a><br>
<a href="/pt-BR/spikes/20-gpu-transvoxel-corner/" target="_blank">Spike 20</a> emenda de canto na GPU · <a href="/pt-BR/blog/spike-source?spike=20-gpu-transvoxel-corner">código-fonte</a><br>
<a href="/pt-BR/spikes/21-gpu-mc-transvoxel-corner/" target="_blank">Spike 21</a> emenda de canto MC na GPU · <a href="/pt-BR/blog/spike-source?spike=21-gpu-mc-transvoxel-corner">código-fonte</a><br>
<a href="/pt-BR/spikes/22-gpu-mc-heightmap-fallback/" target="_blank">Spike 22</a> fallback de MC para HM · <a href="/pt-BR/blog/spike-source?spike=22-gpu-mc-heightmap-fallback">código-fonte</a><br>
<a href="/pt-BR/spikes/23-policy-chunk-modes/" target="_blank">Spike 23</a> modos de chunk baseados em políticas · <a href="/pt-BR/blog/spike-source?spike=23-policy-chunk-modes">código-fonte</a><br>
<a href="/pt-BR/spikes/24-gpu-clipmap-rings/" target="_blank">Spike 24</a> anéis de clipmap e névoa no céu · <a href="/pt-BR/blog/spike-source?spike=24-gpu-clipmap-rings">código-fonte</a><br>
<a href="/pt-BR/spikes/25-heightmap-brush/" target="_blank">Spike 25</a> pincel de mapa de altura sobre dados amostrados · <a href="/pt-BR/blog/spike-source?spike=25-heightmap-brush">código-fonte</a><br>
<a href="/pt-BR/spikes/26-sdf-brush/" target="_blank">Spike 26</a> pincel SDF por computação na GPU · <a href="/pt-BR/blog/spike-source?spike=26-sdf-brush">código-fonte</a><br>
<a href="/pt-BR/spikes/27-hm-mc-brush/" target="_blank">Spike 27</a> integração de HM + MC + pincel · <a href="/pt-BR/blog/spike-source?spike=27-hm-mc-brush">código-fonte</a><br>
<a href="/pt-BR/spikes/28-multi-material/" target="_blank">Spike 28</a> texturização com múltiplos materiais · <a href="/pt-BR/blog/spike-source?spike=28-multi-material">código-fonte</a><br>
<a href="/pt-BR/spikes/29-vegetation/" target="_blank">Spike 29</a> vegetação e grama por shader · <a href="/pt-BR/blog/spike-source?spike=29-vegetation">código-fonte</a><br>
<a href="/pt-BR/spikes/30-physics/" target="_blank">Spike 30</a> física do terreno e controlador de personagem · <a href="/pt-BR/blog/spike-source?spike=30-physics">código-fonte</a><br>
<a href="/pt-BR/spikes/31-multiplayer-sync/" target="_blank">Spike 31</a> sincronização multijogador do terreno · <a href="/pt-BR/blog/spike-source?spike=31-multiplayer-sync">código-fonte</a><br>
<a href="/pt-BR/spikes/32-water-swimming/" target="_blank">Spike 32</a> água e natação · <a href="/pt-BR/blog/spike-source?spike=32-water-swimming">código-fonte</a><br>
<a href="/pt-BR/spikes/33-code-structure/" target="_blank">Spike 33</a> refatoração da estrutura do código · <a href="/pt-BR/blog/spike-source?spike=33-code-structure">código-fonte</a><br>
<a href="/pt-BR/spikes/34-world-authoring/" target="_blank">Spike 34</a> criação dentro do mundo · <a href="/pt-BR/blog/spike-source?spike=34-world-authoring">código-fonte</a><br>
<a href="/pt-BR/spikes/35-combat-animations/" target="_blank">Spike 35</a> animações de combate e retargeting · <a href="/pt-BR/blog/spike-source?spike=35-combat-animations">código-fonte</a><br>
<a href="/pt-BR/spikes/36-polyhaven-models/" target="_blank">Spike 36</a> busca de assets do Poly Haven ao vivo · <a href="/pt-BR/blog/spike-source?spike=36-polyhaven-models">código-fonte</a><br>
<a href="/pt-BR/spikes/37-ai-scattering/" target="_blank">Spike 37</a> distribuição de objetos assistida por IA · <a href="/pt-BR/blog/spike-source?spike=37-ai-scattering">código-fonte</a><br>
<a href="/pt-BR/spikes/38-imposters/" target="_blank">Spike 38</a> impostores octaédricos · <a href="/pt-BR/blog/spike-source?spike=38-imposters">código-fonte</a><br>
<a href="/pt-BR/spikes/39-parallax-pom/" target="_blank">Spike 39</a> mapeamento de oclusão por paralaxe · <a href="/pt-BR/blog/spike-source?spike=39-parallax-pom">código-fonte</a><br>
<a href="/pt-BR/spikes/40-visibility-buffer/" target="_blank">Spike 40</a> renderizador com buffer de visibilidade · <a href="/pt-BR/blog/spike-source?spike=40-visibility-buffer">código-fonte</a><br>
<a href="/pt-BR/spikes/41-imposter-forest/" target="_blank">Spike 41</a> impostores em uma floresta · <a href="/pt-BR/blog/spike-source?spike=41-imposter-forest">código-fonte</a><br>
<a href="/pt-BR/spikes/42-imposter-flicker/" target="_blank">Spike 42</a> correção da cintilação dos impostores · <a href="/pt-BR/blog/spike-source?spike=42-imposter-flicker">código-fonte</a><br>
<a href="/pt-BR/spikes/43-clouds-atmosphere/" target="_blank">Spike 43</a> atmosfera e nuvens volumétricas · <a href="/pt-BR/blog/spike-source?spike=43-clouds-atmosphere">código-fonte</a><br>
<a href="/pt-BR/spikes/44-meshlet-clusters/" target="_blank">Spike 44</a> culling Hi-Z de meshlets · <a href="/pt-BR/blog/spike-source?spike=44-meshlet-clusters">código-fonte</a><br>
<a href="/pt-BR/spikes/45-avatar-network-sync/" target="_blank">Spike 45</a> skinning de avatares em lote e sincronização de rede · <a href="/pt-BR/blog/spike-source?spike=45-avatar-network-sync">código-fonte</a><br>
<a href="/pt-BR/spikes/46-proximity-voice/" target="_blank">Spike 46</a> conversa por voz baseada em proximidade · <a href="/pt-BR/blog/spike-source?spike=46-proximity-voice">código-fonte</a><br>
<a href="/pt-BR/spikes/47-world-persistence/" target="_blank">Spike 47</a> persistência do mundo com servidor autoritativo · <a href="/pt-BR/blog/spike-source?spike=47-world-persistence">código-fonte</a><br>
O Spike 48 integrou o rig dos avatares e foi reutilizado como código compartilhado pelos spikes posteriores (sem página independente)<br>
<a href="/pt-BR/spikes/49-gpu-wind-props/" target="_blank">Spike 49</a> vento na GPU aplicado a objetos e folhagem · <a href="/pt-BR/blog/spike-source?spike=49-gpu-wind-props">código-fonte</a><br>
<a href="/pt-BR/spikes/50-universal-characters/" target="_blank">Spike 50</a> personagens universais e itens vestíveis · <a href="/pt-BR/blog/spike-source?spike=50-universal-characters">código-fonte</a><br>
<a href="/pt-BR/spikes/51-water-ssr-caustics/" target="_blank">Spike 51</a> água com reflexos em espaço de tela · <a href="/pt-BR/blog/spike-source?spike=51-water-ssr-caustics">código-fonte</a><br>
<a href="/pt-BR/spikes/52-water-planar/" target="_blank">Spike 52</a> água com espelho planar · <a href="/pt-BR/blog/spike-source?spike=52-water-planar">código-fonte</a><br>
<a href="/pt-BR/spikes/53-water-pro/" target="_blank">Spike 53</a> biblioteca de água pronta para produção · <a href="/pt-BR/blog/spike-source?spike=53-water-pro">código-fonte</a>
<a href="/pt-BR/spikes/54-procgen-island/" target="_blank">Spike 54</a> geração procedural de ilhas · <a href="/pt-BR/blog/spike-source?spike=54-procgen-island">código-fonte</a><br>
<a href="/pt-BR/spikes/55-terrain-shading/" target="_blank">Spike 55</a> sombreamento de terreno e redução da repetição de texturas · <a href="/pt-BR/blog/spike-source?spike=55-terrain-shading">código-fonte</a><br>
<a href="/pt-BR/spikes/56-grass/" target="_blank">Spike 56</a> grama com quads cruzados na GPU · <a href="/pt-BR/blog/spike-source?spike=56-grass">código-fonte</a><br>
<a href="/pt-BR/spikes/57-terrain-occlusion/" target="_blank">Spike 57</a> occlusion culling do terreno · <a href="/pt-BR/blog/spike-source?spike=57-terrain-occlusion">código-fonte</a><br>
<a href="/pt-BR/spikes/58-pluggable-character/" target="_blank">Spike 58</a> controlador de personagem modular · <a href="/pt-BR/blog/spike-source?spike=58-pluggable-character">código-fonte</a><br>
<a href="/pt-BR/spikes/59-pluggable-with-avatar/" target="_blank">Spike 59</a> controlador com um avatar real · <a href="/pt-BR/blog/spike-source?spike=59-pluggable-with-avatar">código-fonte</a><br>
<a href="/pt-BR/spikes/60-polygon-animations/" target="_blank">Spike 60</a> animações Polygon da Synty · <a href="/pt-BR/blog/spike-source?spike=60-polygon-animations">código-fonte</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Breaker Belt: Snake encontra Arkanoid, criado com vibe coding em três dias]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-18-a-breaker-belt</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-18-a-breaker-belt</guid>
            <pubDate>Wed, 18 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Uma serpente cósmica destrói tijolos ao longo de 50 ondas, com música reativa, narração por IA e 23 tipos de tijolo. Duas pessoas. Três dias. Web, dispositivos móveis e PC.]]></description>
            <content:encoded><![CDATA[<h1>A Breaker Belt: Snake encontra Arkanoid, criado com vibe coding em três dias</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/breaker-belt-cover.webp" alt="Gameplay de A Breaker Belt: uma serpente cósmica de neon destruindo tijolos no espaço" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Criamos um jogo em que sua cobra é a raquete e os tijolos revidam. Ele foi lançado para web, dispositivos móveis e PC. Levou cerca de três dias para nós dois. Trabalhando de vez em quando, sem virar noites. E é realmente divertido de jogar.</p>
<p>Essa última parte é o que merece atenção.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/VaFkfCT3OuU" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<h2>A mistura que ninguém pediu</h2>
<p>Snake é um jogo sobre crescimento e percepção espacial. Arkanoid é sobre reflexos e previsão de ângulos. Eles vêm de filosofias de design completamente diferentes, e combiná-los parece o tipo de proposta que seria educadamente recusada.</p>
<p>Mas <a href="https://app.cinevva.com/engine">A Breaker Belt</a> faz essa combinação funcionar. Você é uma serpente cósmica, um arco vivo de corrente de neon formado por vértebras de metal errante e seda de cometa, deslizando por um campo de asteroides repleto de blocos destrutíveis. Sua cabeça é a raquete. Sua cauda crescente é, ao mesmo tempo, sua maior arma e sua ameaça mais constante. Os orbes ricocheteiam no seu corpo para despedaçar tijolos, mas basta uma curva errada em direção à própria cauda para tudo acabar.</p>
<p>É o tipo de experimento estranho que mistura gêneros e normalmente morre antes que alguém consiga jogá-lo, porque, tradicionalmente, o custo de desenvolvimento para descobrir se uma ideia incomum funciona é medido em meses. Aqui, foi medido em tardes.</p>
<h2>O que realmente foi lançado</h2>
<p>É o escopo que torna isso interessante. Este não é um protótipo de game jam com retângulos provisórios e sem som.</p>
<p>O jogo tem 50 ondas. E não são 50 variações da mesma parede de tijolos. As formações evoluem de arcos suaves de introdução para anéis fortificados que exigem disparos angulados através de aberturas laterais, chegando depois a labirintos diagonais em camadas, com passagens de apenas um bloco de largura que exigem controle preciso. Tijolos explosivos destroem os vizinhos. Tijolos fantasma aparecem e desaparecem. Tijolos regenerativos se recuperam depois de serem quebrados, obrigando você a priorizar alvos em vez de simplesmente varrer a tela da esquerda para a direita. Tijolos-portal teleportam seus orbes pela arena. Poços gravitacionais desviam seus disparos em hinos lentos e curvos. Emissores de laser traçam linhas vermelhas pelo vazio. Tijolos mímicos parecem inofensivos até decidirem que não são. Na onda 15, você já está navegando por algo que parece menos um quebra-cabeça e mais um sistema vivo aprendendo seus hábitos.</p>
<p>A música não é um loop. É uma trilha sonora reativa que se desenvolve de acordo com sua forma de jogar. O sistema por trás dela executa uma linha de baixo, sintetizador principal, pad, bumbo, caixa e chimbal em Mi menor a 140 BPM e, conforme a ação se intensifica, camadas adicionais de percussão e sintetizadores surgem gradualmente. Quando as coisas se acalmam entre as ondas, os pads ambientes assumem o controle. A música respira com você. Um compositor dedicado passaria semanas ajustando esse nível de responsividade. Aqui, isso fez parte do fluxo criativo.</p>
<p>Os efeitos sonoros não são samples retirados de um pacote gratuito. Cada tijolo despedaçado, power-up coletado, orbe rebatido e colisão é sintetizado em tempo real. Há tons diferentes para contatos com a cabeça e com a cauda. Reverberação quente para criar uma sensação espacial. Filtro passa-baixas para manter tudo agradável sem soar estridente. Quando você encadeia um combo, o áudio avisa antes da tela.</p>
<p>E ainda há a narrativa. Cada onda começa com um trecho da história narrado por uma voz gerada por IA. O texto tem personalidade de verdade. Onda um: &quot;Eles o chamam de Cinturão Destruidor: uma faixa de destroços projetados que circunda a velha estrela como um aviso.&quot; Na onda 38: &quot;O Cinturão deixa de parecer uma parede e começa a parecer uma mente. Ele testa não seus reflexos, mas seus hábitos. Você rompe o hábito. O Cinturão percebe.&quot; São 50 capítulos de mitologia cósmica que fazem você se importar com o motivo de ser uma cobra destruindo tijolos no espaço.</p>
<p>Os cenários também evoluem. As primeiras ondas apresentam campos estelares tranquilos em tons de índigo, com uma suave chuva de meteoros. Na metade do jogo, surgem faixas de aurora e nuvens de nebulosa. Mais adiante, o jogo mergulha de vez em uma Tempestade de Íons, com faixas nítidas em ciano contra um espaço quase negro. Da onda 15 em diante, você entra no território do Horizonte de Eventos: um vazio profundo pontuado por filamentos de nebulosa em movimento lento e discretos efeitos de lente de buraco negro. O jogo comunica a progressão tanto pela atmosfera quanto pela dificuldade.</p>
<p>Tudo isso funciona com teclado, controle ou tela sensível ao toque. Publicado para web, dispositivos móveis e PC a partir de uma única base de código. Sem builds separadas. Sem portabilidade.</p>
<h2>A equipe que não foi necessária</h2>
<p>Esta é a parte que deveria fazer qualquer pessoa que desenvolve jogos parar para pensar.</p>
<p>Um jogo com essa profundidade de conteúdo normalmente precisaria de um game designer, alguns programadores, um artista 2D, um designer de som, um compositor, um level designer, um roteirista e QA. São oito ou nove pessoas. Mesmo seguindo um ritmo indie modesto, seriam necessários de três a seis meses de trabalho coordenado. Reuniões diárias, tickets no Jira, pipelines de assets e depuração específica para cada plataforma.</p>
<p>Nós dois fizemos isso em um fim de semana prolongado usando a <a href="https://app.cinevva.com/engine">Cinevva Engine</a>.</p>
<p>O talento necessário não mudou. O que mudou foi a proporção entre intenção criativa e trabalho de implementação. O tempo foi usado para decidir como o jogo deveria ser, não para lutar contra as ferramentas até conseguir concretizá-lo.</p>
<h2>Por que isso importa para quem cria</h2>
<p>A pergunta interessante não é se ferramentas de IA podem ajudar a criar jogos mais rapidamente. Isso já foi respondido. A pergunta interessante é o que acontece com as ideias que antes eram arriscadas demais para serem testadas.</p>
<p>&quot;Snake, mas você é a raquete em uma arena de Arkanoid&quot; não é algo que um produtor aprovaria. Alguns desenvolvedores indie já tentaram variações dessa mistura no <a href="http://itch.io">itch.io</a> (<a href="https://newdron.itch.io/breaksnake">BreakSnake</a>, <a href="https://neop87.itch.io/snakeout">SnakeOut</a>, <a href="https://merrak.itch.io/snake-break">Snake Break</a>), mas todos são pequenos experimentos de game jam. Essa fusão de gêneros nunca recebeu um tratamento de produção completo, com música reativa, narração por IA, dezenas de tipos de tijolo e 50 ondas de level design cada vez mais desafiador. No desenvolvimento tradicional, esse nível de acabamento aplicado a um conceito arriscado seria descartado em uma reunião antes que alguém escrevesse uma única linha de código.</p>
<p>Quando experimentar algo estranho custa uma tarde em vez do orçamento de um trimestre, as ideias incomuns ganham vida. Algumas delas acabam sendo realmente boas. A Breaker Belt é uma delas.</p>
<h2>Jogue. Ou crie o seu próprio.</h2>
<p><a href="https://app.cinevva.com/engine">A Breaker Belt</a> já pode ser jogado na web, em dispositivos móveis e no PC. Se ele despertar sua vontade de criar alguma coisa, a <a href="https://app.cinevva.com/engine">Cinevva Engine</a> é gratuita. Descreva o que você quer, itere sobre o resultado e publique quando estiver pronto. As ferramentas de <a href="https://app.cinevva.com/tools/music">música</a>, <a href="https://app.cinevva.com/tools/sfx">efeitos sonoros</a>, <a href="https://app.cinevva.com/tools/flux">arte</a> e <a href="https://app.cinevva.com/tools/hunyuan3d">modelos 3D</a> estão todas integradas.</p>
<p>Sua ideia estranha de jogo pode estar a apenas três dias de se tornar realidade.</p>
<hr>
<p><em><a href="https://app.cinevva.com/engine">Jogue A Breaker Belt</a> | <a href="https://app.cinevva.com/engine">Crie seu próprio jogo</a> | <a href="https://cinevva.com/charts">Explore os jogos da comunidade</a></em></p>
<p><strong>Relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/signals/2026-03-13-vibe-coding-new-game-jam">Vibe coding é a nova game jam</a> — como as ferramentas de IA eliminam a distância entre ideia e protótipo</li>
<li><a href="/pt-BR/guides/game-jams-hackathons">Game jams e hackathons</a> — o formato de jam que torna possíveis misturas incomuns</li>
<li><a href="/pt-BR/guides/web-game-engines-comparison">Comparativo de engines para jogos web</a> — engines para publicar na web, em dispositivos móveis e no PC</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Não esperávamos criar uma estação de rádio]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-17-we-didnt-expect-a-radio-station</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-17-we-didnt-expect-a-radio-station</guid>
            <pubDate>Tue, 17 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Temos um gerador de música na Cinevva para que criadores de jogos possam produzir trilhas sonoras. As pessoas começaram a fazer música simplesmente porque podiam. Depois de 362 faixas, a Cinevva Radio está no ar.]]></description>
            <content:encoded><![CDATA[<h1>Não esperávamos criar uma estação de rádio</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/cinevva-radio-lofi-cafe.png" alt="Rádio da comunidade Cinevva com 8 estações, incluindo Palco Sonoro, Voltagem, Café Lo-Fi, Palco Principal, Torre de Marfim e Sonhos Elétricos, tocando atualmente Quiet Flute Drone" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Temos um <a href="https://app.cinevva.com/tools/music">gerador de música</a> na plataforma Cinevva para que criadores de jogos possam produzir trilhas sonoras e efeitos sonoros sem dores de cabeça com licenciamento. O que aconteceu depois não estava nos nossos planos. As pessoas começaram a fazer música porque queriam fazer música. Não para jogos ou projetos. Apenas produção musical.</p>
<h2>Bella Bay criou mais de 100 faixas em duas semanas</h2>
<p>Não estou exagerando. Uma das nossas criadoras, Bella Bay, gerou mais de 100 faixas em cerca de duas semanas. Ao ouvi-las na <a href="https://cinevva.com/radio">Cinevva Radio</a>, dá para perceber a evolução. As primeiras faixas são experimentos. As mais recentes soam como o trabalho de alguém que encontrou seu estilo. Esse tipo de aceleração criativa não acontece quando você precisa lutar contra suas ferramentas. Acontece quando as ferramentas saem do caminho.</p>
<p>Bella Bay não é uma exceção em termos de espírito criativo, apenas em volume. Em toda a plataforma, as pessoas começaram a tratar o gerador de música não como uma ferramenta para criar recursos de jogos, mas como um instrumento criativo. Produtores experimentando gêneros que nunca haviam tentado. Pessoas refinando um som até ele finalmente funcionar. Artistas independentes que nunca tiveram acesso a um conjunto completo de ferramentas de produção de repente passaram a ter um — e mergulharam de cabeça.</p>
<h2>Então criamos uma rádio</h2>
<p>Era preciso.</p>
<p>Tínhamos todas essas faixas paradas nas contas das pessoas, sem um espaço coletivo. Nenhuma forma de descobrir o que os outros estavam criando. Nenhuma maneira de esbarrar por acaso em uma faixa capaz de transformar sua tarde.</p>
<p>A <a href="https://cinevva.com/radio">Cinevva Radio</a> já está no ar. Oito estações. 362 faixas criadas pela comunidade, com novas chegando todos os dias. Cada música foi feita por alguém na plataforma. Você abre a rádio e já tem algo tocando. Não precisa pesquisar. Não precisa escolher. É só ouvir.</p>
<p><strong><a href="https://cinevva.com/radio#cinematic">Palco Sonoro</a></strong> é nossa maior estação. São 111 faixas com trilhas cinematográficas e épicas. Sinceramente, eu não esperava por isso. As pessoas adoram criar músicas dramáticas com estilo de trilha de cinema.</p>
<p><strong><a href="https://cinevva.com/radio#mixed">Descobertas</a></strong> reúne tudo o que não se encaixa perfeitamente em um gênero. São 78 faixas. Ritmos latinos ao lado de Afrobeats, que aparecem ao lado de country e de coisas que desafiam qualquer descrição. Essa estação é uma loucura.</p>
<p><strong><a href="https://cinevva.com/radio#pop">Palco Principal</a></strong> reúne faixas pop e vocais. São 56 faixas com letras e vocais completos. As pessoas estão escrevendo canções de amor, músicas sobre términos, músicas de adoração e baladas. Algumas são realmente grudentas.</p>
<p><strong><a href="https://cinevva.com/radio#rock">Voltagem</a></strong> é dedicada ao rock e ao metal. São 53 faixas. Riffs de guitarra, energia punk, grunge e hard rock. Costumo deixar essa estação tocando enquanto trabalho.</p>
<p><strong><a href="https://cinevva.com/radio#lofi">Café Lo-Fi</a></strong> é exatamente o que o nome sugere. São 25 faixas ambientes e relaxantes para estudar, trabalhar ou simplesmente curtir o clima.</p>
<p><strong><a href="https://cinevva.com/radio#electronic">Sonhos Elétricos</a></strong> tem 22 faixas eletrônicas e com sintetizadores. Synthwave, techno, EDM, drum and bass.</p>
<p><strong><a href="https://cinevva.com/radio#classical">Torre de Marfim</a></strong> é dedicada à música clássica e orquestral. São 10 faixas. Peças para piano, arranjos de cordas e composições sinfônicas. Ainda é pequena, mas está crescendo.</p>
<p><strong><a href="https://cinevva.com/radio#hiphop">A Cifra</a></strong> é dedicada ao hip-hop e ao rap. Por enquanto, são 7 faixas. É a estação mais nova e ainda está encontrando sua voz.</p>
<h2>O que você encontra ao ouvir</h2>
<p>Quando você abre a <a href="https://cinevva.com/radio">Cinevva Radio</a>, entra em uma transmissão ao vivo. Pense nela menos como um aplicativo de playlists e mais como uma estação de rádio de verdade. As faixas tocam continuamente. Você pode reagir em tempo real, conversar com outros ouvintes e ver quem criou cada faixa.</p>
<p>Os criadores recebem os créditos na tela enquanto suas faixas tocam. O nome, o prompt e o gênero aparecem ali. Se você ouvir algo de que gosta, saberá quem fez. É conteúdo criado pela comunidade com uma camada de descoberta por cima. Nenhum algoritmo decidindo o que merece atenção. Nenhum guardião controlando o acesso. Você criou uma faixa, ela entra na estação correspondente ao gênero e as pessoas a ouvem.</p>
<h2>Isso é maior do que um recurso</h2>
<p>Durante décadas, a produção musical exigiu softwares caros, anos de treinamento e acesso a equipamentos que a maioria das pessoas não podia pagar. O que estamos vendo é um tipo diferente de criador. Pessoas que pensam em descrições e sentimentos, em vez de notas e fórmulas de compasso.</p>
<p>&quot;Jazz para uma noite chuvosa com um piano quebrado&quot; é uma direção criativa. A pessoa que escreveu esse prompt fez uma escolha artística. Ela definiu o clima, a instrumentação e o tom emocional. Ela só não toca piano.</p>
<p>Não acho que isso torne o resultado menos válido. Acho que significa que a definição de &quot;músico&quot; está se ampliando. E ver alguém como Bella Bay sair do zero e chegar a cem faixas em duas semanas mostra que o impulso criativo sempre esteve lá. As ferramentas é que não estavam.</p>
<h2>O que vem por aí</h2>
<p>Estamos trabalhando em perfis da comunidade nos quais produtores poderão exibir seus catálogos e conquistar seguidores. A <a href="https://cinevva.com/charts">página de rankings</a> já mostra criações da comunidade feitas com todas as ferramentas, e queremos levar essa mesma energia especificamente para a música.</p>
<p>Mas, neste momento, a rádio está no ar. <a href="https://cinevva.com/radio">Vá ouvir</a>. E, se quiser criar suas próprias faixas, o <a href="https://app.cinevva.com/tools/music">gerador de música</a> pode ser usado gratuitamente. Sua faixa pode acabar tocando na rádio.</p>
<hr>
<p><em><a href="https://cinevva.com/radio">Ouça a Cinevva Radio</a> | <a href="https://app.cinevva.com/tools/music">Crie músicas</a> | <a href="https://cinevva.com/charts">Explore as criações da comunidade</a></em></p>
<p><strong>Conteúdo relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/guides/frontier-gen-ai-models">Modelos de IA generativa de código aberto de ponta</a> — os modelos de IA para música e áudio que operam nos bastidores</li>
<li><a href="/pt-BR/tutorials/web-audio-api-games">Web Audio API para jogos</a> — como criar áudio interativo no navegador</li>
<li><a href="/pt-BR/signals/2026-03-13-vibe-coding-new-game-jam">Vibe coding é a nova game jam</a> — como descrever uma intenção se torna a principal forma de expressão criativa</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A mente intuitiva na era da IA]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-02-10-the-intuitive-mind</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-02-10-the-intuitive-mind</guid>
            <pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Jensen Huang afirma que as habilidades matemáticas puras agora são uma commodity. Veja por que isso faz sentido para alguém que foi reprovada no SAT, ignorou as regras e criou algo que as pessoas realmente queriam.]]></description>
            <content:encoded><![CDATA[<h1>A mente intuitiva na era da IA</h1>
<p><em>Por <a href="/pt-BR/about">Mariana Muntean</a>, CEO da Cinevva</em></p>
<img src="https://cdn.cinevva.com/blog/game-jam-houston-2018.png" alt="Mariana Muntean com colegas de turma durante uma game jam de 48 horas em Houston, em 2018" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<small>Com colegas de turma durante uma game jam de 48 horas em Houston, em 2018</small>
<p>A IA basicamente transformou a lógica pura e a capacidade computacional em commodities. As habilidades que costumávamos venerar — o cálculo mental, o reconhecimento de padrões, a capacidade de enfrentar algoritmos complexos —, a IA agora faz tudo isso. Mais rápido. Melhor. Sem se cansar. Ainda precisamos conhecer essas coisas? Sem dúvida, mas as percepções mudam. Agora todos precisam se adaptar à nova era — uma era de interação intuitiva e resultados.</p>
<p>Segundo <a href="https://business.columbia.edu/insights/digital-future/nvidia-ceo-jensen-huang-reveals-keys-ai-and-leadership">Jensen Huang</a>, CEO da NVIDIA, o que importa agora é a capacidade de captar uma &quot;vibe&quot; e enxergar além antes que os dados apareçam. A interseção entre conhecimento técnico e empatia profunda. A compreensão intuitiva que o silício não consegue alcançar.</p>
<p>Nunca ouvi ninguém com a credibilidade dele dizer algo que validasse toda a trajetória da minha vida de forma tão direta.</p>
<h2>O SAT e o sistema que não foi feito para mim</h2>
<p>Há alguns anos, fiz o SAT e fui reprovada. Não me orgulho disso, mas também não tentei de novo. Algo parecia fundamentalmente errado em tudo aquilo, e eu não conseguia me livrar dessa sensação, por mais que as pessoas dissessem que eu só precisava estudar mais e tentar novamente.</p>
<p>Olhando para trás, acho que eu estava certa.</p>
<p>O SAT foi criado para o sistema educacional americano. Isso parece óbvio, mas as implicações são profundas. As escolas de ensino médio americanas ensinam padrões específicos, tipos de pergunta e maneiras de formular problemas. Desde o ensino fundamental, os alunos crescem imersos nesse estilo de prova padronizada. Quando chegam ao SAT, já internalizaram o ritmo.</p>
<p>Estudantes internacionais não têm essa vantagem. Viemos de sistemas com filosofias educacionais diferentes. As escolas europeias muitas vezes enfatizam a profundidade em vez da abrangência, provas discursivas em vez de múltipla escolha, defesas orais em vez de folhas de respostas. Os sistemas asiáticos têm suas próprias provas padronizadas, mas medem coisas diferentes de maneiras diferentes. As tradições educacionais da América do Sul, da África e do Oriente Médio também têm, cada uma, sua própria lógica.</p>
<p>Quando você coloca um estudante internacional diante do SAT, não está testando apenas o conhecimento dele. Está testando a rapidez com que consegue se adaptar a uma cultura de avaliação estrangeira e, ao mesmo tempo, demonstrar domínio do conteúdo. Está testando tanto sua fluência cultural quanto sua capacidade acadêmica.</p>
<p>Esperavam que eu me preparasse durante um verão e tirasse uma nota excelente. Que aprendesse toda uma cultura de avaliação, desaprendesse meus próprios instintos educacionais e tivesse um desempenho capaz de impressionar os responsáveis pelas admissões nas universidades americanas. Tudo em poucos meses.</p>
<p>Escolhi não fazer isso.</p>
<p>Quando somos jovens, somos inteligentes de maneiras diferentes. Maneiras intuitivas. Eu estava abrindo meu próprio caminho, mesmo que, naquela época, não conseguisse explicar exatamente o motivo. Algo dentro de mim sabia que aquele sistema não era para mim.</p>
<h2>Seguindo o fio criativo</h2>
<p>Estudei desenvolvimento e design de jogos porque adorava a ideia de usar criatividade e efeitos visuais para criar mundos virtuais nos quais as pessoas pudessem jogar e interagir. Eu adorava a interseção entre arte e tecnologia, narrativa e interatividade.</p>
<p>O que encontrei me chocou.</p>
<p>Jogos exigem uma profundidade técnica considerável. Simulações de física, detecção de colisões, matemática vetorial, cálculos de iluminação, otimização. Eu sabia disso quando entrei. A matemática e a engenharia não são obstáculos ao desenvolvimento de jogos. Elas fazem parte do que permite que os jogos funcionem.</p>
<p>O problema era a distância entre a visão criativa e a implementação. As engines e ferramentas que dominavam o setor haviam sido criadas por engenheiros para engenheiros. Tudo funcionava com &quot;ses&quot;, &quot;entãos&quot; e blueprints baseados em nós. Queria um efeito de vórtice? Aprenda programação de shaders. Uma atmosfera de iluminação específica? Mergulhe nos grafos de materiais. Um movimento de personagem que pareça natural? Passe horas depurando o controlador do personagem ou os parâmetros de física.</p>
<p>Se você é uma pessoa visual, se pensa em cores vivas e imagens em movimento, se as ideias surgem como cenas completas, com som, textura e peso emocional, precisa traduzir tudo isso para uma linguagem técnica antes de conseguir criar qualquer coisa. Você enxerga um mundo na mente, completo, com iluminação, atmosfera e a maneira como os personagens se movem pelo espaço. Então se senta diante do computador e passa as seis horas seguintes tentando descobrir por que seu personagem atravessa o chão.</p>
<p>A base técnica é importante. Mas as ferramentas obrigavam os criadores a viver nos detalhes da implementação, em vez de abstrair toda essa complexidade. Criar um jogo deveria ser como contar uma história ou dirigir um filme do qual as pessoas podem participar. Essa é a magia do meio. Em vez disso, parecia que era preciso fazer uma prova de engenharia antes mesmo de começar.</p>
<p>Diretores de cinema não passam anos aprendendo engines de física antes de poder expressar sua visão. Eles recebem um orçamento e uma equipe que cuida da execução técnica. Mas, no desenvolvimento independente de jogos, raramente há orçamento. O que existe é tempo e acesso a ferramentas. E, se você for uma pessoa técnica, pode criar um jogo incrível. Mas só nesse caso.</p>
<p>Foi assim que surgiram títulos como Limbo, criado por uma equipe pequena com uma visão artística singular e o domínio técnico necessário para executá-la. Ou Undertale, criado em grande parte por uma única pessoa que, por acaso, tinha a combinação certa de instinto criativo e habilidade de programação. Ou Stardew Valley, para o qual Eric Barone passou anos aprendendo sozinho de tudo, desde pixel art e composição musical até programação em C#.</p>
<p>Esses jogos alcançaram o sucesso apesar de enormes adversidades. Mas, para cada Limbo, existem milhões de visões criativas que morreram porque as ferramentas exigiam uma fluência técnica que seus criadores não tinham. Menos de 3% dos desenvolvedores independentes de jogos chegam a alcançar algum sucesso significativo. Quantos jogos brilhantes nunca foram criados porque seus autores se depararam com uma barreira de &quot;ses&quot; e &quot;entãos&quot; e desistiram?</p>
<p>A barreira de entrada não era a criatividade. Era o controle de acesso técnico incorporado às próprias ferramentas.</p>
<h2>Criando o que já deveria existir</h2>
<p>Então, há cinco anos, comecei a criar algo diferente.</p>
<p>A proposta era simples: o desenvolvimento de jogos deveria ser acessível a qualquer pessoa com uma visão criativa. Você não deveria precisar de um diploma em ciência da computação para se expressar por meio de mídias interativas. As ferramentas deveriam se adaptar à maneira como as pessoas criativas realmente pensam, e não o contrário.</p>
<p>Investidores de capital de risco da Sequoia, Pear, Draper e de dezenas de outras empresas me disseram que não funcionaria. É B2C. Esse mercado não existe. Desenvolvedores independentes não pagam por nada. Não é possível simplificar o desenvolvimento de jogos sem sacrificar recursos. Jogadores querem jogos complexos, e jogos complexos exigem ferramentas complexas. Milhões de desculpas disfarçadas de análise de mercado.</p>
<p>As pessoas diziam que eu era louca. Talvez eu fosse. Mas sempre voltava à mesma pergunta: por que orçamentos multimilionários deveriam ser um pré-requisito para a expressão criativa? Diretores de cinema e produtores famosos de jogos têm equipes e recursos. Todo o restante recebe um editor de código e uma oração. Eu queria criar algo que eliminasse essa distância. Você descreve o que quer, e aquilo acontece diante dos seus olhos. Tudo em mim sabia que isso era o certo. Eu conseguia sentir isso em cada célula do meu corpo.</p>
<p>Hoje, milhares de pessoas usam a Cinevva diariamente para criar assets 3D de jogos, jogos, músicas, fases e experiências. Os projetos criados até agora acumulam milhões de visualizações. E esse número cresce todos os dias. Uma apresentação de dois minutos em Sand Hill Road não é exatamente o formato ideal para dizer: &quot;Fui reprovada no SAT, mas confiem na minha intuição.&quot;</p>
<h2>O que a intuição realmente significa</h2>
<p><a href="https://singjupost.com/transcript-jensen-huangs-interview-cisco-ai-summit-2026/">Huang</a> não estava apenas apresentando uma reflexão filosófica. Ele descrevia uma mudança real no que constitui uma inteligência valiosa.</p>
<p>Durante décadas, otimizamos as coisas erradas. Construímos sistemas educacionais que recompensavam a memorização e o cálculo. Criamos provas padronizadas que mediam a capacidade de identificar padrões em problemas já vistos. Contratamos pessoas com base em credenciais que comprovavam que elas haviam sobrevivido a quatro anos de barreiras acadêmicas. A IA simplesmente tornou tudo isso menos especial.</p>
<p>O que a IA não consegue fazer, pelo menos por enquanto, é perceber o que está faltando. Sentir quando algo não está certo. Intuir o que as pessoas precisam antes mesmo que consigam expressar isso. Captar o clima de um ambiente. Compreender um contexto que não está registrado em nenhum conjunto de dados.</p>
<p>Investi meu tempo, minha experiência de vida internacional, meu dinheiro e meu conhecimento intuitivo na criação da Cinevva. Essa é uma combinação difícil de obter na faculdade. Difícil de avaliar no SAT. Difícil de capturar em qualquer sistema de credenciais criado antes que a IA tornasse abundante o poder cognitivo bruto.</p>
<p>Dezesseis ou vinte anos atrás, cientistas da computação decidiam como as ferramentas deveriam ser e como deveriam funcionar. Eles criavam para si mesmos, para pessoas que pensavam como eles. Esperava-se que todos os demais se adaptassem. Essa era está chegando ao fim. As pessoas que moldarão o que vem a seguir são aquelas que entendem o que os seres humanos realmente querem. Que conseguem sentir quando algo está errado e quando está certo. Que criam para pessoas, em vez de priorizar a elegância técnica.</p>
<p>Confiei em algo dentro de mim que o sistema dizia não ter valor. E eu estava certa.</p>
<hr>
<p><strong>Conteúdo relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/blog/2026-01-18-skills-over-degrees">O mercado de trabalho está se transformando — das credenciais às habilidades</a></li>
<li><a href="/pt-BR/guides/game-dev-courses">Cursos online de desenvolvimento de jogos</a> — caminhos que priorizam habilidades e contornam as barreiras tradicionais</li>
<li><a href="/pt-BR/signals/2026-03-04-everyone-wants-ai-game-engine">Agora todos querem ser a engine de jogos com IA</a> — a transformação do setor rumo a ferramentas que se adaptam à maneira como as pessoas pensam</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A controvérsia da IA, a confiança e a economia pós-IA nos jogos]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-01-18-ai-controversy-and-post-ai-economy</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-01-18-ai-controversy-and-post-ai-economy</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Uma visão prática: a questão não é a IA, mas a confiança. Por que a rotulagem, os filtros e a distribuição justa importam mais do que os debates entre ser 'a favor' ou 'contra' a IA.]]></description>
            <content:encoded><![CDATA[<h1>A controvérsia da IA, a confiança e a economia pós-IA nos jogos</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO da Cinevva</em></p>
<p>A IA nos jogos se transformou em um verdadeiro campo minado por volta de meados de 2024. Empregos desaparecendo. Pessoas convencidas de que serão as próximas. Questões de direitos autorais que nem os advogados conseguem resolver. Debates estéticos intermináveis que sempre voltam ao ponto de partida. A Steam se afogando em coisas que ninguém pediu. E, por trás de tudo, aquela preocupação persistente que todo criador tem agora: &quot;será que estou realmente criando alguma coisa aqui ou apenas... digitando prompts?&quot;</p>
<p>Nós administramos uma plataforma. E, ao fazer isso, há uma coisa que se aprende rápido.</p>
<p><strong>Ninguém gosta de se sentir enganado.</strong></p>
<h2>Estes números nos surpreenderam</h2>
<p>Imaginávamos que a adoção da IA cresceria. Mas não desse jeito. Este era o cenário no fim de 2025:</p>
<table>
<thead>
<tr>
<th>O que acompanhamos</th>
<th>2024</th>
<th>2025</th>
<th>A mudança</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jogos na Steam que declaram uso de IA</td>
<td>~1.000</td>
<td>7.818</td>
<td>Aumento de 7 vezes</td>
</tr>
<tr>
<td>Novos lançamentos na Steam que usam IA</td>
<td>~3%</td>
<td>~20%</td>
<td>Um em cada cinco</td>
</tr>
<tr>
<td>Devs que acham que a IA prejudica a qualidade</td>
<td>34%</td>
<td>47%</td>
<td>Treze pontos a mais</td>
</tr>
<tr>
<td>Devs que acham que a IA melhora a qualidade</td>
<td>—</td>
<td>11%</td>
<td>Poucos</td>
</tr>
<tr>
<td>Receita de jogos que declaram uso de IA</td>
<td>—</td>
<td>US$ 660 mi</td>
<td>Doze jogos superaram oito dígitos</td>
</tr>
</tbody>
</table>
<p>Fontes: análise da Tom's Hardware / Totally Human Media, GDC 2025 Developer Survey, Unity 2025 Gaming Report</p>
<h2>O que realmente aconteceu para deixar os jogadores tão desconfiados</h2>
<p>Isso não surgiu do nada. Problemas reais aconteceram. E as pessoas se lembram.</p>
<h3>Atores de voz descobriram que haviam sido clonados — no dia do lançamento</h3>
<p><em>Tomb Raider 4-6 Remastered</em> foi lançado com versões geradas por IA das interpretações de voz originais. Os atores ficaram sabendo no mesmo momento que todo mundo. No dia do lançamento. A distribuidora acabou removendo as vozes em uma atualização depois que a pressão jurídica aumentou. Aquela greve de onze meses da SAG-AFTRA? Esse exato cenário foi um dos motivos.</p>
<h3>“Lixo de IA” virou uma expressão comum</h3>
<p><em>Call of Duty: Black Ops 6</em> adicionou discretamente uma declaração de uso de IA à sua página na Steam. Depois de já ter sido lançado. Os jogadores haviam percebido algo estranho — falhas visuais espalhadas por toda parte. Em uma tela de carregamento, um personagem tinha seis dedos. “Lixo de IA” virou uma forma rápida de descrever qualquer coisa que pareça... errada. Vazia. Como se ninguém tivesse se importado o suficiente para conferir.</p>
<h3>Uma indicação a prêmio desapareceu</h3>
<p><em>Clair Obscur: Expedition 33</em> perdeu sua indicação a Jogo do Ano no Indie Game Awards. Mas o jogo lançado não usava IA. O problema foram elementos temporários gerados por IA durante o desenvolvimento — materiais internos que nunca chegaram aos jogadores. Os recursos finais foram criados inteiramente por pessoas. Não importou. Foram semanas discutindo onde exatamente deveria estar o limite.</p>
<h3>Equipes que nunca usaram IA tiveram que provar isso</h3>
<p><em>Chessplus</em> e <em>Peak</em> foram acusados de usar IA. Nenhum dos dois usou. Ambos haviam sido indicados a prêmios. As duas equipes de desenvolvimento acabaram vasculhando capturas de tela antigas e arquivos com camadas apenas para demonstrar que seu trabalho havia sido feito à mão. A equipe de <em>Peak</em> resumiu melhor: “Podemos até ser lixo, mas somos um lixo artesanal, produzido localmente e feito por humanos.”</p>
<h3>Os estúdios seguiram direções completamente opostas</h3>
<p><strong>SNK</strong> — moderadores do Discord abandonaram seus postos depois que visuais com aparência de IA surgiram em um trailer de <em>Fatal Fury: City of the Wolves</em>. <strong>Games Workshop</strong> — baniu completamente a IA de todas as propriedades de Warhammer. <strong>Larian Studios</strong> — afirmou ter usado IA apenas nos primeiros rascunhos conceituais de Baldur's Gate 3, sem nada disso no jogo final. <strong>Tim Sweeney</strong> — declarou que os rótulos de IA da Steam não servem para nada e deveriam ser eliminados. <strong>Valve</strong> — rebateu dizendo que os devs que reclamam dos rótulos de IA geralmente temem que seu trabalho pareça “feito sem esforço”.</p>
<h2>Encontrar jogos virou uma questão de confiança</h2>
<p>Agora, as coisas são produzidas mais rápido do que qualquer pessoa consegue avaliá-las adequadamente. As perguntas mudaram:</p>
<p>O que é que estou vendo aqui?
Quem fez isso?
Vou me arrepender do tempo que gastar com isso?
Alguma parte disso corresponde ao que o trailer prometeu?</p>
<p>Provavelmente não haverá uma grande resolução. Nenhuma decisão definitiva sobre IA versus criação humana.</p>
<p>O mais provável é: <strong>filtros, transparência verdadeira e estruturas de incentivo que favoreçam a honestidade</strong>.</p>
<h2>Pequenos estúdios presos no meio de tudo isso</h2>
<p>Desenvolvedores independentes acabaram em uma posição estranha.</p>
<p><strong>É realmente difícil ignorar as vantagens:</strong>
Você itera mais rápido. Os custos caem quando quatro pessoas estão fazendo tudo. Desenvolvedores solo agora conseguem realmente terminar seus projetos. A localização deixa de consumir todo o orçamento.</p>
<p><strong>As desvantagens são igualmente reais:</strong>
Resultados que parecem genéricos. Preocupações com dados de treinamento e propriedade intelectual que os advogados ainda não querem abordar. Lançar sistemas que você não entende por completo — algo que algumas pessoas chamam de dívida de compreensão. Ser colocado no mesmo grupo que a enxurrada de lançamentos feitos sem esforço. Quase metade dos desenvolvedores entrevistados acha que, no geral, a IA piora os jogos.</p>
<p>O relatório de 2025 da Unity diz que 79% dos desenvolvedores veem as ferramentas de IA de forma positiva. Parece bastante conclusivo. A realidade é mais complicada. As equipes que fazem isso bem costumam usar IA nas partes tediosas — trabalho braçal, versões preliminares e tarefas repetitivas de controle de qualidade. Direção criativa? Isso continua nas mãos de pessoas.</p>
<h2>A posição da Cinevva: neutra em relação à IA, rigorosa quanto à honestidade</h2>
<p>Não rejeitamos jogos por usarem IA. Também não damos tratamento especial a eles.</p>
<p>A regra é simples: <strong>se houve participação de IA, informe isso.</strong> Os jogadores decidem com o que se importam. Assim, os filtros realmente funcionam.</p>
<ul>
<li><a href="/pt-BR/ai-content">Política de conteúdo gerado por IA</a></li>
</ul>
<h2>Filtros funcionam melhor do que discussões</h2>
<p>O debate sobre IA nos jogos não chegará a um consenso tão cedo. Talvez nunca chegue. Mas preferências individuais? Essas são suficientemente claras.</p>
<p>Alguns jogadores procuram especificamente arte com direção humana. Textos escritos por uma pessoa. Um trabalho artesanal visível.</p>
<p>Outros realmente não se importam. Diversão é diversão.</p>
<p>Os filtros permitem que os dois grupos encontrem o que procuram. Ninguém precisa vencer.</p>
<h2>Modelos de pagamento importam mais do que a opinião de qualquer pessoa</h2>
<p>Quando a receita está vinculada ao <strong>tempo de jogo</strong>, em vez de vendas unitárias, você ganha dinheiro ao:</p>
<p>Prender rapidamente a atenção dos jogadores. Mantê-los interessados. Entregar o que seu marketing prometeu.</p>
<p>A qualidade se torna o caminho óbvio. A forma como você produziu o jogo importa menos.</p>
<p>Prometeu demais no trailer? Os jogadores vão embora imediatamente. A retenção despenca. Mostrou exatamente o que eles vão receber? Eles continuam jogando. O tempo de jogo aumenta. A receita chega. A própria economia resolve a questão.</p>
<h2>O que 2026 provavelmente trará</h2>
<p><strong>Mais regras</strong> — o Regulamento de IA da UE continua se expandindo. As proteções de voz e imagem estão crescendo nos EUA, especialmente depois da SAG-AFTRA.</p>
<p><strong>Ferramentas especializadas</strong> — IA criada especificamente para equipes pequenas. Projetada para manter as pessoas no controle.</p>
<p><strong>Rótulos mais inteligentes</strong> — “Feito com IA” é uma classificação simplista demais. Devem surgir distinções entre fluxos de trabalho assistidos por IA e recursos finais gerados por IA.</p>
<p><strong>Divisão do público</strong> — alguns jogadores buscarão deliberadamente jogos produzidos de forma tradicional. Outros nem pensarão no assunto. Ambos os grupos são grandes o suficiente para justificar a criação de jogos para eles.</p>
<p><strong>Concorrência entre plataformas</strong> — a forma como as lojas lidam com transparência e descoberta se tornará um verdadeiro diferencial.</p>
<h2>O que realmente faz as coisas avançarem</h2>
<p>Discutir não resolverá a questão da IA. O que resolve:</p>
<ol>
<li><strong>Jogadores</strong> escolhendo com base no que realmente importa para eles</li>
<li><strong>Criadores</strong> sendo honestos sobre como trabalham</li>
<li><strong>Plataformas</strong> criando ferramentas que ajudem os dois grupos a se encontrar</li>
</ol>
<p>Essa é a economia pós-IA. Não são grupos em guerra. É a confiança mantendo tudo unido.</p>
<hr>
<p><strong>Conteúdo relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/ai-content">Política de conteúdo gerado por IA</a></li>
<li><a href="/pt-BR/creators">Para criadores de jogos</a></li>
<li><a href="/pt-BR/faq">Perguntas frequentes</a></li>
<li><a href="/pt-BR/guides/frontier-gen-ai-models">Modelos de IA generativa de código aberto de última geração</a> — os modelos específicos e como eles funcionam</li>
<li><a href="/pt-BR/signals/2026-03-06-open-source-ai-pollution">O código aberto tem um problema de poluição por IA</a> — o que acontece quando resultados de IA inundam projetos de código aberto</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[O mercado de trabalho está se transformando — das credenciais às habilidades]]></title>
            <link>https://app.cinevva.com/pt-BR/blog/2026-01-18-skills-over-degrees</link>
            <guid isPermaLink="true">https://app.cinevva.com/pt-BR/blog/2026-01-18-skills-over-degrees</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[90% das empresas agora priorizam habilidades em vez de diplomas. Veja o que isso significa para recém-formados que estão entrando nas áreas de tecnologia e desenvolvimento de jogos.]]></description>
            <content:encoded><![CDATA[<h1>O mercado de trabalho está se transformando — das credenciais às habilidades</h1>
<p><em>Por <a href="/pt-BR/about">Oleg Sidorkin</a>, CTO da Cinevva</em></p>
<p>Vi meu primo se formar com honras em 2023. Ele tinha um diploma em ciência da computação de uma boa universidade pública e um ótimo coeficiente de rendimento. Passou catorze meses se candidatando a vagas antes de conseguir um emprego. Enquanto isso, um amigo que abandonou a faculdade no segundo ano para criar jogos independentes recebeu três ofertas um mês depois de decidir procurar trabalho.</p>
<p>Isso já não é uma anomalia. É o padrão.</p>
<h2>Os números finalmente confirmaram o que já vínhamos sentindo</h2>
<p>Em 2025, <strong>90% dos líderes de RH</strong> afirmaram contratar pessoas sem diplomas tradicionais de cursos de quatro anos (<a href="https://fortune.com/2025/07/01/90-percent-hr-leaders-looking-to-hire-outside-of-traditional-college-degrees-as-they-prioritize-skills/">Fortune</a>). Um quarto das empresas dos EUA eliminou completamente a exigência de bacharelado (<a href="https://www.hrdive.com/news/employer-eliminate-degree-requirements-2025/748998/">HR Dive</a>). E aqui está a parte que dói se você acabou de quitar o financiamento estudantil: <strong>94% dos empregadores dizem que profissionais contratados com base em habilidades têm um desempenho melhor do que aqueles contratados por suas credenciais</strong> (<a href="https://www.forbes.com/sites/cynthiapong/2024/12/26/90-of-companies-make-better-hires-based-on-skills-over-degrees/">Forbes</a>).</p>
<p>A Verizon agora afirma que 99% de suas vagas não exigem diploma. Sergey Brin admitiu que o Google contrata “muitas” pessoas sem bacharelado. A IBM tornou o diploma opcional em metade de suas vagas nos EUA por meio do programa “New Collar”.</p>
<p>Não é uma questão de caridade por parte das empresas. Elas perceberam que os diplomas não eram capazes de prever quem realmente teria um bom desempenho no trabalho.</p>
<h2>Por que o antigo sistema deixou de funcionar</h2>
<p>A resposta honesta? Os diplomas se tornaram um filtro preguiçoso.</p>
<p>Quando você está contratando e recebe 400 candidaturas, exigir um bacharelado reduz a pilha pela metade. Isso não diz quem realmente sabe fazer o trabalho. Diz quem teve dinheiro, tempo e estabilidade familiar para passar quatro anos em salas de aula. Essa correlação com a capacidade sempre foi mais fraca do que fingíamos.</p>
<p>E agora três coisas aconteceram ao mesmo tempo:</p>
<p><strong>As habilidades evoluíram rápido demais.</strong> Motores de jogos, ferramentas de IA, frameworks web. Quando um comitê curricular aprova um curso sobre alguma tecnologia, ela já está ultrapassada. Um curso de quatro anos ensina a teoria de quatro anos atrás. Em tecnologia, isso é história antiga.</p>
<p><strong>Os portfólios se tornaram irrefutáveis.</strong> Por que tentar adivinhar se alguém sabe programar quando você pode consultar o GitHub dessa pessoa? Por que se perguntar se ela consegue lançar um jogo quando sua página no <a href="http://itch.io">itch.io</a> tem cinco projetos concluídos com avaliações de jogadores?</p>
<p><strong>As empresas ficaram desesperadas.</strong> A escassez de talentos é real. Excluir todo mundo que não tem diploma significa excluir pessoas que podem ser exatamente aquilo de que você precisa. Alguns gestores de contratação aprenderam isso da pior forma, depois de verem desenvolvedores autodidatas superarem de longe os profissionais contratados por suas credenciais.</p>
<h2>A indústria de jogos viu isso chegando</h2>
<p>Acho que a indústria de jogos está à frente nessa questão, e vale a pena entender o motivo.</p>
<p>Os estúdios nunca se importaram muito com a instituição onde você estudou. Eles queriam saber o que você lançou. Um jogo bem-acabado, criado em uma game jam de 48 horas, diz mais a um gestor de contratação do que qualquer diploma de quatro anos em design de jogos. Ele prova que você consegue concluir projetos sob pressão. Prova que tomou decisões difíceis sobre o escopo. Prova que o jogo é jogável, não apenas teórico.</p>
<p>Se você está tentando entrar na indústria de jogos em 2026, isto é o que realmente importa (<a href="https://www.dice.com/career-advice/aspiring-video-game-designers-in-2025-what-you-need-to-know">Dice</a>, <a href="https://combinegr.com/2025-global-gaming-employment-outlook-trends-talent-strategy/">CombineGR</a>): duas ou três demos jogáveis e bem-acabadas. Estudos de caso explicando o que você fez e por quê. Evidências de que você conclui o que começa. Domínio de pelo menos um grande motor de jogos, demonstrado por meio de projetos reais.</p>
<p>Os gestores de contratação não procuram potencial. Procuram provas.</p>
<h2>A parte incômoda sobre a qual ninguém fala</h2>
<p>Encontrei algo frustrante quando me aprofundei nas pesquisas.</p>
<p>Um estudo de Harvard/Burning Glass constatou que as empresas que eliminavam a exigência de diploma muitas vezes não contratavam mais candidatos sem formação superior. O aumento foi de apenas cerca de 3,5 pontos percentuais (<a href="https://www.forbes.com/sites/jenamcgregor/2024/02/14/companies-are-dropping-diploma-requirements-for-more-jobs-but-hiring-few-non-degreed-workers-to-fill-them/">Forbes</a>). A política mudou. As práticas de contratação ficaram para trás.</p>
<p>Isso significa que, se você seguir um caminho não tradicional, ainda terá de se esforçar mais. A porta está mais aberta do que antes, mas você ainda não entra em igualdade de condições. Seu portfólio precisa ser irrefutável. Seus projetos precisam falar mais alto do que as credenciais de outra pessoa.</p>
<p>Não é justo. Mas saber disso ajuda você a se preparar.</p>
<h2>O que eu diria a alguém que está começando agora</h2>
<p>Se você está na faculdade, não pare. Mas entenda que apenas o diploma já não basta. Crie coisas por conta própria. Participe de game jams. Obtenha um <a href="https://grow.google/certificates/">Certificado Profissional do Google</a> ou uma certificação da Unity. Trate o diploma como uma entre várias credenciais, não como a única credencial.</p>
<p>Se você não pretende cursar uma faculdade, seu caminho é diferente, mas não necessariamente mais difícil. Crie de forma intensa. Lance projetos. Documente tudo: seu <a href="https://github.com">GitHub</a>, sua página no <a href="https://itch.io">itch.io</a>, seu site pessoal. Agora, essas são as suas credenciais.</p>
<p>De qualquer forma, pratique como explicar seu trabalho. Não apenas o que você criou, mas por que tomou as decisões que tomou. O que faria diferente. O que aprendeu. Entrevistadores se lembram de pessoas que conseguem articular seu raciocínio.</p>
<h2>Isso vai além da contratação</h2>
<p>O que está acontecendo aqui não é apenas uma mudança na forma como as empresas preenchem vagas. É uma mudança naquilo que valorizamos coletivamente.</p>
<p>Durante décadas, as credenciais serviram como filtro. Caras, demoradas, mas fáceis de interpretar. Se alguém tivesse um diploma, era possível presumir certas coisas. Era um sinal, não uma medida direta, mas era bom o bastante.</p>
<p>Agora, esse sinal está perdendo a força. A tecnologia permite ver o que alguém realmente sabe fazer. O trabalho remoto provou que os resultados importam mais do que o lugar onde você se senta. A IA está tornando o conhecimento teórico menos valioso do que a aplicação prática.</p>
<p>Acho isso genuinamente promissor, mesmo que a transição seja conturbada. Antes, a pergunta era “onde você estudou?”. Agora é “o que você consegue criar?”.</p>
<p>Essa é uma pergunta melhor.</p>
<hr>
<h2>Fontes</h2>
<ul>
<li><a href="https://fortune.com/2025/07/01/90-percent-hr-leaders-looking-to-hire-outside-of-traditional-college-degrees-as-they-prioritize-skills/">Fortune: 90% dos líderes de RH contratam profissionais sem diplomas tradicionais</a></li>
<li><a href="https://www.forbes.com/sites/cynthiapong/2024/12/26/90-of-companies-make-better-hires-based-on-skills-over-degrees/">Forbes: 90% das empresas fazem contratações melhores com base em habilidades</a></li>
<li><a href="https://www.forbes.com/sites/jenamcgregor/2024/02/14/companies-are-dropping-diploma-requirements-for-more-jobs-but-hiring-few-non-degreed-workers-to-fill-them/">Forbes: empresas eliminam a exigência de diploma, mas contratam poucos profissionais sem formação superior</a></li>
<li><a href="https://www.hrdive.com/news/employer-eliminate-degree-requirements-2025/748998/">HR Dive: empregadores eliminam exigências de diploma</a></li>
<li><a href="https://www.computerworld.com/article/1623286/no-degree-no-problem-tech-firms-move-away-from-college-requirement-for-new-hires.html">Computerworld: empresas de tecnologia abandonam a exigência de diploma universitário</a></li>
<li><a href="https://www.dice.com/career-advice/aspiring-video-game-designers-in-2025-what-you-need-to-know">Dice: aspirantes a designers de videogames em 2025</a></li>
<li><a href="https://combinegr.com/2025-global-gaming-employment-outlook-trends-talent-strategy/">CombineGR: perspectivas globais de emprego na indústria de jogos para 2025</a></li>
</ul>
<hr>
<p><strong>Relacionado:</strong></p>
<ul>
<li><a href="/pt-BR/guides/game-jams-hackathons">Como ter sucesso em game jams</a></li>
<li><a href="/pt-BR/guides/game-dev-courses">Cursos on-line de desenvolvimento de jogos</a> — caminhos de aprendizagem focados em habilidades para todos os orçamentos</li>
<li><a href="/pt-BR/tutorials/agentic-code-tools">Ferramentas de programação com IA agêntica</a> — as ferramentas que estão redefinindo quais habilidades importam</li>
<li><a href="/pt-BR/blog/2026-02-10-the-intuitive-mind">A mente intuitiva</a> — por que a intuição importa mais do que as credenciais</li>
</ul>
]]></content:encoded>
        </item>
    </channel>
</rss>
