基于OpenGL与C实现的神经细胞自动机无法正确渲染图形问题求助
问题分析与修复方案
我仔细看了你的代码,问题主要出在卷积计算的索引对应错误、直接修改原网格导致状态污染,还有display函数的更新逻辑混乱这几个地方,直接导致滑翔机的行为完全偏离康威生命游戏的规则。下面逐个拆解问题并给出修复方案:
1. 卷积滤波器的索引对应错误
你的CVFilter函数里,网格邻居和filter的索引完全对应错了。比如你写的grid[y+1][x+1]*filter[2][2],y+1是当前细胞的上方邻居,对应filter的第0行(3x3 filter的[0][0]是左上、[0][1]是上中、[0][2]是右上),但你误用了filter[2][2](右下),行方向完全搞反了,这会让卷积计算结果完全错误。
2. 直接修改原网格导致状态污染
你在计算新细胞状态时,直接把结果写回原grid数组,这样计算后面的细胞时,会用到已经更新过的邻居状态,而不是当前帧的初始状态。这相当于打乱了生命游戏的“同步更新”规则,必须用临时数组存储新状态,所有细胞计算完成后再替换原网格。
3. Display函数的更新逻辑错误
你的display函数只有frame == 1时才调用CVFilter,之后就不再更新网格状态了;而且sleep(1000)的逻辑明显是笔误(会让程序卡住1000秒),加上glutPostRedisplay()会不断触发display,frame会持续递增,最终只有第一帧做了状态更新,之后完全停滞。
修复后的完整代码
#include <stdio.h> #include <windows.h> #include <GL/glut.h> #define sleep(x) Sleep(1000 * (x)) #define sizeX 10 #define sizeY 10 #define wallSize 40 int frame = 0; double grid[sizeY][sizeX]; double filter[3][3] = {{1, 1, 1}, {1, 9, 1}, {1, 1, 1}}; int width = 1000; int height = 500; double activation(double x){ // 对应康威规则: // x=3 → 死细胞周围3个活邻居 → 复活 // x=11 → 活细胞周围2个活邻居(9+2)→ 存活 // x=12 → 活细胞周围3个活邻居(9+3)→ 存活 if(x == 3 || x == 11 || x == 12){ return 1; } else { return 0; } } void CVFilter(){ int x,y; double cv; // 创建临时数组存储新状态,避免污染原网格 double newGrid[sizeY][sizeX]; // 复制原网格的边界(边界细胞不参与规则计算,保持初始状态) for(x = 0; x < sizeX; x++){ newGrid[0][x] = grid[0][x]; newGrid[sizeY-1][x] = grid[sizeY-1][x]; } for(y = 0; y < sizeY; y++){ newGrid[y][0] = grid[y][0]; newGrid[y][sizeX-1] = grid[y][sizeX-1]; } // 计算中间细胞的新状态 for(y=1; y < sizeY-1; y++){ for(x=1; x < sizeX-1; x++){ // 修正filter索引对应关系:上方邻居→filter第0行,当前行→第1行,下方→第2行 cv = grid[y+1][x-1]*filter[0][0] + grid[y+1][x]*filter[0][1] + grid[y+1][x+1]*filter[0][2] + grid[y][x-1]*filter[1][0] + grid[y][x]*filter[1][1] + grid[y][x+1]*filter[1][2] + grid[y-1][x-1]*filter[2][0] + grid[y-1][x]*filter[2][1] + grid[y-1][x+1]*filter[2][2]; newGrid[y][x] = activation(cv); } } // 将临时数组的新状态同步回原网格 for(y=0; y < sizeY; y++){ for(x=0; x < sizeX; x++){ grid[y][x] = newGrid[y][x]; } } } void drawGrid(){ int x,y; double rgb; glPointSize(wallSize); glBegin(GL_POINTS); for(y=0; y < sizeY; y++){ for(x=0; x < sizeX; x++){ rgb = grid[y][x]; glColor3f(rgb, rgb, rgb); glVertex2i((x*wallSize), (y*wallSize)); } } glEnd(); } void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 第一帧显示初始状态,之后每帧更新一次状态 if(frame > 0){ CVFilter(); } drawGrid(); glutSwapBuffers(); glutPostRedisplay(); sleep(1); // 控制帧率,每秒1帧,方便观察滑翔机移动 frame++; } void init(){ glClearColor(0.3, 0.3, 0.3, 0); gluOrtho2D(0, width, height, 0); } void main(int argc, char** argv){ // 初始化标准滑翔机形状 grid[3][5] = 1; grid[4][5] = 1; grid[5][5] = 1; grid[5][4] = 1; grid[4][3] = 1; glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(width, height); glutCreateWindow("OpenGL Game of Life"); init(); glutDisplayFunc(display); glutMainLoop(); }
额外说明
- 我保留了你原本的激活函数逻辑(因为它确实对应康威规则的卷积值判断),只是修正了卷积计算的错误
- 现在每帧都会同步更新所有细胞状态,避免了状态污染问题
- 调整了
sleep逻辑,让每帧间隔1秒,方便清晰观察滑翔机的移动轨迹
修改后,滑翔机应该会按照康威生命游戏的规则,稳定地向右下方移动了。
内容的提问来源于stack exchange,提问作者Saw
相关产品推荐
相关产品推荐

