提问人:Slykapar 提问时间:6/6/2023 最后编辑:Slykapar 更新时间:6/7/2023 访问量:338
如何使用 SDL2 在 C 语言中实现 3D 图形渲染?
How can I implement 3d graphics rendering in C with the SDL2?
问:
我尝试实现一个 C 程序,该程序使用 SDL2 绘制 3D 立方体。对不起,如果这只是一个愚蠢的小错误,我对 C/C++ 非常陌生。我遵循了这个教程:https://youtu.be/ih20l3pJoeU。教程中的那个人用 C++ 编码了所有内容,但我尝试用 C 编码。 但是我的代码有问题。只绘制了立方体的一部分,看起来真的很奇怪。这是我所有的代码:
main.c:
#include "window.h"
int main() {
window_init();
window_startloop();
return 0;
}
窗口.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;
return;
}
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;
}
}
return;
}
void window_startloop() {
while (isRunning) {
window_loop();
}
return;
}
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;
首先,我检查了我的立方体顶点坐标是否正确,它们是正确的。然后我删除了代码的许多部分,并尝试在遵循教程的同时再次编写它,但没有任何效果。
答:
假设您以比静止方式更明智的方式处理了全局可引用性,到目前为止我注意到的问题:
DrawTriangle
将 3 个点传递给 。要画一个三角形,你必须画 3 条线,每条线由 2 个点组成,所以三角形的总点数是 6:SDL_RenderDrawLines
SDL_Point points[] = {
{x1, y1}, {x2, y2},
{x2, y2}, {x3, y3},
{x3, y3}, {x1, y1},
};
int ret = SDL_RenderDrawLines(p_renderer, &points, sizeof(points)/sizeof(points[0]));
在 中,您可以遍历 ,但任何指针类型的 sizeof 都是(例如 8 位、4 位或 32 位系统)。您需要保存顶点的数量,例如添加到网格结构中并用 填充它,其中不是指针而是实际数组
render
sizeof(meshCube.tris)
sizeof(void*)
int num_tris
meshCube.num_tris = sizeof(cubeTris) / sizeof(cubeTris[0]);
window_init
cubeTris
设置为指向三角形临时数组的指针。渲染时,此数组可能已经被踩踏。您必须为该数据分配空间或将其设置为全局/静态。声明它就足够了
tris
static triangle cubeTris[] = { .... }
顺便说一句,使用或多或少的现代 C 语言,您不需要真/假定义,就像我们自 C99 以来所做的那样。另请注意,与 C++ 相比,在 C 中,不是不带参数的函数,而是带不指定数量的参数的函数;函数解冻没有参数将是 .[对于最近的 C2x,这应该不再是事实,它将“很快”作为 C23 发布]#include <stdbool.h>
void foo()
void foo(void)
评论
sizeof(array)/sizeof(array[0])
.c
评论