GAMES101 作业2

请用来检验答案是否正确

思路

  1. 判断点是否在三角形内

    若P点在ABC构成的三角形内,ABC中的任意一点与P点都在另外两点构成的线段的同一侧。假设AB为边,判断C、P点是否在同一侧,可通过AB与AP的叉积、AB与AC的叉积(叉积计算出垂直于这两个向量的向量),判断结果是否同方向来判断是否在同一侧(可以通过点积结果是否大于0)。

  2. 计算Bounds

    遍历三角形的三个点,取出最小和最大的x/y值,就是边界了。同时根据games101课上所说,将x,y分成一个个单位为1的格子,因此边界需要分别上下取整。

  3. 光栅化

    遍历格子中心点,根据案例给出的方法计算出深度值,和深度缓存比较,更小的值就更新深度缓存,设置颜色缓存。如果是开启SSAA,就遍历中心点时候,不直接用小格子的中心点,而是将单位格子再拆分成四个格子,分别计算四个小格子是否三角形内。最后取四个小格子的最小深度值来做深度测试。 这里需要注意的是,采样颜色时针对不在三角形内的小格子的部分,要使用帧缓存中的值来参与计算,否则会出现黑边的问题

发现开启ssaa后性能好低,后面有空看看这块有没有什么优化方法

Code

计算点是否在三角形内:

// 判断c、p点是否在ab边的同一侧
static bool sameSide(Vector3f p, Vector3f a, Vector3f b, Vector3f c)
{
    Vector3f ab = b - a;
    Vector3f ac = c - a;
    Vector3f ap = p - a;
    return (ab.cross(ac)).dot(ab.cross(ap)) >= 0;
}

static bool insideTriangle(float x, float y, const Vector3f* _v)
{   
    // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
    Vector3f pos = {x, y, 1};

    return sameSide(pos, _v[0], _v[1], _v[2]) &&
           sameSide(pos, _v[2], _v[0], _v[1]) &&
           sameSide(pos, _v[1], _v[2], _v[0]);
}

光栅化三角形:

// 获取三个里最小值
static int GetMin(float a, float b, float c){
    return floor(std::min(a, std::min(b,c)));
}
// 获取三个里最大值
static int GetMax(float a, float b, float c){
    return ceil(std::max(a, std::max(b,c)));
}

// Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    // TODO : Find out the bounding box of current triangle.
    int minx = GetMin(v[0][0], v[1][0], v[2][0]);
    int maxx = GetMax(v[0][0], v[1][0], v[2][0]);
    int miny = GetMin(v[0][1], v[1][1], v[2][1]);
    int maxy = GetMax(v[0][1], v[1][1], v[2][1]);
    
    // 是否开启反走样开关
    bool super_sampling = true;

    // iterate through the pixel and find if the current pixel is inside the triangle
    for (int x = minx; x <= maxx; x++)
    {
        for (int y = miny; y <= maxy; y++)
        {
            // 开启反走样计算
            if(super_sampling){
                int count = 0;
                float depth = std::numeric_limits<float>::infinity();
                Vector2f midCube[4]{
                    {0.25, 0.25},
                    {0.25, 0.75},
                    {0.75, 0.25},
                    {0.75, 0.75}
                };
                for (int i = 0; i < 4; i++)
                {
                    float posx = x + midCube[i][0];
                    float posy = y + midCube[i][1];
                    // insideTriangle 这里参数需要改为float或double,避免被强转int
                    if(insideTriangle(posx, posy, t.v)){
                        float alpha;
                        float beta;
                        float gamma;
                        auto zValue = computeBarycentric2D(posx, posy, t.v);
                        std::tie(alpha, beta, gamma) = zValue;
                        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;
                        depth = std::min(depth, z_interpolated);
                        count++;
                    }
                }
                if (count>0) {
                    int index = get_index(x,y);
                    if(depth < depth_buf[index]){
                        Vector3f point = {x, y , depth};
                        // 避免黑线问题
                        Vector3f color = t.getColor() * count / 4.0 + (4 - count) * frame_buf[index] / 4.0;
                        depth_buf[index] = depth;
                        set_pixel(point, color);
                    }
                }
            }
            else 
            {
                float posx = x + 0.5;
                float posy = y + 0.5;
                // If so, use the following code to get the interpolated z value.
                if(insideTriangle(posx, posy, t.v)){
                    float alpha;
                    float beta;
                    float gamma;
                    auto zValue = computeBarycentric2D(posx, posy, t.v);
                    std::tie(alpha, beta, gamma) = zValue;
                    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;
                    // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.

                    int index = get_index(x,y);
                    if(z_interpolated < depth_buf[index]){
                        Vector3f point = {x, y , z_interpolated};
                        Vector3f color = t.getColor();
                        depth_buf[index] = z_interpolated;
                        set_pixel(point, color);
                    }
                }
            }
            
        }
    }
}

未开启ssaa:
未开启ssaa
开启ssaa:
开启ssaa

原文地址:https://www.cnblogs.com/ruoh3kou/p/15522498.html