设置合理的视角
使用过照相机的读者都知道,拍摄时根据不同的情况应当选用不同焦距的镜头,这些不同焦距镜头的一大区别就是视角不同。在同样的位置,视角大可以观察到更宽范围内的景物,但投影到照片里的景物较小;视角小可以观察到的景物范围就窄一些,但投影到照片里的景物也就大一些。
添加ColorRect 类生成正方体的顶点和颜色
#ifndef VULKANEXBASE_COLORRECT_H
#define VULKANEXBASE_COLORRECT_H
#include <cstdint>
class ColorRect
{
public:
static float* vdata;
static int dataByteCount;
static int vCount;
static void genVertexData();
};
#endif
Cube.cpp
#include "ColorRect.h"
#include <vector>
#include <math.h>
#include <string.h>
float* ColorRect::vdata;
int ColorRect::dataByteCount;
int ColorRect::vCount;
void ColorRect::genVertexData(){ //生成顶点数据的方法
vCount=6; //顶点数量
dataByteCount=vCount*6* sizeof(float); //顶点数据总字节数
vdata=new float[vCount*6]{ //顶点数据数组
0,0,0, 1,1,1, //第1 个顶点的位置及颜色数据
30,30,0, 0,0,1, //第2 个顶点的位置及颜色数据
-30,30,0, 0,0,1, //第3 个顶点的位置及颜色数据
-30,-30,0, 0,0,1, //第4 个顶点的位置及颜色数据
30,-30,0, 0,0,1, //第5 个顶点的位置及颜色数据
30,30,0, 0,0,1 //第6 个顶点的位置及颜色数据
};}
添加Cube代码立方体面组装,需要在案例中添加一个将正方形面组装成立方体的辅助类
Cube.h
#ifndef VULKANEXBASE_CUBE_H
#define VULKANEXBASE_CUBE_H
#include "ColorRect.h"
#include "../util/DrawableObjectCommon.h"
class Cube
{
public:
void drawSelf(VkCommandBuffer cmd, //绘制方法
VkPipelineLayout& pipelineLayout,VkPipeline& pipeline,VkDescriptorSet* desSetPointer);
Cube(VkDevice& device,VkPhysicalDeviceMemoryProperties& memoryroperties); //构造函数
~Cube();//析构函数
};
#endif
Cube.cpp
#include <../util/MatrixState3D.h>
#include <../util/DrawableObjectCommon.h>
#include "Cube.h"
#define UINT_SIZE 30 //立方体边长
DrawableObjectCommon *colorRect; //指向绘制对象(正方形面物体)的指针
Cube::Cube(VkDevice& device,VkPhysicalDeviceMemoryProperties& memoryroperties)
{//构造函数
ColorRect::genVertexData(); //生成正方形顶点数据
colorRect=new DrawableObjectCommon(ColorRect::vdata, //创建绘制对象(正方形
ColorRect::dataByteCount,ColorRect::vCount,device,memoryroperties);
}
void Cube::drawSelf(VkCommandBuffer cmd, //绘制方法
VkPipelineLayout& pipelineLayout,VkPipeline& pipeline,VkDescriptorSet* desSetPointer){
MatrixState3D::pushMatrix(); //保护现场
MatrixState3D::translate(0,0,UINT_SIZE); //平移到相应位置
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer); //绘制前面正方形
MatrixState3D::popMatrix();
//上述代码首先在构造函数中创建了用于绘制立方体各个面的正方形面物体对象,
//,然后在绘制方法drawSelf中通过灵活组合平移、旋转变换绘制了立方体的6个面,最后在析构函数中释放了用于绘制立方体各个面的正方形面物体对象。
MatrixState3D::pushMatrix();
MatrixState3D::translate(0,0,-UINT_SIZE);
MatrixState3D::rotate(180,0,1,0);
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer);
MatrixState3D::popMatrix();
//左
MatrixState3D::pushMatrix();
MatrixState3D::translate(-UINT_SIZE,0,0);
MatrixState3D::rotate(-90,0,1,0);
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer);
MatrixState3D::popMatrix();
//右
MatrixState3D::pushMatrix();
MatrixState3D::translate(UINT_SIZE,0,0);
MatrixState3D::rotate(90,0,1,0);
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer);
MatrixState3D::popMatrix();
//上
MatrixState3D::pushMatrix();
MatrixState3D::translate(0,UINT_SIZE,0);
MatrixState3D::rotate(-90,1,0,0);
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer);
MatrixState3D::popMatrix();
//下
MatrixState3D::pushMatrix();
MatrixState3D::translate(0,-UINT_SIZE,0);
MatrixState3D::rotate(90,1,0,0);
colorRect->drawSelf(cmd,pipelineLayout,pipeline,desSetPointer);
MatrixState3D::popMatrix();
}
Cube::~Cube(){ //析构函数
delete colorRect; //销毁绘制对象(正方形面)
}
切换视觉的代码
void MyVulkanManager::initMatrix()
{
MatrixState3D::setInitStack();//初始化基本变换矩阵
float ratio = (float)screenWidth / (float)screenHeight;//求屏幕长宽比
if (ViewPara)
{ //合理的视角
MatrixState3D::setCamera(0, 50, 200, 0, 0, 0, 0, 1, 0); //设置合理视角下的摄像机
MatrixState3D::setProjectFrustum(-ratio, ratio, -1, 1, 1.5f, 1000);
}
else
{ //不合理的视角
MatrixState3D::setCamera(0, 50, 100, 0, 0, 0, 0, 1, 0); //设置不合理视角下的摄像机
MatrixState3D::setProjectFrustum(-ratio*0.7, ratio*0.7, -0.7, 0.7, 0.5f, 1000);//不合理视角下的投影参数
}
}
ViewPara变量值的不同,设置合理与不合理视角下的摄像机和投影参数的相关代码。ViewPara是MyVulkanManager头文件中声明的变量,点击屏幕可改变其值,有关触屏事件处理。
void MyVulkanManager::drawObject()画对象函数
void MyVulkanManager::drawObject()
{
FPSUtil::init();//初始化FPS计算
while (MyVulkanManager::loopDrawFlag)//每循环一次绘制一帧画面
{
FPSUtil::calFPS();//计算FPS
FPSUtil::before();//一帧开始
//获取交换链中的当前帧索引
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, ¤tBuffer);
//为渲染通道设置当前帧缓冲
rp_begin.framebuffer = framebuffers[currentBuffer];
vkResetCommandBuffer(cmdBuffer, 0);//恢复命令缓冲到初始状态
result = vkBeginCommandBuffer(cmdBuffer, &cmd_buf_info);//启动命令缓冲
MyVulkanManager::flushUniformBuffer();//将当前帧相关数据送入一致变量缓冲
MyVulkanManager::flushTexToDesSet();//更新绘制用描述集
vkCmdBeginRenderPass(cmdBuffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);//启动渲染通道
MatrixState3D::pushMatrix(); //保护现场
if (yAngle >= 360) { yAngle = -360; }
MatrixState3D::rotate(yAngle, 0, 1, 0); //绕Y轴旋转yAngle
MatrixState3D::pushMatrix(); //保护现场
MatrixState3D::translate(-80, 0, 0); //沿X 轴负方向平移80
MatrixState3D::rotate(-30, 0, 1, 0); //绕Y 轴旋转-30°
cubeForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout, //绘制第一个立方体
sqsCL->pipeline, &(sqsCL->descSet[0]));
MatrixState3D::popMatrix(); //恢复现场
MatrixState3D::pushMatrix(); //保护现场
MatrixState3D::translate(80, 0, 0); //沿X 轴正方向平移80
MatrixState3D::rotate(30, 0, 1, 0); //绕Y 轴旋转30°
cubeForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout, //绘制第二个立方体
sqsCL->pipeline, &(sqsCL->descSet[0]));
MatrixState3D::popMatrix(); //恢复现场
MatrixState3D::popMatrix();
vkCmdEndRenderPass(cmdBuffer);//结束渲染通道
result = vkEndCommandBuffer(cmdBuffer);//结束命令缓冲
submit_info[0].waitSemaphoreCount = 1;//等待的信号量数量
submit_info[0].pWaitSemaphores = &imageAcquiredSemaphore;//等待的信号量列表
result = vkQueueSubmit(queueGraphics, 1, submit_info, taskFinishFence);//提交命令缓冲
do { //等待渲染完毕
result =vkWaitForFences(device, 1, &taskFinishFence, VK_TRUE, FENCE_TIMEOUT);
} while (result == VK_TIMEOUT);
vkResetFences(device, 1, &taskFinishFence);//重置栅栏
present.pImageIndices = ¤tBuffer;//指定此次呈现的交换链图像索引
result =vkQueuePresentKHR(queueGraphics, &present);//执行呈现
FPSUtil::after(60);//限制FPS不超过指定的值
}
}
平移之后画两正方形,中通过点击屏幕可以在两种不同的视角间进行切换
case XCB_BUTTON_PRESS:
{
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
print_modifiers(ev->state);
printf("XCB_BUTTON_PRESS");
switch (ev->detail)
{
case 3:
mouseLeftDown = true;
printf("鼠标左键按下\n"); //鼠标左键按下
preX = ev->event_x; //鼠标的横坐标
preY = ev->event_y;
isClick = true;//点击标志置为true //鼠标的纵坐标
case 4:
printf("Wheel Button up in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
case 5:
printf("Wheel Button down in window %ld, at coordinates (%d,%d)\n",
ev->event, ev->event_x, ev->event_y);
break;
default:
printf("Button %d pressed in window %ld, at coordinates (%d,%d)\n",
ev->detail, ev->event, ev->event_x, ev->event_y);
}
break;
}
case XCB_BUTTON_RELEASE:
{
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
print_modifiers(ev->state);
switch (ev->detail)
{
case 3:
if (isClick)
{
MyVulkanManager::ViewPara =
++MyVulkanManager::ViewPara % 2; //更新当前采用的视角索引
MyVulkanManager::initMatrix(); //重新初始化矩阵
}
isClick = true;
mouseLeftDown = false;
printf("mouseLeftDown\n");
break;
default:
break;
}
break;
}
case XCB_MOTION_NOTIFY:
{
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
if (mouseLeftDown)
{
int x = ev->event_x; //鼠标的横坐标
int y = ev->event_y; //鼠标的纵坐标
float xDis = (float)(x - preX);
float yDis = (float)(y - preY);
printf("MyVulkanManager::yAngle");
preX = x;
preY = y;
MyVulkanManager::yAngle += xDis *180/ 200; //计算绕y 轴转角
}
break;
}
default:
break;
}
}
两个立方体会绕场景中心轴旋转。旋转过程中会有霓虹灯一样的彩条在立方体上滑过,这一效果是在片元着色器中实现的
#version 400
#extension GL_ARB_separate_shader_objects : enable//启动GL_ARB_separate_shader_objects
#extension GL_ARB_shading_language_420pack : enable//启动GL_ARB_shading_language_420pack
//行分别为顶点着色器传入片元着色器的顶点颜色数据、顶点位置数据以及输出到渲染管线的片元颜色值
layout (location = 0) in vec3 pos;//传入的物体坐标系顶点位置
layout (location = 1) in vec3 color;//传入的顶点颜色
layout (location = 0) out vec3 vcolor;//传到片元着色器的顶点颜色
//顶点位置是本案例新增的由顶点着色器传入的数据
//据,因此还需要对顶点着色器做简单修改
layout (location = 1) out vec3 vPosition;
out gl_PerVertex {
vec4 gl_Position;
};
layout (push_constant) uniform constantVals {
mat4 mvp;
} myConstantVals;
void main() { //主函数
gl_Position = myConstantVals.mvp * vec4(pos,1.0); //计算顶点最终位置
vPosition=gl_Position.xyz;
vcolor=color;//传递顶点颜色给片元着色器
}
#version 400
#extension GL_ARB_separate_shader_objects : enable//启动GL_ARB_separate_shader_objects
#extension GL_ARB_shading_language_420pack : enable//启动GL_ARB_shading_language_420pack
layout (location = 0) in vec3 vcolor;//顶点着色器传入的顶点颜色数据
layout (location = 1) in vec3 vPosition;
layout (location = 0) out vec4 outColor;//输出到渲染管线的片元颜色值
//为片元着色器的主方法,其中将传入的顶点位置数据执行一个旋转变换后根据其位置决定是否添加淡红色,最终将片元颜色值输出,以备渲染管线的后继阶段进行处理。
void main()
{
vec4 finalColor=vec4(vcolor.rgb,0.0);
//绕z轴转20度的旋转变换矩阵
mat4 mm=mat4
( 0.9396926,-0.34202012,0.0,0.0,
0.34202012,0.9396926,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0);
vec4 tPosition=mm*vec4(vPosition,1);//将顶点坐标绕z轴转20度
if(mod(tPosition.x+50.0,8)>6)
{//计算X方向在不在红光色带范围内
finalColor=vec4(0.4,0.0,0.0,1.0)+finalColor;//若在给最终颜色加上淡红色
}
outColor=finalColor;//给此片元颜色值
}