I'm trying to get an effect where my material forms a mosaic which is aligned to the material. So I slapped a simple quad in the scene with a new material on it and set a new shader to that material.

My inputs (at least one thing in here will be removed later since it turns out I'm not using it):

        struct Inputs
            float4 vertex : POSITION;
            float2 uv_panel : TEXCOORD0;
        struct FragmentData
            float4 vertex : POSITION;
            float4 uv_grab : TEXCOORD1;

Vertex function:

        FragmentData Vertex(Inputs i)
            FragmentData o;

            o.vertex = UnityObjectToClipPos(i.vertex);

            // what I actually want to do (the exact maths might be
            //   wrong still):
            float2 uv_panel = floor(i.uv_panel * _Pixels) / _Pixels;

            // Convert that UV value for inside the panel
            // to a UV value for the grab texture
            float4 object_coords = float4(0, 0, 0, 1);
            object_coords.xy = uv_panel - 0.5;
            float4 clip_coords = UnityObjectToClipPos(object_coords);
            o.uv_grab = ComputeGrabScreenPos(clip_coords);

            return o;

And then the fragment function is pretty trivial:

        half4 Fragment(FragmentData i) : COLOR
            return tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uv_grab));

The end result is basically equivalent to doing nothing at all. I can see the objects behind the quad as if it weren't there. Upside is that it means my transformations are correct. Downside is that it means the mosaic effect somehow isn't being applied.

To check whether the floor call was working (and because I currently know no better way to debug this crap) I tried applying it like so:

            float2 uv_panel = floor(i.uv_panel);

This has no effect either.

This seems odd to me, because I thought uv_panel values in x and y would range from 0.0 to 1.0, so almost all values should floor to 0.0.

When I do this:

            float2 uv_panel = float2(0, 0);

It acts like I expected, with the bottom of the grab texture being smeared up the panel with the panel's alignment. So it's definitely using whatever value I put in here.

At this point I have no idea how to proceed because it's like my assumptions about the values in a float2:TEXCOORD0 are completely incorrect, or the floor() function does something completely different to what I expected.

What am I doing wrong here?


1 Answer 1


You're using floor in your vertex shader, which runs only once for each vertex.

So if you're rendering a standard quad or a blit pass, it only sees 4 UV values: (0, 0), (0, 1), (1, 0), (1, 1). And for numbers like these (if _Pixels is an integer), your formula is a no-op:

   floor( (0, 1) * _Pixels ) / _Pixels
   = floor( (0, _Pixels) ) / _Pixels
   = (0, _Pixels) / _Pixels
   = (0, 1)

For each fragment rasterized to the screen, these UV values output by the vertex shader are interpolated. The interpolation unit has no knowledge of how those numbers were calculated, so it doesn't know to perform a floor evaluation in between.

Instead, if you want to get a flooring or any other effect that varies pixel-by-pixel across a triangle, rather than only vertex-by-vertex, you'll want to put it into the fragment shader, which runs for every rasterized fragment individually.

  • \$\begingroup\$ Ah....a........ That makes more sense now. Though I could swear I had seen some other shaders doing between-vertex things in a similar way. :/ \$\endgroup\$
    – Hakanai
    Feb 28, 2018 at 12:34
  • \$\begingroup\$ Some effects interpolate safely, or sometimes we're using a mesh that's subdivided densely enough that evaluating per vertex is enough. \$\endgroup\$
    – DMGregory
    Feb 28, 2018 at 12:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .