逛b站看到会转的甜甜圈,有点上头,自己拿蜥语言做个立方体玩玩

Render 3D Object In Terminal

直接看代码,注释比较详细

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#define size 51//规定屏幕大小
////cal_r用来算个模长
float cal_r(float x,float y,float z){
return sqrt(x*x+y*y+z*z);
}
char shade[]=".,;!o*m0&%@";//阴影字符集
int out_i[size][size];//记录亮度,映射到字符集
float dyn=4.0;//开合幅度
float a=8.0;//立方体边长
float delta=0.75;//遍历立方体的步长
float move=(size-1)/2;//从坐标原点偏移到屏幕中心所需的偏移量
float dis[size][size];//记录最小距离,由此判断遮罩关系
//cal_shade用来算个阴影
//Par{n_i是被选中相对不变的方向,相当于此方向穿过平面;n_j作为判定是上平面还是下平面的值,只可能是1或-1}
void cal_shade(float *n,int n_i,int n_j,float *ro_a,float *ro_b,float *ro_c,float *light,float rotate,int *out_i){
for(int i=0;i<3;i++)n[i]=i==n_i?n_j:0;//确定原始法向量n[]
float n_a[3]={n[0]*ro_a[0]+n[1]*ro_a[3]+n[2]*ro_a[6],
n[0]*ro_a[1]+n[1]*ro_a[4]+n[2]*ro_a[7],
n[0]*ro_a[2]+n[1]*ro_a[5]+n[2]*ro_a[8]};//法向量第一次旋转
float n_b[3]={n_a[0]*ro_b[0]+n_a[1]*ro_b[3]+n_a[2]*ro_b[6],
n_a[0]*ro_b[1]+n_a[1]*ro_b[4]+n_a[2]*ro_b[7],
n_a[0]*ro_b[2]+n_a[1]*ro_b[5]+n_a[2]*ro_b[8]};//法向量第二次旋转
float n_r[3]={n_b[0]*ro_c[0]+n_b[1]*ro_c[3]+n_b[2]*ro_c[6],
n_b[0]*ro_c[1]+n_b[1]*ro_c[4]+n_b[2]*ro_c[7],
n_b[0]*ro_c[2]+n_b[1]*ro_c[5]+n_b[2]*ro_c[8]};
//n_r[]作为绕三轴旋转rotate角度后的法向量
float result=(n_r[0]*light[0]+n_r[1]*light[1]+n_r[2]*light[2]+1.0)*0.5;//点积求反光强度(亮度),并映射到[0,1]
float co[3];//正方体的原始坐标,co意为coordinate
co[n_i]=dyn*cos(rotate*2);//make it 20% cooler
int co_a=n_i==0?1:n_i==1?0:0;
int co_b=n_i==0?2:n_i==1?2:1;//确定了index为n_i的坐标是不需要被遍历的
co[n_i]+=move/2;//无需遍历的方向偏移1/4个屏幕边长
for(co[co_a]=-a;co[co_a]<=a;co[co_a]+=delta)
for(co[co_b]=-a;co[co_b]<=a;co[co_b]+=delta){
int final=(int)(result*10.0);//把亮度int化
final=(final>10||final)<0?final>10?10:0:final;//保险,防止一些刁钻情况
float x_a=co[0]*ro_a[0]+co[1]*ro_a[3]+co[2]*ro_a[6];
float y_a=co[0]*ro_a[1]+co[1]*ro_a[4]+co[2]*ro_a[7];
float z_a=co[0]*ro_a[2]+co[1]*ro_a[5]+co[2]*ro_a[8];//坐标第一次旋转
float x_b=x_a*ro_b[0]+y_a*ro_b[3]+z_a*ro_b[6];
float y_b=x_a*ro_b[1]+y_a*ro_b[4]+z_a*ro_b[7];
float z_b=x_a*ro_b[2]+y_a*ro_b[5]+z_a*ro_b[8];//坐标第二次旋转
float x_r=x_b*ro_c[0]+y_b*ro_c[3]+z_b*ro_c[6];
float y_r=x_b*ro_c[1]+y_b*ro_c[4]+z_b*ro_c[7];
float z_r=x_b*ro_c[2]+y_b*ro_c[5]+z_b*ro_c[8];
//co_r作为绕三轴旋转rotate角度后的空间坐标
int cox=(int)(n_j*x_r+move);
int coy=(int)(n_j*z_r+move);//找到投影到屏幕上的点在哪
if(n_j*y_r<dis[cox][coy]){//只有发现更近的点才替换亮度
out_i[cox*size+coy]=final;
dis[cox][coy]=n_j*y_r;
}
}
}

int main(){
float n[3];//原始法向量n[]
float light[3]={-1,-1,1};//平行光向量
float length=cal_r(light[0],light[1],light[2]);
for(int i=0;i<3;i++)light[i]/=length;//单位化平行光向量
for(float rotate=0.0;;rotate+=0.05){//rotate为旋转角度,增量即旋转速度
for(int i=0;i<size;i++)
for(int j=0;j<size;j++){
out_i[i][j]=-1;
dis[i][j]=(float)(4.0*size);
//初始化亮度与距离
}
float cos_ro=cos(rotate),sin_ro=sin(rotate);
float ro_a[3][3]={{1.0,0,0},{0,cos_ro,-sin_ro},{0,sin_ro,cos_ro}};
float ro_b[3][3]={{cos_ro,0,sin_ro},{0,1.0,0},{-sin_ro,0,cos_ro}};
float ro_c[3][3]={{cos_ro,-sin_ro,0},{sin_ro,cos_ro,0},{0,0,1.0}};
//旋转矩阵,ro表rotate
for(int i=0;i<3;i++){//算个阴影,第三个参数控制对应面
cal_shade(&n[0],i,1,&ro_a[0][0],&ro_b[0][0],&ro_c[0][0],&light[0],rotate,&out_i[0][0]);
cal_shade(&n[0],i,-1,&ro_a[0][0],&ro_b[0][0],&ro_c[0][0],&light[0],rotate,&out_i[0][0]);
}
for(int i=0;i<size;i++){
for(int j=0;j<size;j++) out_i[i][j]!=-1?printf("%c%c",shade[out_i[i][j]],shade[out_i[i][j]]):printf(" ");
printf("\n");
}
printf("\x1b[2J");
printf("\x1b[H");
usleep(30000);
//跟着甜甜圈抄的,这样才能稳定住图像
}
}

Dev C++下则调用Windows.h来解决图像刷新的问题

1
2
3
4
5
6
7
8
#include <Windows.h>
void goto_O()//CSDN上抄的函数,刷新一快光标就飘来飘去,看起来像噪点一样
{
HANDLE hOut;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {0,0};
SetConsoleCursorPosition(hOut, pos);
}

最开始先写了个光源移动球

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <math.h>
#include <Windows.h>
void goto_xy()
{
HANDLE hOut;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {0,0};
SetConsoleCursorPosition(hOut, pos);
}
main(){
int size=45;
double g=0.01;
double move=(size-1)/2;
double r=(double(size)-4)/2.0;
double light[3]={-1.3,1,1};
double length=sqrt(light[0]*light[0]+light[1]*light[1]+light[2]*light[2]);

for(int i=0;i<3;i++){
light[i]=light[i]/length;
}
char shade[]=".;!o*m0&%@";
int out_i[size][size]={};
for(double rotate=0.0;;rotate+=0.05){
for(int i=0;i<size;i++){
for(int j=0;j<size;j++){
out_i[i][j]=-1;
}
}

double ro[3][3]={{1.0,0,0},{0,cos(rotate),-sin(rotate)},{0,sin(rotate),cos(rotate)}};
double light_r[3]={light[0]*ro[0][0]+light[1]*ro[1][0]+light[2]*ro[2][0],
light[0]*ro[0][1]+light[1]*ro[1][1]+light[2]*ro[2][1],
light[0]*ro[0][2]+light[1]*ro[1][2]+light[2]*ro[2][2]};
for(double a=0.0;a<3.15;a+=g){//alpha
for(double b=0.0;b<3.15;b+=g){//beta
double x=r*sin(b)*cos(a);
double y=r*sin(b)*sin(a);
double z=r*cos(b);
double result=(x/r*light_r[0]+y/r*light_r[1]+z/r*light_r[2]+1.0)*0.5;
int final=int(result*10.0-1);
/*
if(final>10||final<0){
if(final>10)final=10;
else if(final<0)final=0;
}
*/
int cx=int(x+move);
int cy=int(z+move);
if(final>out_i[cx][cy])//这种取最大亮度的写法明显是不好的,但这个例子特殊,用起来没问题
out_i[cx][cy]=final;
}
}
for(int i=0;i<size;i++){
for(int j=0;j<size;j++){
if(out_i[i][j]!=-1)
printf("%c%c",shade[out_i[i][j]],shade[out_i[i][j]]);
else
printf(" ");
}
printf("\n");
}
goto_xy();
}
}

附上甜甜圈的代码,写方块之前没仔细看过,所以我写的方块非常冗余(主要是想先自己试试

为了防止你们想看视频找不到传送门,Click Here

Comments

2021-04-19

⬆︎TOP