互动媒体技术——代码本色

互动媒体技术——代码本色

参考《代码本色》的第0~4章内容及其实例程序,针对这5章分别编写1个习作(一共5个),每个习作都有不少于2个案例参考,且必须有一定的拓展,体现随机行为及牛顿运动学;代码大部分参照书上的文档以及浏览网上的创意。

第0章 引言

标题Perlin噪声生成起伏地形


Perlin噪声算法表现出了一定的自然性,因为它能生成符合自然排序(“平滑”)的伪随机 数序列。图I-5展示了Perlin噪声的效果,x轴代表时间;请注意曲线的平滑性。图I-6展示 了纯随机数的效果。(生成图形的代码可以在本书的下载资料中找到。)
图0-5 噪声
图0-6 随机 Processing内置了Perlin噪声算法的实现:noise()函数。noise()函数可以有1~3个参 数,分别代表一维、二维和三维的随机数。我们先从一维的noise()函数开始了解。

int c,s,d=20,w=1000,h=1000;  
float[][] t;  
float p=0;  
void setup()  
{    
  size(800,600,P3D);    
  c=w/d;    
  s=h/d;    
  t=new float[c][s];  
}  
void draw()  
{ 
  background(0);     
  stroke(255);     
  fill(123);        
  translate(width/2,height/2+80);     
  rotateX(7*PI/18);     
  translate(-w/2,-h/2);    
  p+=0.01;    
  float n=p;    
  for(int y=0;y<s;y++)    
  {      
    float m=0;      
    for(int x=0;x<s;x++)      
    {        
      t[x][y]=map(noise(m,n),0,1,0,200);        
      m+=0.15;      
    }      
    n+=0.15;    
  }     
 
  for(int y=0;y<s-1;y++)    
  {      
    beginShape(TRIANGLE_STRIP);            
    for(int x=0;x<s;x++)      
      {        
        vertex(x*d,y*d,t[x][y]);        
        vertex(x*d,(y+1)*d,t[x][y+1]);      
      }      
    endShape();    
  }        
} 

主要函数map(),beginShape(TRIANGLE_STRIP); TRIANGLE_STRIP的运用画地形。

第 1 章 向量

繁星

在这里插入图片描述
以这个长方体的一个顶点为原点,建立世界坐标系。然后选取一个面作为Processing 程序的窗口,建立屏幕坐标系。然后在屏幕上任选一个点,作为消失点,该点在屏幕坐标系下的坐标为(x_endpoint,y_endpoint )。

接着在这个长方体里随机生成一系列的星星,每个星星由一个三维向量(x_world,y_world,z_world)表示,这是它在世界坐标系下的坐标。这个几乎是参考网上的东西。

接下来我们要做的就是把每一个星星的在世界坐标系的坐标,转换成在processing窗口里面的坐标(x_screen,y_screen),这样我们就可以在屏幕上把星星绘制出来。

首先需要计算星星在世界坐标系下相对于消失点(endpoint)x,y方向的偏移量,接下来将这个偏移量根据星星的z_world进行放缩,最后再将放缩完成的偏移量加回消失点的坐标,得到星星在屏幕上的坐标。
公式如下:

xscreen=(xworld−xendpoint)/zworld∗scale+xendpoint x_{screen}=(x_{world}-x_{endpoint})/z_{world} *scale+x_{endpoint}xscreen =(x world−x endpoint)/z world​∗scale+x endpoint​yscreen=(yworld−yendpoint)/zworld∗scale+yendpoint y_{screen}=(y_{world}-y_{endpoint})/z_{world} *scale+y_{endpoint}yscreen=(yworld​	−yendpoint​	)/zworld​	∗scale+yendpoint​

class Star{  
  
  final float MAX_DIAM = 16;       
  final float MAX_DEPTH = width / 2;       
  final float SCALE = MAX_DEPTH;      
  PVector worldPosition, screenPosition, viewPosition;      
  float diam;    
    
  Star(){  
    worldPosition = new PVector(random(0, width), random(0, height), random(0, MAX_DEPTH));  
  }  
  
  void transform(PVector endpoint){   
    viewPosition = PVector.sub(worldPosition, endpoint).div(worldPosition.z).mult(SCALE);     
    screenPosition = PVector.add(endpoint, viewPosition);  
    diam = map(worldPosition.z, 0, MAX_DEPTH, MAX_DIAM, 0);  
  }  

  void checkEdge(){  
    if(screenPosition.x <= 0 || screenPosition.x >= width || screenPosition.y <=0 || screenPosition.y >= height){   
      worldPosition.set(random(0, width), random(0, height), MAX_DEPTH);   
    }   
  }  

  void display(){   
    fill(255,255,0);  
    noStroke();  
    ellipse(screenPosition.x, screenPosition.y, diam, diam);  
  }  
  
  void move(float speed){  
     worldPosition.z -= speed;  
     worldPosition.z = constrain(worldPosition.z, 0, MAX_DEPTH);  
  }  
  class StarField{  
    
  final int STAR_COUNT = width / 2;      
  ArrayList<Star> stars;     
  PVector endpoint;  

    StarField(){   
    endpoint = new PVector(mouseX, mouseY);    
    stars = new ArrayList();  
    for(int i = 0; i < STAR_COUNT; i++){  
      stars.add(new Star());  
    }  
  }  
    void run(){ 
  final int MAX_SPEED = 11, MIN_SPEED = 1;       
  final int SPEED_STEP = 1;    
  int speed;  
  
   speed = (MAX_SPEED + MIN_SPEED) / 2;
   for(Star s : stars){   
     s.transform(endpoint);  
     s.checkEdge();   
     s.display();
     s.move(speed);
   }  
   StarField sf;  
  void setup(){  
    size(600, 600);  
    sf = new StarField();  
  }  
    
  void draw(){  
    background(0);   
    sf.run();  
  }  
     
  void mousePressed(){}  
  void mouseMoved(){}  
  } 
  }
  }  

第2章——力学

在这里插入图片描述
实验模拟了小球受到重力,向左的风的力量,和入睡时水的阻力,其中还包括触碰反弹。

class Ball {
  float ma;
  PVector xy; 
  PVector speedxy;
  float speedmax;
  PVector accel;

  Ball(float m, float x, float y) {
    ma = m;
    xy = new PVector(x, y);
    speedxy = new PVector(random(10,15), 15);
    speedmax = 20;
    accel = new PVector(0, 0);
  }

  void display() {
    fill(255, 255,0);
    noStroke();
    ellipse(xy.x, xy.y, 10*ma, 10*ma);
  }

  void Force2(PVector force) {
    PVector f = force.get();
    f.div(ma);
    accel.add(f);
  }

  void location() {
    speedxy.add(accel);
    speedxy.limit(speedmax);
    xy.add(speedxy); 

    accel.mult(0); 
  }

  void fanzhe() {
    if ( xy.x < 0 ) {
      xy.x = 0;
      speedxy.x *= -1;
    } else if ( xy.x > width ) {
      xy.x = width;
      speedxy.x *= -1;
    }
    if ( xy.y < 0 ) {
      xy.y = 0;
      speedxy.y *= -1;
    } else if ( xy.y > height ) {
      xy.y = height;
      speedxy.y *= -1;
    }
  }

  boolean inwater(Liquid w) {
    if (w.lx<xy.x && xy.x<w.lx+w.lwidth && w.ly<xy.y && xy.y<w.ly+w.lheight) {
      return true;
    } else {
      return false;
    }
  }

  void drag(Liquid w) {
    float lz = 0.024; 
    float v = speedxy.mag(); 
    PVector drag = speedxy.get(); 
    drag.normalize();  
    drag.mult(-1);   

    drag.mult(v * v * lz);

    Force2(drag); 
  }
}




class Liquid {
  float lx, ly, lwidth, lheight;
  float lz; 

  Liquid(float lx_, float ly_, float lwidth_, float lheight_, float lz_){
    lx = lx_;
    ly = ly_;
    lwidth = lwidth_;
    lheight = lheight_;
    lz = lz_;
  }

  void display() {
    noStroke();
    fill(150, 200, 255);
    rect(lx, ly, lwidth, lheight);
  }
}
Ball[] ball = new Ball[20];
Liquid water;

void setup() {
  size(860, 640);
  for (int i = 0; i < ball.length; i++) {
    ball[i] = new Ball(random(2,3), random(0,width/2), random(0,height/2));
  }
  water = new Liquid(0, (height/3)*2, width, height/3, 0.8);
}

void draw() {
  background(124,24,220);
  water.display();

  for (int i = 0; i < ball.length; i++) {
    float m = ball[i].ma;
    PVector gravity = new PVector(0, 0.001*m);
    PVector wind = new PVector(0.03, 0);

    if (ball[i].inwater(water)) {
      ball[i].drag(water); 
    }

    ball[i].Force2(gravity);
    ball[i].Force2(wind);

    ball[i].location();
    ball[i].display();
    ball[i].fanzhe();
  }
}

第三章——振荡

球摆

在这里插入图片描述
由于摆锤运动是以轴点为圆心做运动,所以,我们引入角度,角速度,角加速度等几个概念来表达摆锤的运动。摆锤的长度是固定的,唯一变化的部分只是绳子的角度,我们可以用角速度与角加速度模拟钟摆的运动。小球只受到两个力,一是小球自身的重力,二是绳子对小球的拉力。因为合力和绳子互相垂直,通过绘图可知,最终angularAcceleration = gravity * sin(angle),与小球质量无关。但实际上,钟摆摆臂的长度对加速度的影响很大,摆臂越长,加速度越小。所以,角加速度angularAcceleration = -1 * gravity * sin(angle) / string。 而为了模拟现实世界,还会受到摩擦力和空气阻力,我们可以设置一种衰减方式,设置变量damping。

 class Pendulum { 
  PVector location;
  float angA, angV,angle,string, d;  
  PVector origin;  

  Pendulum(float string_, float angle_) {
    location = new PVector(0, 0); 
    angle = angle_;
    angV = 0;
    angA = 0;
    string = string_;
    origin = new PVector(width/2, 50);
   // origin = (mouseX,mouseY);
    d = 0.995;
  }

  void update() {
    float gravity = 0.98; 
    angA = -1 * sin(angle) * (gravity/string);
    angV += angA;
    angle += angV;
    angA *= d; 
  }

  void display() {
    location = new PVector(string * sin(angle), string * cos(angle)); 
    location.add(origin); 
    stroke(255,0,255);
    fill(255,0,255);
    line(origin.x, origin.y, location.x, location.y);
    ellipse(location.x, location.y, 30, 30);
  }
}
Pendulum[] ball = new Pendulum[20];

void setup() {
  size(1280, 640);
  for (int i=0; i<ball.length; i++) {
    ball[i] = new Pendulum( 30*i, PI/4);
  }
}

void draw() {
  background(0);

  for (int i=0; i<ball.length; i++) {
    ball[i].update();
    ball[i].display();
  }
}

.

第4章——粒子系统

火焰


我们在setup函数中初始化了我们的粒子系统,然后在draw函数中不断更新背景的颜色、粒子的状态,以及监听按下鼠标左键事件,判断是否需要加入新的火焰。

注意这里颜色的模式不是RGB,而是HSB。并且按下一次鼠标左键会调用多次addFire函数,因为按下鼠标不是一瞬间的事情,这段时间会执行多次draw函数。

class Fire extends Particle {
  ArrayList<PVector> plist;
  Fire(float x, float y) {
    super(x, y);
    PVector v = new PVector(mouseX - pmouseX, mouseY - pmouseY);
    v.mult(0.1);
    v.mult(getSpeed(100)).add(v);

    plist = new ArrayList();
    for (int i = 0; i < 3; i++) {
      float xOffset = random(-10, 10);
      float yOffset = random(-10, 10);
      float hue = random(50);
  
      plist.add(new PVector(xOffset, yOffset, hue));
    }
  }

  void update() {
    super.update();

    if (int(random(5)) == 0) {
      int spawnCount = int(random(3))+1;
      for (int i = 0; i < spawnCount; i++) {
        spawn();
      }
    }
  }

  void spawn() {
    float size = random(25, 50)*map(span, maxspan, 0, 1, 0);
    if (size > 0) {
      p.addSmoke(location.x + origin.x , location.y + origin.y, size);
    }
  }

  void drawShape() {
    for (PVector p : plist) {
      stroke(p.z, 255, 255, 20);
      strokeWeight(80);
      point(p.x, p.y);

      stroke(p.z, 255, 255, 100);
      strokeWeight(30);
      point(p.x, p.y);
    }
  }
}
class Particle {
  PVector location, v, acceler, origin;
  float angle, angV, angA;
  float span, Rate, maxspan;
  int type, h;

  Particle(float x, float y) {
    origin = new PVector(x, y);    
    location = new PVector();    
    acceler = new PVector(0, 0.05);  
    v = PVector.random2D();
    span = maxspan = 50; 
    Rate = random(0.35, 1);
    h = 20;
    type = 1;
  }
  
  float getSpeed(float s){
    float t = maxspan / Rate;
    return s / t;
  }

  void run() {
    update();
    display();
  }
  
  void update() {
    v.add(acceler);
    location.add(v);
    
    angV += angA;
    angle += angV;

    span -= Rate;
  }

  boolean isDead() {
    if (span < 0.0) {
      return true;
    } else {
      return false;
    }
  }

  void display() {
    pushMatrix();
    translate(origin.x, origin.y);
    rotate(radians(angle));
    translate(location.x, location.y);
    scale(map(span, 0, maxspan, 0, 1));
    drawShape();
    popMatrix();
  }

  void drawShape() {
    stroke(h, 255, 255);
    strokeWeight(30);
    point(0, 0);
  }
}
class ParticleSystem {
  ArrayList<Particle> plist;

  ParticleSystem() {
    plist = new ArrayList<Particle>();
  }

  void run() {
    for (int i = plist.size() - 1; i >= 0; i--) {
      Particle p = plist.get(i);
      p.run();
    
      if (p.isDead()) {
        plist.remove(i);
      }
    }
  }

  void addFire(float x, float y) {
    plist.add(new Fire(x, y));
  }

  void addSmoke(float x, float y, float size) {
    plist.add(new Smoke(x, y, size));
  }
  
  int getSize() {
    int cnt = 0;
    for(Particle p: plist){
        cnt += p.type;
    }
    return cnt;
  }
}
class Smoke extends Particle {
  float size, alpha;
  Smoke(float x, float y, float _size) {
    super(x, y);
    size = _size;
    alpha = random(10, 150);
    span = maxspan = 30;
    Rate = random(0.4, 1.25);
    
    angle = random(-45, 45);
    angV = random(-2, 2);
    
    v.set(0, getSpeed(random(-100, -100)));
    acceler.mult(0);
    type = 0;
  }
  
  void drawShape() {
    stroke(0, 0, 0, alpha);
    strokeWeight(size);
    point(0, 0);
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值