运行结果:

Untitled

Untitled

源码:

#include "tgaimage.h"
#include "model.h"
#include "geometry.h"
#include <iostream>
#include <map>
using namespace std;

const int width = 500;
const int height = 500;

// 上一节[画线]的函数
void line_rough(int x0, int y0, int x1, int y1, TGAImage& image, TGAColor& color)
{
	int d_x = x1 - x0;// x的公差
	int d_y = y1 - y0;// y的公差
	float X, Y;

	for (float q = 0.; q < 1; q += 0.01)
	{
		X = x0 + q * d_x;
		Y = y0 + q * d_y;
		image.set(X, Y, color);
	}
}

#pragma region LESSON 2
// 声明直线方程的两个变量k,b。
// 比如k_p0p2是指直线p0p2的斜率
float k_p0p2, b_p0p2, k_p0p1, b_p0p1, k_p1p2, b_p1p2;
// 声明循环控制变量i,j
int i, j;

// 扫描线算法优化版
// 优化点:将绘制算法从bresenham算法改成了line_rough(),运行速度提升1000%
// 优化点:去除了上一版的繁琐计算,引入两个数组保存计算结果。
void saomiaoxiansuanfa_160802192023(Vec2i p0, Vec2i p1, Vec2i p2, TGAImage& image, TGAColor color)
{
    // 保证p0是最高的点
    if (p1.y > p0.y) swap(p1, p0);
    if (p2.y > p0.y) swap(p2, p0);
    // 保证p1在中间
    if (p2.y > p1.y) swap(p2, p1);

    // 求直线P0P2方程式
    k_p0p2 = 1.0 * (p0.y - p2.y) / (p0.x - p2.x);
    b_p0p2 = p2.y - k_p0p2 * p2.x;
    // 求直线P0P1方程式
    k_p0p1 = 1.0 * (p0.y - p1.y) / (p0.x - p1.x);
    b_p0p1 = p0.y - k_p0p1 * p0.x;
    // 求直线P1P2方程式
    k_p1p2 = 1.0 * (p1.y - p2.y) / (p1.x - p2.x);
    b_p1p2 = p1.y - k_p1p2 * p1.x;

    // 保存两点的坐标的集合
    vector<vector<float>> memo1, memo2;
		// 计算直线Y与上半块三角形的交点
    for (int Y = p0.y; Y >= p1.y; --Y)
    {
        memo1.push_back({ (Y - b_p0p1) / k_p0p1 , (Y - b_p0p2) / k_p0p2 });
    }
		// 使用两点绘制算法去绘制三角形
    i = 0;
    for (int Y = p0.y; Y >= p1.y; --Y)
    {
        line_rough(memo1[i][0], Y, memo1[i][1], Y, image, color);
        i++;
    }

		// 计算直线Y与下半块三角形的交点
    for (int Y = p1.y; Y >= p2.y; --Y)
    {
        memo2.push_back({ (Y - b_p1p2) / k_p1p2 , (Y - b_p0p2) / k_p0p2 });
    }
		// 使用两点绘制算法去绘制三角形
    j = 0;
    for (int Y = p1.y; Y >= p2.y; --Y)
    {
        line_rough(memo2[j][0], Y, memo2[j][1], Y, image, color);
        j++;
    }
}

int main()
{
    TGAImage image(width, height, TGAImage::RGB);

    Model* model = NULL;
    model = new Model("african_head.obj");

    for (int i = 0; i < model->nfaces(); i++) {
        vector<int> face = model->face(i);
        Vec2i screen_coords[3];
        for (int j = 0; j < 3; j++) {
            Vec3f world_coords = model->vert(face[j]);
            screen_coords[j] = Vec2i((world_coords.x + 1.) * width / 2., (world_coords.y + 1.) * height / 2.);
        }
        saomiaoxiansuanfa_160802192023(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(rand() % 255, rand() % 255, rand() % 255, 255));
    }
    image.write_tga_file("african_head_saomiaoxian.tga");

}
#pragma endregion

这段代码实现了一个扫描线算法,用于在给定的三角形内绘制水平线段。首先将三角形的三个点按照y值的大小排序,然后分别求出三条直线的方程式,将每个y坐标对应的两个x坐标保存在memo1和memo2两个二维数组中,最后依次使用line_rough函数绘制每条水平线段。该算法进行了两次优化:一是将绘制算法从bresenham算法改为了line_rough,二是引入了memo1和memo2两个数组,避免了重复的计算。

源模型:

african_head.obj

运行结果图片:

african_head_saomiaoxian.tga