3
\$\begingroup\$

I'm trying to get a cross at the center of the screen (Two lines will do as well).

I have a 2048x2048 Texture which I have thrown into a canvas. Filtering is set to point. The image is set to be in the middle of the screen no matter the resolution. The image is set to 2048x2048 no matter the screen resolution which works as well.

However when I change the Screen Resolution I get results like the following, no matter what I do, the line refuses to stay at the same thickness. When I enable Bilinear filtering the lines constantly fade and thicken.

enter image description here

enter image description here

enter image description here

This is what I get when I use a 16x16 image:

enter image description here

EDIT: i tried using a black square and setting its width to 1 and height to 2048, and another one with width to 2048 and height to 1. The result i got is not what i expected:

enter image description here

Can someone help me to fix this please?

\$\endgroup\$
  • \$\begingroup\$ I think your main issue is that your crosshair is not pixel-aligned, leading to some or all of those strange artifacts. But your overall approach is "wrong" too, so I'll post an answer with better ones. \$\endgroup\$ – XenoRo Dec 20 '17 at 21:09
  • \$\begingroup\$ @Gabriele Vierti Appreciate the effort but your edit took all life out of my question. XenoRo Anticipating it with great enthusiasm. Nows Bedtime though. \$\endgroup\$ – AzulShiva Dec 20 '17 at 21:26
4
\$\begingroup\$

Textures mapped onto the surfaces of 3D geometry were developed to represent, well, surface texture. This type of detail usually doesn't align precisely with the grid of screen pixels, due to varying object sizes, distances, orientations, and camera perspectives, so it doesn't include any built-in features to snap the sampled texels to screen pixels. In fact, we more frequently want the opposite, smooth blending that hides the effect of the screen grid (which can show up as aliasing).

But Unity gives us the tools to be pixel-perfect when we want to be. Here's two ways we can approach it:

  1. Create a new UI Canvas. Leave it in the default Screen Space - Overlay mode

  2. Check the Pixel Perfect checkbox and set the Canvas Scaler mode to "Constant Pixel Size"

  3. Create two Panels as children of the canvas. Remove their sprite so they're just a flat colour of your choosing.

  4. Set one to centered horizontally / stretched vertically with width 1, and the other to the opposite with height 1.

Or, if you prefer to render this on other geometry, you can use a shader that clips out everything but the center cross:

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"

void vert (
      float4 vertex : POSITION, // vertex position input
      out float4 outpos : SV_POSITION // clip space position output
    )
{
    outpos = UnityObjectToClipPos(vertex);
}


fixed4 frag (UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
{
    // Compute the pixel center of the screen.
    float2 center = _ScreenParams.xy/2.0f;

    // Keep only the first pixel on/beside the center in the positive direction.
    float2 onLine = (screenPos >= center) * (screenPos < (center + 1.0f));

    // Discard all other pixels.
    clip(max(onLine.x, onLine.y) - 0.5f);

    // Draw black. (Or the colour of your choice)
    return 0;
}
ENDCG

With both of these solutions, though I could verify that Unity was writing a crisp single-pixel-wide line into the buffer, I ended up seeing a fuzzy line in screenshots. It turned out to be Windows applying its scaling logic for high-resolution displays - it was forcing Unity to render at a lower-than-native scale and then upscale. Disabling that Windows feature let the lines come out crisp, as expected.

\$\endgroup\$
2
\$\begingroup\$

There are many ways to create cross-hairs and cross-hair-like effects in Unity. Here are just a few of them:


9-sliced image:

You don't need a 2kx2k pixel texture just for a cross-hair. In fact, that would be (very) bad practice. Instead, you can have Unity scale and tile a simple, very small texture, based on it's parts.

In the case of a single-pixel cross-hair, a 3x3 image is enough, through the process of 9-slicing; there the image will be sliced in up to 9 different parts, and where each edge slice will be tiled or stretched to fit/fill a certain area.


Normal image(s):

You don't need a 2kx2k texture. A 1-pixel image stretched across the screen will look like a 1-pixel line. Make two of them cross at the middle, and you have a 1-pixel cross-hair.

When dealing with UI, transformations, variations and combinations of small images are pretty much always better than monolithic textures.


Vector-Graphics:

In engines that support this, or with the help of plugins, vector-graphics is often the best choice for screen-space UI, as it provides clean, crisp, graphics, which are easy to transform, maintain, extend, and can greatly simplify the process of making dynamic effects.

Unfortunately, very few game-engines support this out-of-the-box, and Unity is not one of them. Further unfortunately still, almost all plugins that provide this functionality do so through work-arounds, like transforming the vector-graphics into normal images, which, despite allowing the engine to "support vector-graphics", doesn't really maintain most of the advantages of vector-graphics.


Scripted Graphics-Library rendering:

Most game engines allow (direct or indirect) access to their underlying low-level Graphics-Library (IE OpenGL, DirectX, Vulkan, etc), and it is trivial to draw simple cross-hairs through these libraries.

In Unity, this access is provided through the GL class.

Note: This is usually not recommended, because it's overkill and much harder to change/maintain than an image-based approach, specially since there is no difference if you do the image-based approach correctly.


And many more...

These are just the main/most common few of many, many other methods... You're strongly advised to do some research on this, as many of the methods for creating cross-hairs also provide interesting starting-points and/or clues for more advanced graphics and effects.


NOTE:

To get a pixel-perfect effect, you need a pixel-aligned approach, obviously. In Unity, for screen-space UI, that's as easy as marking the "pixel-perfect" checkbox in the canvas' inspector.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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