当我们实现了第四节的案例后,会苦恼的发现我们虽然可以操控摄影机移动,但转动视角后,摄影机却仍然沿着原来的坐标轴移动。这十分不方便。
这里我们对这个案例补完,实现摄影机永远按照转动后的视角坐标移动。
随着学习的深入,程序难免会越来越复杂,这里我们把程序分割成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什么的。