Hi!

I’am trying to achieve a pixel perfect look, in unity 3D engine.

For my desired effect to take place, the camera has to be snapped to a grid. Which I’ve already achieved. Next, each object that moves has to be snapped to the same camera grid.

To achieve this, I have specific vertex shaders for all materials that should be assigned to objects which are non-static. The way the vertex shader works is described by the following code/comments:

```
// create v2f
v2f o;
// get object center and vertex pos in world space
float4 objectOriginWorld = float4(unity_ObjectToWorld._m03_m13_m23, 1.0);
float4 vertexWorld = mul(unity_ObjectToWorld, v.vertex);
// go from world to view
float4 objectOriginView = mul(UNITY_MATRIX_V, objectOriginWorld);
float4 vertexView = mul(UNITY_MATRIX_V, vertexWorld);
// then view to projection
float4 objectOriginProjection = mul(UNITY_MATRIX_P, objectOriginView);
float4 vertexProjection = mul(UNITY_MATRIX_P, vertexView);
// then snap object origin
float4 objectOriginProjectionSnapped = ClipSnap(objectOriginProjection);
// and use snapped object origin to get snapped vertexProjection
float4 vertexProjectionSnappedDiff = float4(objectOriginProjectionSnapped.xyz - objectOriginProjection.xyz, 1.0);
float4 vertexProjectionSnapped = float4(vertexProjection.xyz + vertexProjectionSnappedDiff, 1.0);
// we only care about vertex so all transformations will be applied to it
// first clip to view
float4 vertexViewSnapped = mul(inverse(UNITY_MATRIX_P), vertexProjectionSnapped);
// then to world
float4 vertexWorldSnapped = mul(inverse(UNITY_MATRIX_V), vertexViewSnapped);
// and output to v.vertex, o.pos and o.worldPosition respectively
o.worldPosition = vertexWorldSnapped;
o.pos = vertexProjectionSnapped;
// independent from vertex snap
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.screenPosition = ComputeScreenPos(o.pos);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Defined in Autolight.cginc. Assigns the above shadow coordinate
// by transforming the vertex from world space to shadow-map space.
TRANSFER_SHADOW(o)
return o;
```

The ClipSnap function:

```
// snaps given clip pos to grid
float3 ClipSnap(float3 clipPos)
{
// note that clipPos goes from -1 to 1 so we transfer it to go from 01
float2 clipPos01 = (clipPos.xy + 1.0) * 0.5;
// then, we have a fault in that when they are smacked down EXACTLY at rounding edge
// because of floating point precision, we add a tiny value to make it go away
//clipPos01 += 1e-5; // commented out because this only changes the edge cases, not a fix!
// get the rounded clipXY (to snap to the camera grid)
float2 rounded = round(renderResolutionExtended * clipPos01) / renderResolutionExtended;
// offset by half a pixel
float2 offset = 0.5 / renderResolutionExtended;
// get the new clippos and remap to -1 to 1
float2 newClipPos = (rounded + offset) * 2.0 - 1.0;
// create float4 clippos and return it
return float3(
newClipPos.xy,
clipPos.z
);
}
float4 ClipSnap(float4 clipPos)
{
return float4(
ClipSnap(clipPos.xyz),
clipPos.w
);
}
```

Tldr:

I go to clip/projection space to snap the rendered object’s midpoint to the closest camera ray. And then back to object space.

However as seen here this results in edge cases where the objects snap to different positions on camera movement. I think this is a floating-point precision issue, since it only happens on very specific object positions and with slight change of position the problem goes away. However, I ofcourse do not want it to have to worry about these edgecases. My thought is that it might be the result of too many matrix multiplications (floating point does not have infinite precision, and multiplying multiple numbers multiple times, might degrade precision). In which case how do i reduce the amount of matrix multiplications? Changing UNITY_MATRIX_V and UNITY_MATRIX_P to use UNITY_MATRIX_VP, as well as inverse(UNITY_MATRIX_V) and inverse(UNITY_MATRIX_P) to inverse(UNITY_MATRIX_VP) does not help! I also use multiple cameras so i do not know how to remove the inverse(UNITY_MATRIX_P) and inverse(UNITY_MATRIX_V).

Any help would be nice, even if it is a complely different system to achive the “pixelperfectness”!