- Relato da experiência de estudar Vulkan por 3 meses e implementar do zero um pequeno motor de jogo com dois jogos-demo incluídos
- Com base na experiência prévia com OpenGL, o autor superou gradualmente a complexidade do Vulkan, implementando recursos centrais como carregamento de glTF, skinning e shadow mapping
- O motor, chamado EDBR (Elias Daler’s Bikeshed Engine), tem cerca de 19 mil linhas de código e usa técnicas modernas de gráficos como bindless descriptors, PVP e BDA
- O texto compartilha detalhes práticos de implementação, incluindo bibliotecas essenciais como vk-bootstrap, VMA e volk, além de padrão de pipeline, automação de build de shaders e gerenciamento de sincronização
- Com a migração para Vulkan, o autor obteve eliminação de estado global, controle explícito, ambiente de depuração melhorado e consistência entre GPUs, e planeja adicionar no futuro render graph, fontes SDF e efeitos volumétricos
Visão geral do aprendizado de Vulkan e do desenvolvimento do motor
- O autor começou a estudar programação gráfica de forma autodidata e, há um ano e meio, já havia escrito um motor 3D em OpenGL
- O motor baseado em Vulkan é adequado para jogos pequenos baseados em fases, com foco mais em aprendizado e experimentação do que em eficiência
- No início, a abordagem foi criar um jogo 3D simples e depois separar as partes reutilizáveis para transformá-las em motor
- O fato de ter sido concluído em 3 meses se deve ao escopo limitado de um motor com propósito específico, e não um motor genérico
Caminho de aprendizado em programação gráfica
- Para iniciantes, recomenda-se começar com OpenGL para aprender exibição de modelos com textura, iluminação Blinn-Phong, shadow mapping etc.
- São indicados materiais como learnopengl.com, Anton’s OpenGL 4 Tutorials e as aulas de Thorsten Thormählen
- Também se destaca a importância de entender álgebra linear (vetores, matrizes e quaternions) junto com materiais práticos atualizados de OpenGL 4.6
Conselhos para evitar bike-shedding
- Evite projeto excessivo e abstrações desnecessárias e mantenha o princípio de “implementar apenas o que é necessário agora”
- Recomenda-se a abordagem de “primeiro faça funcionar, depois melhore”
- Em vez de um motor de uso geral, é mais eficiente terminar primeiro um jogo pequeno
- Não copie diretamente código ou estruturas complexas de outras pessoas; comece por uma estrutura simples
Por que escolher Vulkan
- Em jogos AAA, DirectX é muito usado; em macOS/iOS, Metal; e na web, WebGPU/WebGL
- O autor prefere código aberto e tecnologias padronizadas e escolheu Vulkan por se adequar ao desenvolvimento de pequenos jogos 3D para desktop
- O OpenGL não evolui mais e foi descontinuado no macOS
- O WebGPU é mais conciso, mas ainda tem limitações como falta de maturidade, restrições de recursos e ausência de suporte a bindless e push constants
Processo de aprendizado de Vulkan
- No começo, parecia difícil a ponto de ser “como escrever um driver gráfico na mão”,
mas a acessibilidade melhorou com o surgimento de recursos como dynamic rendering, vk-bootstrap e vkguide
- Principais materiais de estudo:
- vkguide.dev (do básico à prática)
- TU Wien Vulkan Lecture Series
- 3D Graphics Rendering Cookbook, Mastering Graphics Programming with Vulkan
- No primeiro mês, o autor já havia implementado carregamento de glTF, compute skinning, frustum culling e shadow mapping
Estrutura do motor EDBR e processamento de frames
- O código do motor tem cerca de 19.000 linhas, o jogo 3D tem 4.600 linhas e o jogo de plataforma 2D, 1.200 linhas
- Principais etapas de renderização:
- Compute skinning → Cascaded Shadow Mapping (4096×4096) → geometry shading baseado em PBR
- Depth Resolve → Post FX (neblina baseada em profundidade) → renderização de UI
- Todos os sistemas gráficos foram reescritos especificamente para Vulkan, sem misturar com o código anterior em OpenGL
Dicas práticas para desenvolvimento com Vulkan
Bibliotecas recomendadas
- vk-bootstrap: simplifica a inicialização e a configuração do swapchain
- Vulkan Memory Allocator (VMA): automatiza o gerenciamento de memória
- volk: simplifica o carregamento de funções de extensão
Abstração GfxDevice
- Gerencia
VkDevice, VkQueue, VmaAllocator e outros dentro de um único objeto
- Responsável por início/fim de frame, criação de imagens e buffers e gerenciamento de bindless descriptors
Gerenciamento de shaders
- Uso de GLSL, com pré-compilação para SPIR-V em tempo de build via
glslc
- Uso de
DEPFILE do CMake para reconstrução automática quando os shaders mudam
Padrão de pipeline
- Cada etapa de renderização é separada em um pipeline por classe (
init, cleanup, draw)
- Com
VK_KHR_dynamic_rendering, render pass e subpasses são omitidos, mantendo uma estrutura mais simples
Programmable Vertex Pulling + Buffer Device Address
- Um único struct Vertex é usado para tratar todos os meshes
- O shader acessa diretamente via buffer_reference, eliminando a necessidade de VAO
Bindless Descriptor
- Usa arrays globais de textura (
textures[], samplers[]) para sampling baseado em ID de textura
- O struct de material armazena o ID da textura, passado via push constants
Upload de dados dinâmicos
- Os buffers da GPU são trocados por frame, ou os dados são transferidos com buffer de staging na CPU
- A classe
NBuffer gerencia a estrutura de frames in flight
Limpeza de recursos e sincronização
- Uso de funções de cleanup explícito, com gerenciamento manual em vez de depender de destrutores
vkCmdPipelineBarrier2 é usado para sincronização de memória entre passes
- Um Render Graph está planejado para implementação futura
Exemplos de detalhes de implementação
Renderização de sprites
- Com texturas bindless e instancing, milhares de sprites podem ser renderizados de uma vez
- A struct
SpriteDrawCommand é enviada para um buffer da GPU, e é feita a chamada vkCmdDraw(6, N)
- 10 mil sprites são renderizados em 315μs
Compute skinning
- Um compute shader faz a deformação de vértices com base em matrizes de ossos e pesos
- É criado um buffer de saída separado para cada instância, tratado da mesma forma na etapa posterior de renderização
Separação entre jogo e renderizador
- A lógica do jogo usa entt ECS, enquanto o renderizador processa apenas um vetor de DrawCommand
- Os comandos de render são gerados por chamadas como
drawMesh e drawSkinnedMesh
Carregamento de cena e prefabs
- Os níveis são montados no Blender e exportados em glTF, com geração automática de prefabs por convenção de nomes de nós
- Os prefabs são definidos em JSON e referenciam glTF externo
MSAA, UI e ImGui
- Aplicação de MSAA x8 com base em forward rendering
- Implementação de um sistema de layout automático inspirado na API de UI do Roblox
- Para resolver o problema de sRGB do Dear ImGui, o autor escreveu um backend Vulkan próprio
Outros componentes
- Uso de Jolt Physics para física, entt ECS, OpenAL-soft para áudio e Tracy como profiler
Vantagens da migração para Vulkan
- A eliminação de estado global permite uma estrutura de código mais explícita e modular
- As validation layers e o RenderDoc facilitam o rastreamento de problemas
- Há maior consistência entre GPUs e sistemas operacionais, com comportamento mais previsível que no OpenGL
- Fica aberta a possibilidade de expansão para novas linguagens de shader (slang, shady)
- Menos abstração e controle mais claro do pipeline melhoram a manutenção
Planos futuros
- Estão planejados fontes SDF, carregamento paralelo de imagens e geração de mipmaps, Bloom, volumetric fog, animation blending, render graph e AO
- Aprender Vulkan é difícil, mas ajudou muito a entender APIs gráficas modernas e a fortalecer a capacidade de projetar motores
1 comentários
Comentários do Hacker News
Desde o meu post de um ano atrás, minha opinião sobre Vulkan não mudou muito
Pode ser interessante para quem quer controle gráfico de baixo nível, mas para mim foi realmente uma API dolorosa de usar
Ainda quero tentar fazer meu próprio engine de jogo, mas até a configuração inicial do Vulkan ainda me assusta
O que eu quero é algo como uma versão 3D da forma como o SDL lida com gráficos 2D
Para fazer 3D no SDL, no fim você precisa descer para OpenGL, e esse não é o nível que eu gostaria
Talvez o WebGPU seja a alternativa com a qual eu consiga trabalhar de forma agradável
Eu também fiz um engine com isso, mas no fim voltei para um engine baseado em Vulkan porque queria mais controle e desempenho
Mesmo assim, aprender os padrões de sincronização no código de GPU do SDL me ajudou bastante no engine em Vulkan
wgpude Rust é um meio-termo que oferece abstração no nível do WebGPUÉ mais poderoso que OpenGL, mas não exige lidar diretamente com detalhes como barreiras de recurso ou transições de layout
Ele cuida de parte do bookkeeping em tempo de execução e tem restrições como suportar apenas uma única fila
Vulkan é difícil, mas melhora bastante se você usar extensões com suporte dos principais vendors
Mesmo assim, ainda existe complexidade desnecessária, como exigir configurações detalhadas que os drivers acabam ignorando
Hoje desapareceu a API intermediária entre engines de jogo de alto nível e Vulkan/Metal de baixo nível
Para um iniciante aprender gráficos 3D, é preciso uma API simples no nível de “desenhar um triângulo”, que não exija conhecer conceitos como shaders ou buffers
O controle minucioso do Vulkan só é necessário para um número muito pequeno de desenvolvedores de engine, e para a maioria algo no nível do OpenGL já basta
O 3D tem muito mais elementos combináveis do que o 2D, então uma API gráfica simples não dá conta
O OpenGL originalmente também buscava isso, mas acabou ficando complexo
“bike shedding” significa ficar obcecado com problemas triviais e deixar passar o que importa
O que foi descrito no texto original está mais para feature creep ou over-engineering
Ela significa focar apenas no prazer pessoal e travar o progresso do projeto
bike shedding costuma ser explicado como “escolher a cor do bicicletário antes de terminar a casa”
Disseram que “não é uma boa ideia começar o desenvolvimento de engine com um clone multiplayer de Minecraft”,
mas na prática muita gente faz jogos estilo Minecraft como primeiro projeto de engine
Em engines de voxel, isso é quase um “Hello, world”
Na época, em 2024, o post teve 625 pontos e 260 comentários — link do original
Vulkan foi a tecnologia mais difícil que eu já aprendi
É tão pouco intuitiva e repetitiva que acaba roubando a diversão de programar
Se quiser começar de forma mais simples, recomendo OpenGL, especialmente as versões anteriores à introdução de shaders
Mas a indústria está empurrando OpenGL cada vez mais para fora
Eu só copiava código seguindo tutoriais e não entendia os conceitos
Então migrei para WebGPU (Google Dawn), que era muito mais simples que Vulkan
Depois de aprender os conceitos graças às restrições do WebGPU, voltar para Vulkan ficou bem mais fácil
O WebGPU não tem push constant e sofre com explosão de pipelines, mas Vulkan é mais difícil em sincronização e gerenciamento de memória
SDL_GPU também é uma API em nível parecido e é boa para iniciantes
Vulkan é uma API excessivamente projetada
Uma alocação de memória de GPU que no CUDA cabe em uma linha exige uma quantidade enorme de boilerplate no Vulkan
O Vulkan moderno melhorou bastante, mas ainda há muito caminho pela frente
Espero que SDL3 ou wgpu virem a camada de abstração que alivie essa complexidade
Como a Valve apoia o SDL3, acho que esse lado tem mais chance
A primeira pergunta deveria ser: “os gráficos precisam ser processados em multithread?”
Se não, não há motivo para usar Vulkan/DX12
Até aparecer um problema de desempenho, é muito melhor usar OpenGL, DX11 ou um engine pronto
Eu sou fascinado por programação 3D/de jogos e frequentemente assisto a youtubers mostrando o processo de fazer jogos
Mas, comparado a webapps ou DevOps, é um mundo muito mais complexo
Aparecem pixel shaders, compute shaders, geometria, álgebra linear e até PDEs
canal do YouTube TokyoSpliff
Acho legal que hoje em dia criar um engine de jogo como hobby é visto como algo bacana
Eu também desenvolvo meu engine pessoal há 10 anos, e tem sido uma experiência muito recompensadora
Se você está começando em programação gráfica, é melhor começar por OpenGL
Li os tutoriais de OpenGL da NeHe há 23 anos e ainda acho que são alguns dos materiais de aprendizado mais bem estruturados que existem
Só para constar, eu não sou o autor do post original; apenas mantive o título