用的库有opencv4.5.5 (vc15)和eign3.4.0
这篇的代码是在我上篇作业1的基础上魔改出来的,目的是为了实现MSAA四倍的抗锯齿技术和实心三角形的光栅化。
#include<Eigen/Eigen>
#include<stdio.h>
#include<cmath>
#include <iostream>
#include <vector>
#include <cctype>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
cv::Mat bg_image = cv::Mat(400, 400, CV_8UC3, Scalar(0, 0, 0));
//质点绕任意过原点的旋转轴进行旋转的操作
Eigen::Vector4d rotating(Eigen::Vector4d point_vector, Eigen::Vector3d Rotary_shaft, double angle)
{
double u = Rotary_shaft.x(), v = Rotary_shaft.y(), w = Rotary_shaft.z(), o = angle;
double s = sqrt(u * u + v * v + w * w);
u = u / s;
v = v / s;
w = w / s;
Eigen::Matrix4d rotating;
rotating <<
u * u + (1 - u * u) * cos(o), u* v* (1 - cos(o)) - w * sin(o), u* w* (1 - cos(o)) + v * sin(o), 0,
u* v* (1 - cos(o)) + w * sin(o), v* v + (1 - v * v) * cos(o), v* w* (1 - cos(o)) - u * sin(o), 0,
u* w* (1 - cos(o)) - v * sin(o), v* w* (1 - cos(o)) + u * sin(o), w* w + (1 - w * w) * cos(o), 0,
0, 0, 0, 1;
Eigen::Vector4d ret = rotating * point_vector;
return ret;
}
//计算质点相对于摄像机的位置,以摄像机的焦点为坐标原点新建的坐标系,也可以拿来把任意点作为原点建系,求相对位置
Eigen::Vector3d Relative_position(Eigen::Vector4d point_vector, Eigen::Vector4d camera_positon, Eigen::Vector4d camera_z, Eigen::Vector4d camera_x)
{
Eigen::Vector3d Apart_position;//摄像机位置点到质点的向量
Apart_position <<
point_vector.x() - camera_positon.x(),
point_vector.y() - camera_positon.y(),
point_vector.z() - camera_positon.z();
Eigen::Vector3d camera_x_3d, camera_z_3d;
camera_x_3d <<
camera_x.x(), camera_x.y(), camera_x.z();
camera_z_3d <<
camera_z.x(), camera_z.y(), camera_z.z();
Eigen::Vector3d camera_y_3d = camera_x_3d.cross(camera_z_3d);//向量叉乘得到垂直于它的向量
Eigen::Vector3d Relative_position;
double x = Apart_position.dot(camera_x_3d) / Apart_position.norm();//计算质点在摄像机为原点的坐标系中的x值
double y = Apart_position.dot(camera_y_3d) / Apart_position.norm();
double z = Apart_position.dot(camera_z_3d) / Apart_position.norm();
Relative_position <<
x, y, z;
return Relative_position;
}
//摄像机透视成像
Eigen::Vector2d perspective(double fov_w, int width, int height, Eigen::Vector3d Relative_position)
{
double focal_length = (width / tan(fov_w / 2)) / 20000;
double x = Relative_position.x() * focal_length / Relative_position.z();
double y = Relative_position.y() * focal_length / Relative_position.z();
double z = focal_length;
Eigen::Vector2d ret;
ret <<
(x * 10000) + 200,
(y * 10000) + 200;
return ret;
}
//三个顶点连线绘制三角形
void triangle(Point A, Point B, Point C)
{
Scalar line_color = cv::Scalar(5, 5, 4);
line(bg_image, A, B, line_color, 2, LINE_8);
line(bg_image, B, C, line_color, 2, LINE_8);
line(bg_image, C, A, line_color, 2, LINE_8);
printf("%d %d\n", A.x, A.y);
}
//判断点D是否在三角形ABC内
bool if_in(Eigen::Vector2d& A, Eigen::Vector2d& B, Eigen::Vector2d& C, double x, double y)
{
Eigen::Vector3d a, b, c, d;
a << A.x(), A.y(), 0;
b << B.x(), B.y(), 0;
c << C.x(), C.y(), 0;
d << x, y, 0;
Eigen::Vector3d AB, AC, AD;
AB = b - a;
AC = c - a;
AD = d - a;
if (AB.cross(AD).z() > 0 && AD.cross(AC).z() > 0)return 1;
else return 0;
}
void wirte_point(int x, int y, int r, int g, int b)
{
bg_image.at<Vec3b>(x, y)[0] = b;
bg_image.at<Vec3b>(x, y)[1] = g;
bg_image.at<Vec3b>(x, y)[2] = r;
}
void MSAA_4x(Eigen::Vector2d& A, Eigen::Vector2d& B, Eigen::Vector2d& C, int width, int height)
{
int x = 0, y = 0;
for (x = 0; x < width-1; x++)
{
for (y = 0; y < height-1; y++)
{
int num = 0;
double m = x * 1.0, n = y * 1.0;
if (if_in(A, B, C, m + 0.25, n + 0.25))num++;
if (if_in(A, B, C, m + 0.75, n + 0.25))num++;
if (if_in(A, B, C, m + 0.25, n + 0.75))num++;
if (if_in(A, B, C, m + 0.75, n + 0.75))num++;
switch (num)
{
case 0:wirte_point(x, y, 255, 255, 255); break;
case 1:wirte_point(x, y, 192, 192, 192); break;
case 2:wirte_point(x, y, 128, 128, 128); break;
case 3:wirte_point(x, y, 64, 64, 64); break;
case 4:wirte_point(x, y, 0, 0, 0); break;
default:printf("MSAA_4X error");
break;
}
}
}
}
//打点
void tick(Eigen::Vector4d A, Eigen::Vector4d B, Eigen::Vector4d C)//暂时废弃掉的tick刷新函数,以后会有用
{
}
int main(void)
{
namedWindow("GAMES101-homework2-super-plus - SWPU - Li_JinWen", 1);
int weith = 400, height = 400;
double FOV = 3.1415926 / 4;
Eigen::Vector4d S[3];
S[0] << 3, 0, 4, 0;
S[1] << -3, 0, 4, 0;
S[2] << 0, 0, 2, 0;
double rotating_speed = 0.05;
Eigen::Vector3d Rotary_shaft;
Eigen::Vector4d camera_positon, camera_z, camera_x;
Rotary_shaft << 0, 0, 1;
camera_positon << 30, 0, 0, 0;//这里可以修改摄像机焦点摆放的位置
camera_z << -1, 0, 0, 0;//这里可以修改摄像机的朝向
camera_x << 0, 1, 0, 0;//这里可以修改摄像机的水平方向
Eigen::Vector2d A, B, C;
while (true)
{
for (int i = 0; i < 3; i++)
{
S[i] = rotating(S[i], Rotary_shaft, rotating_speed);
}
A << perspective(FOV, weith, height, Relative_position(S[0], camera_positon, camera_z, camera_x)).x(),
perspective(FOV, weith, height, Relative_position(S[0], camera_positon, camera_z, camera_x)).y();
B << perspective(FOV, weith, height, Relative_position(S[1], camera_positon, camera_z, camera_x)).x(),
perspective(FOV, weith, height, Relative_position(S[1], camera_positon, camera_z, camera_x)).y();
C << perspective(FOV, weith, height, Relative_position(S[2], camera_positon, camera_z, camera_x)).x(),
perspective(FOV, weith, height, Relative_position(S[2], camera_positon, camera_z, camera_x)).y();
MSAA_4x(A, B, C, weith, height);
imshow("GAMES101-homework2-Original - SWPU - Li_JinWen", bg_image);
waitKey(100);
bg_image = cv::Mat(weith, height, CV_8UC3, Scalar(0, 0, 0));
imshow("GAMES101-homework2-Original - SWPU - Li_JinWen", bg_image);
}
}
因为,没有用官方的代码框架,然后本人技术菜,也懒得做太复杂,所以是整个是单线程历遍的,所以分辨率降到了400*400也很卡,但是能体现MSAA抗锯齿算法就行了。
效果图如下,这应该是400*400分辨率和4x采样下的极限了吧