【JUEGOS101】Resumen del estudio de tareas 2

Esta serie de blogs es para registrar los problemas y pensamientos que encontró el autor mientras estudiaba el curso GAMES101.


1. Preguntas básicas

El propósito de esta tarea es familiarizarnos con las operaciones relacionadas con la rasterización de triángulos. De la Tarea 2.pdf , podemos saber que la tarea de esta tarea es completar las dos funciones

  • static bool insideTriangle(): Comprueba si el punto está dentro del triángulo.
  • rasterize_triangle(): Realizar el algoritmo de rasterización de triángulos
  1. En primer lugar, necesitamos copiar la función get_projection_matrix() de la tarea 1 a la función correspondiente de la tarea 2. Un punto a tener en cuenta es comentar la zNear = -zNear;suma zFar = -zFar;, de lo contrario, el triángulo final se invertirá, la razón es porque Yan As the el profesor dijo en clase, al hacer pruebas de profundidad, necesitamos cambiar un concepto, es decir, aquellos con valores de profundidad pequeños están cerca de nosotros, y aquellos con valores de profundidad grandes están lejos de nosotros, pero la función de correlación de la tarea 0 es que nos movemos hacia el negativo del eje z . Mirando en la dirección, es decir, el número con un valor de profundidad mayor en realidad está más cerca de nosotros.

  2. Luego explique la función insideTriangle() y analice primero el significado de los parámetros de la función como de costumbre:

    • x/y: Indica las coordenadas x/y del punto de prueba requerido
    • _v: Al observar el archivo Triangle.cpp , podemos ver que _v es un vector tridimensional, pero cada dimensión es un vector tridimensional, que representa las coordenadas de los tres vértices del triángulo.
      Triangle::Triangle() {
              
              
          v[0] << 0,0,0;
          v[1] << 0,0,0;
          v[2] << 0,0,0;
      
          color[0] << 0.0, 0.0, 0.0;
          color[1] << 0.0, 0.0, 0.0;
          color[2] << 0.0, 0.0, 0.0;
      
          tex_coords[0] << 0.0, 0.0;
          tex_coords[1] << 0.0, 0.0;
          tex_coords[2] << 0.0, 0.0;
      }
      

    El profesor Yan mencionó en su conferencia que una forma de juzgar si un punto está en un triángulo es usar este punto para conectar con los tres vértices del triángulo para formar tres vectores, y luego conectar estos tres vértices para formar tres vectores, y luego deje que los vectores correspondientes a los vértices se multipliquen entre sí, y se obtendrán tres números.Si los tres números tienen el mismo signo, significa que el punto está dentro del triángulo, por lo que puede escribir directamente el triángulo interior () función :

    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]
        Eigen::Vector3f point(x, y, 0);
        Eigen::Vector3f side1, side2, side3;
        side1 << _v[1] - _v[0];
        side2 << _v[2] - _v[1];
        side3 << _v[0] - _v[2];
        // calculate the cross of two vector
        float z1 = ((point - _v[0]).cross(side1)).z();
        float z2 = ((point - _v[1]).cross(side2)).z();
        float z3 = ((point - _v[2]).cross(side3)).z();
        // Determine if the sybol is the same
        if ((z1 > 0 && z2 > 0 && z3 > 0) || (z1 < 0 && z2 < 0 && z3 < 0))
            return true;
        return false;
    }
    
  3. Luego analice la función rasterize_triangle() , donde el parámetro formal tes el tipo Triangle , que es el código en el archivo Triangle.cpp mencionado anteriormente .
    El profesor Yan dijo que hay una forma de rasterizar, que es enmarcar un cubo que ocupa el espacio del triángulo, es decir, el cubo simplemente rodea el triángulo y luego atraviesa cada píxel en el cubo para probar la profundidad. tiñe este píxel.

    void rst::rasterizer::rasterize_triangle(const Triangle& t) {
          
          
        auto v = t.toVector4();
        
        // TODO : Find out the bounding box of current triangle.
        // iterate through the pixel and find if the current pixel is inside the triangle
    
        // If so, use the following code to get the interpolated z value.
        //auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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;
    
        // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
        Eigen::Vector2f min_p, max_p;
        min_p.x() = MIN(MIN(v[0].x(), v[1].x()), v[2].x());
        min_p.y() = MIN(MIN(v[0].y(), v[1].y()), v[2].y());
        max_p.x() = MAX(MAX(v[0].x(), v[1].x()), v[2].x());
        max_p.y() = MAX(MAX(v[0].y(), v[1].y()), v[2].y());
        
        for (int i = min_p.x(); i <= max_p.x(); i++) {
          
          
            for (int j = min_p.y(); j <= max_p.y(); j++) {
          
          
                if(insideTriangle(i, j, t.v)) {
          
          
                    auto[alpha, beta, gamma] = computeBarycentric2D(i, j, 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;
                    if (z_interpolated < depth_buf[get_index(i, j)]) {
          
          
                        set_pixel(Eigen::Vector3f((float)i, (float)j, z_interpolated), t.getColor());
                        depth_buf[get_index(i, j)] = z_interpolated;
                    }
                }
            }
        }
    }
    

    Entre ellos min_p, y max_prepresentan los valores mínimo y máximo de x/y en el triángulo, respectivamente, por lo que el cubo rodeado por estos dos puntos puede rodear todo el triángulo. De esta manera, se puede atravesar todo el cubo y se puede juzgar cada píxel en el espacio.

    El bucle de doble capa en el código anterior foratraviesa todo el espacio, y cada vez que se atraviesa un píxel, debe juzgar si el punto está en el triángulo. Si no está en el triángulo, no es necesario realizar la operación de teñido. en absoluto, porque nuestro propósito es teñir todo el triángulo.

    Luego, estas cuatro líneas de código obtienen el método de valor de profundidad por diferencia, porque el contenido relevante no se ha cubierto en el curso, por lo que el marco ya procesó esta parte, solo llámelo directamente.

    auto[alpha, beta, gamma] = computeBarycentric2D(i, j, 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;
    

    Finalmente, se juzga si el valor de profundidad es menor que la profundidad del píxel. Si es menor, significa que el triángulo está más cerca de nosotros y cubrirá el triángulo detrás de él. Por lo tanto, es necesario actualizar el color de el píxel y, a continuación, actualice la profundidad del píxel.

    if (z_interpolated < depth_buf[get_index(i, j)]) {
          
          
    	set_pixel(Eigen::Vector3f((float)i, (float)j, z_interpolated), t.getColor());
    	depth_buf[get_index(i, j)] = z_interpolated;
    }
    

    Si todo va bien, la siguiente imagen debería aparecer después de ejecutar la función run2.sh
    inserte la descripción de la imagen aquí
    : Si acerca la imagen, puede encontrar Jaggies!irregularidades evidentes Antialiasing.
    inserte la descripción de la imagen aquí

Dos, mejorar la pregunta.

1. Suavizado con SMAA

El principio de MSAA (ver GAMES101_Lecture_06.pdf página 63 para más detalles) es dividir un píxel en cuatro puntos de 2 × 2. Hay varios puntos en el triángulo y el color se promedia, de modo que el límite del triángulo se puede difuminar para lograr el propósito de anti-aliasing.

if (MSAA) {
    
    
    std::vector<Eigen::Vector2f> super_sample_step {
    
    
        {
    
    0.25, 0.25},
        {
    
    0.75, 0.25},
        {
    
    0.25, 0.75},
        {
    
    0.75, 0.75},
    };
    for (int i = min_p.x(); i <= max_p.x(); i++) {
    
    
        for (int j = min_p.y(); j <= max_p.y(); j++) {
    
    
            int cnt = 0;
            for (int k = 0; k < 4; k++) {
    
    
                if (insideTriangle((float)i + super_sample_step[k][0], (float)j + super_sample_step[k][1], t.v)) {
    
    
                    cnt++;
                }
            }
            if (cnt != 0) {
    
    
                auto[alpha, beta, gamma] = computeBarycentric2D(i, j, 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;
                if(z_interpolated < depth_buf[get_index(i, j)]){
    
    
                    set_pixel(Eigen::Vector3f((float)i, (float)j, z_interpolated), t.getColor() * cnt / 4.0);
                    depth_buf[get_index(i, j)] = z_interpolated;
                }

            }
        }
    }
}

La ejecución de este código muestra que el efecto suavizado se logra, pero aparecen bordes negros anormales. Después de mucho tiempo de revisión y análisis de datos, sabemos que la razón de la aparición del borde negro es que cuando el triángulo verde se representa por primera vez, el color se promedia con verde y negro en el borde, y la proporción de píxeles negros es relativamente grande en el borde. Por lo tanto, el borde del triángulo verde tendrá un borde negro más oscuro. Al renderizar el triángulo azul, debido a que la distancia del triángulo azul está muy lejos, falla la prueba de profundidad, por lo que terminará con un borde negro en el borde del triángulo verde.
inserte la descripción de la imagen aquí

2. Optimizar el funcionamiento de SMAA

De hecho, me culpo por no leer con atención el contenido de Assignment2.pdf El profesor Yan ya nos ha recordado que cada muestra en un píxel debe mantener su propio valor de profundidad, es decir, cada píxel debe mantener una lista de muestras. De esta forma, cuando finalmente se represente el triángulo azul, se promediará con el borde negro del triángulo verde, haciendo que la transición de color sea más suave.

Primero, cree una matriz de vectores vectoriales bidimensionales para cada píxel para mantener la lista de muestra de este píxel, y cree la matriz vectorial vectorial bidimensional correspondiente en rasterizer.hpp :

bool MSAA = true;
std::vector<Eigen::Vector3f> frame_buf;
std::vector<std::vector<Eigen::Vector3f>> sample_list_frame_buf;

std::vector<float> depth_buf;
std::vector<std::vector<float>> sample_list_depth_buf;

Entre ellos frame_buf, ya depth_bufexisten en el marco original, sample_list_frame_bufy sample_list_depth_bufson arreglos de mantenimiento creados por nosotros mismos. Una vez completada la creación, debe cambiar la función de inicialización correspondiente y la función de limpieza en rasterizer.cpp :rasterizer()clear()

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 (MSAA) {
    
    
            std::fill(sample_list_frame_buf.begin(), sample_list_frame_buf.end(), std::vector<Eigen::Vector3f>(4, {
    
    0, 0, 0}));
        }
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
    
    
        std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
        if (MSAA) {
    
    
            std::fill(sample_list_depth_buf.begin(), sample_list_depth_buf.end(), std::vector<float>(4, std::numeric_limits<float>::infinity()));
        }
    }
}

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    
    
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);
    if (MSAA) {
    
    
        sample_list_frame_buf.resize(w * h);
        for (auto& row : sample_list_frame_buf) {
    
    
            row.resize(4);
        }
        sample_list_depth_buf.resize(w * h);
        for (auto& row : sample_list_depth_buf) {
    
    
            row.resize(4);
        }
    }
}

Finalmente, cambie el código MSAA para que la prueba de profundidad, la actualización sample_list_frame_bufy sample_list_depth_bufla matriz de vectores se realicen cada vez que se juzguen los cuatro puntos, y finalmente se realice el promedio de color de los cuatro puntos, para que se pueda lograr una transición de borde más suave.

    if (MSAA) {
    
    
        std::vector<Eigen::Vector2f> super_sample_step {
    
    
            {
    
    0.25, 0.25},
            {
    
    0.75, 0.25},
            {
    
    0.25, 0.75},
            {
    
    0.75, 0.75},
        };
        for (int i = min_p.x(); i <= max_p.x(); i++) {
    
    
            for (int j = min_p.y(); j <= max_p.y(); j++) {
    
    
                int cnt = 0;
                float minDepth = FLT_MAX;
                for (int k = 0; k < 4; k++) {
    
    
                    if (insideTriangle((float)i + super_sample_step[k][0], (float)j + super_sample_step[k][1], t.v)) {
    
    
                        auto[alpha, beta, gamma] = computeBarycentric2D(i, j, 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;
                        if (z_interpolated < minDepth) {
    
    
                            minDepth = z_interpolated;
                        }
                        if (z_interpolated < sample_list_depth_buf[get_index(i, j)][k]) {
    
    
                            sample_list_depth_buf[get_index(i, j)][k] = z_interpolated;
                            sample_list_frame_buf[get_index(i, j)][k] = t.getColor();
                        }
                        cnt++;
                    }
                }
                if (cnt != 0) {
    
    
                    Eigen::Vector3f color = {
    
    0, 0, 0};
                    for (int k = 0; k < 4; k++) {
    
    
                        color += sample_list_frame_buf[get_index(i, j)][k];
                    }
                    set_pixel(Eigen::Vector3f((float)i, (float)j, minDepth), color / 4.0);
                    depth_buf[get_index(i, j)] = minDepth;
                }
            }
        }
    }

Obtenga el resultado después de ejecutar:
inserte la descripción de la imagen aquí
hasta ahora, el trabajo 2 se completó

Supongo que te gusta

Origin blog.csdn.net/qq_21891843/article/details/130656194
Recomendado
Clasificación