Shaders Case Study - Dishonored 2: A Crack in the Slab

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Hey r/Unity3D!

Got a new exciting bideo for ya. Check it out! There were like 3 things in my recreation of this effect that I had to skip over other wise the video was going to be 20+ minutes so I expect some follow up questions. Hit me up here or in the comment section if you've got some!

Thanks for watching everyone!

👍︎︎ 17 👤︎︎ u/Broxxar 📅︎︎ Feb 17 2017 🗫︎ replies

When I grow up, I wanna be like you

👍︎︎ 8 👤︎︎ u/thatsabingou 📅︎︎ Feb 17 2017 🗫︎ replies

I maintain my stance that shaders are sorcery.

👍︎︎ 4 👤︎︎ u/kindath 📅︎︎ Feb 18 2017 🗫︎ replies

this guys channel is awesome!

👍︎︎ 2 👤︎︎ u/Errigan 📅︎︎ Feb 17 2017 🗫︎ replies

Thank you for making this video! I was looking forwards to it ever since you teased it on the Debug Log :) As a graphic artist I've been dabbing into CG as a side project for a while, and your videos were a great help in getting me started with C#. Keep it up!

👍︎︎ 2 👤︎︎ u/[deleted] 📅︎︎ Feb 18 2017 🗫︎ replies
Captions
[Music] hello and welcome to another episode of making stuff look good in video games Dishonored 2 is a pretty dope looking game it also has some of the most clever level design of anything I played last year the game striking visuals and cunning design are best represented by the 7th mission a crack in the slab it's hard to say whether the outsider's timepiece is more visually or mechanically interesting anyway you cut it though the depths pulled off something super cool so let's break down the effect and see if we can recreate something similar in unity at any point we can look through the timepiece and see into the other timeline we know it's more than just discolorations and distortions because there are plenty of differences such as variant meshes and NPCs that are only visible in one timeline or the other so I suspect we're seeing two different scenes being rendered the timeline the player is not in it's rendered to an off-screen buffer next to the scene is rendered normally to the screen and finally we render the timepiece itself which samples from the first steps buffer to create the illusion of looking between the two timelines as for where the two scenes contents are actually positioned my suspicion is that this came down more to the physics and mechanical implementation more so than the visuals from a graphics perspective there's not a whole lot of difference between having two scenes worth of geometry stacked right on top of each other versus physically separating them either you'll have to make your camera's only render the geometry they should see or you'll have to position the cameras in both scenes such that their view is the same the timepiece itself is doing a bit more than just sampling screen positions into the offspring target it appears to be a refractive piece of glass and it's likely using a normal map to distort the sampled image it's also probably using some variation of a PBR shader with some colored specular highlights and I think it might be using the roughness map to dynamically blur the sampled image or possibly just blend between a crisp and a blurred result this stuff all gets a bit more complicated and add a scope for what I'm hoping to cover today so we'll just cover the refraction which is really the cooler part of the effect aside from the timepiece and it's fancy rendering shows the effect of jumping between the two timelines when the timepiece is activated there's a lot going on here but it's nothing too complicated the feeling of warping comes from animating all the cameras field of view increasing with some fall off and then snapping back it looks as if the players held items move closer to the view as a field of view increases this gives the illusion of the object staying the same distance away but stretching outwards as the field of view stretches out the screen color desaturate this makes the transition when we eventually swap cameras less noticeable because the two scenes look much more similar in greyscale than they do in color there is also some blurring going around with a bias towards the corners but it seems to be inconsistent with each use of the time piece it might be randomized in some way or it may be it's just some weird byproduct of changing the field of view with motion blurring enabled while before going this part of the effect in favor of a more traditional than getting which gives a similar tunneling effect after we swap cameras all the stretching and desaturation snaps back very quickly like in less than 100 milliseconds it seems a particle system of floating light sparks is spawned at the player location and a short-lived screen effect with some distortions please I'm going to gloss over the post-war perfects but covering the refraction on the timepiece will give you a good idea of how you might implement the screen effect seen here okay let's jump into unity and make some stuff there's a lot of flexibility in how you set up your scene for this effect so here's how I went about it in one scene I have the regular level geometry and all of that has its layer set to universe a we then build universe be in a separate scene this lets us keep the light map for the two scenes separate which will be helpful for more complex scenes again all the geometry here is placed on its own layer called universe B we'll add both scenes to the build settings and in a small script in universe a will load the universe B additively when the game starts the player will actually have two cameras attached to them one which has to call flags set to only see universe a and another that only sees universe B if we played right now both things are going to be rendered and which one we see is dependent on their debt note this depth value on unities cameras has no relation to the dead buffer it actually is just a floating point value use the sort which order cameras should render in from low to high if we were going to swap between universes without being able to spy into the other we could just set only one camera to be active at a time but we're going to get a bit fancier than that so let's get set up to render our time peace I made a small script called the twin camera controller when the game starts it creates a rendered texture that is the size of the screen and sets it as a global texture available to shaders will set the hidden cameras render target to this new render texture the hidden camera is a universe we aren't currently in so it will start as a camera for universe B when we want to swap between universes we'll have the active camera set it to target to the hidden cameras target and the hidden camera set its target to null which means it will render to the screen then we'll swap the references to which cameras which so that the next time we call our swap cameras function will switch back from universe b to universe a in our game if we call swap cameras will instantly switch from one universe to the other meanwhile the hidden camera will actually be rendering off screen for use and shader so now we'll need our magic object that can spy between universes I made this janky looking model of a handheld mirror but you can use whatever you want here the most basic implementation of our magic item shader will be one which computes screen coordinates for each pixel and then uses those screen coordinates to sample the render texture we set up earlier so let's start with that we'll need an interpolator to store a screen position in as it goes from the vertex shader to the fragment but notice that I'm using a float 3 which is one more dimension than you might expect to see considering we just need a 2d screen coordinate the reason is explained here in our vert function after our vertex position has been multiplied by the MVP matrix it's in clip space coordinates which is in the range negative 1 to 1 now before we remap the point by adding 1 and multiplying by 1/2 we first need to account for prospective distortion for our usual fragment position the GPU will divide out W component of the vertex position for us but for our own coordinate we'll need to divide out the W component ourselves the issue is if we perform this perspective correction before interpolation our values get all jacked up they look kind of correct but at grazing angles and points close to the camera in particular won't look quite right so we actually want to perform the prospective correction after interpolation this is why we use a float 3 instead of just a float tube will store the W component in the third channel so that we can use it in the fragran now we can safely remap our values from negative 1 1 to 0 1 as a little optimization you can still do the remapping in the vertex shader you just have to be crafty we know that this value is going to have the W component divided out eventually so adding one wouldn't be correct so what number could we add will be one after the W component gets divided out the W component so here's the final line in the vertex shader and the line performing the perspective correction in the fragment shader if we use this screen coordinate to sample our render texture we get this simple base for our effect if I hide the metal part of the mesh you can see our efforts to have aligned cameras viewing identical geometry have paid off and it's a seamless transition from one universe to the next the glass on my mirror is broken up a bit and I've gone one step further and made a smashed glass normal map that should make for some pretty interesting refractions so let's tackle that next I'm going to gloss over normal mapping code here and possibly cover it more in depth in a later video but these chunks of code are pretty standard and you can find them in various flavors and methods of them on different tutorial sites and different rendering forms the end result of normal mapping code is usually a normal vector in world space visualized here on the mirrors but before we can use these normals to distort how we sample the render texture we'll want to transform them into view space when we visualize the view space normals they appear consistently colored as we move above the scene instead of changing drastically with our world space rotation now we can use the normals x and y components multiplied by some value to create a refractive effect on our glass at this point you might be asking a very important question isn't rendering the entire scene twice like really taxing the answer is of course yes drawing the whole world twice basically means doubling your base frame time so there's a funny little hack we can do to cut down on all that over draw this episode's already going to go way over on time so I'll point you to the code on github for implementation details and just give the high-level description we're only seeing a small portion of the other universe that is the area covered by our mirror but we're drawing the entire screens worth of pixels what if we could only draw a small area around our mirror that we'll need to sample our refractive glass room when we render the opposite universe instead of clearing the Zed buffer to the maximum distance as you typically would in the frame will instead clear to the minimum set value such as everything would be cold then before anything else draws an invisible qualities drawn this quad is set to always pass the Zed test and regardless of where it sits physically in 3-space it always writes the maximum depth value it's almost like we're punching a hole through which the rest of the scene will draw we can see the net effect visualized in the frame debugger where pixels outside of our special depth quad are called and only a much smaller portion of the screen is drawn with that optimization and we've just about covered everything we can jump between universes and our magic mirror lets us peek into the opposite universe but before we wrap up let's make the transition between universes a little bit more satisfying we'll basically just animate all those things I mentioned earlier there is a lot of freedom and how you implement this I just used a color team and that doesn't animation curves to control the various values to reiterate will animate all the cameras field of view a full-screen desaturation of an yet effect and the mirrors position relative to the player and then there's one extra thing you might want to animate that affects both the visuals and the mechanics the global time scale it creates a feeling of time crawling to a halt before jolting back to its regular speed most of my curves have a shape like this with the value easing toward its target and then rapidly moving back to the default value but with a little bounce pass through the resting value this little bounce is especially noticeable in the field of view and creates a wobbling sensation after the player changes universes and lastly never underestimate the power of adding a good sound effect to your game to make everything just a little bit more compelling [Music] the sample project and source code used in this episode are available on my github I hope you enjoyed this episode of making stuff look good if you want to support the show you can ship in a buck or two on patreon for each new video thank you to all my patrons for your continued support you rock hey do you like video game development do you like podcasts well if you answered yes to one or both of those questions then you should check out the debug log it's a podcast by devs for DES I work with a few of the hosts and converts for them being equal parts knowledgeable and then obtaining this week's cast features yours truly so if you want to listen to me babble on about shaders and junk for like 45 minutes you can totally go do that you can find the debug log on itunes or at its home on the debug log comm and as always thank you all for watching keep on making those videogames [Music] [Music] [Music] you
Info
Channel: Makin' Stuff Look Good
Views: 51,131
Rating: undefined out of 5
Keywords: Unity, Unity3D, Game Development, Programming, Dishonored, Dishonored 2, Portal Effect
Id: dBsmaSJhUsc
Channel Id: undefined
Length: 12min 16sec (736 seconds)
Published: Fri Feb 17 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.