Modelagem low-poly no Blender
Low poly é um estilo, não apenas uma baixa contagem de triângulos: formas grandes e legíveis, facetas planas visíveis, um punhado de cores sólidas e proporções um pouco além da realidade. Um modelo realista com menos polígonos não é low poly; é um modelo realista bruto.
O loop de trabalho geral (olhar a cena, planejar em partes, uma parte por chamada, screenshot
após cada etapa, nunca apagar o que você não criou) está na skill blender-modeling:
siga-o. Esta skill diz o que é diferente para low poly.
Sua conexão Blender
Você trabalha através do BlenderLab, o servidor oficial do Blender Lab; suas ferramentas são
blenderlab_…:
- O que está na cena:
get_objects_summary; um objeto em detalhe:get_object_detail_summary(porname). O arquivo, salvo ou não, e seu caminho:get_blendfile_summary_path_info. - Executar Python:
execute_blender_code. Nada persiste entre chamadas (carregue seus auxiliares em cada script); o que o script imprime é retornado, e um dicionário atribuído aresultretorna como dados. Sua proteção recusa alguns operadores (sair do Blender, reset de fábrica). - Ver o viewport 3D:
get_screenshot_of_area_as_imagecomarea_ui_type: "VIEW_3D"(a janela inteira:get_screenshot_of_window_as_image); aponte para um objeto:jump_to_view3d_object_by_name. - Verificar a API Python:
get_python_api_docs(um identificador comobpy.types.BevelModifier),search_api_docs; o manual:search_manual_docs. - Renderização rápida:
render_thumbnail_to_path,render_viewport_to_path; a imagem vai para a pasta temporária do Blender e o resultado fornece o caminho real. - Exportar: código (
bpy.ops.export_scene.gltf,bpy.ops.export_scene.fbx). - Bibliotecas de assets e geradores 3D: nenhum. Construa o objeto ou peça ao usuário um arquivo para importar.
Se essas ferramentas não estiverem disponíveis, pare e avise: o usuário precisa instalar a conexão BlenderLab (Ferramentas → Conexões), fornecê-la a este agente, e ter o Blender aberto com o servidor do add-on iniciado. Não descreva o trabalho como concluído quando você não pôde realizá-lo.
O estilo, como regras
- Sombreamento plano em toda parte. Cada face é uma cor plana; sem sombreamento suave, sem suavização automática. Isto é o que faz as facetas aparecerem.
- Poucos segmentos, de propósito. Cilindros com 6-8 lados, cones com 5-6, esferas como icospheras com 1-2 subdivisões. Um cilindro de 32 lados estraga o visual mais do que qualquer outra coisa.
- Sem Chanfro, sem Superfície de Subdivisão. Eles arredondam as facetas. Para uma borda mais suave, use no máximo um chanfro de um segmento.
- Cor sólida, sem texturas. Um material por cor, Principled BSDF com Cor Base, Rugosidade 0.8-1.0, Metálico 0, sem brilho especular. Reutilize materiais: uma cena inteira geralmente precisa de 6-12.
- Uma paleta deliberada. Escolha antes de modelar: 1-2 cores dominantes, 2-3 de suporte, 1 de destaque. Meio-tons ligeiramente dessaturados, equilíbrio entre quentes e frios, e dois tons de cada cor principal (claro e escuro) para variedade entre partes vizinhas.
- Proporções encorpadas e exageradas. Paredes, vigas e troncos mais grossos que na vida real; telhados, portas e janelas superdimensionados; formas cônicas (mais largas na base, ou no topo para um visual cartoon). Partes finas desaparecem neste nível de detalhe.
- Imperfeição manual, em pequenas doses. Nada perfeitamente alinhado: incline uma chaminé 2-4°, varie telhas em alguns por cento de seu tamanho e 2-3° de inclinação, mova vértices de rochas e terreno aleatoriamente. A estrutura ainda deve ser legível: fileiras de telhas permanecem fileiras, nenhuma peça virada de lado ou levantada da superfície. Com uma semente aleatória fixa, para que uma reexecução dê o mesmo resultado.
- Detalhe através de pequenas peças separadas, não subdividindo. Uma janela é um quadro de quatro caixas mais uma vidraça; telhas são fileiras de pequenas lajes; pedras são alguns blocos irregulares embutidos na parede. Cada peça é simples; a disposição dá a riqueza.
- Peças podem se intersectar. Partes low-poly são empurradas umas nas outras em vez de serem soldadas: uma viga afunda em uma parede, uma rocha no chão. Não gaste esforço com booleanos ou junções limpas.
Fluxo de trabalho
- Leia a referência, se houver uma: liste as partes, conte o que se repete (fileiras de telhas, número de vigas, janelas por parede), anote as proporções entre partes como razões (altura do telhado contra altura da parede, largura contra altura), e nomeie as cores da paleta. Construa a partir dessa lista. A imagem decide: ancore um tamanho e derive o resto das razões, não invente tamanhos em metros que a imagem contradiz, e não adicione o que ela não mostra (uma base, adereços) a menos que seja solicitado.
- Blockout primeiro as grandes massas com caixas e cunhas nas proporções corretas. Tire um screenshot e compare a silhueta: se a silhueta estiver errada, os detalhes não a salvarão.
- Paleta: crie os materiais uma vez, nomeados pelo que colorem (
LP_Parede_Claro,LP_Telhado_Vermelho,LP_Madeira_Escuro). - Estrutura, depois partes secundárias (vigas, quadros, chaminé, degraus), depois pequenos detalhes (telhas, pedras, tábuas, rachaduras), depois arredores (laje de chão, algumas rochas, tufos de grama).
- Enrugar: os deslocamentos e inclinações aleatórios da regra 7.
- Iluminação e enquadramento (abaixo), depois coloque a vista no ângulo da referência, capture-a e compare parte por parte: silhueta, proporções, onde cada parte se senta, cores. Corrija.
- Finalize com
final_check, a última chamada da tarefa: a função está abaixo, em "A verificação final". Você não vê seus renders; ela mede o render para você (o modelo separado do fundo, contra a imagem de referência quando você tem seu nome de arquivo) e imprime uma linhaPROBLEM:para o que está errado. Corrija, execute novamente (no máximo duas rodadas) e coloque sua saída no relatório. Medidas que você mesmo faz não a substituem: uma tarefa que constrói ou altera um modelo não está concluída até que essa função tenha sido executada.
Espere duas ou três rodadas de correção contra a referência. Diga o que ainda difere.
Você tem um número limitado de rodadas de ferramenta por tarefa (veja "Seu orçamento de etapas" em
blender-modeling): construa duas ou três partes relacionadas por chamada, capture nos
marcos (blockout, detalhes, final) e coloque as partes mais visíveis primeiro.
Código para cada receita está em references/low-poly-recipes.md: paleta, sombreamento plano, caixas cônicas, telhados de duas águas, fileiras de telhas, janelas e portas, esquadrias de madeira, rochas, árvores, terreno, dispersão e a configuração de iluminação. Abra-o antes do primeiro script.
Muitos blocos em um objeto
Fiadas de pedras, ameias, pavimentos, tábuas e tijolos são muitas caixas pequenas que pertencem a
um objeto. Construa-os com lp_blocks: ela finaliza cada caixa em seu próprio bmesh e só
então a adiciona ao objeto. Não junte caixas anexando listas de vértices e faces manualmente,
e não mova "os últimos N vértices" após uma operação que muda seu número (um
chanfro faz isso): as faces acabam nos vértices errados e o modelo sai como picos esticados.
final_check nomeia um objeto quebrado assim; reconstrua-o, não o reporte.
import bpy, bmesh
from mathutils import Matrix, Vector
def lp_blocks(name, col, mat, blocks, chamfer=0.03):
"""Muitas caixas em UM objeto com sombreamento plano. blocks é uma lista de (centro, tamanho) ou
(centro, tamanho, rotação_z): centro e tamanho são (x, y, z) em metros, a rotação em radianos.
Cada caixa é finalizada em seu próprio bmesh e só então adicionada ao objeto."""
whole = bmesh.new()
for center, size, *rot in blocks:
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=1.0)
bmesh.ops.scale(bm, vec=Vector(size), verts=bm.verts)
if chamfer > 0:
bmesh.ops.bevel(bm, geom=bm.edges[:], offset=min(chamfer, min(size) * 0.25), segments=1, affect='EDGES')
bmesh.ops.transform(bm, matrix=Matrix.Translation(Vector(center)) @ Matrix.Rotation(rot[0] if rot else 0.0, 4, 'Z'), verts=bm.verts)
piece = bpy.data.meshes.new("_piece")
bm.to_mesh(piece)
bm.free()
whole.from_mesh(piece) # adiciona a caixa finalizada à malha do objeto
bpy.data.meshes.remove(piece)
mesh = bpy.data.meshes.new(name)
whole.to_mesh(mesh)
whole.free()
for poly in mesh.polygons:
poly.use_smooth = False
obj = bpy.data.objects.get(name)
if obj is None:
obj = bpy.data.objects.new(name, mesh)
col.objects.link(obj)
else:
obj.data = mesh
obj.data.materials.append(mat)
return obj
# Uma fiada de pedras de parede, alternando dois tamanhos, em um único objeto.
col = bpy.data.collections["LP_Model"]
stone = bpy.data.materials["LP_Pedra_Claro"]
lp_blocks("Parede_Fiada_01", col, stone,
[((x * 0.42 - 1.26, -1.0, 0.35), (0.40 if x % 2 else 0.36, 0.20, 0.30)) for x in range(7)])
Formas e como obtê-las
| Desejo | Construa como |
|---|---|
| Paredes, vigas, tábuas, degraus | Caixas, escaladas; afile o topo ou a base para dar caráter |
| Blocos de pedra, ameias, pavimento, tijolos | lp_blocks (acima): muitas caixas chanfradas em um objeto |
| Telhado de duas águas | Um prisma (perfil de triângulo extrusado), com beiral em todos os lados |
| Telhas | Fileiras de lajes finas na inclinação do telhado, cada fileira sobrepondo a de baixo, com pequeno tamanho e inclinação aleatórios |
| Chaminé, torre, pilar | Caixa cônica; laje de topo ligeiramente mais larga |
| Árvore | Tronco: cilindro cônico de 5-6 lados. Folhagem: 2-3 cones empilhados (pinheiro) ou 1-3 icospheras deslocadas (frondosa) |
| Rocha | Icosphere com 1 subdivisão, vértices movidos aleatoriamente, achatada em Z, afundada no chão |
| Terreno, ilha | Uma grade com poucos cortes, vértices elevados por ruído, depois sombreamento plano; ou uma laje irregular grossa para base de diorama |
| Água | Um plano ou laje plana, azul, ligeiramente brilhante (Rugosidade 0.2) |
| Grama, arbustos | Pequenos cones ou icospheras achatadas em aglomerados |
| Coisas redondas (barril, roda, poço) | Cilindros de 6-8 lados |
Orçamentos
Contagens aproximadas de triângulos, para julgar se algo está superconstruído:
| Asset | Triângulos |
|---|---|
| Pequeno adereço (rocha, caixote, arbusto) | 20-150 |
| Árvore | 60-300 |
| Casa ou edifício | 500-3,000 |
| Personagem | 300-1,500 |
| Diorama inteiro | 3,000-15,000 |
Imprima a contagem ao finalizar. Muito acima do orçamento geralmente significa muitos segmentos ou um modificador que subdivide.
Iluminação e apresentação
O estilo depende da luz: as facetas só são lidas quando faces vizinhas recebem brilhos diferentes.
- Um sol em ângulo (cerca de 45° para cima, 30-45° para o lado), força 3-4, com sombras suaves; mais um mundo de uma cor de céu clara com força 0.6-1.0 para que as sombras não sejam pretas. Nenhuma configuração de estúdio de três pontos. Não aumente a luz para obter um fundo branco, e não tinge o mundo para colori-lo: o material do próprio fundo dá a cor.
- Um sol ligeiramente quente contra um mundo ligeiramente frio dá o visual clássico.
- Defina a transformação de visualização para
Standardpara que as cores da paleta permaneçam como escolhidas (a transformação padrão as acinzenta). - Câmera: vista de três quartos de cima (30-35° para baixo), lente longa (50-85 mm) ou ortográfica para um diorama.
- Um fundo simples em uma cor da paleta, ou uma laje de chão que termina em uma borda limpa. Um plano de fundo tem que ser muito maior que o modelo (20 vezes ou mais), ou o mundo da mesma cor: nenhuma borda ou horizonte pode aparecer no quadro.
- EEVEE é suficiente e rápido. Sombras suaves e oclusão ambiente ajudam muito.
Materiais, renderização para arquivo e solução de problemas estão em blender-materials-render.
Para um motor de jogo
Assets low-poly geralmente vão para um motor. Mantenha o sombreamento plano ao exportar (as facetas
são armazenadas como normais divididas, o que aumenta a contagem de vértices, mas não a de triângulos),
mescle as peças de um asset em uma única malha com alguns slots de material, e coloque a
origem no centro inferior. Siga blender-game-export para o resto.
A verificação final (sempre sua última chamada)
As ferramentas de render escrevem um arquivo e fornecem seu caminho: você nunca vê a imagem. Então, a última
chamada execute_blender_code da tarefa é final_check, copiada como está, e o que ela
imprime vai para o relatório como está. Ela faz um pequeno render da câmera da cena e
nomeia o que está errado: objetos sem material, geometria quebrada, um modelo fora do
quadro, um render queimado ou escuro, e, com o nome do arquivo da referência, as diferenças em
enquadramento, brilho, cor e quão sólida é a silhueta.
import bpy, os, re
import numpy as np
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view
BACKDROP_WORDS = ("chão", "fundo", "piso", "céu", "sombra")
def subject_stats(path, size=128):
"""Uma imagem dividida em fundo (as cores de seus quatro cantos) e assunto (o resto):
quanto da imagem o assunto ocupa, sua caixa, seu brilho, saturação e cores."""
img = bpy.data.images.load(path, check_existing=False)
w, h = img.size
sw, sh = (size, max(8, round(size * h / w))) if w >= h else (max(8, round(size * w / h)), size)
img.scale(sw, sh)
px = np.empty(len(img.pixels), dtype=np.float32)
img.pixels.foreach_get(px)
bpy.data.images.remove(img)
rgb = px.reshape(sh, sw, 4)[:, :, :3] # linha 0 é a parte inferior da imagem
cy, cx = max(2, sh // 8), max(2, sw // 8)
corners = np.concatenate([rgb[:cy, :cx].reshape(-1, 3), rgb[:cy, -cx:].reshape(-1, 3),
rgb[-cy:, :cx].reshape(-1, 3), rgb[-cy:, -cx:].reshape(-1, 3)])
# Fundo = próximo a uma das cores do canto (um gradiente ou uma sombra suave ainda conta).
q = np.unique(np.round(corners * 8).astype(int), axis=0)
flat = rgb.reshape(-1, 3)
dist = np.min(np.abs(flat[:, None, :] * 8 - q[None, :, :]).max(axis=2), axis=1)
subject = (dist > 1.0).reshape(sh, sw)
share = float(subject.mean())
whole = flat @ np.array([0.2126, 0.7152, 0.0722], dtype=np.float32)
out = {"size": (w, h), "image_brightness": round(float(whole.mean()), 3), "subject_pct": round(100 * share, 1)}
if share < 0.005:
return
…
[truncado para tradução]