admin管理员组文章数量:1612823
目录
题目概要
完善代码
绕Z轴变换矩阵
构建透视矩阵
先展示完成的代码
参数理解
运行结果
过程中出现一次报错
提高作业
提高作业代码
将代码运用于图像
main.cpp完整代码
rasterizer.hpp完整代码
rasterizer.cpp完整代码
题目概要
我用的是VS2019完成的作业,所以要先手动添加Eigen和opencv库,分别把源文件和头文件加进去就行。
如果可以成功运行,什么都不修改的情况下得到的是一个直线:
完善代码
绕Z轴变换矩阵
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
Eigen::Matrix4f rot; //构建旋转矩阵
float angle = rotation_angle / 180 * M_PI;
rot << cos(angle), -sin(angle), 0, 0,
sin(angle), sin(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = rot * model;
return model;
}
构建透视矩阵
先展示完成的代码
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
//完全按照课程里的参数取值,这道题的相机就在(0,0,0),因此远近平面都是在z的负半轴,所以n和f的值应该为负
float f, n, l, r, b, t, fov;
fov = eye_fov / 180 * MY_PI;
n = -zNear; //znear是正值
f = zFar;
t = tan(fov/2) * zNear;
b = -t;
r = t * aspect_ratio;
l = -r;
//透视->正交 perspective->orthographic
Eigen::Matrix4f pertoorth;
pertoorth << n, 0, 0, 0,
0, n, 0, 0,
0, 0, n + f, -n*f,
0, 0, 1, 0;
//正交——移动
Eigen::Matrix4f orth1;
orth1 << 1, 0, 0, -(r + l) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(n + f) / 2,
0, 0, 0, 1;
//正交——缩放
Eigen::Matrix4f orth2;
orth2 << 2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (n - f), 0,
0, 0, 0, 1;
projection = orth2*orth1 * pertoorth;//注意矩阵顺序,变换从右往左依次进行
return projection;
}
参数理解
给到了eye_fov aspect ratio等参数,这两个点在课程P5有提到:如下截图
在OpenGL中,就是用的给定的四个参数eye_fovy, aspect_ratio, zNear, zFar来定义一个view frustum,即视锥体,人眼只能看到这个视锥体里面的东西。
其中,aspect ratio是视口的长宽比: width/height
zNear和zFar定义相机位置与near平面和far平面之间的距离(zNear、zFar > 0),也就是比zNear近或比zFar远的东西都看不到。
运行结果
(补充:这里如果n和f取的是正值,出来的三角形会是倒三角。)
按了一次D:
过程中出现一次报错
报错内容:
Assertion failed: ((m_row+m_currentBlockRows) == m_xpr.rows() || m_xpr.cols() == 0) && m_col == m_xpr.cols() && "Too few coefficients passed to comma initializer (operator<<)", file D:\games101\Eigen\src\Core\CommaInitializer.h, line 124
通过检查发现定义矩阵时如果Matrix4f的矩阵只写了三行,就会发生Eigen库的报错,改正后可正常运行。
提高作业
要用到罗德里格旋转公式 Rodrigues' rotation formula:
没什么好说的,就是最开始输入的是Vector3f,那么在根据公式求的时候得到的R矩阵是3X3的,要转化成齐次坐标。
提高作业代码
Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
{
//会用到罗德里格旋转公式 Rodrigues' rotation formula
Eigen::Matrix4f I = Eigen::Matrix4f::Identity();
float a = angle / 180 * MY_PI;
Eigen::Matrix3f N, R;
N <<
0, -axis[2], axis[1],
axis[2], 0, -axis[0],
-axis[1], axis[0], 0;
R = cos(a) * I + (1 - cos(a)) * axis * axis.transpose() + sin(a) * N;
Eigen::Matrix4f rotation;
rotation <<
R(0, 0), R(0, 1), R(0, 2), 0,
R(1, 0), R(1, 1), R(1, 2), 0,
R(2, 0), R(2, 1), R(2, 2), 0,
0, 0, 0, 1;
return rotation;
}
将代码运用于图像
这一部分参考了:【GAMES101】作业1(提高)与框架理解_ycrsw的博客-CSDN博客
main.cpp完整代码
#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <Eigen>
#include <iostream>
#include <opencv2/opencv.hpp>
#include<cmath>
constexpr double MY_PI = 3.1415926;
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
Eigen::Matrix4f translate;
translate << 1, 0, 0, -eye_pos[0], 0, 1, 0, -eye_pos[1], 0, 0, 1,
-eye_pos[2], 0, 0, 0, 1;
view = translate * view;
return view;
}
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
Eigen::Matrix4f rot;
float angle = rotation_angle / 180 * MY_PI;
rot <<
cos(angle), -sin(angle), 0, 0,
sin(angle), cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = rot * model;
return model;
}
Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
{
//会用到罗德里格旋转公式 Rodrigues' rotation formula
Eigen::Matrix3f I = Eigen::Matrix3f::Identity();
double a = angle / 180 * MY_PI;
Eigen::Matrix3f N, R;
N <<
0, -axis[2], axis[1],
axis[2], 0, -axis[0],
-axis[1], axis[0], 0;
R = cos(a) * I + (1 - cos(a)) * axis * axis.transpose() + sin(a) * N;
Eigen::Matrix4f rotation;
rotation <<
R(0, 0), R(0, 1), R(0, 2), 0,
R(1, 0), R(1, 1), R(1, 2), 0,
R(2, 0), R(2, 1), R(2, 2), 0,
0, 0, 0, 1;
return rotation;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
//完全按照课程里的参数取值,这道题的相机就在(0,0,0),因此远近平面都是在z的负半轴,所以n和f的值应该为负
float f, n, l, r, b, t, fov;
fov = eye_fov / 180 * MY_PI;
n = -zNear; //znear是正值
f = zFar;
t = tan(fov/2) * zNear;
b = -t;
r = t * aspect_ratio;
l = -r;
//透视->正交 perspective->orthographic
Eigen::Matrix4f pertoorth;
pertoorth << n, 0, 0, 0,
0, n, 0, 0,
0, 0, n + f, -n*f,
0, 0, 1, 0;
//正交——移动
Eigen::Matrix4f orth1;
orth1 <<
1, 0, 0, -(r + l) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(n + f) / 2,
0, 0, 0, 1;
//正交——缩放
Eigen::Matrix4f orth2;
orth2 <<
2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (n - f), 0,
0, 0, 0, 1;
projection = orth2*orth1 * pertoorth;//注意矩阵顺序,变换从右往左依次进行
return projection;
}
int main(int argc, const char** argv)
{
float angle = 0;//定义角度
bool command_line = false;//定义命令行开关标志,默认为关掉
std::string filename = "output.png";//定义文件名称
Eigen::Vector3f raxis(0,0,1);
double rangle = 0, ra;
if (argc >= 3) {
command_line = true;
angle = std::stof(argv[2]); // -r by default
if (argc == 4) {
filename = std::string(argv[3]);
}
}
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}};
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) {
r.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.set_rodrigues(get_rotation(raxis, rangle));
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;
}
bool rflag = false;
std::cout << "Please enter the axis and angle:" << std::endl;
std::cin >> raxis.x() >> raxis.y() >> raxis.z() >> ra;//定义罗德里格斯旋转轴和角
while (key != 27) {
r.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));
if (rflag) //如果按下r了,就开始绕给定任意轴旋转
r.set_rodrigues(get_rotation(raxis, rangle));
else
r.set_rodrigues(get_rotation({ 0,0,1 }, 0));
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') {
angle += 10;
}
else if (key == 'd') {
angle -= 10;
}
else if (key == 'r') {
rflag = true;
rangle += ra;
}
}
return 0;
}
rasterizer.hpp完整代码
//
// Created by goksu on 4/6/19.
//
#pragma once
#include "Triangle.hpp"
#include <algorithm>
#include <Eigen>
using namespace Eigen;
namespace rst {
enum class Buffers
{
Color = 1,
Depth = 2
};
inline Buffers operator|(Buffers a, Buffers b)
{
return Buffers((int)a | (int)b);
}
inline Buffers operator&(Buffers a, Buffers b)
{
return Buffers((int)a & (int)b);
}
enum class Primitive
{
Line,
Triangle
};
/*
* For the curious : The draw function takes two buffer id's as its arguments.
* These two structs make sure that if you mix up with their orders, the
* compiler won't compile it. Aka : Type safety
* */
struct pos_buf_id
{
int pos_id = 0;
};
struct ind_buf_id
{
int ind_id = 0;
};
class rasterizer
{
public:
rasterizer(int w, int h);
pos_buf_id load_positions(const std::vector<Eigen::Vector3f>& positions);
ind_buf_id load_indices(const std::vector<Eigen::Vector3i>& indices);
void set_model(const Eigen::Matrix4f& m);
void set_view(const Eigen::Matrix4f& v);
void set_projection(const Eigen::Matrix4f& p);
void set_rodrigues(const Eigen::Matrix4f& r);
void set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color);
void clear(Buffers buff);
void draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, Primitive type);
std::vector<Eigen::Vector3f>& frame_buffer() { return frame_buf; }
private:
Eigen::Matrix4f rodrigues;
void draw_line(Eigen::Vector3f begin, Eigen::Vector3f end);
void rasterize_wireframe(const Triangle& t);
private:
Eigen::Matrix4f model;
Eigen::Matrix4f view;
Eigen::Matrix4f projection;
std::map<int, std::vector<Eigen::Vector3f>> pos_buf;
std::map<int, std::vector<Eigen::Vector3i>> ind_buf;
std::vector<Eigen::Vector3f> frame_buf;
std::vector<float> depth_buf;
int get_index(int x, int y);
int width, height;
int next_id = 0;
int get_next_id() { return next_id++; }
};
} // namespace rst
rasterizer.cpp完整代码
//
// Created by goksu on 4/6/19.
//
#include <algorithm>
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>
#include <stdexcept>
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
auto id = get_next_id();
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
// Code taken from a stack overflow answer: https://stackoverflow/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};
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)
{
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)
{
px=px+2*dy1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
y=y+1;
}
else
{
y=y-1;
}
px=px+2*(dy1-dx1);
}
// delay(0);
Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
set_pixel(point,line_color);
}
}
else
{
if(dy>=0)
{
x=x1;
y=y1;
ye=y2;
}
else
{
x=x2;
y=y2;
ye=y1;
}
Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
set_pixel(point,line_color);
for(i=0;y<ye;i++)
{
y=y+1;
if(py<=0)
{
py=py+2*dx1;
}
else
{
if((dx<0 && dy<0) || (dx>0 && dy>0))
{
x=x+1;
}
else
{
x=x-1;
}
py=py+2*(dx1-dy1);
}
// delay(0);
Eigen::Vector3f point = Eigen::Vector3f(x, y, 1.0f);
set_pixel(point,line_color);
}
}
}
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!");
}
auto& buf = pos_buf[pos_buffer.pos_id];
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 * rodrigues;
for (auto& i : ind)
{
Triangle t;
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();
}
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());
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::set_rodrigues(const Eigen::Matrix4f& r)
{
rodrigues = r;
}
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)
{
//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;
}
本文标签: 作业
版权声明:本文标题:GAMES101作业1-VS2019 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1728631644a1167152.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论