1.实验内容
在计算机图形学中,在屏幕上显示对象时,可能会出现许多的“锯齿”,这些锯齿是由顶点数据像素化之后成为片段的方式所引起的,由于将数学意义上的坐标转换到物理的显示器硬件上进行显示,显示器是有一个个像素点构成的,并不能实现数学意义上的“无限小”的描述。为了消除“锯齿”,图形工作者提出了许多抗锯齿的算法(也称为反走样[Anti-aliasing]算法),本文主要介绍OpenGL中提到的一种反走样方法——多重采样(Multisample antialiasing简称 MSAA)。
只计算每个像素的颜色,而对于那些子采样点只计算一个覆盖信息和遮挡信息来把像素的颜色信息写到每个子采样点里面,最终根据子采样点里面的颜色值来通过某个重建过滤器来降采样生成目标图像。这就是MSAA的原理。
2.MSAA基本原理和方法
MSAA的原理就是通过采样一个像素内的多个位置并平均它们的值来近似1像素框过滤器的效果。该方法只是提高了采样率,并没有真正的提高分辨率,屏幕上负责显示的像素还是原始的数量。MSAA能实现不错的反走样效果,但是其代价就是计算量的翻倍。不过,实际工业上应用时,增加的采样点并不是规则分布在每个像素内的,而是会按照特定的图案;同时某些采样点还可能被多个像素复用。这样,计算效率便会有所提升。
3.实验结果与分析
#include<iostream>
#include <opencv2/opencv.hpp>
#include<vector>
#include<ctime>
#include<cmath>
using namespace std;
using namespace cv;
//代表二维坐标系下的一点
struct Points {
double x;
double y;
};
//err用来代表浮点型的误差,当差值小于err时,代表相同
const float err = 0.01;
//求取两个向量的叉积
float CrossProduct(Points A, Points B, Points P)
{
Points a, b;//设AB两点间的向量为a,AP两点间的向量为b
a.x = B.x - A.x;
a.y = B.y - A.y;//向量a的坐标
b.x = P.x - A.x;
b.y = P.y - A.y;//向量b的坐标
return (a.x * b.y - a.y * b.x);
}
//判断点P是否在向量左侧
bool IsOnLeft(Points A, Points B, Points P)
{
//double计算可能会有误差
if (CrossProduct(A, B, P) > 1e-7)
return true;
else
return false;
}
//利用向量法判断一点是否在三角形内
bool pointInTri(Points A, Points B, Points C, Points D)
{
bool Left = IsOnLeft(A, B, D);//以点D相对于AB的方位来作为参考
//判断点是否在三角形内部
if ((Left == IsOnLeft(B, C, D)) && (Left == IsOnLeft(C, A, D)))
return true;
else
return false;
}
int main() {
//背景图片
cv::Mat image(200, 200, CV_8UC3, cv::Scalar(100, 100, 100));
//三角形的三个顶点像素的中心坐标
Points triP1 = {30, 30 }, triP2 = { 180, 180 }, triP3 = { 150, 190 };
//对每个像素进行遍历
for (int row = 0; row < image.rows; row++)
{
for (int col = 0; col < image.cols; col++)
{
int inTriNums = 0;
//以该像素为中心点,分别在左上,右上,左下,右下采样
Points sam1 = { row - 0.5, col - 0.5 }, sam2 = { row - 0.5, col + 0.5 };
Points sam3 = { row + 0.5, col - 0.5 }, sam4 = { row + 0.5, col + 0.5 };
if (pointInTri(triP1, triP2, triP3, sam1)) inTriNums++;
if (pointInTri(triP1, triP2, triP3, sam2)) inTriNums++;
if (pointInTri(triP1, triP2, triP3, sam3)) inTriNums++;
if (pointInTri(triP1, triP2, triP3, sam4)) inTriNums++;
//根据MSAA的原理计算深度
unsigned char pixels = 255 * (inTriNums / 4);
if(inTriNums)image.at<Vec3b>(row, col) = {0, 0, pixels};
}
}
cv::imshow("image", image);
cv::waitKey(0);
}