Painting with Pins: Creating the Surveyor’s Map

Welcome back to another year with the Starry Expanse Project!

The period between late December through early January is traditionally a time for taking stock of our progress, critically examining our pipeline, and spending a little time developing R&D prototypes to prepare for the year ahead.  We’d like to share one of these with you today.

You might recognise it, despite the gaudy colour scheme (which is for clarity during testing). It’s our prototype version of the famous Survey Island pin table. A visually striking puzzle element in the original game, the pin table consists of thousands of tiny pins that move up and down to form the contours of each island.

Our version is a little more dynamic as we can pan, scale, and rotate a height map image to get any shape we need. You can see a bit more of how it works behind the scenes in this video.

We’re happy with the visual results of our pin table, but all those pins do come with a non-trivial cost to performance (and our test map is less than half the resolution of the one in the original). Our prototypes are not always successful, and sometimes ideas don’t work out. It’s why we do these small, isolated tests in the first place!

We have a few other ideas for how this effect could be achieved, and we’ll be having a go at those sometime in the near future.

We’d like to welcome our newest team member, Tom Owen – a multi-talented programmer who is passionate about virtual reality. Good to have you with us, Tom!

14 Responses to “Painting with Pins: Creating the Surveyor’s Map”.

Team members' usernames are in red.
  • Aloys Says:

    Hmm, I had never tought about that one,
    A shader with displacement and self shadowing is quite heavy but there’s not much that can be optimized here. (maybe remove the self shadowing?) Then again, I suppose it beats having 2500 individual objects that you move around..

    But have you thought about going the old fashionned Riven way and having actual pre-rendered transparent videos that you would display in world space?..
    I haven’t thought this through but it might work because the point of view would be fixed here. Given that the player cannot move around the map, and you could only enable the video when the player is standing in that precise spot and disable it as soon as he/she moves away.

  • Phil Says:

    Oh wow, I completely forgot about this part of the game. Looks complex, I don’t envy you working on this!

  • Illuminerdi Says:

    Let’s start with some math so I can get my head around this problem:

    The “pin board” is 20x20x5x5, so a single section of the pin map is 10,000 pins. The sqrt of 10,000 is 100.

    FIrst up: if your current method involves a single heightmap for each island, that is potentially very inefficient because it means up to 96% of a given heightmap is not rendered at any one time. So the engine and/or GPU has to calculate and cull at least 50% and at most 96% of the heightmap.

    That’s potentially a LOT of wasted render operations, multiplied exponentially depending on the difference between your heightmap resolution and your worldspace.

    So if you haven’t already done so already, the first thing to do would be to subdivide the island heightmaps into individual chunks 100px x 100px. If that’s a lot of work (I don’t know how difficult generating the island heightmaps is for your team), then skip it for now and just make a test 100 x 100px heightmap to use as a performance test, then smoke test how efficient then engine is at scaling a heightmap that isn’t 1:1 to worldspace.

    Also – have you done smoke tests without using heightmaps at all? If your pin map is 10,000 individual rendered “pins” and each pin is a flat rectangle, that’s 20,000 tris per render pass times the number of visible sides. Since a 6 sided rectangular object in 3d space couldn’t have more than 3 faces rendered at once, your minimum render count is 20,000 tris and your maximum render count is 60,000 tris onscreen, but your maximum calculated tris is 120,000. That’s a decent amount of rendering for a modern GPU, assuming the engine isn’t wasting a ton of time calculating stuff for culled faces.

    If you haven’t already done so, cut the bottom face off each pin. That’ll save you 1/5 of your tri count since those faces will never be visible, and cutting them off entirely means the engine doesn’t have to bother even culling them. Next make sure your pin geometry is as simple as possible, since any increase in the pin geometry means you’re exponentially increasing the render complexity of the scene by the number of additional tris added to the face of each pin. Even a simple chamfer on the edges would double your polycount!

    I hope this helps!

    tl;dr: Check that heightmap isn’t being unnecessarily scaled by the engine, and check that you have sane polycounts on the pin map since there’s 10,000 pins and a mathematical minimum of 10 triangles per pin before culling.

    • Aloys Says:

      If we go with polygons, I’m pretty sure the limiting factor isn’t the polycount; it’s the draw calls. For an average GPU having 10K+ draw calls seems to be quite a lot…

      DirectX 12 and consoles might have an advantage here, but for PC GPUs older than 5 years that won’t work.

  • Mark Says:

    You’ve probably thought of this…
    If you took your height map, and didn’t animate the strength of it, but move it vertically through a plane. That would give the look of the original, with the higher points pushing through progressively to the lowest points.
    You could then render off the animation of the z-depth from above, to generate an animated height map.
    The new animated height map could then be used instead.

    • Robert Says:

      Yeah, you’ll likely need to set some kind of a floor limit to get that “object pushing through a plate of pins” effect as right now it’s more a gradual effect. I don’t know UE4, but I guess this is done employing an animated mask with levels applied to the height map to step out the animation layers.

      As a prototype or “proof of concept” it gets the job done. However, I’m excited about seeing phase 2 where they work to tweak the heightmap animation to perform more like the original.

  • Vincent Krebs Says:

    Interesting, doesn’t look hard to reproduce, from a total ignorant’s point of view =)

  • Averagemoe Says:

    What if you modeled out each of the height maps as a single object, (one for each square, not the whole thing) and have that rise through the flat table?

  • catalina Says:

    just push a model up from a flat one. no one will know

  • BuddhaMaster Says:

    Where every problem is solved, using VOXELS

    Seriously, how hard is it to program a simple Voxel Engine?
    Maybe you can even use it to create natural variables to surfaces. Its not easily noticed and can be handy

    • Aloys Says:

      Writing a game rendering engine from scratch is a *ton* of work. ((even a “simple” one). Also they are already using one (Unreal) and you can’t simply add another one on top.

  • Bookwyrm Says:

    I’ve been giving the pin problem a lot of thought.

    The problem comes down to 4 possibilities that I can see:

    1: A mesh with a special depth shader. As others have pointed out, such a shader can be very expensive. But it might be possible to optimize one that approximates the look of a forest of pins without a huge performance cost. I’ve never tried it so I wouldn’t know. 😉

    2: Polygon count, if using a grid of individual pins.

    3: A solution not yet proposed; to use a bunch of billboard objects (flat planes with a transparent texture that rotate around the up axis so they’re always facing the camera). This gives a lower polycount than #2 but comes with the issue of truckloads of overdraw. Most GPUs are not particularly optimized for layers and layers of alpha sampling…
    But there are some VFX solutions like the Unity VFX Graph can handle similar things. In Unity it’d be simple as a grid of axis-aligned particles whose vertical scale is driven by the heightmap, using their highly performant particle compute shader. I don’t know for sure if Unreal has any similar systems that could do the same thing, though I imagine they do exist, if only as expensive 3rd-party plugins.

    4: Use pre-rendered animations for the entire thing. That would give maximum fidelity for nearly zero runtime cost. The only problem there is if the player can move around the pin table to view it from a different angle.

    I wish you the best of luck in finding a solution that works for you.

    Keep up the great work! These blog posts highlight some truly fascinating problems that I hadn’t really considered before, and I always look forward to the next one. 🙂

    • Illuminerdi Says:

      @Bookwyrm – I think #2 is still the best option. I did the math, and the poly count is acceptable. Not great, and it’s going to make iGPUs a bit unhappy, but it’s also an easily scalable solution since they could just conglomerate the pins by a factor of 2 or 4 to lower the poly count for people who have their detail level setting reduced, and it would probably still look “good enough” without totally killing the framerate, and again, the poly count for a fully polygonal pin board is only really a factor for iGPUs, and realistically speaking if someone’s GPU can’t render 50k – 100k polys, they’re probably going to have a bad time with the rest of Starry Expanse…

Leave a Reply