定义变量

cube: 六个面,每个面由三个点确定,第四个是面的法向量(朝向外)

face: 六个面的图案

float cube[6][4][3] = {
    {{-0.5, -0.5, 0.5}, {0.5, -0.5, 0.5}, {-0.5, 0.5, 0.5}, {0.0, 0.0, 1.0}},    // UP
    {{-0.5, -0.5, 0.5}, {-0.5, -0.5, -0.5}, {-0.5, 0.5, 0.5}, {-1.0, 0.0, 0.0}}, // BEHIND->LEFT 
    {{-0.5, -0.5, 0.5}, {-0.5, -0.5, -0.5}, {0.5, -0.5, 0.5}, {0.0, -1.0, 0.0}}, // LEFT->FRONT
    {{-0.5, 0.5, 0.5}, {0.5, 0.5, 0.5}, {-0.5, 0.5, -0.5}, {0.0, 1.0, 0.0}},     // RIGHT->BEHIND
    {{0.5, -0.5, 0.5}, {0.5, -0.5, -0.5}, {0.5, 0.5, 0.5}, {1.0, 0.0, 0.0}},     // FRONT->RIGHT
    {{-0.5, -0.5, -0.5}, {0.5, -0.5, -0.5}, {-0.5, 0.5, -0.5}, {0.0, 0.0, -1.0}} // DOWN
};
int face[6][3][3] = {
    {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}, // UP 1
    {{0, 0, 1}, {0, 0, 0}, {1, 0, 0}}, // BEHIND 2
    {{0, 0, 1}, {0, 1, 0}, {1, 0, 0}}, // LEFT 3
    {{1, 0, 1}, {0, 0, 0}, {1, 0, 1}}, // RIGHT 4
    {{1, 0, 1}, {0, 1, 0}, {1, 0, 1}}, // FRONT 5
    {{1, 0, 1}, {1, 0, 1}, {1, 0, 1}}  // DOWN 6
};

旋转矩阵

void rotate(float *x_, float *y_, float *z_, float ux, float uy, float uz, float angle) {
    float len = hypot(ux, uy, uz);
    ux /= len, uy /= len, uz /= len;
    float x = *x_, y = *y_, z = *z_;
    float c = cos(rad), s = sin(rad);
    float m00 = c + ux * ux * (1 - c);
    float m01 = ux * uy * (1 - c) - uz * s;
    float m02 = ux * uz * (1 - c) + uy * s;
    float m10 = uy * ux * (1 - c) + uz * s;
    float m11 = c + uy * uy * (1 - c);
    float m12 = uy * uz * (1 - c) - ux * s;
    float m20 = uz * ux * (1 - c) - uy * s;
    float m21 = uz * uy * (1 - c) + ux * s;
    float m22 = c + uz * uz * (1 - c);
    *x_ = m00 * x + m01 * y + m02 * z;
    *y_ = m10 * x + m11 * y + m12 * z;
    *z_ = m20 * x + m21 * y + m22 * z;
}
void rotateAll(float ux, float uy, float uz, float angle) {
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 4; j++) {
            rotate(&cube[i][j][0], &cube[i][j][1], &cube[i][j][2], ux, uy, uz, angle);
        }
    }
}

计算旋转矩阵

rotMatrix[x_, y_, z_, theta_] := Module[
  {normalizedVector = Normalize[{x, y, z}], c = Cos[theta], s = Sin[theta],},
  With[{nx = normalizedVector[[1]], ny = normalizedVector[[2]], nz = normalizedVector[[3]]},
   {{c + nx^2*(1 - c), nx*ny*(1 - c) - nz*s, nx*nz*(1 - c) + ny*s},
    {nx*ny*(1 - c) + nz*s, c + ny^2*(1 - c), ny*nz*(1 - c) - nx*s},
    {nx*nz*(1 - c) - ny*s, ny*nz*(1 - c) + nx*s, c + nz^2*(1 - c)}}]]
import math
from sympy import symbols, cos, sin, Matrix
 
x, y, z, theta = symbols("x y z θ")
x, y, z = (1, 1, 0)
len = math.sqrt(x**2 + y**2 + z**2)
x /= len
y /= len
z /= len
c = cos(theta)
s = sin(theta)
rotation_matrix = Matrix(
    [
        [c + x**2 * (1 - c), x * y * (1 - c) - z * s, x * z * (1 - c) + y * s],
        [x * y * (1 - c) + z * s, c + y**2 * (1 - c), y * z * (1 - c) - x * s],
        [x * z * (1 - c) - y * s, y * z * (1 - c) + x * s, c + z**2 * (1 - c)],
    ]
)
print(rotation_matrix)

渲染函数

预处理旋转到一个角度。

float time = 0;
rotateAll(0, 1, 1, 45);
while (1) {
    time = time + 0.05;
    // ...
}

Z_buffer 和 output

// 定义 z_buffer 深度缓冲区
float z_buffer[screen_height + 1][screen_width + 1];
for (int i = 0; i <= screen_height; i++)
    for (int j = 0; j <= screen_width; j++)
        z_buffer[i][j] = -100;
// 定义屏幕输出
char output[screen_height + 1][screen_width + 1];
memset(output, ' ', sizeof(output));

计算投影

通过面内基向量的线性组合,面内的每个点 。每个点和面的法向量绕y轴旋转time(rad)

根据投影公式

计算出在平面 ,视点为 的投影 ,根据投影点更新z_buffer

判断面法向量的z分量,如果为正,则可以看见,根据面的法向量z分量设置不同亮度的字符,注意特殊判断点是否在空心圆内,在圆内亮度为0。

for (int i = 0; i < 6; i++) {
    for (float u = 0; u < 1; u = u + 0.01)
        for (float v = 0; v < 1; v = v + 0.01) {
            // 基向量m
            float m_x = (cube[i][1][0] - cube[i][0][0]);
            float m_y = (cube[i][1][1] - cube[i][0][1]);
            float m_z = (cube[i][1][2] - cube[i][0][2]);
            // 基向量n
            float n_x = (cube[i][2][0] - cube[i][0][0]);
            float n_y = (cube[i][2][1] - cube[i][0][1]);
            float n_z = (cube[i][2][2] - cube[i][0][2]);
            // 面内每一个坐标(x,y,z)
            float x = m_x * u + n_x * v + cube[i][0][0];
            float y = m_y * u + n_y * v + cube[i][0][1];
            float z = m_z * u + n_z * v + cube[i][0][2];
            // 绕y轴旋转time
            float rotation_x = cos(time) * x - sin(time) * z;
            float rotation_y = y;
            float rotation_z = sin(time) * x + cos(time) * z;
            // 法向量旋转time
            float normal_x = (cube[i][3][0]) * cos(time) - sin(time) * (cube[i][3][2]);
            float normal_y = cube[i][3][1];
            float normal_z = (cube[i][3][0]) * sin(time) + cos(time) * (cube[i][3][2]);
            // 投影到屏幕
            int screen_x = (rotation_x / (1 - rotation_z / cc) + 1) / 2 * screen_width;
            int screen_y = (rotation_y / (1 - rotation_z / cc) + 1) / 2 * screen_height;
            float screen_z = rotation_z / (1 - rotation_z / cc);
 
            float L = normal_z;
            // 更新z_buffer
            if (z_buffer[screen_y][screen_x] < screen_z) {
                z_buffer[screen_y][screen_x] = screen_z;
                // 法向量z分量>0,可以看到
                if (L > 0) {
                    if (judgeCircle(i, u, v))
                        L = 0;
                    else
                        L = (L + 0.1) * sqrt(2);
                    int luminance_index = L * 8;
                    if (luminance_index > 11)
                        luminance_index = 11;
                    output[screen_y][screen_x] = ".,-~:;=!*#$@"[luminance_index];
                }
            }
        }
}

打印字符

for (int j = screen_height; j >= 0; j--) {
    putchar('|');
    for (int i = 0; i <= screen_width; i++)
        putchar(output[j][i]);
    putchar('|');
    putchar('\n');
}
usleep(15000);
printf("\x1b[%dA", screen_height + 1);