PS1/BallisticNG PSX 'Wobbly' vertices effect/shader?

Hi all,

I’ve been doing research recently on how the PS1 rendering system worked. I’m trying to understand how they achieved their ‘wobbly’ jittery vertices look where pixel centers snap to integer coordinates.

The effect is most noticeable when models are animating. I’m sure you know what I’m talking about I basically want to nail down the PS1 look for this game we’re making. I also know that the PS1 had linear texture mapping and that’s where the swimming texture effect came from, that’s a fairly easy to achieve effect. I’m just trying to understand the how they achieved the ‘wobbly’ wrong-offsetted pixel centers. Is it just due to using low accuracy fixed-point math? Is it cause their rotation or matrix multiplication math was all hacky bit manipulation/shifting?

I’m not even sure where that effect happens, although I think it is not game specific (i.e. it’s not some math code only in some games), it could be that it happens on the GTE because almost every PS1 game does it.

What led me to post here is BallisticNG, which claim to be a Unity 5 game. They have an option in the game to turn on PSX shaders. Here’s a demonstration. Obviously it’s not as good as the real thing, but it’s not a bad start either. Anyone have any ideas how to achieve that effect?

Any pointers would be appreciated.
Cheers

You could use a modification of the built in UnityPixelSnap() function with a custom “resolution” rather than the actual screen resolution, unless you’re planning on using an actual low resolution render texture as your camera’s render target in which case the built in function should do exactly what you want.

@bgolus Thanks. Looking at the source:

// snaps post-transformed position to screen pixels
inline float4 UnityPixelSnap (float4 pos)
{
   float2 hpc = _ScreenParams.xy * 0.5f;
   #ifdef UNITY_HALF_TEXEL_OFFSET
   float2 hpcO = float2(-0.5f, 0.5f);
   #else
   float2 hpcO = float2(0,0);
   #endif 
   float2 pixelPos = round ((pos.xy / pos.w) * hpc);
   pos.xy = (pixelPos + hpcO) / hpc * pos.w;
   return pos;
}

I get that it’s trying to snap the vertex position to a screenspace pixel position but I’m not sure I get how it’s doing it.

  • What is _ScreenParams?
  • hpc, short for half pixel center?
  • I assume pixelPos is the position of the pixel we’re snapping to? Why are we dividing by w and multiplying by hpc?
  • Why not set pos.xy to pixelPos directly? Not sure I get the last divide by hpc and mul by w as well

Any explanation would be appreciated
Thanks

Possibly, I would suggest not trying to understand variable names to often. The first “hpc” would be more accuratelty labelled “hsw”, or halfScreenWidth. Not sure why it’s using hpc and hpc0 as variable names, but ultimately it is unimportant.

Both of these questions are answered by understanding clip space / projection space. Clip space is the position of the vertices that are output by the vertex shader. The xy values for clip space are often referred to as being in a -1 to 1 range, but more accurately they’re in a -w to w range, hence the divide and multiply by w to actually put it in a -1 to 1 range. The multiply by the _ScreenParams.xy * 0.5 is calculating their eventual pixel relative locations (again, -1 to 1 rather than 0 to 1 so * 0.5 so the result is -half resolution to +hall resolution). The actual half pixel offset thing is a DX9 vs DX11 issue you can ignore.

1 Like

Keijiro made an awesome shader for this :

https://github.com/keijiro/Retro3D

2 Likes