PDA

View Full Version : Pixel-based effects and screen DPI dependency



Keiron
06-16-2017, 02:06 AM
I'm currently working on a game that has several pixel-based post-processing effects : for example dark edges. Whenever a pixel is different than its neighbors, I make it go dark, so that it creates dark lines on every edge.

However, depending on screen density, this effect might look very different. On a very dense screen the edges will look very small, but much thicker on a screen with low density. I want to make sure the game at least looks the same on every screen.

There's a lot of documentation about post-processing effects but none I've found talk about such problems. How come no one has encountered this or at least discussed it? Is everyone just avoiding doing things like this because it's a bad idea?

Here's a snapshot of what I'm working on. It's isometric with no zooming possible, thus with my current screen there is no thin/thick lines problems. However if I allowed zooming there would be a problem. The lines wouldn't become thicker, because it's pixel size dependent.

2398

One solution could be to sample more pixels if the density is higher. I could create an array of shaders for different ranges of density. But I don't think you can find out easily what the DPI is, so I would like to avoid toying around with that.

One dirty solution might be to double or triple every pixel on big screens, i.e rescale the buffer to the screen with interpolation. But perhaps it wouldn't work very well for any resolution and it wouldn't take density into account.

Anything obvious I'm missing? I just shouldn't be doing this right?

GClements
06-16-2017, 04:53 AM
Rather than directly modifying pixels which differ from their neighbours, first create a mask which is one for pixels which differ and zero for those which don't, then dilate (https://en.wikipedia.org/wiki/Dilation_%28morphology%29) the mask (i.e. set each output pixel to one if any input pixel within a given radius is one, zero otherwise), then modify pixels according to the dilated mask.

The dilation radius should probably be a fraction of a the window dimensions, so the physical size depends upon the physical size of the screen but not its resolution (DPI). If the dilation radius is only a few pixels, the entire process can be implemented as a single shader; for larger radii, it may be more efficient to perform dilation as a separate step (or steps).

For a more general solution, start by treating the source image as a continuous function rather than as an array of samples. So you end up with a filter defined in terms of integrals and derivatives. Then consider approximations which use a finite set of samples (i.e. pixel values), such that the sample density only affects the quality of the approximation rather than the ideal value.

Keiron
06-16-2017, 05:21 AM
Rather than directly modifying pixels which differ from their neighbours, first create a mask which is one for pixels which differ and zero for those which don't, then dilate (https://en.wikipedia.org/wiki/Dilation_%28morphology%29) the mask (i.e. set each output pixel to one if any input pixel within a given radius is one, zero otherwise), then modify pixels according to the dilated mask.

The dilation radius should probably be a fraction of a the window dimensions, so the physical size depends upon the physical size of the screen but not its resolution (DPI). If the dilation radius is only a few pixels, the entire process can be implemented as a single shader; for larger radii, it may be more efficient to perform dilation as a separate step (or steps).


Hmm, that sounds like a really good process. That's kinda what I meant by sampling more pixels but you managed to clarify exactly how to. I'm going to try it.

Though for really big screens or a big zoom, even by dividing the dilation into multiple steps, I feel like the slowdown is going to hit on performances pretty hard. That's probably why the effect has never really been properly done yet.

Also that gave me an idea. Why not make some sort of first person game with the same lines, but the thickness (= the dilation radius) scales inversely to the depth, so that the horizon isn't hidden by big fat lines. Well there's still the 1px limit but I'm sure there could be a way to make this nice.

Keiron
06-16-2017, 07:01 AM
[...] (i.e. set each output pixel to one if any input pixel within a given radius is one, zero otherwise) [...]

But now I'm wondering ; why do you suggest a hard binary dilation? Perhaps some sort of interpolation would make the scaling smoother. I'm a bit afraid that increasing the radius will make the lines jump from 1x1 to 3x3 dark pixels in a very brutal way.

Keiron
06-20-2017, 04:16 PM
I've decided to give up on making something scaling smoothly. It simply doesn't make sense for what I'm currently doing (100% black lines) and for the amount of screen resolutions possible.

The wisest solution is to simple create an array of different resolutions and the result will look almost the same anyway. Say the base resolution is 1920x1080, anything smaller should be given the exact same line density (below 1px is impossible anyway). At some point, the screen gets really big and it becomes interesting to double the size of every line in order to preserve at least approximately the look expected. I haven't decided exactly at what threshold the doubling/tripling happens but that threshold should be probably based on only one component of the window size (width or height). I think height in my case is more interesting, and stretching the window for more or less width will simply hide parts of the environment.

The goal of all of this is not to be exact, but at least make it plausible when going from 1920x1080 to 3840x2160 for example. No smoothness because I really don't want my lines to be stretched (it creates an ugly aliasing effect, and I want regularity).

Overall, I've greatly underestimated the problem of making a game look good on many screens. I think UI and handling the window resolution are the trickiest part of an engine and I simply didn't care about it when I started. Some games seem to find ways to be lazy on this - mostly by not making their own engine or just disabling window flexibility - but overall if you want to make something good you have to really CARE about what happens on different resolutions. According to Steam stats, only 50% of users have a 1080p resolution, and the rest is a wild jungle.