[Music] so hello my name is Shalon I'm Technical Architect at Ubisoft Montreal I'll be talking today about some of the architectural organization with it on Rainbow six siege so of course the work and presenting today is the work of the whole 3d team so I'd like to thank them in this line so I will start by giving an overview of some of the rendering passes we're doing that in Rainbow then I'm going to talk about two of my babies on this production which is the material based or cross system and the checkerboard rendering so the rendering overview so where is Rainbow six siege first so it's the rebirth of a very large franchise so that meant that a lot of hardcore players were waiting for the game and for the hardcore players 60 FPS on competitive gameplay is really important so it was really important for
us to hit that target also we had to give more importance to the PC build since a lot of hardcore players actually play on PCs also we have a gameplay driven game so if you have to do a trade-off between each graphic beautifulness or animation Beauty or pretty much going to always go for for for gameplay and something that's that that's gonna improve the gameplay and the user experience of the player so also have the destruction as a core gameplay mechanic so and destruction needs to be pretty much consistent between all platforms so since in competitive modes you know manilow and PC to have different outcome than a high-end PC so most most of the destruction needs to be equivalent also was the first rendered that ships it was the first Rainbow six that ships with the engine and we had a
lot of legacy code so Rainbow six a lot of development so through the years a lot of ps3 specific code that needs to be clean up cleaned up so we had to survive legacy code a letter video in Reverse that I just pulled off of the Internet showing what we achieved in Reverse because it's cooler yeah so on on the on the tech side so we were targeting 60fps that meant that we need to run the GPU at 14 milliseconds on average on non-combat creations and we also like negotiated with the engine team at 28 millisecond linear on CPU and our critical path was around 10 milliseconds
on average I also needed to provide scaleable destruction which is important since he won't when a level start to start at 60fps and also when it ends when there's been a lot of combat and a lot of stuff happening on the level to still be at 60fps we also wanted to ship at a resolution bigger than 720p on all platforms and we wanted to combat for 4k on PC so also to support a wide range of pcs so we need to support one gig video graphic card and turns out to be quite difficult when you target current gen since if there's like one thing that we have I think plenty off on the car engine is actually memory so yeah that needed a lot of like worked pretty much get back all the default cost of the engine to be able to run one big graphic cards so one one thing does see is like
live games so we're still doing updates we're still like working on some graphic features we're still doing development one of the stuff we're looking at is auto exposure a lot of players are complaining of visibility from inside to outside and it's stuff that we're still looking for and we have to be careful not to break things like just a couple of weeks ago released an update where some traders were listening we ended up with this bug on screen so here is a hierarchical view of a GPU frame on Rainbow six siege so we spent on average 5 millisecond on geometry rendering so to achieve that we rely heavily on Colleen it's gonna be like the protocol-based Nishida system that I'm talking about in the second part and we also do a lot of shadow caching we spend also average 5 millisecond on lighting we do screen space reflection and this actually also includes screen
space reflection Pass not the ray tracing because it's done on a sink in parallel off of Shadows and here is where the checkerboard rendering technique which is going to be the second part of the talk helps the most and also spend an average like formula second on various post processing like down scaling hierarchical Z buffers and stuff like that on CPU our critical path was around 10 millisecond and we implemented the s cuter we're actually most of the rendering passes can be automatically independent on the number of broad codes which we can do gonna be split up and smaller pieces so even though our back pretty much if we know that it's gonna take around 4 milliseconds we're gonna automatically split it and different pieces and we're going to try to maximize the number of command buffer execution by having the
graphic thread which is our immediate context read steal tasks in order and so pretty much like visibility in all other passes like gets stolen and we don't have any overhead on that and we're able to sell squeeze the most out of our main graphic thread also like education helps here a lot I'm gonna be talking more about shadow caching we like I was saying for me ii actually like currently the 4 millisecond is spent on the back pass and we rarely like go above for millisecond even like with the number of door calls we're doing from virtual dog calls so for example like one of our passes so I'm gonna be repeating : in high Z a lot in this talk because pretty much we do Heisey : and every single pass on our
render so for first person rendering would be much start by rendering the first person character to the G befriend buffer from that we just render the first 100 best occluder that we have on our scene which is usually just the 400 closest object to the camera to the duck buffer we downscaled that depth buffer to generates a high Z Heisey buffer starting from 512 256 and we use that hierarchical Z buffer to actually perform : on the the the subsequent passes that uses the main view and most notably the back pass by itself so for shadows all shadows prima starts off with with a cache and we always when doing caching we generate high detail Z buffer from that cache to perform calling for the resolution part of the
shadows we do it in a separate pass pretty much we had its in the beginning merge with our lighting result but the VG our pressure was too big so we just moved it from the from the lighting resolve and also used iqz buffer of the shadow map to pretty much determine if a whole block is tried or not and skip skip work for a lot of lights we just resolve them in quarter rest turns out to be like a good quality versus performance trade-off so for shadow maps we always talk when loading a level by shooting a static shadow map which is 6 K by 6k 16-bit we do it at low time not at map generation time because we have a lot of gameplays that's where we're actually level designer place objects
are random you're gonna appear and block some passages blocks on Windows and those actually are very good operators that we want to get into the static shadow map and most of the time like 99% of the time there are static objects so it's actually better to have them on the on the static shadow map so from that we generates a 512 by 512 ESM that's going to be used for many large particle shadowing and also generate the hierarchical z-buffer so to scale our shadow rendering we blend between the static shadow map that we generated at map load and dynamic Cascades so and all the dynamic Cascades are going to actually use the static shadow map to do column so like on consoles Xbox one for example the first cascade is fully dynamic because we don't have enough resolution with 6k to actually provide
good enough quality the second and third cascades are contained just the dynamic object and when we render those dynamic object we use the static shot on the static Heisey buffer to actually do Colin and reduce the amount of draw calls and the cost of doing the rendering of dynamic parts and we just blend between the static and the dynamic I don't have the forth cascades pretty much substituted by the by the static shadow map and we have the ability pretty much to scale that then dynamically or like on low-end PC we just generate one one dynamic cascade and everything else is the static shadow map circle because we have a lot of locking lights like usually a level is have like more than 80 shadow spotlights we can't have them all in cash at any given point what goes to machine memory so what we do is when we encounter on
lights and we actually only support a maximum of eight shadowed lights on the frustum so we encounter a new light we always shoot the static part of the shadow map when we shoot the static part we generate I can see buffer from it then we rendering the lights with just instead of clearing the dead buffer we just copy the static part into the dynamic part we render the dynamic objects on top and of course we do Conan since we already have a hierarchical z-buffer and then the result resides in an array that can be accessed in further classes so for the lighting our lighting is based on clustered structure each frog cell is 32 by 32 pixel based and has a ZX motion distribution and we also perform how he called Conan on the lights volume is in the hi-c the view
has a buffer and other like just going through the structure hierarchically and we fill each cell with the list of light that's that we gonna render on that cell and we also see local cue maps as as light so they are gathered within the lags list volume and just like when we render them like they're pulled off the same way we just maintain an index and they actually reside in a in a cube map array the same thing we do for for shadows and gobos so pretty much everything is in arrays and we only maintain indices in that array and you know during the rendering we just need to fetch the shadow or the the cube map for a given cell so that allows us to pretty much handle the same features between the four world and the deferred pass so I'm gonna be switching to to the
second part of the talk which is the material based doorto system so Rainbow six destruction we had like a main guideline from the art direction which was when destruction happens really needs to be visible so when you go into a room you didn't see destruction happening you just see the aftermath of it you need to know that there has been like a big change so we need to pretty much have very persistency of objects and have this feeling of a lot of volume getting generated from destruction fortunately we're dealing with a close quarter game so we have walls and floors mainly to do to destroy procedurally they generates a unique geometry problem that we have with that is that they're usually very good occluders but not in our case when you start poking holes into all the floors like you lose accurate efficiency we also have a lot of to do a lot of
debrief and improv so those are usually smaller meshes but there are in greater numbers can be also be procedurally generated so they have unique geometry or instance so here's an example of a destruction so I'm gonna decompose that so in our game it's really important to have like the the player model the the destruction the way he wants so if he wants to shoot a wall that's a hole that's like just two centimeter from the doorframe you can do so so generate a lot of unique
geometry for that and to pretty much have this like volume that gets in conserved from from from the hole in the first place we generate a lot of debris to fill up like the space beyond the player like in this situation we have more than 100 degrees that got generated from the destruction some of them are going to disappear but most of them are gonna stay persistent during the whole actually around which can last up to three minutes or even more on PvE games for the illusion of efficiency so I took a wall and shot a couple of bullets into it so you can see how pretty much those holes are going to propagate along the article z-buffer and thus making like an object that's a small enough on screen but we're gonna pretty much select this this high Z
level the high Z texture level to do the a/b test and we're going to actually pass that as visible even though it was test like on a bigger level it will have afraid to test so pretty much easily we'd use our illusionary efficiency just by poking a couple of holes in any given wall and that happens a lot in game playing actually so the early prototypes were largely graphic CPU and GPU bound like once we go through completing the destruction by itself I have to render it so there is all the degrees that get generated all the unique geometry that gets added into the so we started thinking about ways improving that and of course our first implementation only had the PC DirectX 11 deferred context style of render which is not great at scaling so we thought of like what we have been much
on our scenes and you can see that when you do a distraction usually a lot of stuff share the same material so most of the debris that are going to be generated share the same material walls usually also share the same material it also goes from like this art direction where destruction has to be like I visualized by the players so if like we use in a red wall means that can be destroyed so it's a lot of red walls here in there so we can actually batch a lot of stuff by a material by design and as I said all the debris share the symmetry which which helps there has been a presentation because we're sharing a lot of work with the assassin creed unity done by a record last year that's interesting like there's stuff that I'm not covering but actually they are covering and I invite you to go see it also we wanted more granularity in Colin since when when we decrease like the efficiency of our column we want
smaller unit to actually be able to select a higher level map in our high Z buffer to do the cutting so to implement that we start off by unifying a lot of buffer buffers that we have on our render so in Rainbow six a lot of resources will reside in some unified buffer of some sort so we have the unified vertex buffer which is like one buffer that contains the charity for the whole map and you find index buffer same thing unified constant buffer it contains all the contacts all the constants for the whole vehicle so we pretty much simulate structured buffers on top of that they're implemented as byte address buffers but we journeyed some C++ code to help access that from the CPU and from the chip and some hlsl code to help access it from the GPU we also add some metadata to pretty much explained how we're going to access this
data to do it more efficiently so here's an example of how we generate that code so we go from the scripture that is in C++ that pretty much it just goes to the different different values we're going to pass on to the GPU like we declare them we generate an equivalent HLSL code that's going to help us load that that chunk of data from unified constant buffer so all the protocols are going to be used just and bind just this buffer and organize use and this is that we pass along with our pipeline to be able to fit to fetch those values so what's cool about that is that we have complete control over data layouts so again easily like we tested a lot of stuff like structure of you 32 arrays and just a couple of lines actually in our code to change from one method of access to
another like a OSS way I can also support in constant buffers on your type of data and be pretty much going to be hidden from from the high level programmer can have like RGB a8 or other type of format and it's just going to be transparent because we generates all the code that does that in and in some certain extent and especially on GCM anyways those are instructions that do the same thing so we'll just do it ourselves so we also support some high level API to be able to broadcast values into into different constants so since all the copies and all the management of those buffers we handle ourselves using compute shaders can pretty much to do broadcasts and values quite easily and code generation are always nice on iteration and to to migrate access
pattern to do tests and to pretty much do I really want with our data and give us more control on our render so as I said so the geometry which is the unified vertex buffer unified index buffer and constant are unified in our renderer so at Oracle is going to be defined by the shader it uses the non unified resources like textures and the render state like center state rested States etcetera so elements that are going to share all of the above are going to be batched together and all the passes that actually just going to use a subset of the different non unified resources like in a shadow pass which is going to if you don't have alpha testing we're just gonna skip fetching the the textures with just like remove them into our our
batching computation so when we initialize our render or when we load on map so each sub mesh instance is going to be mapped to three types of batches that are going to be used like altered rendering which is the normal shadow and visibility so those kind of like types just are a helper to mask different type of data and try to to remove some of of the stuff that is not used in any given path to to increase the amount of batching and each one of this batch in the end when doing the rendering is going to correspond to a mutual indirect index in direct command so here have an example of three some mesh instances so each one of them is going to map to a different normal batch two of them are going to share the same visibility batch then it going to be drawn with the same
which is drawing indexed in direct command and the instance two is going to have its own visibility batch and then the three of them are going to share the shadow batch just usually the case like shadows are more vegetable by design so and then on the GPU to pretty much access the data for each of those sub mesh instance instances we maintain a globally unique index ultra our pipeline that is maintained by the GPU and help us fetch any kind of data for our sub mesh instance so from that in index we can go to a true multiple level of indirection to fetch like the vertices that are going to be used when when writing the vertex shader the transform matrix so just like depending on to know the data it's me on a search and it's frequency of access is going to have different index and it enables us to fetch whatever we want
what's important to note is that sometimes we we take one level of indirection off just to speed up the access because we don't want to do too much interact accesses to some of the like further laid out by data so for each pass that we do like the Oh back pass what we start is gathering dynamic buffer all those globally unique in this is so each pass is going to map only to one batch type like normal shadows or visibility and takes us around 1.5 million to gather all of those indices for all for all the passes so we just like spawn jobs at the beginning of the frame we gather all those indices and then we start rendering using using the dynamic buffers we also append some extra data that can change between frames and we
also write our offset into into the dynamic index buffer that we're going to be building when we gonna do with the advanced calling on per triangle so from this path buffer that contains the list of all the sub meshes for a given path we we start by doing sub mash instance column which is like gallon per dad frequency it's either going to draw the object it's gonna go either brightly to the draw path or it's gonna be further cold or just going to discard it from the start so I think if it goes further down the column path so all of our measures are split in chunks of 64 triangles we do calling for those 60 64 triangles and if the chunk passes this this face we do back face culling per triangle and in the end we only draw pretty much the
triangles that are that passed all of those those calling levels so on the first level of calling which is like per submission since we do we start by screen space sighs : then we do distance : frost sampling occlusion culling so our render actually starts by doing like a very coarse curling phase which gathers a bulk of objects and then we just go through them and further call them can actually discuss a lot of stuff on the first term : pass to the second level of Conan which is by 64 triangle chunk of each one of those measures that passed the first level of Colleen we're going to pretty much go to the same thing we have we substitute the decisions Cullen by our orientation Colleen so in a baking phase and we just try to compare computer normally and spread for each chunk and we do orientation calling at that stage and then we do occlusion
culling so occlusion culling on that stage enables us to improve our culling efficiency since we reduced the size of any given object we're going to call to two tranches of 64 triangles so you have better chances to fetch the disease samples from higher level map on the high Z buffer then if everything passes we do just triangle normal Conan we maintain a buffer actually for each triangle and we don't fetch the vertices and we have a buffer with per triangle normals that we fetch and do the so we go from this past buffer we do all this type of cutting then we're gonna fill up Richie drop our meter buffer and for each batch it's going to be me to draw interact with all the parameters laid out so and then on the on the draw
call itself what we do is we start from the start in instance that we fill out during the column to fetch the sub mesh instance index which actually the source instance it's directly to some mesh instance index and that is used to fetch the the unified constant buffer for that for for that instance we're going to help us through an indirection get the mesh descriptor and using the sort index which is a vertex ID and the dynamic index buffer and unified vertex buffer we're going to fetch the vertex data so in the end like we run our vertex shader and you fetch all of the data ourselves yeah so vertex shader is gonna pretty much consists with this of this fixed
input which is the per instance primitive info that we're going to just fill out with the global instance index of each sub mesh and then the hardware generated vertex ID and then we already generated the code to actually fetch from the global buffer using those indices the the vertex info by itself then to pass on the data from the vertex shader to the pixel shader since we don't want per pixel to actually fetch all the interactions we pack up all the resultant indices that are going to help us like get any kind of info using the vertex shader in a non interpolated attribute passed on from the vertex shader to the pixel shader and what's important to to notice is the good optimization is actually use read first
lane on that value since we want both everything in there to be in in the scaler registers and we got pretty good winds on on Xbox one and ps4 were using that so here an example of like doing this fraction and amount of draw calls added to the scene so just took a walls that similar in material showed a couple of explosive into into them and we just added five protocols compared to to the ocean and seen that we were drawing so bad visualization pretty much that shows you the color coding of the back you can see all the debris is sharing the same the same batch also the D cost is all drawn on top of the debris czar also using the same pipeline we have some tricks to actually do sorting per batch level on them and we pretty much impose some constraints on the artists on the
number of lovers that they can have on Aundre on their D cars and there are actually the sorting of the decals it's kind of fixed so they have to choose from the beginning that's always gonna have because decal like type one on top of the car type to whatever happens so for this scene for example we go from ten thousand five hundred protocols like those are draw calls that we were we would do if we didn't have the whole system so they correspond actually to the sub mesh instance the submission instances that we're going to draw in the scene in the end from that we end up with 500 or 412 batch draw calls for the apogee cars and visibility pass this each pass is usually on our lender just six I think to eight draw calls on an
average like something like really ridiculous and 64 broad calls for the shadows and from beginning to start we managed to drop off 73% of all the triangles that were originally on the scene knowing that we already do course calling on CPU to start on so what problem that we shipped with and we were working actually to improve in future update is that we push in a lot of empty bottles empty protocols have a cost so just like the command buffer running on only those empty comments is gonna take time and it's time that's the GPU is doing nothing we try to hide it with a sink with a sink jobs but it's not enough it's actually if the GPU can manage the work it's better to do work so the next thing is pretty much just be able to specify the file and number of
goals on the GPU and when we know that we called we called on the GPU like 10 out of of 20 draw calls will just gonna do in issue those tentacles instead of issuing the whole the whole 20 protocols and have some of them with zero indices you can pretty much move to bind these resources to to improve the batching we still don't see the definite the benefits right now for it but can help us also reduce even further more load on the CPU and we want to move all the travel of our scene graph to the you pretty much have all the seen being the scripted on GPU the CPU doesn't know pretty much anything about it right now we have very complex CPU code that does energy selection we're moving it to the GPU and then we're going to do a lot this election on the GPU and everything is going to go without any CPU
intervention so the last part of the talk just checkerboard rendering so for us as I said 60fps was important for adversarial PvP games so we wanted to target early on in production so we're working hard like pretty much to reach it like in our first playable we managed to have 50 fps a couple weeks later we were in 60fps and and afterwards were just like pushed to maintain that framerate so anything that goes in in the engine or in the art have to have to be performance improved approved and to be able to have like a quick jump on on the framerate we started thinking out of the box like Killzone came out when you are like pretty much working on those technique and one interesting thing that
they were doing is pretty much rendering half the pixels on screen on their adversarial games they keep the same cost per pixel as a 30fps game but their technique was a QA based and we wanted our stuff to run on PC too since we wanted really to to to have like the best performance possible on low-end PC and have decent framerate with decent GPU on 4k was a big quick win for us we just enabled it like without telling anyone on the floor that what has been enable just to see if people will notice like an equality equality drop luckily people noticed like just the increase on frame rate which is good so what we implemented was pretty much instead of rendering to 1080p render target we reduce the resolution on the y-axis to
960 by 1080 we maintain a velocity vector per pixel which is 3d and we we have our 12 V 12 B 8 format that you're gonna have in watch the implementation of it on the bonus lab if you're interested then each flame we just offset the projection matrix to to render either the even or the old lines and one important thing to note is that to get correct texture gradients you need to fix them up since the scanning is not uniform between the two access we need to divide the d/dx by 2 when doing the sampling so we need to go through all our shaders and fix them up depending on what they are doing on screen so of course things that are not represented by motion need to be dealt with so lighting changes shadow changes transparency we started
like doing a lot of like work to maintain a history buffer just off light and in shadows and try to determine air for a given missing pixel there has been like it a change just on either of those to better fill that missing pixel what's taking like too much actually GPU time in too much memory additional mirrors if you have to maintain between frame we end up pretty much just relying on on color clamp and most of the time as explained in the in reality a paper also a lot of data I needed to be tweaked artists like to do very sudden effect and usually both effects are the ones that are not represented in the with velocities like they're going to do like one frame flashes particles that are going to change per frame so you can
have that if you want to have decent quality so a lot of stuff needed to pretty much between to have like less harsh transition and all our like engine associators we just pack them to never have a 0 to 1 transition it always go to a smoother curve so I had a lot of issue with areas and on vertical lines and like fixing that wasn't easy after after all and took us I think a couple of months to to get to something working properly but we were not satisfied with the ideas and properties of the technique we chose so this were checkerboard rendering comes so the base idea was to actually solve the aliasing issues we experimented on a series of images and compared the signal-to-noise ratio and was usually better if we were using checkerboard
pattern to interpolate between between the missing pixels so the idea of using emissary to X like was bison bouncing around since the beginning and we made that push for it 43 2015 in two weeks we switch to the technique that is cry right now so here is a couple of examples so we took some images to carve the the vertical lines the vertical lines and we reconstruct the image from from the neighbors the direct neighbors of each pixel so you can see line versus checkerboard like there's less visible areas in on the image sorry about then soirée the resolution of of those screens are not high enough to display the image properly so other example checkerboard versus line less areas in like an hour computer generated English
less aliens sorry this is actually shows the death so how do we manage that so we started by a render into a quarter s render target squats or rather on your target is going to be MSA to X so quarter res which applied by two half the pixels they must be ends up being the same amount of pixels as the full the previous technique so we just use the standard MSA a pattern which has two color and Z samples which is perfect we want this thing to run on PC and that works on PC and we just need to run our pixel shaders for example instead of per pixel because we still want the frequency of radium to happen on each of those samples we are rendering so in the
end each of those sample is going to line up exactly with one pixel on the final render target so checkerboard render the good thing that we get with that is that on our particles we can just instead of evaluating the pixel shader for sample can do it per pixel we do that for smoke and other effects so we don't have to deal with sorting between like small particle buffers and and normal particles everything that needs to go full quarter res it just gonna be evaluated per pixel instead of per sample also can fit a lot more stuff on the SRAM so all of our render targets are actually half the size which is good and we don't need to fix up the gradients anymore why because right now the scanning becomes uniform so instead of dividing the dxdy which actually costs some ALU
we just bias the maps by by one and we have the same result so instead of being some cost that we do on the CP on the CPU on the GPU it just like all of our samplers change the mid bias when we switched on this technique actually this technique can be switched on and off on PC so consoles give you the control of choose in your sample locations it's not possible on all GPUs on PCs I think if only on Maxwell on Nvidia but has been there for a long time on AMD and actually so 25-hour implementation instead of switching the sample location we just offset the same way we were doing before between two
frames so it's going to yield the same result but we're going to end up with one one missing line that we have to do proper clamping when you actually fetch fetch the values so to reconstruct the missing pixels which are represented here by P and Q we we sample the current frame dark neighbor are in linear Z we sample the linear Z direct number neighbors we also sent to say sample direct neighbors colors for each missing pixel and also we compute a history color there's a seamless thing in Z so to compute this history color we we're
going to select one of the direct neighbors velocity to do reprojection so we choose we test the wall a couple of stuff we ended up just choosing the closest one to the camera since we always want to preserve the first person object on screen and you don't want its silhouette it should be to be mixed up with other stuff so we chose the further the closest pixel like currently rendered pixel to the camera and with that million motion velocity we run we sample the previously resolved colors so the full resolution render target so we use a linear filtering and actually I think we need to try to choose captain or run interpolation it's gonna eat better results but that can introduce accumulation color we didn't see that much effect on our rendering but it's it's a possibility that we
didn't actually run into and then we clamp the reprojected color there's history color we computed to a beast to the neighbors berrak neighbors like 4q it's a BEF and then using the the previous depth because we maintain a 3d velocity vector we determine if the pixel was hidden next frame and we blend back between the arkla and clamped and clamped value dependent on how sure are we of that pixel being hidden or occluded in the in the previous frame so now having this history color and the interpolated direct neighbors the enterprise is color from direct neighbors we we define a weight that we're gonna use to blend between the two since like usually when you have
transparency when you have lighting changes it's always or most of the time it's better to to just use the interpolated color instead of the rejected color it's going to here like very results on the image so depending on the minimum difference that we have between the doors neighbors we're going to define a weight and we combine it with different weight depending on the magnitude of the velocity so the bigger the velocity the more we can attend toward the the interpolated color instead of the clamped color historic historic color and yeah we using those two weights we just write out the final the final color so here I'm not gonna explain this diagram just like to show you like the complete pipeline that we go to to determine this
this missing color is actually a lot of passes and a lot of determinations and we have a lot of knobs - sweet - to either have more ghosting or more flickering depending on on the situation on the map so the resolve is quite contacts and we did a lot of tweaks to get it right for our for our rendering cost which runs in full res and cost about one point four point one point four milliseconds on Xbox one and we have a net win of ten of eight to ten millisecond each frame so on top of that we run taa so it integrates well with with checkerboard render is it already like doing the whole clamping and velocity vector reprojection and actually can it can run on the same shader but we separated just also for VP
or usage so it's kind of like imagine that our pixel is actually our one of our samples so per sub sample level we have this flip board NSA or exile jitter the friggin eyes just flip through each frame so we have a total cycle of eight frames - to determine like the final color of one pixel and the report exit color uses similar weighting logic that we use for for determining the missing pixels and on top of that we run this untighten algorithm - to smooth out some of the imperfection that we that we have when we do in the resolve so because during the resolve we're gonna enter leave perfect pixels that were rendered the frame we sure about to unperfect pixels that were projected and that went to
show a lot of like history so we apply a filter to detect patterns that we might introduce when doing the resolve so that that filter the detector filter is gonna run on five horizontal or vertical adjacent pixels we determine the threshold and depending on that threshold and if each of the five pixel either are above or under that threshold we we generate a mask of either of 0 1 0 1 0 or 1 0 0 1 0 1 anyway and we we detect that pretty much we have the stitching pattern on screen and if that's the case we just blend more between the different sample to try to remove it so it's more blurriness but for better image quality in the end so the overall technique was like very good for us we managed to get a lot of
GPU time out of that a similar quality life is close enough to that like for our 3d programmers of course we're gonna see that there is something being done on the image but for the average user and just like means better frame rate which we are what we were targeting and it's really interesting when you want to target 4k because instead of like having this 4 times the resolution skin is just 2 times the imposition wasn't very scientific was pretty much trial and error just testing stuff seeing if it looks better on our game so maybe we need to define a better framework for what we did and have a more scientific approach of whatever ways we choose to have in the technique and pretty much we're still gonna like run with it even if we have like better GPUs or whyever just because allows us to actually push
the quality per pixel which is interesting so special thanks to all those guys that helped on the presentation and also for the insights on the techniques thank you guys questions hey great talks from playing Rainbow six I noticed there's no motion blur in the game is that because of the checkerboard pattern it was actually a choice the art direction choice not to have motion blur and the technique actually and GA by itself introduces this kind of like burn on the image that I think was enough of
Lorraine for for for what we had so was a kind of like an art direction choice not to have motion blur but actually makes sense to have motion vs. is going to help smooth out the results and less than any artifact you might have from implementing the technique good thanks thank you great talk so the checkerboard rendering introduces interframe dependency how do you deal with multigp rendering so it's one RGB eight buffer that we have to think between frames so on Rainbow six we we disable all all driver syncing and it's like the driver is not aware of any of the buffers well we just disable like hardware handling driver handling from our resources and we do our thinking ourselves it's gonna
costs but we pretty much managed to have the scaling on other parts of our rendering and we still end up sinking that buffer which is one let's say of the few buffers we are thinking the technique we use for other stuff is pretty much we do in the work each frame on a GPU rather than having the between frame dependency thank you other questions yes that's it cool thank you okay I'll be at the Ubisoft lounge tomorrow at 11 if anyone have additional question I just want to talk with me I'll be like on the wrap-up room right now thank you guys you
Auto-generated captions from YouTube — names and numbers may be misheard.
Game Developers Conference · 2026
Game Developers Conference · 2017
Comments