Games101
变换
齐次坐标
缩放变换与平移变换
旋转操作
3D旋转:
观测变换
定义相机的位置、视角: 规定相机放置在(0,0,0)位置,看往-z方向,向上是y轴方向:
正交投影
透视投影
齐次坐标的补充: 挤压+正交投影 远平面和近平面不变:利用两条件可以列出方程式求解
可视角:
屏幕空间
光栅化
显存控制:
三角形
判断一个点在三角形内
利用叉积!符号一致,在三角形内,否则在三角形外!
运算
用包围盒去简化
结果
反走样问题
采样理论
位置采样: 时间采样:
采样前先做模糊操作
注意:顺序不能反过来!
频域
傅里叶变换:
滤波
右图是频域图:最中心是低频,周围是会越来越高!在不同频率上存在的信息表示为亮度,这张图表示大部分信息都在低频中,高频信息比较少。 高通滤波器: 只有高频信号可以通过,提取出边界,在边界两侧信号发生剧烈变化,频率非常大。 低通滤波器: 低频率通过,看不到边界。 中通滤波: 中等频率通过.
filtering = convolution
实现了低通滤波:模糊处理
Sampling 采样
采样频率太低,发生重叠,导致结果走样:
解决方法
1.增加采样率 2.先模糊后采样
示例:
MSAA
代价:增大计算量,可以通过不规则分布,进行复用,提高利用率,减小计算率!
作业1
注意:openCV中默认是左上角为顶点,而传入的z又都是正值,在这里,我改为传入负值,与原视频中保持一致,同时改动了set_pixel的显示方式,理论上应该得到倒三角形,因为y轴方向相反! main.cpp
#include "Triangle.hpp"
#include "rasterizer.hpp"
#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <opencv4/opencv2/opencv.hpp>
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)
{
float theta = rotation_angle / 180.0 * M_PI;
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
model << std::cos(theta), -std::sin(theta), 0, 0,
std::sin(theta), std::cos(theta), 0, 0,
0, 0, 1.0, 0,
0, 0, 0, 1;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f p1 = Eigen::Matrix4f::Identity();
p1 << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zFar * zNear,
0, 0, 1.0, 0;
float theta = eye_fov / 2.0 / 180.0 * M_PI;
float y_top = -zNear * std::tan(theta);
float x_left = -y_top * aspect_ratio;
float x_right = -x_left;
float y_down = -y_top;
Eigen::Matrix4f scale = Eigen::Matrix4f::Identity();
scale << 2.0 / (x_right - x_left), 0, 0, 0,
0, 2.0 / (y_top - y_down), 0, 0,
0, 0, 2.0 / (zNear - zFar), 0,
0, 0, 0, 1.0;
Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
trans << 1, 0, 0, -(x_left + x_right) /2,
0, 1, 0, -(y_top + y_down) / 2,
0, 0, 1, -(zNear + zFar) / 2,
0, 0, 0, 1;
projection = scale * trans * p1;
return projection;
}
int main(int argc, const char **argv)
{
float angle = 0;
bool command_line = false;
std::string filename = "output.png";
if (argc >= 3)
{
command_line = true;
angle = std::stof(argv[2]);
if (argc == 4)
{
filename = std::string(argv[3]);
}
else
return 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}};
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.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)
{
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.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;
}
}
return 0;
}
rasterizer.cpp
#include <algorithm>
#include "rasterizer.hpp"
#include <opencv4/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};
}
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);
}
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);
}
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;
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::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)
{
if (point.x() < 0 || point.x() >= width ||
point.y() < 0 || point.y() >= height) return;
auto ind = point.y()*width + point.x();
frame_buf[ind] = color;
}
效果图:在这里插入代码片
作业2
main.cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include "rasterizer.hpp"
#include "global.hpp"
#include "Triangle.hpp"
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)
{
float theta = rotation_angle / 180.0 * M_PI;
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
model << std::cos(theta), -std::sin(theta), 0, 0,
std::sin(theta), std::cos(theta), 0, 0,
0, 0, 1.0, 0,
0, 0, 0, 1;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f p1 = Eigen::Matrix4f::Identity();
p1 << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zFar * zNear,
0, 0, 1.0, 0;
float theta = eye_fov / 2.0 / 180.0 * M_PI;
float y_top = -zNear * std::tan(theta);
float x_left = -y_top * aspect_ratio;
float x_right = -x_left;
float y_down = -y_top;
Eigen::Matrix4f scale = Eigen::Matrix4f::Identity();
scale << 2.0 / (x_right - x_left), 0, 0, 0,
0, 2.0 / (y_top - y_down), 0, 0,
0, 0, 2.0 / (zNear - zFar), 0,
0, 0, 0, 1.0;
Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
trans << 1, 0, 0, -(x_left + x_right) /2,
0, 1, 0, -(y_top + y_down) / 2,
0, 0, 1, -(zNear + zFar) / 2,
0, 0, 0, 1;
projection = scale * trans * p1;
return projection;
}
int main(int argc, const char** argv)
{
float angle = 0;
bool command_line = false;
std::string filename = "output.png";
if (argc == 2)
{
command_line = true;
filename = std::string(argv[1]);
}
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.5, -1, -5},
{2.5, 1.5, -5},
{-1, 0.5, -5}
};
std::vector<Eigen::Vector3i> ind
{
{0, 1, 2},
{3, 4, 5}
};
std::vector<Eigen::Vector3f> cols
{
{217.0, 238.0, 185.0},
{217.0, 238.0, 185.0},
{217.0, 238.0, 185.0},
{185.0, 217.0, 238.0},
{185.0, 217.0, 238.0},
{185.0, 217.0, 238.0}
};
auto pos_id = r.load_positions(pos);
auto ind_id = r.load_indices(ind);
auto col_id = r.load_colors(cols);
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.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
cv::imwrite(filename, image);
return 0;
}
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));
r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
cv::imshow("image", image);
key = cv::waitKey(10);
std::cout << "frame count: " << frame_count++ << '\n';
}
return 0;
}
rasterizer.cpp
#include <algorithm>
#include <vector>
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>
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};
}
rst::col_buf_id rst::rasterizer::load_colors(const std::vector<Eigen::Vector3f> &cols)
{
auto id = get_next_id();
col_buf.emplace(id, cols);
return {id};
}
auto to_vec4(const Eigen::Vector3f& v3, float w = 1.0f)
{
return Vector4f(v3.x(), v3.y(), v3.z(), w);
}
static bool insideTriangle(float x, float y, const Vector3f* _v)
{
Eigen::Vector2f p;
p << x, y;
Eigen::Vector2f AB = _v[1].head(2) - _v[0].head(2);
Eigen::Vector2f BC = _v[2].head(2) - _v[1].head(2);
Eigen::Vector2f CA = _v[0].head(2) - _v[2].head(2);
Eigen::Vector2f AP = p - _v[0].head(2);
Eigen::Vector2f BP = p - _v[1].head(2);
Eigen::Vector2f CP = p - _v[2].head(2);
return AB[0] * AP[1] - AB[1] * AP[0] >0 &&
BC[0] * BP[1] - BC[1] * BP[0] >0 &&
CA[0] * CP[1] - CA[1] * CP[0] >0;
}
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector3f* v)
{
float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
return {c1,c2,c3};
}
void rst::rasterizer::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type)
{
auto& buf = pos_buf[pos_buffer.pos_id];
auto& ind = ind_buf[ind_buffer.ind_id];
auto& col = col_buf[col_buffer.col_id];
float f1 = (50 - 0.1) / 2.0;
float f2 = (50 + 0.1) / 2.0;
Eigen::Matrix4f mvp = projection * view * model;
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>());
}
auto col_x = col[i[0]];
auto col_y = col[i[1]];
auto col_z = col[i[2]];
t.setColor(0, col_x[0], col_x[1], col_x[2]);
t.setColor(1, col_y[0], col_y[1], col_y[2]);
t.setColor(2, col_z[0], col_z[1], col_z[2]);
rasterize_triangle(t);
}
}
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
float x_min1 = std::min(v[0][0], std::min(v[1][0], v[2][0]));
float x_max1 = std::max(v[0][0], std::max(v[1][0], v[2][0]));
float y_min1 = std::min(v[0][1], std::min(v[1][1], v[2][1]));
float y_max1 = std::max(v[0][1], std::max(v[1][1], v[2][1]));
int x_min = std::floor(x_min1);
int x_max = std::ceil(x_max1);
int y_min = std::floor(y_min1);
int y_max = std::ceil(y_max1);
bool MSAA = false;
if(MSAA){
std::vector<Eigen::Vector2f> pos = {
{0.25, 0.25},
{0.25,0.75},
{0.75,0.25},
{0.75,0.75}
};
for(int x = x_min; x <= x_max; ++x){
for(int y = y_min; y <= y_max; ++y){
float count = 0;
float dep = FLT_MAX;
for(int i = 0; i < 4; ++i){
if(insideTriangle((float)x + pos[i][0], (float)y + pos[i][1], t.v)){
count += 1.0;
auto tup = computeBarycentric2D((float)x + pos[i][0], (float)y + pos[i][1], t.v);
float alpha, beta, gamma;
std::tie(alpha, beta, gamma) = tup;
float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
dep = std::min(dep, z_interpolated);
}
}
if(count){
if(depth_buf[get_index(x, y)] > dep){
Vector3f color = t.getColor() * count / 4.0;
Vector3f point;
point << x, y, dep;
depth_buf[get_index(x, y)] = dep;
set_pixel(point, color);
}
}
}
}
}
else{
for(int x = x_min; x <= x_max; ++x){
for(int y = y_min; y <= y_max; ++y){
if(insideTriangle(x, y, t.v)){
auto tup = computeBarycentric2D(x, y, t.v);
float alpha, beta, gamma;
std::tie(alpha, beta, gamma) = tup;
float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
if(depth_buf[get_index(x, y)] > z_interpolated){
Vector3f color = t.getColor();
Vector3f point;
point << x, y, z_interpolated;
depth_buf[get_index(x, y)] = z_interpolated;
set_pixel(point, color);
}
}
}
}
}
}
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-1-y)*width + x;
}
void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color)
{
auto ind = (height-1-point.y())*width + point.x();
frame_buf[ind] = color;
}
采用MSAA抗锯齿: 仔细看边界处: 不采用MSAA: 仔细看边界处:可以看到明显的锯齿!
|