dethrace-labs / dethrace

Reverse engineering the 1997 game "Carmageddon"
https://twitter.com/dethrace_labs
GNU General Public License v3.0
859 stars 44 forks source link

Rendering artifacts at different viewport sizes #282

Closed zear closed 3 months ago

zear commented 1 year ago

Deathrace exhibits multiple issues related to rendering the 3d part of the screen in non-full resolution (i.e. manipulating the viewport size with -/+ keys):

1. Depth buffer

When the gRender_screen is scaled such that it doesn't match dimensions of gBack_screen, the depth buffer's position and size differs from the color buffer, causing visible artifacts on the screen:

sshot3

2. Blending

The viewport height passed to the shader should be the one of the renderer, not the color buffer. Otherwise, geometry behind transparent materials is sampled at the wrong y offset:

sshot1

3. Blending in map mode

When map mode is enabled, the scene is rendered thrice to a single frame. This breaks blending in GL renderer, which depends on a clear depth buffer. The result is fully opaque transparencies:

sshot2

4. External sky

Depending on the screen size, something seems to obstruct different parts of the external sky:

https://user-images.githubusercontent.com/510643/213887149-ec42931d-8d5b-4891-949f-cbcfd60c602b.mp4

Currently, I can't tell whether this is geometry, or the sky bitmap not rendering properly.

zear commented 1 year ago

Parts 1-3 are now fixed in https://github.com/dethrace-labs/dethrace/pull/283.

zear commented 1 year ago

Regarding part 4, it is the chunk of ExternalSky code responsible for filling in the background with uniform color around the sky pixelmap that is at fault. After a change to draw in white, everything becomes clear:

https://user-images.githubusercontent.com/510643/213915480-20f67a8c-1386-46a6-871a-388aaaa9bbc6.mp4

Now, the reason this happens is because the below code doesn't take pRender_buffer->base_x/pRender_buffer->base_y into account while calculating DRPixelmapRectangleFill offsets:

https://github.com/dethrace-labs/dethrace/blob/83ba373426385882fdf098ce363bb0cf6ea2e71c/src/DETHRACE/common/depth.c#L498-L506

This can be easily fixed by respecting said offsets:

    /* Viewport offsets - added by Dethrace. */
    int off_x = 0;
    int off_y = 0;

(...)

#if defined(DETHRACE_FIX_BUGS)
    /* Take viewport offsets into account. */
    off_x = pRender_buffer->base_x;
    off_y = pRender_buffer->base_y;
#endif

    if (top_y > 0) { 
        top_col = ((tU8*)col_map->pixels)[0]; 
        DRPixelmapRectangleFill(pRender_buffer, off_x + -pRender_buffer->origin_x, off_y + -pRender_buffer->origin_y, pRender_buffer->width, top_y, top_col); 
    } 
    bot_height = pRender_buffer->height - pRender_buffer->origin_y - hori_y - col_map->height; 
    if (bot_height > 0) { 
        bot_col = ((tU8*)col_map->pixels)[col_map->row_bytes * (col_map->height - 1)]; 
        DRPixelmapRectangleFill(pRender_buffer, off_x + -pRender_buffer->origin_x, off_y + hori_y + col_map->height, pRender_buffer->width, bot_height, bot_col); 
    } 

However, the OG does not do that. I wonder why it works for them - they still keep track of base_x/base_y and update those fields in AdjustRenderScreenSize, so those values are most definitely not 0 at the time ExternalSky is called. And yet, the sky is rendered just fine.

@dethrace-labs @madebr Should we debug this deeper, or is the above solution satisfactory for now?

dethrace-labs commented 3 months ago

Fixed with software renderer