You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:42:05