📜  计算机图形学Gouraud底纹(1)

📅  最后修改于: 2023-12-03 15:41:39.610000             🧑  作者: Mango

计算机图形学Gouraud底纹

简介

Gouraud底纹是计算机图形学中用于模拟光照效果的一种方法。通过在三维模型的表面上分别计算每个顶点的颜色,并根据三角形内部的线性插值来计算出每个像素的颜色,从而实现光照效果。

原理

Gouraud底纹是通过计算每个顶点的颜色(以及表面法线向量、环境光、漫反射光、镜面反射光等信息),并根据三角形内部的线性插值来计算每个像素的颜色。因为计算是在顶点级别进行的,所以需要存储每个顶点的信息(即各种光照参数)。

具体来说,Gouraud底纹的计算过程如下:

  1. 对每个三角形的各个顶点进行光照计算,得到各自的颜色;
  2. 对三角形内部的每个像素进行线性插值,得到像素的颜色。

其中,线性插值的公式如下:

$$ C_i = (1 - w - u)C_1 + uC_2 + wC_3 $$

其中,$C_1, C_2, C_3$ 为三个顶点的颜色,$w, u$ 分别代表像素距离 $C_1$ 和 $C_2$ 的距离占三角形边长的比例。

实现

Gouraud底纹的实现主要分为两个步骤:

  1. 光照计算。对每个顶点进行光照计算,并将计算得到的颜色存储在顶点的数据结构中;
  2. 底纹生成。对每个像素进行线性插值,得到像素的颜色。具体来说,对于每个像素,首先需要确定它属于哪个三角形,然后根据线性插值公式计算出颜色值。最后,将颜色值赋给像素。

以下是C++代码片段,用于计算每个顶点的颜色:

struct Vertex {
    Vector3f position;
    Vector3f normal;
    Vector3f color;
};

void compute_vertex_color(Vertex& v, const Vector3f& ambient, const Vector3f& light_pos, const Vector3f& light_color) {
    // compute ambient color
    Vector3f ambient_color = v.color * ambient;

    // compute diffuse color
    Vector3f to_light = (light_pos - v.position).normalized();
    float diffuse = max(v.normal.dot(to_light), 0.0f);
    Vector3f diffuse_color = v.color * light_color * diffuse;

    // add ambient and diffuse color
    v.color = ambient_color + diffuse_color;
}

以下是C++代码片段,用于生成底纹:

void generate_gouraud_mesh(const Mesh& mesh, const Vector3f& ambient, const Vector3f& light_pos, const Vector3f& light_color, std::vector<unsigned char>& image) {
    // create a buffer for storing vertex colors
    std::vector<Vector3f> vertex_colors(mesh.vertices.size());

    // compute colors for all vertices
    for (int i = 0; i < mesh.vertices.size(); i++) {
        compute_vertex_color(mesh.vertices[i], ambient, light_pos, light_color);
        vertex_colors[i] = mesh.vertices[i].color;
    }

    // iterate over all triangles and generate pixels
    for (int i = 0; i < mesh.triangles.size(); i++) {
        // get triangle vertices and colors
        const Triangle& tri = mesh.triangles[i];
        const Vector3f& c1 = vertex_colors[tri.i1];
        const Vector3f& c2 = vertex_colors[tri.i2];
        const Vector3f& c3 = vertex_colors[tri.i3];

        // calculate triangle normal
        Vector3f normal = (tri.v2 - tri.v1).cross(tri.v3 - tri.v1).normalized();

        // calculate triangle bounding box
        Vector2i min_pos, max_pos;
        compute_bounding_box(tri, min_pos, max_pos, image.width(), image.height());

        // iterate over pixels inside the bounding box
        for (int y = min_pos.y(); y <= max_pos.y(); y++) {
            for (int x = min_pos.x(); x <= max_pos.x(); x++) {
                // check if pixel is inside triangle
                Vector2f point(x + 0.5f, y + 0.5f);
                float w = calculate_barycentric_weight(point, tri.v1, tri.v2, tri.v3);
                if (w >= 0 && w <= 1) {
                    // calculate pixel color using Gouraud shading
                    float u = calculate_barycentric_weight(point, tri.v1, tri.v2, tri.v3);
                    Vector3f pixel_color = (1 - w - u) * c1 + u * c2 + w * c3;

                    // set pixel color
                    int index = x + y * image.width();
                    image[index * 3 + 0] = static_cast<unsigned char>(255 * pixel_color.x());
                    image[index * 3 + 1] = static_cast<unsigned char>(255 * pixel_color.y());
                    image[index * 3 + 2] = static_cast<unsigned char>(255 * pixel_color.z());
                }
            }
        }
    }
}
结束语

Gouraud底纹是一种简单有效的计算机图形学技术,可以用于模拟光照效果,使3D模型看起来更加真实。但它也有一些缺点,例如需要存储每个顶点的光照信息,且线性插值可能会导致细节丢失。因此,对于一些需要更精细光照效果的场景,我们可以考虑使用其他方法,例如Phong底纹、Blinn-Phong底纹等。