4 pontos por GN⁺ 2023-12-08 | 2 comentários | Compartilhar no WhatsApp
  • À 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 usando mask e linearGradient
  • 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 SourceAlpha com <feComposite>
    • Reduz as variações de cor com <feColorMatrix type="luminanceToAlpha">
    • Combina com o gráfico original usando <feBlend mode="color-burn">
  • O filtro é aplicado ao elemento <use> final com filter="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 com stop-opacity="0.6"
    • Nos pontos 65% e 75%, usa branco para ajustar gradualmente a transparência
  • 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

 
ndrgrd 2023-12-09

Como sempre, o Safari não decepciona! O nome IE dos anos 20 não é à toa.

 
GN⁺ 2023-12-08
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

    • Por enquanto, provavelmente não vai convergir. Infelizmente, não existe nenhuma implementação da especificação completa de SVG, e no SVG 2 isso fica ainda mais distante
      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
    • Quem entra na página ‘logo’, vê só a forma de onda padrão e vai embora, deveria experimentar alguns dos presets no menu suspenso no canto inferior direito
      É 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

    • Na verdade, isso está mais para ruído puro e, mais especificamente, uma combinação de ruído do tipo Perlin com o modo de mesclagem “color burn”. Só parece dithering porque está sobre um gradiente, mas não é dithering
      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
    • Dithering é uma técnica real relacionada, com efeito final parecido, mas não significa por si só adicionar granulação, e este exemplo também não foi feito com dithering
      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
    • Se não está tentando simular uma faixa de cores maior com uma paleta limitada, não sei se dá mesmo para chamar de dithering
      Na verdade, isso me lembra mais pôsteres de viagem vintage. Ex.: https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
    • Tem relação com dithering, mas pessoalmente me parece mais próximo de pontilhismo [0]
      [0] https://en.wikipedia.org/wiki/Stippling
    • Fiquei até um pouco sem graça. Acho que vou atualizar o post para linkar esse artigo
      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

    • Melhor não generalizar demais. Desde o surgimento do vídeo digital e depois da fotografia digital, sempre existiram ao mesmo tempo pessoas tentando reduzir ruído, eliminá-lo, imitá-lo ou usá-lo de forma intencional
      O problema nunca foi o ruído em si, mas sim quando havia ruído demais
    • O Foveon X3 não resolveu isso até certo ponto? De um jeito que parece filme, mas é digital
  • 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 feTurbulence
    Por 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

    • Não estou no PC agora para conferir diretamente, mas fiquei curioso se ele incorpora uma única textura de ruído e a reutiliza várias vezes, ou se cria uma nova textura toda vez que você usa ruído
      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 Affinity Designer 2 foi bem bom como alternativa a não pagar Adobe Creative Cloud, mas eu realmente tive que ajustar o SVG manualmente muitas vezes
  • 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...

    • o efeito do apefest usa muita CPU e, consequentemente, muita energia
  • 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 feDisplacementMap acabam com o antialiasing, estragando mais ou menos metade dos casos em que dá vontade de usá-los

  • O 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

    • Aqui também. Estou usando Android Chrome
    • Parece que o Android Chrome não suporta algum recurso usado no código
  • 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

    • Sou o autor. Dá sim. O algoritmo base e a implementação do SVG aceitam um valor de seed: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/s...
    • Como disse a outra resposta, existe o atributo 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 resultado
      Dito 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...