https://haqr.eu/tinyrenderer/shading/

Refactor

2026-06-27_08-58-50.png

首先回顾渲染管线,目前我们有了 Model View Perspective Projection Viewport zbuffer draw_trangle 等等,我们把文件拆分一下,一些不会再修改的内容放入一个 gl Module 中。把我们顶点坐标变换的部分放在 Vertex Shader 中,Fragment Shader 部分就应用一下 Random Color 就可以。

060.png

060_rotate_180.png

main :: () {
    model = rotate_y(30.0);
    view = lookat(eye, center, up);
    projection = make_perspective(radian_to_angle(45), 1.0, 0.1, 100.0);
    viewport   = make_viewport(0, 0, width, height);
    zbuffer    = make_zbuffer(width, height);

    load_obj("./assets/african_head.obj", *vertices, *indices);
    defer array_free(vertices);
    defer array_free(indices);

    framebuffer := make_image(width, height, BLACK);
    defer free_image(*framebuffer);

    shader := Shader.{vertex_pass, fragment_pass};

    for indices {
        triangle := make_triangle(vertices, it, shader);         // vertex shader
        draw_triangle(*framebuffer, *zbuffer, triangle, shader); // fragment shasder
    }

    ppm.write_p3("061.ppm", *framebuffer);
}

Phong model

简化版的全局光照: Ambient + Duffuse + Specular

061.png

环境光 + 漫反射 + 高光

环境光 + 漫反射 + 高光

GAMES 101 07 Shading 1 (Illumination, Shading)

$$ \begin{aligned} L & =L_a+L_d+L_s \\ & =k_a I_a+k_d\left(I / r^2\right) \max (0, \mathbf{n} \cdot \mathbf{l})+k_s\left(I / r^2\right) \max (0, \mathbf{n} \cdot \mathbf{h})^p \end{aligned} $$

  1. 环境光:恒定的背景光,直接调整一个百分比就可以。

  2. 漫反射

    image.png

    计算三角形ABC的法向量 $\vec{n} =\frac{\overrightarrow{AB} \times \overrightarrow{AC}}{\left\lVert \overrightarrow{AB} \times \overrightarrow{AC} \right\rVert}$, $\cos \alpha = \vec{n} \cdot \vec{l}$

    Diffuse = $\text{max}(0, \vec{n} \cdot \vec{l})$

    image.png

  3. 高光项

    image.png

    首先图里的向量都是单位向量, $\vec{v}$ 是片元到摄像机。$\vec{l}$ 是入射平行光的方向(反过来了,把向量起点放在了入射点位置), $\vec{r}$ 是高光反射的方向。向量加法很容易得到 $\vec{l} + \vec{r} = 2\vec{n}(\vec{n} \cdot \vec{l})$,那么 $\vec{r} = 2\vec{n}(\vec{n} \cdot \vec{l}) - \vec{l}$。

    specular := pow(max(dot(r, v), 0.0), shininess);

    其中 shininess = 8 -> 宽而柔和的高光 = 32 -> 比较明显的高光 = 128 -> 很小、很亮、很尖锐的高光

    exponent.png

fragment_pass :: () -> Vector4 {
    ambient := 0.3;
    l := l_dir; using triangle_world;
    n := normalize(cross(b - a, c - a));     // triangle normal
    r := normalize(n * dot(n, l) * 2.0 - l); // reflected light direction
    diffuse := max(0.0, dot(n, l));
    specular := pow(max(r.z, 0.0), 35);

    color := make_vector4(min(1.0, ambient + 0.4 * diffuse + 0.9 * specular));
    color.w = 1.0;
    return color;
}