Criação de SVGs com textura de ruído
(daniel.do)- À medida que as ilustrações web se expandem para além dos vetores planos, passando a usar textura de ruído e iluminação/sombreamento, torna-se importante implementá-las como SVGs leves e reutilizáveis
- O objetivo central é adicionar a formas simples um efeito de sombra texturizada e permitir que isso seja levado para componentes Svelte ou React, possibilitando alterar cores e formas
- A estrutura básica define a forma uma vez em
<defs>e a reutiliza com<use>, depois compõe uma máscara de gradiente menos presa à cor usandomaskelinearGradient - A granulação é criada com um filtro SVG baseado em
<feTurbulence>, combinando<feComposite>,<feColorMatrix>e<feBlend>para misturá-la com a cor de preenchimento escolhida - O resultado chegou à textura pretendida, mas ainda há diferenças de renderização no Safari, exigindo ajustes adicionais para a aparência em cada navegador e para aplicação em ilustrações mais complexas
Adicionando textura de ruído a SVGs só com código
- No design web recente, ilustrações vetoriais continuam sendo usadas, enquanto estilos de iluminação e sombreamento com granulação áspera chamam mais atenção do que formas planas e limpas
- Mesmo que esse estilo possa ser exportado como SVG a partir de programas de ilustração, há a possibilidade de a textura entrar como raster ou de o tamanho do vetor aumentar
- O objetivo é criar um efeito de sombra texturizada com ruído usando apenas a especificação SVG comum e mantê-lo em uma estrutura fácil de transformar em componente
- A configuração foi pensada para que o mesmo método funcione mesmo ao trocar a forma e a cor, favorecendo a reutilização
Forma básica e gradiente
- Formas básicas de SVG, como círculos ou retângulos, são fáceis de desenhar, mas em componentes reutilizáveis um gradiente de cores fixas não é suficiente
- Para criar um gradiente que varie de acordo com a cor base, usa-se uma máscara (mask)
- Máscaras SVG controlam as áreas que aparecem ou ficam ocultas por meio de preto e branco
- Embora a abordagem possa ser um pouco confusa, é possível começar com um exemplo simples
- Se a estrutura usa a mesma forma de círculo várias vezes, define-se
<circle id="shape">dentro de<defs>e referencia-se com<use href="#shape"> - Essa abordagem reduz repetição e concentra a definição da forma em um único lugar, em uma estrutura DRY
Como gerar a textura de ruído
- Para sobrepor textura ao gradiente, usa-se um filtro (filter) SVG
- A geração de ruído fica a cargo de
<feTurbulence><feTurbulence>é uma primitiva de filtro SVG que cria texturas artificiais- Para obter a sensação desejada, é preciso ajustar vários valores
- O filtro final mistura a textura com a cor em várias etapas
- Gera o ruído com
<feTurbulence type="fractalNoise" baseFrequency="19.5" numOctaves="10"> - Compõe o resultado de turbulence dentro de
SourceAlphacom<feComposite> - Reduz as variações de cor com
<feColorMatrix type="luminanceToAlpha"> - Combina com o gráfico original usando
<feBlend mode="color-burn">
- Gera o ruído com
- O filtro é aplicado ao elemento
<use>final comfilter="url('#noise')"
Composição final do SVG
- O exemplo final define juntos, dentro de
<defs>, a forma, o filtro, a máscara e o gradiente - O gradiente é configurado como
linearGradient id="fade"- No ponto
0%, usa preto comstop-opacity="0.6" - Nos pontos
65%e75%, usa branco para ajustar gradualmente a transparência
- No ponto
- Na forma final, a cor de preenchimento, a máscara e o filtro são aplicados juntos
fill="hsl(337, 92%, 69%)"mask="url(#gradient)"filter="url('#noise')"
- Ao adicionar cor e ajustar o gradiente de forma mais sutil, obtém-se um resultado mais próximo do efeito visual desejado
- O resultado completo pode ser visto no Full Demo, e o código de implementação está disponível no repository
Resultado e limitações
- Após experimentos adicionais, foi possível criar uma ilustração satisfatória, mas o resultado ainda não é perfeito
- Em especial, no Safari a renderização é diferente da de outros navegadores
- Ainda é simples em comparação com a ilustração complexa usada como referência, mas, com mais refinamento, pode vir a ser útil em mais situações
- Segundo um acréscimo de 7 de dezembro de 2023, após o envio ao Hacker News, o autor tomou conhecimento do artigo de Jimmy Chion de 2021 na CSS-Tricks, Grainy Gradients
- A técnica a que esse artigo chegou parece semelhante a esta abordagem e pode ser consultada também como material complementar
2 comentários
Como sempre, o Safari não decepciona! O nome IE dos anos 20 não é à toa.
Comentários do Hacker News
Recentemente, mexendo com SVG, tentei adicionar uma sombra projetada que era difícil de fazer com canvas, mas no Safari a sombra projetada via CSS dentro do SVG não funcionava, então tive que usar o filtro nativo de sombra projetada do SVG, e ainda assim o resultado ficou diferente dos outros navegadores
Um exemplo específico está em https://www.einarmagnus.net/pages/logo, onde detecto o Safari com JavaScript para ativar a versão em SVG e desativar a versão em CSS
O fundo de “yin-yang recursivo” está em https://einarmagnus.net/blog/2020/01/22/finding-the-logo
Também quero experimentar mais com texturas, mas é frustrante ver como no Safari fica tão diferente. Fico me perguntando se um dia a forma de interpretar SVG vai convergir
Vale a leitura de https://razrfalcon.github.io/notes-on-svg-parsing/ sobre esse tema
Em relatórios de bugs de bibliotecas SVG, vejo com frequência a frase “meu SVG não está sendo renderizado corretamente”, mas a ideia é que, na prática, não existe SVG renderizado corretamente. Quando você começa a usar recursos avançados como texto, filtros e animações, fica fácil quebrar em cada renderizador
É incrível como mudanças pequenas nos parâmetros geram designs bem diferentes, como scorpion, balance e spruce
O termo que o autor estava procurando é mais próximo de dithering. “Grainy Gradients”, do CSS-Tricks, explica a mesma técnica
https://css-tricks.com/grainy-gradients/
Também existe uma alternativa parecida em CSS, mas aplicada à imagem inteira
https://github.com/tomren1/dither-with-css
Dithering é a técnica de, ao quantizar uma imagem suave de alta profundidade de cor para um número limitado de cores, escolher probabilisticamente para cada pixel um dos valores acima ou abaixo para reduzir banding. Em pontos que correspondem exatamente à cor desejada, pode nem parecer ruído
Aqui, o que surge é apenas uma textura uniforme de ruído, sem realmente reduzir o banding. Para fins estéticos, isso até parece melhor do que um dithering de verdade
Ainda assim, como no dithering, pode haver o efeito de esconder defeitos e aumentar a nitidez percebida, fazendo a imagem parecer de melhor qualidade
Parece que o autor procura um termo de design para nomear um estilo visual, como “flat”, “skeuomorphic” ou “italian style”
Pela combinação de formas geométricas com granulação, isso lembra Memphis style
Na verdade, isso me lembra mais pôsteres de viagem vintage. Ex.: https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
[0] https://en.wikipedia.org/wiki/Stippling
Quando fiz isso, eu realmente não tinha visto, e cheguei à mesma conclusão por conta própria, experimentando sozinho
Isso me lembra como fotógrafos de filme e cineastas se esforçavam para eliminar o grão de filme, enquanto fotógrafos digitais agora querem colocá-lo de volta. A grama do vizinho é sempre mais verde
Em meio a tantas coisas digitalmente estéreis, o exemplo desta página parece bem atraente
O problema nunca foi o ruído em si, mas sim quando havia ruído demais
Existe algum material que organize quais recursos quebram em cada implementação? Quero saber como preservar minha sanidade mental com SVG
Recentemente cometi o erro de fazer um pôster acadêmico complexo em SVG, e agora ele é renderizado de forma diferente em cada navegador e programa
https://kxygk.github.io/imergination/
O que encontrei ao fazer isso foi o seguinte: dá para incorporar ou linkar SVG, mas se houver outro SVG dentro dele, não funciona. Por exemplo, numa estrutura pôster → diagrama → vários gráficos/mapas, um SVG atualizado dinamicamente quando a imagem de baixo muda é, na prática, impossível
Às vezes os navegadores simplesmente não exibem imagens embutidas/linkadas como bem entendem por motivos de “segurança”. Se você abrir o SVG do link acima em uma aba separada, ele não aparece corretamente: https://raw.githubusercontent.com/wiki/kxygk/imergination/ag...
Também é difícil converter SVGs complexos para PDF sem inconsistências, e às vezes a memória acaba. Cada conversor de SVG para PDF gera um resultado diferente
Várias implementações, como o Inkscape, às vezes tratavam SVGs incorporados como se fossem gráficos raster, e no fim nunca consegui identificar exatamente quando isso acontecia. Também houve um problema em que o Inkscape não propagava a configuração de DPI para as imagens filhas ao renderizar em PNG
Legal. O Inkscape aparentemente oferece esse tipo de adição de ruído por meio de vários efeitos no menu Filters. Exportei como Plain SVG e, olhando o código, confirmei que ele usa
feTurbulencePor comparação, também testei o preenchimento com ruído do Affinity Designer 2, e no SVG exportado o ruído vinha embutido como uma imagem JPEG em base64
Como é difícil justificar uma licença da Adobe e o InkScape também não me convence muito, tirando essas esquisitices o Designer teria sido a alternativa perfeita
O site do “ApeFest”[1] também tem ruído dinâmico, então fui inspecionar o CSS. É o site do evento que queimou as retinas dos participantes com UV
Na prática, ele usa uma sobreposição PNG em mosaico[2] e a sacode com animação em CSS para parecer “aleatória”. Correção: na verdade parece ser movida por JS. É bem low-tech, mas funciona
[1] https://apefest.com/
[2] https://framerusercontent.com/images/rR6HYXBrMmX4cRpXfXUOvpv...
Eu queria usar esses filtros SVG várias vezes, mas o problema é que a aparência varia muito entre mecanismos e níveis de zoom
Então só dá para usar em efeitos sutis, onde a imprecisão não importa muito, e no fim, infelizmente, sempre acabo voltando para raster
Além disso, como eles operam em nível de pixel, coisas como
feDisplacementMapacabam com o antialiasing, estragando mais ou menos metade dos casos em que dá vontade de usá-losO ruído Perlin e seu sucessor, o ruído fractal, têm uma relação custo-benefício absurda quando se quer adicionar realismo ou uma sensação orgânica
Dá para ver isso sendo usado com muito efeito, por exemplo, na cena de fuga da Cave of Wonders em Aladdin (1992)
Não sei como isso deveria aparecer originalmente, mas no meu celular vejo só um gradiente suave, sem ruído nenhum
Gostei do efeito, mas existe uma forma de torná-lo independente do nível de zoom?
Aproveitando, isso também parece adicionar aleatoriedade, então queria saber se existe um jeito de fornecer uma semente. Por exemplo, seria bom poder definir um único resultado de renderização correto em um tamanho específico
seed, e o algoritmo da função aleatória também é definido na especificação em 186 linhas de código C[0], então em teoria as implementações deveriam produzir o mesmo resultadoDito isso, olhando o código, ele usa operações de ponto flutuante, e pelo que sei isso é definido pela implementação, então ainda parece possível haver diferenças entre plataformas
[0]: https://drafts.fxtf.org/filter-effects/#elementdef-feturbule...