谈谈Processing 3D世界 四(补充)

当我们实现了第四节的案例后,会苦恼的发现我们虽然可以操控摄影机移动,但转动视角后,摄影机却仍然沿着原来的坐标轴移动。这十分不方便。

这里我们对这个案例补完,实现摄影机永远按照转动后的视角坐标移动。


随着学习的深入,程序难免会越来越复杂,这里我们把程序分割成3页,使程序的脉络更清晰一些:

step1 定义顶点数据

我们新建一个Data的标签页:

float max = 2.0;

void initVertices() {
 // 设置顶点
  ver = new PVector[8];
  ver[0] = new PVector(-0.5, -0.5,  0.5); // 顶点1
  ver[1] = new PVector(-0.5, -0.5, -0.5); // 顶点2
  ver[2] = new PVector( 0.5, -0.5, -0.5); // ...
  ver[3] = new PVector( 0.5, -0.5,  0.5);


  ver[4] = new PVector(-0.5,  0.5,  0.5);
  ver[5] = new PVector(-0.5,  0.5, -0.5);
  ver[6] = new PVector( 0.5,  0.5, -0.5);
  ver[7] = new PVector( 0.5,  0.5,  0.5);


  // 设置顶点索引
  face = new PVector[12];
  // top
  face[0]  = new PVector(0, 1, 2);
  face[1]  = new PVector(0, 2, 3);
  // front
  face[2]  = new PVector(0, 3, 7);
  face[3]  = new PVector(0, 7, 4);
  // back
  face[4]  = new PVector(1, 2, 6);
  face[5]  = new PVector(1, 6, 5);
  // right
  face[6]  = new PVector(3, 2, 7);
  face[7]  = new PVector(2, 6, 7);
  // left
  face[8]  = new PVector(0, 4, 5);
  face[9]  = new PVector(1, 5, 0);
  // bottom
  face[10] = new PVector(4, 5, 7);
  face[11] = new PVector(5, 6, 7);
  
  // 设置UV
  // 目前有一点苦力活需要干,但很快,将会有东西拯救我们。
  uv = new PVector[12][3]; // 12个面,每个面3个顶点,为每个顶点描述UV
  // top
  uv[0][0]  = new PVector(0,   max); // face0
  uv[0][1]  = new PVector(0,   0);
  uv[0][2]  = new PVector(max, 0);
  uv[1][0]  = new PVector(0,   max); // face1
  uv[1][1]  = new PVector(max, 0);
  uv[1][2]  = new PVector(max, max);
  // front
  uv[2][0]  = new PVector(0,   0); // face2
  uv[2][1]  = new PVector(max, 0);
  uv[2][2]  = new PVector(max, max);
  uv[3][0]  = new PVector(0,   0); // face3
  uv[3][1]  = new PVector(max, max);
  uv[3][2]  = new PVector(0,   max);
  // back
  uv[4][0]  = new PVector(0,   0); // face4
  uv[4][1]  = new PVector(max, 0);
  uv[4][2]  = new PVector(max, max);
  uv[5][0]  = new PVector(0,   0); // face5
  uv[5][1]  = new PVector(max, max);
  uv[5][2]  = new PVector(0,   max);
  // right
  uv[6][0]  = new PVector(0,   0); // face6
  uv[6][1]  = new PVector(max, 0);
  uv[6][2]  = new PVector(0,   max);
  uv[7][0]  = new PVector(max, 0); // face7
  uv[7][1]  = new PVector(max, max);
  uv[7][2]  = new PVector(0,   max);
  // left
  uv[8][0]  = new PVector(0,   0); // face8
  uv[8][1]  = new PVector(0,   max);
  uv[8][2]  = new PVector(max, max);
  uv[9][0]  = new PVector(max, 0); // face9
  uv[9][1]  = new PVector(max, max);
  uv[9][2]  = new PVector(0,   0);
  // bottom
  uv[10][0] = new PVector(0,   max); // face10
  uv[10][1] = new PVector(0,   0);
  uv[10][2] = new PVector(max, max);
  uv[11][0] = new PVector(0,   0); // face11
  uv[11][1] = new PVector(max, 0);
  uv[11][2] = new PVector(max, max); 
}

void initLocation() {
  // 设置cubes的位置坐标
  cubesPos = new PVector[8];
  cubesPos[0] = new PVector();
  cubesPos[1] = new PVector(100.0,   0.0, -400.0);
  cubesPos[2] = new PVector(400.0,   0.0, -400.0);
  cubesPos[3] = new PVector(400.0, 400.0, -400.0);
  cubesPos[4] = new PVector(400.0, 400.0,    0.0);
  cubesPos[5] = new PVector(  0.0, 400.0, -400.0);
  cubesPos[6] = new PVector(400.0,   0.0,    0.0);
  cubesPos[7] = new PVector(700.0,   0.0,    0.0);
}

void initTexture() {
  // 载入贴图
  tex = loadImage("t3.jpg"); // 贴图在前面的章节有链接
  tex.resize(int(256/max), 0);
  
  // 设置纹理属性
  textureMode(NORMAL);
  textureWrap(REPEAT);
}

step2 设置摄影机

让我们新建一个Camera的标签页:

void initCamera() {
  // 设置摄影机
  focalLength  = (height/2.0) / tan(PI*30.0/180.0); 
  cameraPos    = new PVector(width/2.0,  height/2.0, focalLength);
  cameraFront  = new PVector(0.0, 0.0, -1.0);
  up           = new PVector(0.0, 1.0, 0.0);
  
  // 投影矩阵参数
  fov    = radians(60);                // 视野(Field of View)
  aspect = float(width)/float(height); // 画幅比例
  zNear  = cameraPos.z/10.0;           // 近焦平面
  zFar   = cameraPos.z*10.0;           // 远焦平面
  // 摄影机旋转属性
  yaw    = -90.0;
  pitch  =  0.0; 
}

void do_movement() {
  float cameraSpeed = 5.0;
  // 移动摄影机(同时移动目标)
  if (keyPressed) {
    // 前后  
    if (key == 'w') { // 沿着摄影机的方向cameraDront前行
      cameraPos.add(PVector.mult(cameraFront,cameraSpeed));
    }
    
    if (key == 's') { // 沿着摄影机的方向cameraDront后退
      cameraPos.sub(PVector.mult(cameraFront,cameraSpeed));
    }
    // 左右
    if (key == 'a') { // 后退即向左
      PVector v = cameraFront.get();
      v = v.cross(up); // 利用叉乘求出摄影机方向的右向量
      v.normalize(); 
      cameraPos.sub(PVector.mult(v,cameraSpeed));
    }
    if (key == 'd') { // 前进即向右
      PVector v = cameraFront.get();
      v = v.cross(up);
      v.normalize();
      cameraPos.add(PVector.mult(v,cameraSpeed));
    }
  }
  
  // 转动摄影机视角
  float sensitivity = 0.05; // 灵敏度 
  if (mousePressed) {
    // 偏航
    yaw += (mouseX - pmouseX)*sensitivity;
    // 俯仰
    pitch += (mouseY - pmouseY)*sensitivity;
    // 限值俯仰值
    if (pitch > 89.0f)
    pitch = 89.0f;
    if (pitch < -89.0f)
    pitch = -89.0f;
    // 更新摄影机目标
    cameraFront.x = cos(radians(yaw))*cos(radians(pitch));
    cameraFront.y = sin(radians(pitch));
    cameraFront.z = sin(radians(yaw))*cos(radians(pitch));
    cameraFront.normalize();
  }
  
  // 重置
  if (keyPressed) {
    
    if (key == 'r') {
      yaw   = -90.0;
      pitch =  0.0;
     
      cameraFront.x = cos(radians(yaw))*cos(radians(pitch));
      cameraFront.y = sin(radians(pitch));
      cameraFront.z = sin(radians(yaw))*cos(radians(pitch));
      cameraFront.normalize();
      
      cameraPos = new PVector(width/2.0,  height/2.0, focalLength);
    }
  }
}

step3 程序主体

// 窗口属性
int w = 720;
int h = 480;

// 定义顶点
PVector[] ver;
PVector[] face;
PVector[][] uv;
PVector[] cubesPos;
PImage tex;

// 摄影机属性
PVector cameraPos, cameraTarget, up, cameraFront;
float focalLength; // 焦距
float fov, aspect, zNear, zFar;
float yaw, pitch;  // 偏航、俯仰

float[] angle = new float[8];

void settings() {
  //fullScreen();
  size(w, h, P3D);
}

void setup() {
  initVertices(); // 初始化顶点数据
  initLocation(); // 初始化网格位置
  initTexture();  // 初始化纹理
  initCamera();   // 初始化摄影机
  
  // 其他
  noStroke();
  for (int i = 0; i < 8; i++) {
    angle[i] = random(2*PI); 
  }
}

void draw() {
  // 清楚缓冲区
  background(0);
  // 事件处理
  do_movement();
  
  // 设置摄影机
  cameraTarget = PVector.add(cameraPos, cameraFront);
  camera(cameraPos.x,    cameraPos.y,    cameraPos.z,
         cameraTarget.x, cameraTarget.y, cameraTarget.z,
         up.x,           up.y,           up.z);
  perspective(fov, aspect, zNear, zFar);

  // 绘制图形
  for (int n = 0; n < cubesPos.length; n++) {
    
    pushMatrix();
      // 分配模型网格位置
      translate(cubesPos[n].x, cubesPos[n].y, cubesPos[n].z);
      
      pushMatrix();
        // 单个模型矩阵变换
        translate(width/2, height/2, -100.0);
        rotateX(angle[n]);
        rotateY(angle[n]*0.3);
        rotateZ(angle[n]*0.05);
        randomSeed(n*100);
        angle[n] += 0.005 * random(0, 1);
        scale(200);
        
        // 绘制单个网格物体
        //fill(255, 127, 39);
        for (int i = 0; i < face.length; i++) {
          beginShape();
          texture(tex);
          //     x                    , y                    , z                    , u         , v
          vertex(ver[int(face[i].x)].x, ver[int(face[i].x)].y, ver[int(face[i].x)].z, uv[i][0].x, uv[i][0].y);
          vertex(ver[int(face[i].y)].x, ver[int(face[i].y)].y, ver[int(face[i].y)].z, uv[i][1].x, uv[i][1].y);
          vertex(ver[int(face[i].z)].x, ver[int(face[i].z)].y, ver[int(face[i].z)].z, uv[i][2].x, uv[i][2].y);
          endShape(TRIANGLES);
        }
      popMatrix();
    popMatrix();
  }
  
  // 打印信息
  text("w、s、a、d          - Move the camera.\nPress the mouse - turn the camera.", 10,10);
}

好啦这样程序就完工啦!
你可以自己摆弄下cube的位置分布。用鼠标滚轮控制摄影机fov什么的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值