搭建虚拟机环境
- 安装Oracle VM VirtualBox虚拟机,安装虚拟硬盘,配置Linux Ubuntu-64 bit系统,
- 启动虚拟机,发生冲突错误:
- 将Vmware虚拟设备取消挂起状态,关机
- 确保 Hyper-V 完全关闭:bcdedit /set hypervisorlaunchtype off
- 重启计算机
- 安装增强功能,未找到iso错误:ISO下载地址:Index of http://download.virtualbox.org/virtualbox
- 首先升级虚拟机版本置7.0.2
- 通过update Guest additions
- 最后安装增强功能
- 通过终端调用cmake编译文件,
- 使用硬盘中的vscode编写main.cpp
- 通过终端创建文件touch CMakeLists.txt
- 编写文件(作业框架里有)
- cmake编译目录
- make编译
- 执行运行程序
# 指定最低版本要求
cmake_minimum_required(VERSION 2.8.5)
# 项目名称
project(Hello)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
#
find_package(Eigen3 REQUIRED)
include_directories(EIGEN3_INCLUDE_DIR)
# 添加源文件
add_executable(pa0 main.cpp)
作业0:
题目:
给定一个点 P=(2,1), 将该点绕原点先逆时针旋转 45◦,再平移 (1,2), 计算出 变换后点的坐标(要求用齐次坐标进行计算)。
解:
首先为向量增加一个分量(1),将变换的值带入,SL:3*3的旋转(绕z) * 平移矩阵公式中,最后乘以具有3个分量的向量
auto angle = 45 * 3.14 / 180.0f;Eigen::Matrix3f R_Mat3;R_Mat3 <<std::cos(angle),-std::sin(angle),0,std::sin(angle), std::cos(angle),0,0,0,1;Eigen::Matrix3f T_Mat3;T_Mat3 <<1,0,1,0,1,2,0,0,1;Eigen::Vector3f p(2.0f,1.0f,1.0f);auto result = T_Mat3 * R_Mat3* p;std::cout << result;
作业1(OpenCV,Eigen):
题目:
在给定作业框架填写一个旋转矩阵get_model_matrix()和一个透视投影矩阵get_projection_matrix(),给定三维下三个 点 v0(2.0, 0.0, −2.0), v1(0.0, 2.0, −2.0), v2(−2.0, 0.0, −2.0), 你需要将这三个点的坐 标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形
解:
旋转矩阵:就是利用绕z旋转矩阵公式
double fangle = rotation_angle / 180 * MY_PI;Eigen::Matrix4f rotation;rotation << cos(fangle), -sin(fangle), 0, 0,sin(fangle), cos(fangle), 0, 0,0, 0, 1, 0,0, 0, 0, 1;model = rotation * model;
透视投影矩阵:同样带入公式即可
Eigen::Matrix4f proj, ortho;proj << zNear, 0, 0, 0,0, zNear, 0, 0,0, 0, zNear + zFar, -zNear * zFar,0, 0, 1, 0;double w, h, z;h = zNear * tan(eye_fov / 2) * 2;w = h * aspect_ratio;z = zFar - zNear;ortho << 2 / w, 0, 0, 0,0, 2 / h, 0, 0,0, 0, 2 / z, -(zFar+zNear) / 2,0, 0, 0, 1;projection = ortho * proj * projection;
提高部分题目:
在 main.cpp 中构造一个函数,该函数的作用是得到绕任意 过原点的轴的旋转变换矩阵。Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
解:
很明显从函数参数可以看出应用的是罗德里格斯公式,所以这里其实就是带入公式
Eigen::Matrix4f get_rotation(Vector3f axis, float angle) {double fangle = angle / 180 * MY_PI;Eigen::Matrix4f I, N, Rod;Eigen::Vector4f axi;Eigen::RowVector4f taxi;axi << axis.x(), axis.y(), axis.z(), 0;I=Eigen::Matrix4f::Identity();N << 0, -axis.z(), axis.y(), 0,axis.z(), 0, -axis.x(), 0,-axis.y(), axis.x(), 0, 0,0, 0, 0, 1;Rod = cos(fangle) * I + (1 - cos(fangle)) * axi * axi.transpose() + sin(fangle) * N;Rod(3, 3) = 1;return Rod;
}
理解代码:
对于c语言使用.h,.c的扩展名,而对于c++使用.h,hpp(为了区分 C 和 C++ 代码),.cpp扩展名
main.cpp:变换矩阵的计算,模拟了图形管线,旋转三角形的控制
int argc,命令行参数的个数 const char** argv |const char* argv[]是一个数组,每个元素都是指向常量字符的指针
为什么argv[2]没有解引用?因为std::cout
会自动将 const char*
解释为一个以 \0
结尾的字符串。
int main(int argc, const char** argv)//在运行是输入命令行参数
{float angle = 0;//摄像机角度bool command_line = false;//设置命令行开关std::string filename = "output.png";//字符串if (argc >= 3) {//当3个参数command_line = true;angle = std::stof(argv[2]); // -r by default//从命令行获取旋转角度if (argc == 4) {//当4个参数filename = std::string(argv[3]);//从命令行获取文件名}elsereturn 0;}rst::rasterizer r(700, 700);//光栅化器类的实例,传入视口大小Eigen::Vector3f eye_pos = {0, 0, 5};//相机未知std::vector<Eigen::Vector3f> pos{{2, 0, -2}, {0, 2, -2}, {-2, 0, -2}};//3个顶点std::vector<Eigen::Vector3i> ind{{0, 1, 2}};//顶点索引auto pos_id = r.load_positions(pos);//保存顶点auto ind_id = r.load_indices(ind);//保存索引int key = 0;//键盘输入int frame_count = 0;if (command_line) {//如果传入参数>=3r.clear(rst::Buffers::Color | rst::Buffers::Depth);r.set_model(get_model_matrix(angle));r.set_view(get_view_matrix(eye_pos));r.set_projection(get_projection_matrix(45, 1, 0.1, 50));r.draw(pos_id, ind_id, rst::Primitive::Triangle);cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());image.convertTo(image, CV_8UC3, 1.0f);cv::imwrite(filename, image);return 0;}while (key != 27) {//非ECSr.clear(rst::Buffers::Color | rst::Buffers::Depth);//计算模型、视图和投影矩阵,并传入光栅化器中,光栅化器在屏幕上显示出变换的结果r.set_model(get_model_matrix(angle));r.set_view(get_view_matrix(eye_pos));r.set_projection(get_projection_matrix(45, 1, 0.1, 50));r.draw(pos_id, ind_id, rst::Primitive::Triangle);//绘制三角形cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());image.convertTo(image, CV_8UC3, 1.0f);cv::imshow("image", image);//显示图像key = cv::waitKey(10);std::cout << "frame count: " << frame_count++ << '\n';if (key == 'a') {//逆时针旋转10angle += 10;}else if (key == 'd') {//顺时针旋转10angle -= 10;}}return 0;
}
rasterizer.hpp , rasterizer.cpp光栅器 | 渲染器,生成渲染器界面与绘制。
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions) // 保存到
{auto id = get_next_id(); // 从0开始分配,每次调用后索引增加pos_buf.emplace(id, positions); // 存放到顶点数组中return {id};
}rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{auto id = get_next_id();ind_buf.emplace(id, indices);return {id};
}// Bresenham's line drawing algorithm,
// Bresenham算法,绘制线
// Code taken from a stack overflow answer: https://stackoverflow.com/a/16405254
void rst::rasterizer::draw_line(Eigen::Vector3f begin, Eigen::Vector3f end)
{/* 起点和终点坐标 */auto x1 = begin.x();auto y1 = begin.y();auto x2 = end.x();auto y2 = end.y();/* 颜色 */Eigen::Vector3f line_color = {255, 255, 255};/*x, y当前绘制的像素点的坐标,dx, dy直线在 x 和 y 方向上的增量,dx1, dy1用于计算偏移量,px, py用于确定下一个像素点的坐标 ,xe, ye: 用于循环控制,表示直线的终点*/int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;dx = x2 - x1;dy = y2 - y1;dx1 = fabs(dx);dy1 = fabs(dy);px = 2 * dy1 - dx1;py = 2 * dx1 - dy1;if (dy1 <= dx1)/* 如果在y轴上的偏移量比x轴上要小,更趋向于水平线 */{if (dx >= 0)/* 从起点坐标开始 */{x = x1;y = y1;xe = x2;}else/* 从终点坐标开始 */{x = x2;y = y2;xe = x1;}Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);/* 创建起点 */set_pixel(point, line_color);/* 设置为白色 */for (i = 0; x < xe; i++)/* 通过迭代来逐步绘制直线的每个像素点。 */{x = x + 1;if (px < 0)/* 像素的 y 坐标不需要改变 */{px = px + 2 * dy1;/* 更新 px 的值 */}else{if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))/* 表示 y 方向向上移动 */{y = y + 1;}else/* 表示 y 方向向下移动。 */{y = y - 1;}px = px + 2 * (dy1 - dx1);/* 更新 px 的值 */}//delay(0);Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);/* 新的点绘制到屏幕上 */set_pixel(point, line_color);}}else{……}
}auto to_vec4(const Eigen::Vector3f &v3, float w = 1.0f) // 默认创建点,引入齐次坐标
{return Vector4f(v3.x(), v3.y(), v3.z(), w);
}void rst::rasterizer::draw(rst::pos_buf_id pos_buffer, rst::ind_buf_id ind_buffer, rst::Primitive type)
{if (type != rst::Primitive::Triangle){throw std::runtime_error("Drawing primitives other than triangle is not implemented yet!");}//2维数组auto &buf = pos_buf[pos_buffer.pos_id];/* 根据id访问实际数据,3个顶点的坐标 */auto &ind = ind_buf[ind_buffer.ind_id];/* 三个顶点的索引 */float f1 = (100 - 0.1) / 2.0;float f2 = (100 + 0.1) / 2.0;Eigen::Matrix4f mvp = projection * view * model; // MVPfor (auto &i : ind)//第一次为{0,1,2}{Triangle t;/* 三角形对象 *//* MVP变换 ,对3个顶点 */Eigen::Vector4f v[] = {mvp * to_vec4(buf[i[0]], 1.0f),mvp * to_vec4(buf[i[1]], 1.0f),mvp * to_vec4(buf[i[2]], 1.0f)};/* 遍历每个顶点,除齐次坐标 */for (auto &vec : v){vec /= vec.w();}/* 对每个顶点,分解为xyz,变换到视口,应用视口矩阵 */for (auto &vert : v){vert.x() = 0.5 * width * (vert.x() + 1.0);vert.y() = 0.5 * height * (vert.y() + 1.0);vert.z() = vert.z() * f1 + f2;}/* 为三角形设置数据 */for (int i = 0; i < 3; ++i){t.setVertex(i, v[i].head<3>());t.setVertex(i, v[i].head<3>());t.setVertex(i, v[i].head<3>());}// 设置颜色t.setColor(0, 255.0, 0.0, 0.0);t.setColor(1, 0.0, 255.0, 0.0);t.setColor(2, 0.0, 0.0, 255.0);rasterize_wireframe(t); // 绘制}
}void rst::rasterizer::rasterize_wireframe(const Triangle &t) // 绘制三角形
{draw_line(t.c(), t.a());/* abc为3个顶点 */draw_line(t.c(), t.b());draw_line(t.b(), t.a());
}void rst::rasterizer::set_model(const Eigen::Matrix4f &m) // 设置成员变量
{model = m;
}void rst::rasterizer::set_view(const Eigen::Matrix4f &v)
{view = v;
}void rst::rasterizer::set_projection(const Eigen::Matrix4f &p)
{projection = p;
}void rst::rasterizer::clear(rst::Buffers buff) // 清理颜色和深度缓冲区
{if ((buff & rst::Buffers::Color) == rst::Buffers::Color){std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});}if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth){std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());}
}rst::rasterizer::rasterizer(int w, int h) : width(w), height(h) // 根据宽高比设置帧缓冲大小和深度缓冲大小
{frame_buf.resize(w * h);depth_buf.resize(w * h);
}int rst::rasterizer::get_index(int x, int y) // 根据坐标求像素在缓冲区的序号
{return (height - y) * width + x;
}void rst::rasterizer::set_pixel(const Eigen::Vector3f &point, const Eigen::Vector3f &color) // 将屏幕像素点 (x, y) 设 为 (r, g, b) 的颜色,并写入相应的帧缓冲区位置。
{// old index: auto ind = point.y() + point.x() * width;if (point.x() < 0 || point.x() >= width ||point.y() < 0 || point.y() >= height)return;auto ind = (height - point.y()) * width + point.x();frame_buf[ind] = color;
}
Triangle.hpp, Triangle.cpp三角形数据 ,提供了数据设置的接口
class Triangle
{public:/*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*//*Per vertex values*//* 顶点,颜色,纹理坐标,法线 */Vector3f v[3]; Vector3f color[3]; // color at each vertex;Vector2f tex_coords[3]; // texture u,vVector3f normal[3]; // normal vector for each vertex// Texture *tex;Triangle();/* 返回三角形顶点坐标 */Eigen::Vector3f a() const { return v[0]; }Eigen::Vector3f b() const { return v[1]; }Eigen::Vector3f c() const { return v[2]; }void setVertex(int ind, Vector3f ver); /*set i-th vertex coordinates */void setNormal(int ind, Vector3f n); /*set i-th vertex normal vector*/void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/void setTexCoord(int ind, float s,float t); /*set i-th vertex texture coordinate*/std::array<Vector4f, 3> toVector4() const;
};#endif // RASTERIZER_TRIANGLE_H//cppTriangle::Triangle()/* 初始值都为0 */
{v[0] << 0, 0, 0;v[1] << 0, 0, 0;v[2] << 0, 0, 0;color[0] << 0.0, 0.0, 0.0;color[1] << 0.0, 0.0, 0.0;color[2] << 0.0, 0.0, 0.0;tex_coords[0] << 0.0, 0.0;tex_coords[1] << 0.0, 0.0;tex_coords[2] << 0.0, 0.0;
}void Triangle::setVertex(int ind, Eigen::Vector3f ver) { v[ind] = ver; }/* 设置指定索引的值 */void Triangle::setNormal(int ind, Vector3f n) { normal[ind] = n; }void Triangle::setColor(int ind, float r, float g, float b)/* 将颜色从0--255,转到0--1 */
{if ((r < 0.0) || (r > 255.) || (g < 0.0) || (g > 255.) || (b < 0.0) ||(b > 255.)) {throw std::runtime_error("Invalid color values");/* 超出范围抛出异常 */}color[ind] = Vector3f((float)r / 255., (float)g / 255., (float)b / 255.);return;
}
void Triangle::setTexCoord(int ind, float s, float t)
{tex_coords[ind] = Vector2f(s, t);
}std::array<Vector4f, 3> Triangle::toVector4() const/* 三个顶点从 Vector3f 类型转换为 Vector4f 类型 */
{std::array<Vector4f, 3> res;std::transform(std::begin(v), std::end(v), res.begin(), [](auto& vec) {/* 遍历每个顶点 */return Vector4f(vec.x(), vec.y(), vec.z(), 1.f);});return res;
}
阅读复杂指针
如何阅读C/C++中的复杂的指针类型声明(源码中常遇到)_c++复杂阅读指针-CSDN博客
右左法则:从标识符开始,从右到左,优先级越高
括号法则:运算优先级更高
规律:普通变量(左边是本身类型),指针|引用(左边表示指向的类型),数组(左边表示元素类型),函数(左边表示返回类型)
int * a [10];a是一个包含10个数据的数组,每个元素是一个指向int的指针
int (*a) [3]; a是一个指针,指向3个元素的数组,每个元素是int类型
int *foo();foo是一个函数,返回指向int指针
int (*foo)();foo是一个指针,指向函数,函数返回int
int (*(*vtable)[])();vtable是一个指针,指向数组,每个元素是指针,并指向函数,函数返回类型为int
int name[] [n],name是一个数组,每个元素也是数组,包含n个int类型元素
const char** argv,argv是一个指针,指向常量字符的指针
const char* argv[]argv是一个数组,每个元素都是指向常量字符的指针