gpt4 book ai didi

c - 如何修复我的 raycaster 中的扭曲透视?

转载 作者:行者123 更新时间:2023-12-05 06:49:35 25 4
gpt4 key购买 nike

我正在使用 SDL 的 C API 编写一个 raycaster。我花了数周时间试图修复臭名昭著的鱼眼效果,但无济于事。根据this源,我可以将我计算出的距离乘以一半 FOV 的余弦来修复它。那对我不起作用。尽管如此,我的代码中仍然有余弦校正。

这里有两张图片展示了失真:

Image 1

Image 2

我认为我的代码的一个核心问题可能是我的角度增量是恒定的,而当我靠近屏幕边界时增量应该更小。不幸的是,我不知道如何开始实现。

如果可能的话,任何人都可以看看我的代码并给我一个如何去除鱼眼的提示吗?要向任何方向移动,请使用箭头键。使用“a”和“s”键分别向左和向右转动。

这就是我编译的方式:clang `pkg-config --cflags --libs sdl2` raycaster.c

#include <SDL2/SDL.h>
#include <math.h>

typedef struct {
float x, y, prev_x, prev_y, angle, fov;
} Player;

enum {
map_width = 12, map_height = 15,
screen_width = 800, screen_height = 500
};

const float
move_speed_decr = 0.08,
angle_turn = 2.0,
ray_theta_step = 0.4,
ray_dist_step = 0.8,
darkening = 1.8,
width_ratio = (float) screen_width / map_width,
height_ratio = (float) screen_height / map_height;

const unsigned char map[map_height][map_width] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
{1, 0, 4, 3, 2, 0, 0, 0, 2, 0, 0, 1},
{1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 1},
{1, 0, 0, 0, 4, 3, 2, 1, 4, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};

SDL_Window* window;
SDL_Renderer* renderer;

float to_radians(float degrees) {
return degrees * M_PI / 180;
}

float distance(float x0, float y0, float x1, float y1) {
return sqrt(((x1 - x0) * (x1 - x0)) + ((y1 - y0) * (y1 - y0)));
}

void shade(int* color, int darkener) {
int darkened = *color - darkener;
*color = darkened < 0 ? 0 : darkened;
}

void draw_rectangle(SDL_Rect rectangle, int r, int g, int b) {
SDL_SetRenderDrawColor(renderer, r, g, b, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(renderer, &rectangle);
SDL_RenderDrawRect(renderer, &rectangle);
}

void raycast(Player player) {
float relative_x = player.x * width_ratio;
float relative_y = player.y * height_ratio;
float half_fov = player.fov / 2;
float width_fov_ratio = (screen_width / player.fov) / 2;
float distort_adjust = cos(to_radians(half_fov));

// the core of my problem may be in the constant increment of my angle
for (float theta = player.angle - half_fov, screen_x = 0;
theta < player.angle + half_fov;
theta += ray_theta_step, screen_x += width_fov_ratio) {

float radian_theta = to_radians(theta);
float cos_theta = cos(radian_theta), sin_theta = sin(radian_theta);

float d = 0, new_x, new_y;
while (d += ray_dist_step) {
new_x = cos_theta * d + relative_x;
new_y = sin_theta * d + relative_y;

int map_x = new_x / width_ratio, map_y = new_y / height_ratio;
int map_point = map[map_y][map_x];

if (map_point) {
int dist_wall = distance(relative_x, relative_y, new_x, new_y) * distort_adjust;
int twice_dist_wall = 2 * dist_wall;
if (twice_dist_wall >= screen_height) break;
else if (map_point) { // succeeds when a wall is present
int r, g, b;
switch (map_point) {
case 1: r = 255, g = 255, b = 0; break;
case 2: r = 0, g = 128, b = 128; break;
case 3: r = 255, g = 165, b = 0; break;
case 4: r = 255, g = 0, b = 0; break;
}

int color_decr = dist_wall / darkening;
shade(&r, color_decr);
shade(&g, color_decr);
shade(&b, color_decr);

SDL_Rect vertical_line = {
screen_x, dist_wall,
width_fov_ratio + 1,
screen_height - twice_dist_wall
};

draw_rectangle(vertical_line, r, g, b);
break;
}
}
}
}
}

void handle_input(const Uint8* keys, Player* player) {
SDL_Event event;

while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
exit(0);
}

else if (event.type == SDL_KEYDOWN) {
float radian_theta = to_radians(player -> angle);
float move_x = cos(radian_theta) * move_speed_decr,
move_y = sin(radian_theta) * move_speed_decr;

// handle arrow keys
if (keys[SDL_SCANCODE_UP]) player -> x += move_x, player -> y += move_y;
if (keys[SDL_SCANCODE_DOWN]) player -> x -= move_x, player -> y -= move_y;
if (keys[SDL_SCANCODE_LEFT]) player -> x += move_y, player -> y -= move_x;
if (keys[SDL_SCANCODE_RIGHT]) player -> x -= move_y, player -> y += move_x;

// handle 'a' and 's' for angle changes
if (keys[SDL_SCANCODE_A]) player -> angle -= angle_turn;
if (keys[SDL_SCANCODE_S]) player -> angle += angle_turn;

// safeguards for invalid positions and angles
if (player -> x < 0) player -> x = 0;
else if (player -> x > screen_width) player -> x = screen_width;

if (player -> y < 0) player -> y = 0;
else if (player -> y > screen_height) player -> y = screen_height;

// move the player to their previous coordinate if they're in a wall
if (map[(int) player -> y][(int) player -> x])
player -> y = player -> prev_y, player -> x = player -> prev_x;

if (player -> angle > 360) player -> angle = 0;
else if (player -> angle < 0) player -> angle = 360;

player -> prev_y = player -> y, player -> prev_x = player -> x;
}
}
}

int main() {
SDL_CreateWindowAndRenderer(screen_width, screen_height, 0, &window, &renderer);
SDL_SetWindowTitle(window, "Raycaster");

Player player = {5, 5, 0, 0, 0, 60};
SDL_Rect the_ceiling = {0, 0, screen_width, screen_height / 2};
SDL_Rect the_floor = {0, screen_height / 2, screen_width, screen_height};
const Uint8* keys = SDL_GetKeyboardState(NULL);

while (1) {
handle_input(keys, &player);

draw_rectangle(the_ceiling, 96, 96, 96);
draw_rectangle(the_floor, 210, 180, 140);

raycast(player);

SDL_RenderPresent(renderer);
SDL_UpdateWindowSurface(window);
}
}

在 RandomDavis 的帮助下,失真有所减轻。这是新的结果。尽管如此,仍然存在一些扭曲:

new 1

new 2

new 3

注意:对于仍在为这个问题苦苦挣扎的任何人,我在这里解决了它: How do I fix warped walls in my raycaster?

最佳答案

好的,我找到了 guide其中谈到了这个确切的问题。

Before drawing the wall, there is one problem that must be taken care of. This problem is known as the "fishbowl effect." Fishbowl effect happens because ray-casting implementation mixes polar coordinate and Cartesian coordinate together. Therefore, using the above formula on wall slices that are not directly in front of the viewer will gives a longer distance. This is not what we want because it will cause a viewing distortion such as illustrated below.Blockquote

enter image description here

Thus to remove the viewing distortion, the resulting distance obtained from equations in Figure 17 must be multiplied by cos(BETA); where BETA is the angle of the ray that is being cast relative to the viewing angle. On the figure above, the viewing angle (ALPHA) is 90 degrees because the player is facing straight upward. Because we have 60 degrees field of view, BETA is 30 degrees for the leftmost ray and it is -30 degrees for the rightmost ray.

关于c - 如何修复我的 raycaster 中的扭曲透视?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66591163/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com