作業柵格化一個三角形。進階是super-sampling
insideTriangle
// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
/**
* @brief 判斷一個像素點在不在三角形內
* @note
* @param x: 像素橫座標
* @param y: 像素縱座標
* @param _v: 三角形頂點
* @retval true:在三角形內;false:不在
*
*/
static bool insideTriangle(int x, int y, const Vector3f* _v)
{
// 叉乘同號 那麼這個點在三角形內部
Eigen::Vector3f P(x,y,0);
std::array<Eigen::Vector3f, 3> res;
for(int i=0;i<3;i++){
Eigen::Vector3f A(_v[i](0),_v[i](1),0);
Eigen::Vector3f B(_v[(i+1)%3](0),_v[(i+1)%3](1),0);
Eigen::Vector3f AB=B-A;
Eigen::Vector3f BP=P-B;
res.at(i)=AB.cross(BP);
}
bool flag=true;
for(int i=0;i<3;i++){
Eigen::Vector3f res1=res.at(i);
Eigen::Vector3f res2=res.at((i+1)%3);
if(res1.dot(res2)<0){
flag=false;
break;
}
}
return flag;
}
rasterize_triangle
注意這裏是使用的投影矩陣沒有發現翻轉z的跡象,還是右手系,z越大越靠近攝像機
//Screen space rasterization
// TODO : Find out the bounding box of current triangle.
// iterate through the pixel and find if the current pixel is inside the triangle
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
/**
* @brief 將已經在正交投影下的三角形光柵化
* @note
* @param t: 三角形
* @retval None
*
*/
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// 三角形包圍盒
float x_max=v.at(0)(0);
float x_min=v.at(0)(0);
float y_max=v.at(0)(1);
float y_min=v.at(0)(1);
for(auto vv:v){
if(vv(0)>x_max){
x_max=vv(0);
}
if(vv(0)<x_min){
x_min=vv(0);
}
if(vv(1)>y_max){
y_max=vv(1);
}
if(vv(1)<y_min){
y_min=vv(1);
}
}
for(int x=x_min;x<=x_max;x++){
for(int y=y_min;y<=y_max;y++){
// 每一個像素中心是不是在三角形中
if(insideTriangle(x+0.5, y+0.5 , t.v)){
// If so, use the following code to get the interpolated z value.
auto[alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, t.v);
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;
int depth_index=get_index(x+0.5,y+0.5);
if(depth_buf[depth_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf[depth_index]){
// 這裏並沒有找到有關翻轉z軸的代碼,認爲沒有翻轉,因此越大的數值應該越在前面
// 渲染
Eigen::Vector3f point(x+0.5,y+0.5,z_interpolated);
set_pixel(point,t.getColor());
// 更新zBuffer
depth_buf[depth_index]=z_interpolated;
}
}
}
}
}
進階
寫完整個思路之後,看了一些大佬的總結。補充一下相關知識:super-sampling 處理 Anti-aliasing也就是SSAA。是把每個像素的分塊都記錄下來(這就要維護新的sample list)然後最後渲染之前再平均給buffer裏面。與之類似有一個簡便計算的MSAA。在當前情況下,SSAA和MSAA區別不是很大,因爲SSAA是(寫不下去,先馬住,有精力再寫:參考教程https://www.zhihu.com/question/496212252和https://zhuanlan.zhihu.com/p/484890144)
super-sampling 處理 Anti-aliasing(SSAA)
具體思路是先將每一個像素分成2×2塊,然後遍歷存入一個depth_buf_supersample和frame_buf_supersample的buffer,記錄每一個分塊的顏色和深度。然後再將分塊的值平均給depth_buf和frame_buffer。
代碼實現
定義SUPER_SAMPLING控制超採樣:
#ifndef DEFINITION_H
#define DEFINITION_H
#define SUPER_SAMPLING
#endif //DEFINITION_H
要判斷點的分塊是否在三角形內,要做浮點數上的判斷(int直接變整數了):
// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
/**
* @brief 判斷一個像素點在不在三角形內
* @note
* @param x: 像素橫座標
* @param y: 像素縱座標
* @param _v: 三角形頂點
* @retval true:在三角形內;false:不在
*
*/
static bool insideTriangle_float(float x, float y, const Vector3f* _v)
{
// 叉乘同號 那麼這個點在三角形內部
Eigen::Vector3f P(x,y,0);
std::array<Eigen::Vector3f, 3> res;
for(int i=0;i<3;i++){
Eigen::Vector3f A(_v[i](0),_v[i](1),0);
Eigen::Vector3f B(_v[(i+1)%3](0),_v[(i+1)%3](1),0);
Eigen::Vector3f AB=B-A;
Eigen::Vector3f BP=P-B;
res.at(i)=AB.cross(BP);
}
bool flag=true;
for(int i=0;i<3;i++){
Eigen::Vector3f res1=res.at(i);
Eigen::Vector3f res2=res.at((i+1)%3);
if(res1.dot(res2)<0){
flag=false;
break;
}
}
return flag;
}
在rasterizer.hpp中增加數據結構和控制supersample的參數
#ifdef SUPER_SAMPLING
std::vector<std::vector<float>> depth_buf_supersample;
std::vector<std::vector<Eigen::Vector3f>> frame_buf_supersample;
int x_size=3,y_size=3;// 將一個像素分爲x_size*y_size塊
#endif
總的代碼實現如下:
//Screen space rasterization
// TODO : Find out the bounding box of current triangle.
// iterate through the pixel and find if the current pixel is inside the triangle
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
/**
* @brief 將已經在正交投影下的三角形光柵化
* @note
* @param t: 三角形
* @retval None
*
*/
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// 三角形包圍盒
float x_max=v.at(0)(0);
float x_min=v.at(0)(0);
float y_max=v.at(0)(1);
float y_min=v.at(0)(1);
for(auto vv:v){
if(vv(0)>x_max){
x_max=vv(0);
}
if(vv(0)<x_min){
x_min=vv(0);
}
if(vv(1)>y_max){
y_max=vv(1);
}
if(vv(1)<y_min){
y_min=vv(1);
}
}
#ifdef SUPER_SAMPLING
for(int x=x_min;x<=x_max;x++){
for(int y=y_min;y<=y_max;y++){
// 對每一個像素進行supersimple
//在三角形內部的分塊 填充 depth_buf_supersample 和 frame_buf_supersample
bool isUpdate=false;
for(int i=0;i<x_size;i++){
for(int j=0;j<y_size;j++){
float x_index=x+0.5/x_size+i*1.0/x_size;
float y_index=y+0.5/y_size+j*1.0/y_size;
if(insideTriangle_float(x_index,y_index,t.v)){
isUpdate=true;
// 計算z
auto[alpha, beta, gamma] = computeBarycentric2D(x_index, y_index, t.v);
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;
int depth_index=get_index(x+0.5, y+0.5);
int supersampling_index=i*x_size+j;
if(depth_buf_supersample[depth_index][supersampling_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf_supersample[depth_index][supersampling_index]){
// 這裏並沒有找到有關翻轉z軸的代碼,認爲沒有翻轉,因此越大的數值應該越在前面
// 渲染
Eigen::Vector3f point(x,y,z_interpolated);
auto ind = (height-1-point.y())*width + point.x();
frame_buf_supersample[ind][supersampling_index] = t.getColor();
// 更新zBuffer
depth_buf_supersample[depth_index][supersampling_index]=z_interpolated;
}
}
}
}
if(isUpdate){// 這個像素有在三角形內部的部分 更新 frame_buf 和 depth_buf
// 更新 depth_buf
int depth_index=get_index(x+0.5,y+0.5);
float z_average=0; // 因爲有更新 一定有深度
for(float zzz:depth_buf_supersample[depth_index]){
if(zzz!=std::numeric_limits<float>::infinity())
z_average+=zzz;
}
z_average/=(x_size*y_size);
depth_buf[depth_index]=z_average;
// 更新frame_buf
auto ind = (height-1-y)*width + x;
Eigen::Vector3f color_average(0,0,0);
for(Vector3f ccc:frame_buf_supersample[ind])// 寫到這裏我不禁思考 RGB通道每個通道取平均應該是合理的吧?
{
color_average=color_average+ccc;
}
color_average=color_average/(x_size*y_size);
frame_buf[ind]=color_average;
}
}
}
#else
for(int x=x_min;x<=x_max;x++){
for(int y=y_min;y<=y_max;y++){
// 每一個像素中心是不是在三角形中
if(insideTriangle(x+0.5, y+0.5 , t.v)){
// If so, use the following code to get the interpolated z value.
auto[alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, t.v);
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;
int depth_index=get_index(x+0.5,y+0.5);
if(depth_buf[depth_index]==std::numeric_limits<float>::infinity()||z_interpolated > depth_buf[depth_index]){
// 這裏並沒有找到有關翻轉z軸的代碼,認爲沒有翻轉,因此越大的數值應該越在前面
// 渲染
Eigen::Vector3f point(x,y,z_interpolated);
set_pixel(point,t.getColor());
// 更新zBuffer
depth_buf[depth_index]=z_interpolated;
}
}
}
}
#endif
}
depth_buf_supersample和frame_buf_supersample的初始化和更新
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());
}
#ifdef SUPER_SAMPLING
// 如果是動態的話 這裏也需要清除
if ((buff & rst::Buffers::Depth_supersampling) == rst::Buffers::Depth_supersampling)
{
std::vector<float> dept;
dept.resize(x_size*y_size);
std::fill(dept.begin(), dept.end(), std::numeric_limits<float>::infinity());
std::fill(depth_buf_supersample.begin(), depth_buf_supersample.end(), dept);// 這裏是深拷貝還是淺拷貝????
}
if ((buff & rst::Buffers::Color_supersampling) == rst::Buffers::Color_supersampling)
{
std::vector<Eigen::Vector3f> col;
col.resize(x_size*y_size);
std::fill(col.begin(), col.end(), Eigen::Vector3f{0, 0, 0});
std::fill(frame_buf_supersample.begin(), frame_buf_supersample.end(), col);// 這裏是深拷貝還是淺拷貝????
}
#endif
}
rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h);
depth_buf.resize(w * h);
#ifdef SUPER_SAMPLING
depth_buf_supersample.resize(w * h);
for(int i=0;i<w;i++){
for(int j=0;j<h;j++){
int index=i*w+j;
depth_buf_supersample[index].resize(x_size*y_size);
}
}
frame_buf_supersample.resize(w*h);
for(int i=0;i<w;i++){
for(int j=0;j<h;j++){
int index=i*w+j;
frame_buf_supersample[index].resize(x_size*y_size);
}
}
#endif
}
運行結果
1*1:
2*2:
3*3:
放大一下