创意编程——半球水波运动临摹

基于processing实现

观察分析

在这里插入图片描述
看这个作品我们不难发现其基本元素为空心半球面;其运动呈现内圈更小的部分幅度更大,外圈越大的部分幅度越小,这种运动规律更加类似水波向外圈衰减的规律;内外圈并不是同步进行的,而是向内的起始时间早,类似水波向外扩散的规律。

临摹实现

建立模型

首先在任意建模软件中建一个空心半球面模型,在此处我是用c4d建模:

在这里插入图片描述
由于proccessing支持obj格式模型导入,因此在c4d中将模型导出为obj,注意在此处须将材质选择为没有材质,并且翻转Y轴

在这里插入图片描述

初始化环境

在processing中,setup在程序刚启动时执行一次进行初始化,而draw不断循环进行重绘,‘因此环境的初始化在setup中实现。
目的为了实现随时间变化的动效,因此先定义了一个全局变量time用于记录时间;loopx、loopz分别定义半球相对于x轴与z轴的角度改变次数;sphere_num定义半球的数目。
全局变量hmispheres为一个list,用于存放所有PShape以存放空心半球体

ArrayList<PShape> hmispheres;
int time,loopx,loopz,sphere_num;

void setup() {
  size(1000, 600, P3D);
  
  noStroke();
  
  time = 0;
  loopx = 20;
  loopz = 24;
  sphere_num = 12;
}

光照

将光照参数写入一个函数,方便每次调用。其中pointLight为点光源directionalLight为聚光灯,ambientLight为环境光,emissive、lightSpecular、shininess用于设置材质,参数在processing的调试模式下进行调整:

void set_light(){
  //Light
  pointLight(138, 182, 254, 153, -264, -74);
  directionalLight(109, 89, 137,   0.2, 0.5, -0.5);
  ambientLight(106, 66, 90);
  emissive(219, 130, 155);
  lightSpecular(122,51,194);
  shininess(3);
}

重绘

在进行重绘时刷新背景、灯光材质渲染,并将原点设置在画布中央,并刷新每个空心半球体的状态,最后将时间加1,达到记录时间与改变球状态的效果:

void draw() {
  background(231, 128, 139);
  translate(width/2,height/2);
  set_light();  
  hmispheres = GetHmispheres(time);
  
  for (int i = 0; i < hmispheres.size(); i++) {
            shape(hmispheres.get(i),0,0);
        }
        
  time++;    
}

Sigmoid函数

尽管Sigmoid常用于机器学习中作为激活函数使用,但由于其将值映射在[0,1]区间且随着值距0的距离增大梯度趋近于0的特性,在此处我将其用于最大rotate的区间映射。
其公式为:
f ( x ) = 1 1 + e − x f(x) = \frac{1}{{1 + {e^{ - x}}}} f(x)=1+ex1
其函数样式为:
在这里插入图片描述

代码如下图所示:

double sigMoid(double value) {
  double ey = Math.pow(Math.E, -value);
  double result = 1 / (1 + ey);
  return result;
}

获取空心半球体

GetHmispheres用于重绘函数调用,得到所有的半球体,其中最重要的函数为GetEachHmisphere,获取每一个半球体。
GetHmispheres函数具有一个参数time,根据时间的不同改变球的状态:

ArrayList<PShape> GetHmispheres(int time){
  ArrayList<PShape>hmispheres = new ArrayList<PShape>();
  
  for(int i=1;i<=sphere_num;i++){
    print(sigMoid((sphere_num-i)/12.0));
    PShape s = GetEachHmisphere(i,sigMoid(21.9-i-sphere_num),time);

    hmispheres.add(s);
  }
  return hmispheres;
}

GetHmisphere函数具有三个参数num,max_rotate,time,num为当前球的标号,max_rotate为球的最大旋转角度,time为当前时间。max_rotate的输入为sigMoid(21.9-i-sphere_num),随着球的标号增大,也就是球越像外圈,max_rotate越小:

PShape GetEachHmisphere(int num,double max_rotate,int time){
    PShape s = loadShape("hemisphere.obj");
    float initX = -0.4;
    
    s.scale(num/7.0);
    
    if(time>=num){
      int ax = (time-num)%loopx;
      int az = (time-num)%loopz;
      
      if(ax==0){
        s.rotateX(initX + (float)(0* max_rotate/5));
      }
      else if(ax==1){
        s.rotateX(initX + (float)(1* max_rotate/5));
      }
      else if(ax==2){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==3){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==4){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==5){
        s.rotateX(initX+ (float)(5* max_rotate/5));
      }
      else if(ax==6){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==7){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==8){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==9){
        s.rotateX(initX+ (float)(1* max_rotate/5));
      }
      else if(ax==10){
        s.rotateX(initX - (float)(0* max_rotate/5));
      }
      else if(ax==11){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      else if(ax==12){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==13){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==14){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==15){
        s.rotateX(initX - (float)(5* max_rotate/5));
      }
      else if(ax==16){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==17){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==18){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==19){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      
      
      if(az==0){
        s.rotateZ(0.0 + (float)(0* max_rotate/6));
      }
      else if(az==1){
        s.rotateZ(0.0+ (float)(1* max_rotate/6));
      }
      else if(az==2){
        s.rotateZ(0.0+ (float)(2* max_rotate/6));
      }
      else if(az==3){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==4){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==5){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==6){
        s.rotateZ(0.0+ (float)(6* max_rotate/6));
      }
      else if(az==7){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==8){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==9){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==10){
        s.rotateZ(0.0 + (float)(2* max_rotate/6));
      }
      else if(az==11){
        s.rotateZ(0.0 + (float)(1* max_rotate/6));
      }
      else if(az==12){
        s.rotateZ(0.0 - (float)(0* max_rotate/6));
      }
      else if(az==13){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      else if(az==14){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==15){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==16){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==17){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==18){
        s.rotateZ(0.0 - (float)(6* max_rotate/6));
      }
      else if(az==19){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==20){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==21){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==22){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==23){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      
    }
    else{
      s.rotateX(initX);
      s.rotateY(0.0);
    }
    
    return s;
}

效果

在这里插入图片描述

创新

临摹的效果并没有添加任何的交互效果,仅仅为单纯的展示,因此我想添加一些有意思的交互方式,让体验者可进行一定操作。
考虑到如果圆形改为椎体其会一定程度穿模,且圆环具有分段的特性,采用圆环作为交互对象。

模型

在C4D中创建一个圆环,调小其导管半径,避免穿模:
在这里插入图片描述
分别到处其3、4、5……分段的obj模型:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
分别存入obj文件方便后续导入,obj的导出设置与临摹相同:

在这里插入图片描述

交互

鼠标滚轮

通过滚轮操作可以改变物体向外扩展的数目。
添加一个全局变量用于记录物体数目:

int sphere_num;

通过滚轮操作进行相加或是相减:

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  sphere_num += int(e);
}

鼠标左键

通过鼠标左键点击更改物体的样式,从3段到4段,到5段,直到圆环。
添加一个全局变量记录所有模型的文件名称,并在setup函数中进行初始化,currentFile用于标记当前的所取文件,通过点击加一最终除余进行修改模型:

ArrayList<String> fileNames;
int currentFile;

void setup() {
  ……
  currentFile = 0;
  fileNames = new ArrayList<String>();
  fileNames.add("loop3.obj");
  fileNames.add("loop4.obj");
  fileNames.add("loop5.obj");
  fileNames.add("loop6.obj");
  fileNames.add("loop7.obj");
  fileNames.add("loop8.obj");
  fileNames.add("loop9.obj");
  fileNames.add("loop10.obj");
  fileNames.add("loop11.obj");
  fileNames.add("loop12.obj");
  fileNames.add("loop13.obj");
  fileNames.add("loop.obj");
}

void mouseClicked() {
  currentFile++;

PShape GetEachHmisphere(int num,double max_rotate,int time){
	PShape s = loadShape(fileNames.get(currentFile%12));
	……
}

效果

在这里插入图片描述

完整代码

临摹

ArrayList<PShape> hmispheres;
int time,loopx,loopz,sphere_num;

void setup() {
  size(1000, 600, P3D);
  
  noStroke();
  
  time = 0;
  loopx = 20;
  loopz = 24;
  sphere_num = 12;
}



void draw() {
  background(231, 128, 139);
  translate(width/2,height/2);
  set_light();  
  hmispheres = GetHmispheres(time);
  
  for (int i = 0; i < hmispheres.size(); i++) {
            shape(hmispheres.get(i),0,0);
        }
        
  time++;    
}

void set_light(){
  //Light
  pointLight(138, 182, 254, 153, -264, -74);
  directionalLight(109, 89, 137,   0.2, 0.5, -0.5);
  ambientLight(106, 66, 90);
  emissive(219, 130, 155);
  lightSpecular(122,51,194);
  shininess(3);
}

double sigMoid(double value) {
  double ey = Math.pow(Math.E, -value);
  double result = 1 / (1 + ey);
  return result;
    }



ArrayList<PShape> GetHmispheres(int time){
  ArrayList<PShape>hmispheres = new ArrayList<PShape>();
  
  for(int i=1;i<=sphere_num;i++){
    print(sigMoid((sphere_num-i)/12.0));
    PShape s = GetEachHmisphere(i,sigMoid(21.9-i-sphere_num),time);

    hmispheres.add(s);
  }
  return hmispheres;
}

PShape GetEachHmisphere(int num,double max_rotate,int time){
    PShape s = loadShape("hemisphere.obj");
    float initX = -0.4;
    
    s.scale(num/7.0);
    
    if(time>=num){
      int ax = (time-num)%loopx;
      int az = (time-num)%loopz;
      
      if(ax==0){
        s.rotateX(initX + (float)(0* max_rotate/5));
      }
      else if(ax==1){
        s.rotateX(initX + (float)(1* max_rotate/5));
      }
      else if(ax==2){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==3){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==4){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==5){
        s.rotateX(initX+ (float)(5* max_rotate/5));
      }
      else if(ax==6){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==7){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==8){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==9){
        s.rotateX(initX+ (float)(1* max_rotate/5));
      }
      else if(ax==10){
        s.rotateX(initX - (float)(0* max_rotate/5));
      }
      else if(ax==11){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      else if(ax==12){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==13){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==14){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==15){
        s.rotateX(initX - (float)(5* max_rotate/5));
      }
      else if(ax==16){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==17){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==18){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==19){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      
      
      if(az==0){
        s.rotateZ(0.0 + (float)(0* max_rotate/6));
      }
      else if(az==1){
        s.rotateZ(0.0+ (float)(1* max_rotate/6));
      }
      else if(az==2){
        s.rotateZ(0.0+ (float)(2* max_rotate/6));
      }
      else if(az==3){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==4){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==5){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==6){
        s.rotateZ(0.0+ (float)(6* max_rotate/6));
      }
      else if(az==7){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==8){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==9){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==10){
        s.rotateZ(0.0 + (float)(2* max_rotate/6));
      }
      else if(az==11){
        s.rotateZ(0.0 + (float)(1* max_rotate/6));
      }
      else if(az==12){
        s.rotateZ(0.0 - (float)(0* max_rotate/6));
      }
      else if(az==13){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      else if(az==14){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==15){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==16){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==17){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==18){
        s.rotateZ(0.0 - (float)(6* max_rotate/6));
      }
      else if(az==19){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==20){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==21){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==22){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==23){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      
    }
    else{
      s.rotateX(initX);
      s.rotateY(0.0);
    }
    
    return s;
}

创新

ArrayList<PShape> hmispheres;
int time,loopx,loopz,sphere_num;
ArrayList<String> fileNames;
String fileName;
int currentFile;
void setup() {
  size(1000, 600, P3D);
  
  noStroke();
  
  time = 0;
  loopx = 20;
  loopz = 24;
  sphere_num = 7;
  currentFile = 0;
  fileName = "hemisphere.obj";
  fileName = "loop.obj";
  fileNames = new ArrayList<String>();
  fileNames.add("loop3.obj");
  fileNames.add("loop4.obj");
  fileNames.add("loop5.obj");
  fileNames.add("loop6.obj");
  fileNames.add("loop7.obj");
  fileNames.add("loop8.obj");
  fileNames.add("loop9.obj");
  fileNames.add("loop10.obj");
  fileNames.add("loop11.obj");
  fileNames.add("loop12.obj");
  fileNames.add("loop13.obj");
  fileNames.add("loop.obj");

}



void draw() {
  background(231, 128, 139);
  //translate(width/2,height/2);
  set_light();  
  hmispheres = GetHmispheres(time);
  
  for (int i = 0; i < hmispheres.size(); i++) {
            shape(hmispheres.get(i),mouseX,mouseY);
        }
        
  time++;    
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  sphere_num += int(e);
}

void mouseClicked() {
  currentFile++;
}

void set_light(){
  //Light
  pointLight(138, 182, 254, 153, -264, -74);
  directionalLight(109, 89, 137,   0.2, 0.5, -0.5);
  ambientLight(106, 66, 90);
  emissive(219, 130, 155);
  lightSpecular(122,51,194);
  shininess(3);
}

double sigMoid(double value) {
  double ey = Math.pow(Math.E, -value);
  double result = 1 / (1 + ey);
  return result;
    }



ArrayList<PShape> GetHmispheres(int time){
  ArrayList<PShape>hmispheres = new ArrayList<PShape>();
  
  for(int i=1;i<=sphere_num;i++){
    //print(sigMoid((sphere_num-i)/12.0));
    PShape s = GetEachHmisphere(i,sigMoid(11.1-i-sphere_num),time);

    hmispheres.add(s);
  }
  return hmispheres;
}

PShape GetEachHmisphere(int num,double max_rotate,int time){
    PShape s = loadShape(fileNames.get(currentFile%12));
    float initX = -0.4;
    
    s.scale(num/7.0);
    
    if(time>=num){
      int ax = (time-num)%loopx;
      int az = (time-num)%loopz;
      
      if(ax==0){
        s.rotateX(initX + (float)(0* max_rotate/5));
      }
      else if(ax==1){
        s.rotateX(initX + (float)(1* max_rotate/5));
      }
      else if(ax==2){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==3){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==4){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==5){
        s.rotateX(initX+ (float)(5* max_rotate/5));
      }
      else if(ax==6){
        s.rotateX(initX+ (float)(4* max_rotate/5));
      }
      else if(ax==7){
        s.rotateX(initX+ (float)(3* max_rotate/5));
      }
      else if(ax==8){
        s.rotateX(initX+ (float)(2* max_rotate/5));
      }
      else if(ax==9){
        s.rotateX(initX+ (float)(1* max_rotate/5));
      }
      else if(ax==10){
        s.rotateX(initX - (float)(0* max_rotate/5));
      }
      else if(ax==11){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      else if(ax==12){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==13){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==14){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==15){
        s.rotateX(initX - (float)(5* max_rotate/5));
      }
      else if(ax==16){
        s.rotateX(initX - (float)(4* max_rotate/5));
      }
      else if(ax==17){
        s.rotateX(initX - (float)(3* max_rotate/5));
      }
      else if(ax==18){
        s.rotateX(initX - (float)(2* max_rotate/5));
      }
      else if(ax==19){
        s.rotateX(initX - (float)(1* max_rotate/5));
      }
      
      
      if(az==0){
        s.rotateZ(0.0 + (float)(0* max_rotate/6));
      }
      else if(az==1){
        s.rotateZ(0.0+ (float)(1* max_rotate/6));
      }
      else if(az==2){
        s.rotateZ(0.0+ (float)(2* max_rotate/6));
      }
      else if(az==3){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==4){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==5){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==6){
        s.rotateZ(0.0+ (float)(6* max_rotate/6));
      }
      else if(az==7){
        s.rotateZ(0.0+ (float)(5* max_rotate/6));
      }
      else if(az==8){
        s.rotateZ(0.0+ (float)(4* max_rotate/6));
      }
      else if(az==9){
        s.rotateZ(0.0+ (float)(3* max_rotate/6));
      }
      else if(az==10){
        s.rotateZ(0.0 + (float)(2* max_rotate/6));
      }
      else if(az==11){
        s.rotateZ(0.0 + (float)(1* max_rotate/6));
      }
      else if(az==12){
        s.rotateZ(0.0 - (float)(0* max_rotate/6));
      }
      else if(az==13){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      else if(az==14){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==15){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==16){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==17){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==18){
        s.rotateZ(0.0 - (float)(6* max_rotate/6));
      }
      else if(az==19){
        s.rotateZ(0.0 - (float)(5* max_rotate/6));
      }
      else if(az==20){
        s.rotateZ(0.0 - (float)(4* max_rotate/6));
      }
      else if(az==21){
        s.rotateZ(0.0 - (float)(3* max_rotate/6));
      }
      else if(az==22){
        s.rotateZ(0.0 - (float)(2* max_rotate/6));
      }
      else if(az==23){
        s.rotateZ(0.0 - (float)(1* max_rotate/6));
      }
      
    }
    else{
      s.rotateX(initX);
      s.rotateY(0.0);
    }
    
    return s;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值