1
$\begingroup$

I'm working on a 2D (pseudo-3D) raycaster which operates on a 2D tile grid.

For each ray sent out, the screen draws a rectangle with a certain height depending on the distance between the camera and the wall the ray collided with (like Wolfenstein 3D).

I am now working on drawing texture images and have run into a problem:

If for example six rays hits a wall segment (tile) then the image which corresponds to this tile should be sliced into six parts. If there was 100 rays then the image needs to have 100 slices.

However, obviously there exist no image width which can be divided by all numbers.

How to go about this?

My current idea is:

  • Load several resolutions of the image texture, for example: (100x100, 200x200, 400x400, 800x800)

  • Identify how many rays has hit wall segment X

  • Identify which image resolution fits best to the scaled height of the rectangle (depending on distance from camera to target). Lower resolution for more distant objects etc.

  • Transform the selected image into the closest resolution which can be divided into a whole number by the amounts of rays which hit the segment

I believe this could work in principle; however I would hope there is an easier solution which requires less image transforms.

$\endgroup$

1 Answer 1

0
$\begingroup$

You have independently rediscovered part of the problem known as texture filtering.

The simplest possible solution is to figure out where on the texture the ray hit, considering it as a mathematical rectangle, and just take whichever texel (or in your case, column of texels) is nearest to that point — nearest-neighbor sampling. This is probably (I'm not personally familiar) how Wolfenstein 3D actually works — it creates a blocky, pixelated look, which we now often find “retro” and aesthetically pleasing when applicable.

To reduce the pixelated look, the next step is to interpolate between multiple texels — nearly every texture in “realistic” graphics is processed this way. In this world, you think about texels as being infinitesimal point samples of the “real” texture, and approximate all the points in between the actual stored texels by linear interpolation. If the ray hit an x position that's 1/4 of the way to texture column 7 and 3/4 of the way to texture column 8, you'd compute 1/4 of column 7's texels + 3/4 of column 8's texels. This is done for every pixel, every frame.


Regardless of whether you use interpolation or not, you will observe chaotic noise (aliasing) when the texture is displayed so far away (relative to the screen resolution) that it is smaller than its original resolution and details are intermittently lost. Your idea of creating multiple resolutions of the input image is a way to solve this problem, and it is called mipmapping. However, mipmapping can also cause textures to be noticeably low-resolution; it's a tradeoff, that should be made with consideration for the contents of the texture, the visual effect you want, and the maximum view distance.


However, in all of the above cases, fitting the on-screen width to the texture width by an exact ratio is never done. If it was, the user would observe glitchy effects as their viewpoint changed by small amounts. The answer here is to just not worry about it — just use floating-point or fixed-point arithmetic to compute where on the texture the ray landed, and find the nearest texel(s), and interpolate or don't as you see fit. The calculation for each screen pixel (or column of pixels in your case) is independent of its neighbors except for purposes of choosing a mipmap level if you are doing mipmapping.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.