Aleatoriedade? Não obrigado, prefiro o Perlin Noise

Exemplo de uma progressão com Perlin Noise

Nas aulas de hoje estivemos a ver alguns exemplos e pesquisas de designers portuguesas. Da Alda Rosa e da Cristina Reis —que estão muito bem documentadas neste site: https://www.wgd-pt.com/— à Inês Nepomuceno foram muitas as gerações e os nomes mencionados.

Um dos mais populares foi a Lizá Ramalho (R2). Os padrões, texturas tipográficas e os modelos dimensionais são sempre mencionados. Quase que dá para fazer todo tipo de trabalhos a partir de referência visual deles.

Por isso, depois de ver o trabalho dos estudantes, atacámos alguns exemplos. Como a instalação “Futuro” que esteve patente na fábrica Oliva

Manipular o Processing em 3D e muito fácil. Basta acrescentar o “modo” P3D como um parâmetro no size e começar a manipular os sistema de coordenadas adicionando uma terceira: o Z (para a profundidade).

Alguns aspetos importantes a mencionar quando se começa a trabalhar com o modo 3D é que o a colocação de objetos é sempre feita no “X: 0, Y:0, e Z:0” do mundo. Por isso é importante compreender a transformação da matriz (e talvez o push e o popMatrix?). São as operações mais importantes do Transform:

Transform

Estes são essenciais para começar a usar e manipular. Imediatamente, vai tornar-se necessário o:

(que na realidade já usámos para aldrabar rapidamente o desenho dos robôs…) Os mais aventurosos, vão precisar de mais funções. Por isso, vale a pena procurar e estudar

No futuro, tenho que aprender a dominar (e a explicar?) o

Outro aspeto importante quando se manipula o mundo 3D é a noção do ponto de vista do observador. No mundo 3D o observador é a câmara. Hoje só manipulamos a posição “simples” de uma câmara (nunca me lembro como funciona)

Exemplo de controlo geometria 3D e controlo de câmaras
size(500, 500, P3D);

// vai para o meio do ecrã
translate(width/2, height/2, 0); 

// a camera permite mudar o ponto de vista do modelo/mundo 3D
camera(  0,   0,  400, // eye (isto é que é a camera)
        -80,  0,  0,   // center (target object)
         0,   1,  0);    // up direction

// muda o ângulo da câmera
rotateY( radians(30) );

// o push e o popMatrix permitem mexer "temporariamente" as coordenadas
// vamos ver mais à frente
// https://lsi.fba.up.pt/2019/2020/03/21/mais-uma-moeda-mais-uma-volta-todos-a-distancia-claro/
pushMatrix();

// mexe o "mundo 3D" um pouco para a frente
translate(0, 0, -100);

// desenha uma caixa no centro do mundo
box(30, 200, 10);
popMatrix(); // restaura as coordenadas

// volta a mexer o munod para trás
//translate(0, 0, 100);

// Desenha a primeira caixa
box(30, 200, 10);

// vai um pouco para a direita e para baixo
translate(65, 85, 0);
box(100, 30, 10);


Mas, tal como em 3D, é importante reter que é necessário configurar as luzes, a câmara e os materiais dos objetos e do mundo. Hoje esqueci-me de mexer na perspetiva da câmara… que é sempre top e seria a opção que devia ter manipulado imediatamente a seguir.

Mas pronto, fica aqui este primeiro “gostinho” para mais à frente voltarmos ao 3D? Bom. D quelaquer forma, não esquecer as luzes do mundo também. Super importante.

Lights, Camera

Lights

ambientLight() directionalLight() lightFalloff() lights()lightSpecular() noLights() normal() pointLight() spotLight()

Camera

beginCamera() endCamera() frustum() ortho() perspective() printCamera() printProjection()

Coordinates

modelX() modelY() modelZ() screenX() screenY() screenZ()

Material Properties

ambient() emissive() shininess() specular()


Depois, num outro exemplo, usamos manipulação de Arrays. Isto, combinado com carateres e Strings, permite mapear os valores das imagens a letras. A explicação começou com um array de caraters, mas rapidamente fizemos evoluir o exemplos para usar apenas um String como “uma espécie array” de carateres para fazer um “filtro ASCII art” com 3 intervalos. Ficou a ideia para aplicar a isto a poesia concetual! 😉

Han Solo in simple ASCII
// declarar variáveis
String texturas;
PImage han;

int n, m;

// inicializar
size(500, 500);
han = loadImage("han.jpg");

// esta variávei é a minha "base de dados" de letras para desenhar
texturas = " .iO"; // espaço = 0, "." = claro, "i" = médio, e "O" = escuro

n = han.width;
m = han.height;

for (int k = 0; k < m; k++) {
  for (int i = 0; i < n; i++) {

    //char cc = cores.charAt(i);
    color tc = han.get(i,k) ; // vai buscar a cor do pixel
    float tb = brightness( tc ); // só o brilho
    // regra de "3-simples" para "mapear" os valores de uma escala para outra
    float tbconv = map(tb, 0, 255, texturas.length(), 0);
    
    int vfinal = int(tbconv);
    //print(vfinal);
    
    if (vfinal == 0) {
      print(texturas.charAt(0));
    } else if (vfinal == 1) {
      print(texturas.charAt(1));
    } else if (vfinal == 2) {
      print(texturas.charAt(2));
    } else if (vfinal == 3) {
      print(texturas.charAt(3));
    }
    
  }
  println();
}

Ainda durante a manhã foi possível usar e abusar dos objetos vetoriais PShape. Para usar —apesar de dizer isto várias vezes, parece que é sempre preciso repetir— é preciso criar objetos SVG compatíveis no Illustrator.

Isto significa que as formas não podem ter “coisas estranhas”. Isto é, assim que se faz o desenho, antes de exportar é necessário “limpar” as formas de elementos “não-standard”, como os brushes ou propriedades específicas do software (round corners, effects, etc…). E isso é fácil com o Expand [appearance] do Illustrator. É só preciso lembrar de converter sempre antes de exportar em SVG.

Nas opções de exportação, para funcionar direitinho com as cores, e strokes, exportámos em SVG 1.0 com as CSS Properties em Presentation Attributes.


No final do dia, a Inês ainda nos desafiou a experimentar um Perlin Noise. Já tinha sido abordado, mas acho que acabámos por não fazer grande coisa, na aula da manhã.

Random

É sempre uma boa altura para ver os vídeos do Shiffman:

Mas, se a explicação for “demais”, podemos sempre colocar isto em ação. O sketch ficou um pouco mais longo, mas inclui alguns exemplos alternativos e explicações em comentário.

// declarar (atenção aos tipos de variáveis…)
float nh, nv;
float ts;
float ph, ninc;

// inicializar
size(2000, 1000);
background(255);

// tamanho dos tiles/bolas
ts = 5;

// numero de repetições (bolas/tiles)
nh = width/ts;
nv = 10;

// valor inicial de progressão no Perlin Noise
ph = 0.0;

// valor de incremento para a progressão do noise
ninc = 0.05;

// desenhar —————————————————————————————————— //

// configurar gráficos
noStroke();
fill(230, 80, 0);
//fill(230, 80, 0, 100);

// ajustar ligeiramente a "folha" do desenho
translate(0, 25);

// repetir para as linhas (vertical)
for (int k = 0; k < nv; k++) {

  // repetir para as colunas (horizontal)
  for (int i = 0; i < nh; i++) {

    // dá-me um novo valor (ph) a partir da sequência perlin (noise)
    // quanto mais pequeno, menor a variação
    ph += ninc;
    float displacement = noise(ph);

    // o resultado é um número (float) pequeno
    // para "mostrar" visualmente em gráficos é preciso "aumentar"

    //displacement *= 200; // progressão aritmética simples
    displacement = displacement * displacement * 300; // ou uma progressão geométrica! (maiores diferenças)

    noStroke();
    fill(230, 80, 0, 200);
    
    // desenhar uma nova posição mais ou menos aleatório, mas parecida à anterior
    // circle(ts*i, 50 + displacement, ts ); // apenas posição

    // desenhar uma nova posição e um tamanho mais ou menos aleatório mas parecido ao anteiror
    //circle(ts*i, 50 + displacement, displacement/10 );
    
    float tpos = noise(ph); // usar o noise para afetar a posição vertical inicial
    
    // usar a função map para fazer uma "regra de 3-simples" 
    // e converter a escala do valor entre o intervalo original e um novo intervalo
    // o noise é um valor entre 0 e 1
    // e a conversão vai ser 0 = 2 e o 1 = 20
    // https://processing.org/reference/map_.html
    tpos = map(tpos, 0, 1, -30, 30);
    
    float tsize = map(noise(ph), 0, 1, 0, 100 ); // usar o noise para afetar o tamanho
    float tsize1 = map(noise(ph+ninc), 0, 1, 0, 100 ); // usar o que vai ser o valor seguinte do noise
    
    float tpos1 = map(noise(ph+ninc), 0, 1, -30, 30); // para a versão shape > ponto da direita 
    
    // desenhar em "contínuo" com shapes
    beginShape();
    vertex(ts*i,     tpos -tsize );   // top-left
    vertex(ts*i +ts, tpos1 -tsize1);  // top-right
    vertex(ts*i +ts, tpos1 +tsize1);  // bottom-right
    vertex(ts*i,     tpos +tsize);    // bottom-left
    endShape(CLOSE);
    
    // desenhar apenas uma posição
    noFill();
    stroke(0);
    strokeWeight(0.5);
    //strokeWeight(noise(ph)*5 + 1);
    line(ts*i, tpos, ts*i +ts, tpos1);
    
    //fill(0);
    //circle(ts*i, ts+tpos, ts);
    
  }
  translate(0, height/nv);
}


Neste exemplo ainda usámos e abusámos da função map(). Não era preciso, mas, tal como na manhã, aproveitei para explicar isto porque poupa imenso trabalho nas contas! Não me lembrei, mas podíamos também ter usado o exp() para acentuar mais as diferenças produzidas no noise.

Aliás, toda a secção de cálculo matemático é importante (mas pode ser estudada mais tarde):

Calculation

abs() ceil() constrain() dist() exp() floor() lerp()l og() mag() map() max() min() norm() pow() round() sq() sqrt()

Deixe um comentário

O seu endereço de email não será publicado.