Sign in to follow this  

DX11 DX11 - Instancing - The choice of method

This topic is 1655 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello guys!

 

As you know, Directx has a feature called 'Instancing', which is quite wonderful actually. But how to use it properly is an entirely different chapter. And that's why I'm here.

 

Imagine Crysis or some other game where high level of vegetation is needed, as grass, and you need to render that. What I would do, and what I actually do:

 

When creating the scene:

 

When Creating the scene:

  • Add all instances of grass
  • Split all instances into different patches
  • Create Instance Buffers

When Rendering the scene:

  • Do some range testing and frustum culling for all patches, and all valid are added to a list.
  • Render each individual element in the list

 

Now that method works fine for me when using static non destroyable or creatable objects, but imagine if one object has to be created? Do I recalculate the respective buffer for the bullets location.

 

Also in the Crysis Sandbox, or CryEngine 3 SDK, when placing/painting vegetation over the terrain, they appear lightning fast! If you have time, how does produceal vegetation work in a nutshell?

 

But what if we are simulating a space ship, IN SPACE! Where the space is almost unlimited, and then the player shoots, I can't have buffers for an unlimited space! (I could create buffers dynamically for where I am, but I'm not sure if that's efficient).

 

So, now, I ask you, what, if it exists, is the most efficient way of handeling instances of an object?

 

Thank You, again...

Share this post


Link to post
Share on other sites

Do I recalculate the respective buffer for the bullets location.
 
i don't quite follow, how'd you get from instancing vegetation to drawing bullets? 
 
some cry3 specific thing i assume.
 
generally speaking, you'll have a model in buffers (vertex and index), and transforms for each instance in another buffer.
 
for modifiable vegetation, simply recalculate part or all of the transform buffer when required. you may also be able to do some indexing trick to only draw a range of instances in the transform buffer, and include/exclude vegetation that way.
 
although i'm not that well versed in dx11, i think you could also pass in flags in a constant buffer that could be used to determine which instances to draw.
 
it my also be possible to use some reserved value for a variable in the instance buffer that indicates "don't draw this instance".
 
it also may be possible to pass in some sort of "instance buffer" as well as the "transform buffer". the instance buffer would have a flag for each instance or perhaps work with indexes. not sure how many input buffers of what type you can use in dx11.
 
in the long run, recalculating the transform buffer, passing constant buffers, reserved values, an instance buffer (if supported), and drawing index ranges are all just different ways of telling directx which plants to draw.
 
me personally, i'd probably just recalculate the transform buffer as needed. i do a similar thing in my current title. vegetation is modifiable. terrain is drawn in chunks which are generated as needed from the various data structures that describe whats in the world. Any immobile object created in the world (a hut, landmark, storage pit, temporary shelter, or bedding) means the plants there should not be drawn. so whenever an immobile world object is created or destroyed, its "chunk" gets regenerated to add or remove the plants as needed.
 
when i generate a chunk, i basically do my own instancing to create the ground mesh for each ground texture tile. the "model" is a simple quad. a "pattern map" says which quads in the chunk's ground mesh use a given tile texture. for each quad that uses a given texture, the "model" is transformed to the correct x,z location in the chunk, procedurally height mapped in y, and added to a VB and IB. there's a VB and IB for each ground tile texture in the chunk. when its time to draw, i just set the ground texture and draw the VB for that texture, as the VB is already "pre-instanced" so to speak.
 
i still don't get the part about bullets and buffers though...    

Share this post


Link to post
Share on other sites

With the bullets, i mean this:

 

(Just an example)

 

Imagine thousands of spaceships, shooting lasers (or something) all the time, I would imagine that I would instance all bullets, wouldn't I? In that case I don't really see how I would split up each bullet up into a chunk, as they could go anywhere...

 

But Thanks!

Share this post


Link to post
Share on other sites

In general you take a single geometry object (i.e. a vertex buffer and possibly an index buffer) and replicate it as needed. Where and how many to replicate is determined by the instance buffer. Here you will put the world matrix (for example) of each bullet in the world. In your shader you will multiply your model's vertices with the particular instance's world matrix to determine where in the world that particular instance is. It's like you were using the geometry you wanted to instance as a stamp, then the instance buffer would contain the locations where you need to stamp :) (and other properties, like the colour for example)

 

If the number of instances changes, one approach is to recompute all of it and re-bind it. If your geometry is mostly static, then another approach is to a create a "large enough" buffer. You can bind it even though it could not be full. When the need arises, you simply add more entries to the buffer, without having to recreate the other ones. This also sets some sort of limit for your rendering engine as it's like saying that you can render up to X instances. 

In the specific case of spaceships though, I think you'd need to update them each frame. BUT! If you go for the hard science approach, then you don't need to worry about any of this at all, beam weapons are supposed to travel at the speed of light so .. problem solved!

Share this post


Link to post
Share on other sites

Procedural generation of vegetation typically means that you don't have to know / create all the instances before you need them. That is, when the vegetation block is close enough you can calculate what kind of foliage / vegetation should occupy the area. 

 

That means that you'll need to update your buffers dynamically. You should however split the problem a bit. Instancing and how to handle vegetation are two separate problems. 

 

Instancing just enables you to draw multiple meshes with same / similar parameters in one draw call. How you update / organize your data is a different matter.

 

I'm copying my instance data every frame from main memory to a generic float4 buffer object. Typically this means something like 1-3 megabytes of data per frame depending on the amount of instances and data required by one instance. The data transfer isn't a bottleneck if implemented correctly. This kind of arrangement allows me to cull objects pretty well (foliage and such in bigger blocks) and have exactly one draw call per mesh.

 

Cheers!

Edited by kauna

Share this post


Link to post
Share on other sites

note that if your vegetation doesn't change often, you can use static VB's and IB's. if it changes every frame or you recalculate every frame, then you'll want to go dynamic. so you can probably use static for vegetation, and dynamic for bullets. you'll have a VB and IB for your bullet or grass clump. then you'll have an instance buffer of world transforms for each instance you want to draw. the whole idea is to reduce the number of times you bind the instance buffer to a minimum. think of it this way: binding is bad. different binds have different costs: textures are highest (?).  then VB's, then IB's, then constant buffers. apparently the hit for constant buffers isn't that bad. can't recall if binding dynamic vs static buffers is more costly (i think so). all this info is available in the docs.

Share this post


Link to post
Share on other sites

With the bullets, i mean this:

 

(Just an example)

 

Imagine thousands of spaceships, shooting lasers (or something) all the time, I would imagine that I would instance all bullets, wouldn't I? In that case I don't really see how I would split up each bullet up into a chunk, as they could go anywhere...

 

But Thanks!

You are dealing with the problem of what objects worth drawing, One solution is keeping a buffer large enough only to draw the bullets near the camera and dynamically insert / remove the elements. You can specify the number of instances inside the buffer.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Similar Content

    • By mister345
      Hi, can somebody please tell me in clear simple steps how to debug and step through an hlsl shader file?
      I already did Debug > Start Graphics Debugging > then captured some frames from Visual Studio and
      double clicked on the frame to open it, but no idea where to go from there.
       
      I've been searching for hours and there's no information on this, not even on the Microsoft Website!
      They say "open the  Graphics Pixel History window" but there is no such window!
      Then they say, in the "Pipeline Stages choose Start Debugging"  but the Start Debugging option is nowhere to be found in the whole interface.
      Also, how do I even open the hlsl file that I want to set a break point in from inside the Graphics Debugger?
       
      All I want to do is set a break point in a specific hlsl file, step thru it, and see the data, but this is so unbelievably complicated
      and Microsoft's instructions are horrible! Somebody please, please help.
       
       
       

    • By mister345
      I finally ported Rastertek's tutorial # 42 on soft shadows and blur shading. This tutorial has a ton of really useful effects and there's no working version anywhere online.
      Unfortunately it just draws a black screen. Not sure what's causing it. I'm guessing the camera or ortho matrix transforms are wrong, light directions, or maybe texture resources not being properly initialized.  I didnt change any of the variables though, only upgraded all types and functions DirectX3DVector3 to XMFLOAT3, and used DirectXTK for texture loading. If anyone is willing to take a look at what might be causing the black screen, maybe something pops out to you, let me know, thanks.
      https://github.com/mister51213/DX11Port_SoftShadows
       
      Also, for reference, here's tutorial #40 which has normal shadows but no blur, which I also ported, and it works perfectly.
      https://github.com/mister51213/DX11Port_ShadowMapping
       
    • By xhcao
      Is Direct3D 11 an api function like glMemoryBarrier in OpenGL? For example, if binds a texture to compute shader, compute shader writes some values to texture, then dispatchCompute, after that, read texture content to CPU side. I know, In OpenGL, we could call glMemoryBarrier before reading to assure that texture all content has been updated by compute shader.
      How to handle incoherent memory access in Direct3D 11? Thank you.
    • By _Engine_
      Atum engine is a newcomer in a row of game engines. Most game engines focus on render
      techniques in features list. The main task of Atum is to deliver the best toolset; that’s why,
      as I hope, Atum will be a good light weighted alternative to Unity for indie games. Atum already
      has fully workable editor that has an ability to play test edited scene. All system code has
      simple ideas behind them and focuses on easy to use functionality. That’s why code is minimized
      as much as possible.
      Currently the engine consists from:
      - Scene Editor with ability to play test edited scene;
      - Powerful system for binding properties into the editor;
      - Render system based on DX11 but created as multi API; so, adding support of another GAPI
        is planned;
      - Controls system based on aliases;
      - Font system based on stb_truetype.h;
      - Support of PhysX 3.0, there are samples in repo that use physics;
      - Network code which allows to create server/clinet; there is some code in repo which allows
        to create a simple network game
      I plan to use this engine in multiplayer game - so, I definitely will evolve the engine. Also
      I plan to add support for mobile devices. And of course, the main focus is to create a toolset
      that will ease games creation.
      Link to repo on source code is - https://github.com/ENgineE777/Atum
      Video of work process in track based editor can be at follow link: 
       
       

    • By mister345
      I made a spotlight that
      1. Projects 3d models onto a render target from each light POV to simulate shadows
      2. Cuts a circle out of the square of light that has been projected onto the render target
      as a result of the light frustum, then only lights up the pixels inside that circle 
      (except the shadowed parts of course), so you dont see the square edges of the projected frustum.
       
      After doing an if check to see if the dot product of light direction and light to vertex vector is greater than .95
      to get my initial cutoff, I then multiply the light intensity value inside the resulting circle by the same dot product value,
      which should range between .95 and 1.0.
       
      This should give the light inside that circle a falloff from 100% lit to 0% lit toward the edge of the circle. However,
      there is no falloff. It's just all equally lit inside the circle. Why on earth, I have no idea. If someone could take a gander
      and let me know, please help, thank you so much.
      float CalculateSpotLightIntensity(     float3 LightPos_VertexSpace,      float3 LightDirection_WS,      float3 SurfaceNormal_WS) {     //float3 lightToVertex = normalize(SurfacePosition - LightPos_VertexSpace);     float3 lightToVertex_WS = -LightPos_VertexSpace;          float dotProduct = saturate(dot(normalize(lightToVertex_WS), normalize(LightDirection_WS)));     // METALLIC EFFECT (deactivate for now)     float metalEffect = saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace)));     if(dotProduct > .95 /*&& metalEffect > .55*/)     {         return saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace)));         //return saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace))) * dotProduct;         //return dotProduct;     }     else     {         return 0;     } } float4 LightPixelShader(PixelInputType input) : SV_TARGET {     float2 projectTexCoord;     float depthValue;     float lightDepthValue;     float4 textureColor;     // Set the bias value for fixing the floating point precision issues.     float bias = 0.001f;     // Set the default output color to the ambient light value for all pixels.     float4 lightColor = cb_ambientColor;     /////////////////// NORMAL MAPPING //////////////////     float4 bumpMap = shaderTextures[4].Sample(SampleType, input.tex);     // Expand the range of the normal value from (0, +1) to (-1, +1).     bumpMap = (bumpMap * 2.0f) - 1.0f;     // Change the COORDINATE BASIS of the normal into the space represented by basis vectors tangent, binormal, and normal!     float3 bumpNormal = normalize((bumpMap.x * input.tangent) + (bumpMap.y * input.binormal) + (bumpMap.z * input.normal));     //////////////// LIGHT LOOP ////////////////     for(int i = 0; i < NUM_LIGHTS; ++i)     {     // Calculate the projected texture coordinates.     projectTexCoord.x =  input.vertex_ProjLightSpace[i].x / input.vertex_ProjLightSpace[i].w / 2.0f + 0.5f;     projectTexCoord.y = -input.vertex_ProjLightSpace[i].y / input.vertex_ProjLightSpace[i].w / 2.0f + 0.5f;     if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))     {         // Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.         depthValue = shaderTextures[6 + i].Sample(SampleTypeClamp, projectTexCoord).r;         // Calculate the depth of the light.         lightDepthValue = input.vertex_ProjLightSpace[i].z / input.vertex_ProjLightSpace[i].w;         // Subtract the bias from the lightDepthValue.         lightDepthValue = lightDepthValue - bias;         float lightVisibility = shaderTextures[6 + i].SampleCmp(SampleTypeComp, projectTexCoord, lightDepthValue );         // Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.         // If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.             if(lightDepthValue < depthValue)             {                 // Calculate the amount of light on this pixel.                 float lightIntensity = saturate(dot(bumpNormal, normalize(input.lightPos_LS[i])));                 if(lightIntensity > 0.0f)                 {                     // Determine the final diffuse color based on the diffuse color and the amount of light intensity.                     float spotLightIntensity = CalculateSpotLightIntensity(                         input.lightPos_LS[i], // NOTE - this is NOT NORMALIZED!!!                         cb_lights[i].lightDirection,                          bumpNormal/*input.normal*/);                     lightColor += cb_lights[i].diffuseColor*spotLightIntensity* .18f; // spotlight                     //lightColor += cb_lights[i].diffuseColor*lightIntensity* .2f; // square light                 }             }         }     }     // Saturate the final light color.     lightColor = saturate(lightColor);    // lightColor = saturate( CalculateNormalMapIntensity(input, lightColor, cb_lights[0].lightDirection));     // TEXTURE ANIMATION -  Sample pixel color from texture at this texture coordinate location.     input.tex.x += textureTranslation;     // BLENDING     float4 color1 = shaderTextures[0].Sample(SampleTypeWrap, input.tex);     float4 color2 = shaderTextures[1].Sample(SampleTypeWrap, input.tex);     float4 alphaValue = shaderTextures[3].Sample(SampleTypeWrap, input.tex);     textureColor = saturate((alphaValue * color1) + ((1.0f - alphaValue) * color2));     // Combine the light and texture color.     float4 finalColor = lightColor * textureColor;     /////// TRANSPARENCY /////////     //finalColor.a = 0.2f;     return finalColor; }  
      Light_vs.hlsl
      Light_ps.hlsl
  • Popular Now