It’s also an effective way to do object selection under a mouse click. Assign each object a unique color and draw them to an off-screen buffer with a 1x1 viewport. Read back the viewport to the CPU. It’s great because since it reuses the same code, data and hardware that draws the visible screen, you know that it is pixel-accurate and reasonably fast.
Long ago I used this in a protein visualization where you could select individual atoms (with 100s of thousands on the screen). I definitely would not want to promise a ray-sphere test that guarantees pixel-perfect consistency with the render forever.
Clever, but it sounds expensive! Re-rendering the entire scene (especially with that many objects) instead of a single raycast? I'm guessing you had no acceleration structure for any kind of raycasts, and if this was the only thing you needed raycasts for, it wasn't worth making one?
That's assuming you have raycasting rendering(most game engines don't) and/or hoping that the way you draw your ray cast scene matches up pixel perfect with the GPU(it won't).
Generally rendering Geo + Fill is cheap, it's the complex shaders and post that tend to drag down your GPU. Heck, if you're doing GPU occlusion culling you're basically doing the same idea but for rendering whole objects.
You can also do this every 1/10 frames or so to keep overhead low. UE3 and a bunch of game editors use this technique pretty extensively.
Most game engines do have acceleration structure that can be used for raycasts such as one provided by physics system though. Of course it is not 1:1 compared to rendering but coarse bboxes are in many cases more useful than really accurate methods. And you can avoid syncing.
It's "a lot of work" for the machine, but very little work for the programmer. It is assumed that rendering the scene is already fast enough to be happily interactive for the UI. Mouse picking is a human timeline interaction. And, it's likely that the scene is static during the pick (do you want to pick a tiny, moving atom?). So, if the pick is as fast or faster than a single frame of spinning the model, it should be unnoticeably fast for the user.
you only need to draw one pixel, the one under the mouse. cull to only objects under that pixel, set your frustum, viewport, and scissor for that one pixel and I'm guessing drawing is faster than walking triangles in the gpu
It's what developers went to after the even more classic GL_SELECT render mode was removed.
It's not suitable to happen every frame because the CPU stall waiting on the frame to render is unacceptable. Most engines will do a CPU raycast into the scene. I don't know why that you say it wouldn't be pixel-perfect, because it should be. The math will work out.
When you're dealing with GPUs you're never dealing with just the math. Various vendors make shortcuts in the name of performance so there's a good chance your raycast code doesn't match 100%.
Seen this a lot with "golden image" tests, one vendor's rendered scene has bunch of inconsistencies with another vendor's(or different driver version) causing sporadic test failures.
Usually those tricks show up during texturing or because of float/half imprecision in the pixel shaders. Rasterization of should be the same across all hardware, mandated by the D3D spec, and a very well-tested thing in the D3D compliance suite [0]. If not, you would see various seams in applications.
Very nice writeup. A non-obvious bug, easy to follow debugging steps, interesting and simple solution. As a bonus, the color based object selection trick was new to me.
I’m curious because I can’t quite tell. Are they modifying game code, or the code of the emulator?
If it’s game code, how is it so readable in its disassembled form? I may be really misunderstanding but I’d love to hear about how they are modifying the game code so cleanly if that’s what’s happening.
Edit: pretty sure this is a fix to the cpu emulator.
Not the CPU emulator, the GPU emulator. The game is asking the GPU to render an object, which requires it to provide three coordinates. However, the game never initialises the first of these coordinates, so the emulator was drawing an object with the first coordinates initialised to garbage values, because for every new object draw call the emulator was discarding the previous state. On the real hardware it seems that the hardware simply reuses the coordinates from the previous object, so the fix in the emulator was simply to move the coordinates from being local to the function (in which case they're reinitialised on every call) to being global (in which case they remain populated with the previous values between calls).
Supermodel author here. Ian has been doing some amazing work reverse engineering the Model 3 graphics hardware. It's quite an interesting and unconventional architecture, developed by a Lockheed Martin spinoff (Real3D) and pre-dating the more-or-less standardized approach popularized by 3Dfx/Nvidia/ATI a few years later. It was incompatible with OpenGL AFAIK.
For those curious about the architecture and the various discoveries Ian and others have made, I invite you to peruse the Supermodel development forum: http://www.supermodel3.com/Forum/viewforum.php?f=7
24 comments
[ 5.0 ms ] story [ 62.2 ms ] threadLong ago I used this in a protein visualization where you could select individual atoms (with 100s of thousands on the screen). I definitely would not want to promise a ray-sphere test that guarantees pixel-perfect consistency with the render forever.
Generally rendering Geo + Fill is cheap, it's the complex shaders and post that tend to drag down your GPU. Heck, if you're doing GPU occlusion culling you're basically doing the same idea but for rendering whole objects.
You can also do this every 1/10 frames or so to keep overhead low. UE3 and a bunch of game editors use this technique pretty extensively.
Adding one more draw call for mouse interaction, especially one that requires no shading at all, is a pretty minimal additional cost.
It's what developers went to after the even more classic GL_SELECT render mode was removed.
It's not suitable to happen every frame because the CPU stall waiting on the frame to render is unacceptable. Most engines will do a CPU raycast into the scene. I don't know why that you say it wouldn't be pixel-perfect, because it should be. The math will work out.
When you're dealing with GPUs you're never dealing with just the math. Various vendors make shortcuts in the name of performance so there's a good chance your raycast code doesn't match 100%.
Seen this a lot with "golden image" tests, one vendor's rendered scene has bunch of inconsistencies with another vendor's(or different driver version) causing sporadic test failures.
[0] https://docs.microsoft.com/en-us/windows-hardware/drivers/di...
If it’s game code, how is it so readable in its disassembled form? I may be really misunderstanding but I’d love to hear about how they are modifying the game code so cleanly if that’s what’s happening.
Edit: pretty sure this is a fix to the cpu emulator.
For those curious about the architecture and the various discoveries Ian and others have made, I invite you to peruse the Supermodel development forum: http://www.supermodel3.com/Forum/viewforum.php?f=7
One of the more exciting things Ian has figured out is how the Model 3 handles transparency: http://www.supermodel3.com/Forum/viewtopic.php?f=7&t=1523