walnutpedia / notes

1 stars 0 forks source link

Processing学习笔记 01 - Basics & NoC Intro #3

Open walnutpedia opened 8 years ago

walnutpedia commented 8 years ago

开始学Processing了,使用的资料是:

The Nature of Code 这本不算基础书籍,主要讲怎样用 Processing 来模拟真实世界的规律,涉及到利用(伪)随机数和概率来设计算法。因为这本没有 Processing 语法和API的讲解,可能不太适合没有任何编程经验的人来入门。

Processing.org tutorials 这个是 Processing 官方的教程了,Text Tutorials 那部分组织得挺好的,基础 API 和语法可以从这边来了解。

今天主要看的是 https://processing.org/tutorials/overview/ 这篇overview 和 http://natureofcode.com/book/introduction/ NoC的 Introduction章节。

Processing 概述与环境准备

其实 Porcessing 应该算是几个东西:

语言这边其实没什么特别要说的,基本是简化了的 Java。编译的时候会先转换成Java代码。其实不用 Processing 语言直接用 Java 与 Processing 的 API 来写也是可以的,而且可能更适合复杂一些的项目。初期先使用 Processing 语言来熟悉它提供的API 吧(这也是官方教程推荐的做法)。

关于 IDE, 懒得吐槽了…… 实在太差劲了。使用了之后马上抛弃,先安装 processing-java 命令行工具, 然后在 Atom 里安装 Processing 插件和语法高亮。除了没有实时错误提示,其他都OK。

Processing 项目

每个Processing项目叫做一个 sketch。 代码文件的后缀为 .pde 。

Processing 里比较基础的方法有:

size(), background(), stroke(), line() 之类的。 如果只用这些语句,那么生成的图像就是一个静态的图像。

如果提供了 void setup(){} 和 void draw(){} 的话,那就是一个 interactive program. setup 只执行一次,draw 会不断重复执行。 所以 setup 用来初始化环境(比如设定 sketch 的尺寸), draw 用来执行动画。 此外还有一些事件方法,比如 void mousePressed(){}, 每次点击鼠标就会执行。

另外每个 sketch 里还有一些全局变量, 比如 mouseX, mouseY 提供了鼠标在画布里的坐标;width 和 height 则是画布的宽高(不过这俩是 size() 执行设定尺寸之后才设定的)。

saveFrame() 方法可以将项目输出为图像。注意,如果这句放在 draw 里, 每次draw执行都会输出一张新的图片。

size(400, 400, P2D) 这句, P2D 的意思是用 OpenGL 来快速渲染2D图形。 P3D 是用 OpenGL 渲染3D。还可以是 PDF,后面接输出路径。

loadStrings() 和 loadImage() 这俩可以从sketch项目文件夹下的data文件夹(手动创建)里读取数据。

NoC Intro

随机数

利用随机数和概率可以构建出特定的算法,来实现某种目的。 Processing 提供了生成随机数的伪随机算法 random()。 如果想要让某种事件发生的可能性高于平均值,那就需要用某种方法来影响概率的分布。 比如可以建一个数组,里面某几项的值是重复的,然后用 random 来随机一个数组index,那么重复的项被随机到的几率就会大一些。 还可以生成0-1之间的一个随机浮点数 x ,然后判断 x 的范围,比如 x < 0.1 的概率就是 10% ,小与 0.4 的概率就是 40%。

正态分布

实际上自然界很多事情的概率分布是遵循正态分布的。比如一群猴子的身高…… 这里面涉及的主要概念是 平均值(Mean) 、标准差(Standard Deviation, SD, sigma) 。 可以使用Java 提供的 Random 类来自动处理正态分布相关的计算。


void setup() {
  size(640,360);
  generator = new Random();
}

void draw() {
  // Asking for a Gaussian random number. (Note nextGaussian() returns a double and must be converted to float.)
  float num = (float) generator.nextGaussian();
}

nextGaussian() 是根据 平均值0,标准差 1 来生成的。 下面的代码会让生成的数符合自己需要的正态分布:

void draw() {
  // Note that nextGaussian() returns a double.
  float num = (float) generator.nextGaussian();
  float sd = 60;
  float mean = 320;

  // Multiply by the standard deviation and add the mean.
  float x = sd * num + mean;

  noStroke();
  fill(255,10);
  ellipse(x,180,16,16);
}

Perlin Noise

Processing 提供了一个 noise(), 用来生成 Perlin Noise. 是变化很平滑的一个函数。传入一个值会生成0-1之间一个与之对应的唯一值。也就是说,传进去的参数会有一个对应的确定返回值。 noise() 可以传入1、2、3 个参数。 分别对应 一维、二维、三维。

这里需要理解的地方是,什么是二维噪音?一维 Perlin Noise 生成的某个数 x 有这样的规律: 它与它前一个值还有后一个值离得很近,具有相似性。 二维的 Perlin Noise 生成的某个数 x 则与它所有平面方向上相邻的值都相似。

一维的 Perlin Noise 值会有一种 「wandering」「晃来晃去」 的感觉。 二维的 Perlin Noise 值可以用来模拟云或者一些特定材质。

Perlin Noise 有很大的作用。它既不是颜色,也不是坐标,只是值。 可以用它生成的值来操作其他值的呈现,从而实现很多效果。非常好玩儿。

NoC Intro 总结

introduction 这章看起来为接下来的章节定下了基调。random 随机、正态分布随机、Perlin Noise 都是为了模拟现实世界的规律可以用到的工具。但这些不是万金油,还有更多的工具需要学习。学起来太好玩儿了。只是 Intro 章就这么大的信息量,后面的信息量估计也不会小…… OK,今天是学习的第二天,Intro 章学完了。

可能遇到的问题

walnutpedia commented 8 years ago

NoC Excercise 1.4

下面代码里我用 colorMode() 将 sketch 的颜色模式设置为 HSB(HSL),然后在下面正态分布随机的时候把 S 和 B 设置高一点的值,这样随机出来的颜色会比较明亮小清新。

import java.util.Random;

Random generator;

void setup(){
  size(400,400);
  background(255);
  colorMode(HSB, 360, 100, 100);
  generator = new Random();
}

float getValue(float original, float mean, float sd){
  return sd * original + mean;
}

void draw(){

  //get x
  float posX = (float) generator.nextGaussian();
  posX = getValue(posX, width/2, width/6);

  //get y
  float posY = (float) generator.nextGaussian();
  posY = getValue(posY, height/2, height/6);

  //get color
  float dotFillH = (float) generator.nextGaussian();
  dotFillH = (int) getValue(dotFillH, 180, 90);

  float dotFillS = (float) generator.nextGaussian();
  dotFillS = (int) getValue(dotFillS, 70, 5);

  float dotFillB = (float) generator.nextGaussian();
  dotFillB = (int) getValue(dotFillB, 85, 2);

  //get ellipse size
  float dotSize = (float) generator.nextGaussian();
  dotSize = getValue(dotSize, 30, 15);

  noStroke();
  fill(dotFillH, dotFillS, dotFillB, 70);
  ellipse(posX, posY, dotSize, dotSize);

}
walnutpedia commented 8 years ago

一段使用 Perlin Noise 的代码

import gifAnimation.*;

float t = 0;
int h = 0;

GifMaker gifExport;

void setup(){
  size(400,400);
  colorMode(HSB, 360, 100, 100);
  background(h,100,100);
  gifExport = new GifMaker(this, "export.gif");
  gifExport.setRepeat(0);
}

void draw(){
  if(h==360){
    h = -1;
  }
  h++;
  background(h,72,82);
  float n = noise(t);
  float x = map(n,0,1,0,width);
  ellipse(x, 180, 16, 16);
  t += 0.01;

  gifExport.setDelay(1);
  gifExport.addFrame();
}

void mousePressed(){
  gifExport.finish();
}

将背景设置为渐变的颜色。 使用 noise() 按照 t 递增生成的随机数决定小球的X坐标,来来回回的,好像迷了路踌躇不定。效果如下:

export

生成 gif 文件的方法见代码。gifAnimation 这个库得单独下载。