C语言结合SDL2实现3D立方体渲染异常求助
SDL2绘制3D立方体仅显示部分区域的问题排查与修复
我是C/C新手,参考C教程改用C语言编写SDL2程序绘制3D立方体,但代码仅显示立方体的部分区域,效果异常。已检查顶点坐标无误,多次重写代码仍无法解决问题,以下是完整代码:
原代码文件
main.c
#include "window.h" int main() { window_init(); window_startloop(); return 0; }
window.c
#include "window.h" #define false 0 #define true 1 void MultMatrixVec(vec3 *i, vec3 *o, mat4x4 *m) { o->x = i->x * m->m[0][0] + i->y * m->m[1][0] + i->z * m->m[2][0] + m->m[3][0]; o->y = i->x * m->m[0][1] + i->y * m->m[1][1] + i->z * m->m[2][1] + m->m[3][1]; o->z = i->x * m->m[0][2] + i->y * m->m[1][2] + i->z * m->m[2][2] + m->m[3][2]; float w = i->x * m->m[0][3] + i->y * m->m[1][3] + i->z * m->m[2][3] + m->m[3][3]; if (w != 0) { o->x /= w; o->y /= w; o->z /= w; } } int DrawTriangle(SDL_Renderer* p_renderer, int x1, int y1, int x2, int y2, int x3, int y3, Uint8 p_r, Uint8 p_g, Uint8 p_b, Uint8 p_a) { Uint8 r, g, b, a; SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); SDL_SetRenderDrawColor(renderer, p_r, p_g, p_b, p_a); SDL_Point points[3] = { { x1, y1 }, { x2, y2 }, { x3, y3 } }; int ret = SDL_RenderDrawLines(p_renderer, &points, 3); SDL_SetRenderDrawColor(renderer, r, g, b, a); return ret; } int sdl_init() { if (SDL_Init(SDL_INIT_EVERYTHING) || SDL_InitSubSystem(SDL_INIT_EVERYTHING)) { printf("Error at initializing SDL2 Error: %s", SDL_GetError()); return 1; } return 0; } int window_init() { if (sdl_init()) { return 1; } window = SDL_CreateWindow(WINDOW_TITLE, NULL, NULL, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_FULLSCREEN); renderer = SDL_CreateRenderer(window, -1, 0); triangle cubeTris[] = { // SOUTH { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f }, // EAST { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }, // NORTH { 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f }, // WEST { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f }, // TOP { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }, // BOTTOM { 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }, }; meshCube.tris = cubeTris; float fNear = 0.1f; float fFar = 1000.f; float fFov = 90; float fAspectRatio = (float)WINDOW_HEIGHT / (float)WINDOW_WIDTH; float fFovRad = 1.f / tanf(fFov / 2.f / 180.f * 3.14159f); matProj.m[0][0] = fAspectRatio * fFovRad; matProj.m[1][1] = fFovRad; matProj.m[2][2] = fFar / (fFar - fNear); matProj.m[2][3] = 1.f; matProj.m[3][3] = 0.f; isRunning = true; return 0; } void window_destroy() { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); isRunning = false; } void render() { SDL_RenderClear(renderer); for (int i = 0; i < sizeof(meshCube.tris); i++) { triangle triProjected, triTranslated; triTranslated = meshCube.tris[i]; triTranslated.p[0].z = meshCube.tris[i].p[0].z + 3.f; triTranslated.p[1].z = meshCube.tris[i].p[0].z + 3.f; triTranslated.p[2].z = meshCube.tris[i].p[0].z + 3.f; MultMatrixVec(&triTranslated.p[0], &triProjected.p[0], &matProj); MultMatrixVec(&triTranslated.p[1], &triProjected.p[1], &matProj); MultMatrixVec(&triTranslated.p[2], &triProjected.p[2], &matProj); triProjected.p[0].x += 1.f; triProjected.p[0].y += 1.f; triProjected.p[1].x += 1.f; triProjected.p[1].y += 1.f; triProjected.p[2].x += 1.f; triProjected.p[2].y += 1.f; triProjected.p[0].x *= 0.5f * (float) WINDOW_WIDTH; triProjected.p[0].y *= 0.5f * (float)WINDOW_HEIGHT; triProjected.p[1].x *= 0.5f * (float)WINDOW_WIDTH; triProjected.p[1].y *= 0.5f * (float)WINDOW_HEIGHT; triProjected.p[2].x *= 0.5f * (float)WINDOW_WIDTH; triProjected.p[2].y *= 0.5f * (float)WINDOW_HEIGHT; DrawTriangle(renderer, triProjected.p[0].x, triProjected.p[0].y, triProjected.p[1].x, triProjected.p[1].y, triProjected.p[2].x, triProjected.p[2].y, 255, 255, 255, 255); } SDL_RenderPresent(renderer); } void window_loop() { render(); SDL_Event e; while (SDL_PollEvent(&e) > 0) { switch (e.type) { case SDL_QUIT: window_destroy(); break; } } } void window_startloop() { while (isRunning) { window_loop(); } }
window.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <SDL.h> #include "3dmath.h" #define WINDOW_TITLE "Epic 3d Game" #define WINDOW_WIDTH 1920 #define WINDOW_HEIGHT 1080 SDL_Window* window; SDL_Renderer* renderer; int isRunning; mesh meshCube; mat4x4 matProj; float fTheta; int window_init(); void window_startloop(); void window_destroy();
3dmath.h
#pragma once typedef struct { float x, y, z; } vec3; typedef struct { vec3 p[3]; } triangle; typedef struct { triangle* tris; } mesh; typedef struct { float m[4][4]; } mat4x4;
问题分析与修复
1. 三角形数组遍历错误
sizeof(meshCube.tris)得到的是指针的字节数(通常4或8),不是数组中三角形的数量。修复步骤:
- 修改
mesh结构体,增加三角形数量字段:typedef struct { triangle* tris; int triCount; // 新增字段 } mesh; - 在
window_init中计算并初始化三角形数量:meshCube.tris = cubeTris; meshCube.triCount = sizeof(cubeTris)/sizeof(cubeTris[0]); - 更新循环条件:
for (int i = 0; i < meshCube.triCount; i++)
2. 三角形平移错误
平移时错误地将三个顶点的z值都设置为第一个顶点的z+3,应分别使用各自顶点的z值:
triTranslated.p[0].z = meshCube.tris[i].p[0].z + 3.f; triTranslated.p[1].z = meshCube.tris[i].p[1].z + 3.f; // 修复为p[1].z triTranslated.p[2].z = meshCube.tris[i].p[2].z + 3.f; // 修复为p[2].z
3. 三角形绘制不闭合
SDL_RenderDrawLines只会绘制点1→点2→点3的折线,不会闭合三角形。修改DrawTriangle函数手动绘制三条边:
int DrawTriangle(SDL_Renderer* p_renderer, int x1, int y1, int x2, int y2, int x3, int y3, Uint8 p_r, Uint8 p_g, Uint8 p_b, Uint8 p_a) { Uint8 r, g, b, a; SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); SDL_SetRenderDrawColor(renderer, p_r, p_g, p_b, p_a); // 绘制三条边,闭合三角形 SDL_RenderDrawLine(p_renderer, x1, y1, x2, y2); SDL_RenderDrawLine(p_renderer, x2, y2, x3, y3); SDL_RenderDrawLine(p_renderer, x3, y3, x1, y1); SDL_SetRenderDrawColor(renderer, r, g, b, a); return 0; }
4. 透视投影矩阵错误
透视投影矩阵缺少关键元素,导致z轴变换错误。补充m[3][2]元素:
matProj.m[0][0] = fAspectRatio * fFovRad; matProj.m[1][1] = fFovRad; matProj.m[2][2] = fFar / (fFar - fNear); matProj.m[2][3] = 1.f; matProj.m[3][2] = -(fFar * fNear) / (fFar - fNear); // 新增该行 matProj.m[3][3] = 0.f;
5. 局部数组生命周期问题
window_init中的cubeTris是局部变量,函数结束后内存被释放,meshCube.tris指向无效内存。将cubeTris改为全局变量:
// 在window.c顶部定义全局数组 triangle cubeTris[] = { // 原顶点数据... }; // 在window_init中直接赋值 meshCube.tris = cubeTris; meshCube.triCount = sizeof(cubeTris)/sizeof(cubeTris[0]);
修复后效果
修改以上问题后,SDL2程序将正确绘制完整的3D立方体线框。
内容的提问来源于stack exchange,提问作者Slykapar
相关产品推荐
相关产品推荐

