I have some instanced skinned models. I would to know if it is possible to use Sunburn's lighting/shadows on them via custom effects.
I read that I should be able to configure the models to use sunburn lighting by converting their material to my shader. The problem is that the skinned instancing implementation requires the use of its own processor. So, in the game, the models cannot use a sunburn material. I do see my effect in the list of options to "Convert To..." though.
Am I missing something or is there another way to include the instanced models in the lighting effects?
Not sure if you've seen
it's based on the XNA skinned model example and shows you how to use sunburn using the skinned model fx
hope that helps :)
That may be helpful. I will have to look at it some more. However, I think it might not work for me because I am already using a custom model processor which sets the effect on the model.
I think my biggest problem is that there is a lot of custom drawing logic related to the instanced models. So they are not sceneobjects or drawn by the sunburn engine. I have adopted this project http://bphelpsdev.spaces.live.com/ to work in an Windows environment. I guess I would have to create SceneoObjects from the vertex buffer, add those SceneObjects to the sunburn scene and then use the SkinnedModelInstancing effect on them as a custom effect.
I think I just need a more detailed explanation on how John meant it should be implemented in this post: http://www.synapsegaming.com/forums/p/1159/5941.aspx#5941
Hi khayman,
Are the models loaded as custom objects or XNA Models? If they're loaded as XNA Models you can call the SunBurn processor in your processor to generate the ModelContent, then run any additional processing afterward (see the Integrating Content Processors article for details).
With deferred you can use practically any object rendering shader and still receive automatic lighting, shadows, and fog. If the shader doesn't process the vertex data using only the Bones, World, View, and Projection transform, then you need to supply a simple shadow generation technique (shown in the Custom Effect example's Wobble shader - also see the Custom Effects Deferred article).
With forward rendering the shader must process the vertex data using only the Bones, World, View, and Projection transform (see the Custom Effects Forward article).
Let me know if this helps!
Follow me on Twitter – development and personal tweetsAwesome XNA Videos – Lighting, Rendering, and game videos
I am adding the index of each model instance into the vertex data (so the shader can look up the correct animation data). So I guess I will have to go the deferred route, and supply a shadow generation technique.
I am a bit confused about where/how to render it. Right now, I am drawing them via a call like this:
public void Draw(Matrix [] transforms, int [] animations, Matrix view, Matrix projection) { if (transforms.Length != animations.Length) throw new ArgumentException("Transforms array and animation frames array must have same length."); // Set the graphics device to use our vertex data this.graphicsDevice.Indices = this.indexBuffer ; // Draw each meshPart for(int i = 0; i < this.mesh.MeshParts.Count; i++) { ModelMeshPart part = this.mesh.MeshParts[i]; this.graphicsDevice.VertexDeclaration = vertexDeclaration;//part.VertexDeclaration; this.graphicsDevice.Vertices[0].SetSource(vertexBuffer, part.StreamOffset, vertexStride);//part.VertexStride); Effect effect = part.Effect; //set the target color effect.Parameters["TargetColor"].SetValue(TargetColor); // Pass camera matrices through to the effect. effect.Parameters["View"].SetValue(view); effect.Parameters["Projection"].SetValue(projection); // Set the vertex count (used by the VFetch instancing technique). // And also set all the parameters the shader needs to get the animation data //effect.Parameters["VertexCount"].SetValue(this.vertexCount); effect.Parameters["AnimationTexture"].SetValue(this.animationTexture); // Bone delta and row delta are basically pixel width and pixel height, respectively effect.Parameters["BoneDelta"].SetValue(1f / this.animationTexture.Width); effect.Parameters["RowDelta"].SetValue(1f / this.animationTexture.Height); // Do the actual drawing effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); this.DrawPart(transforms, animations, effect, part); pass.End(); } effect.End(); } } /// <summary> /// DrawPart draws the actual ModelMeshPart /// </summary> /// <param name="transforms"></param> /// <param name="animations"></param> /// <param name="effect"></param> /// <param name="part"></param> private void DrawPart(Matrix [] transforms, int [] animations, Effect effect, ModelMeshPart part) { for (int i = 0; i < transforms.Length; i += maxInstances) { // How many instances can we fit into this batch? int instanceCount = transforms.Length- i; if (instanceCount > maxInstances) instanceCount = maxInstances; // Copy transform and animation data Array.Copy(transforms, i, tempTransforms, 0, instanceCount); Array.Copy(animations, i, tempAnimations, 0, instanceCount); // Send the transform and animation data to the shader effect.Parameters["InstanceTransforms"].SetValue(tempTransforms); effect.Parameters["InstanceAnimations"].SetValue(tempAnimations); effect.CommitChanges(); // Draw maxInstances copies of our geometry in a single batch. this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.BaseVertex, 0, part.NumVertices * instanceCount, part.StartIndex, part.PrimitiveCount * instanceCount); } }
That iterates through my instance batches and draws them. Do I have to create a sceneobject from the vertex buffer and add that to the object manager? Or can I just make sure that draw call is within my Begin/EndFrameRendering() calls?
khayman218:Do I have to create a sceneobject from the vertex buffer and add that to the object manager?
Yes, also add a material content item to your project, then load the material item as an effect through the content manager and assign it to the scene object.
The material will be visible in the SunBurn editor, you can right-click and "Convert To" a custom shader (selecting your shader from the list, assuming it's in the content folder).
The objects will not need any rendering code, SunBurn handles this for you. The shader is where all the magic happens - deferred techniques need to be created, which render the object to the gbuffer and finally the screen (see the Custom Effects example Wobble shader for an example).
And the effect parameters (like the animation texture) can either be set in the SunBurn editor or via code using the same Effect.Parameters property.
Thanks for the help! That worked perfectly. I now have a custom deferred effect in place.
But I am seeing a minor issue with the effect not being applied in certain camera positions. Here is a video of the example:
http://www.youtube.com/watch?v=E1er8kDt46g
I modified the wobble.fx from the example. It is mostly unchanged. I simply removed the position manipulation in the VShader and added this in the beginning of the Final Pass Pixel Shader:
float4 color = tex2D(DiffuseSampler, input.uvCoord); if(color.a < 1) { color.r = TargetColorR * .5 + color.r * (.5); color.g = TargetColorG * .5 + color.g * (.5); color.b = TargetColorB * .5 + color.b * (.5); } float3 diffuse = color.rgb;
The TargetColor fields are passed in from the material. In the video it seems like either the TargetColor values are not being applied, or the entire effect is not running?
I am setting the effect on the mesh at runtime like this:
scnObj.RenderableMeshes[0].Effect = Game.Content.Load<DeferredSasEffect>(@"Models\InteractiveObjects\castle_" + Stats.Team.ToString());
The various materials specify different TargetColor values (for each team).
Are the effects assigned only on load or while the game is running? If while the game is running, is it possible the code is assigning the wrong material and causing the flickering?
Also are instances for all teams packed into the same RenderableMesh? If so this could be the problem, each mesh can only use a single material, so instances need to be separated into meshes based on the team (eg: each team gets its own mesh).
JohnK "bobthecbuilder":Also are instances for all teams packed into the same RenderableMesh?
Yes, unfortunately that is the case. I am surprised how I consistently make this mistake. Before Sunburn, I would set the effect on each model before drawing, so I guess I am used to being able to change that. Sorry for the ill-informed question. Based on what you said, I assume there is no mechanism (e.g. callback) to assign an effect to a mesh immediately prior to drawing.
JohnK "bobthecbuilder":instances need to be separated into meshes based on the team (eg: each team gets its own mesh).
Since I cannot clone the model/mesh data in memory. Do I need to have separate model content files for each separate effect I want to simultaneously draw? I guess that only impacts the disk space the game takes up.
As an alternative, I could instance the castle models and then pass in an array of the TargetColors (which is indexed the same as the model tranforms) in the effect. If I were to instance them, is the shader from the XNA Creator's example usable as a custom deferred effect for Sunburn instanced models? I think it might not because the XNA CC example has the instance transforms passed into the shader separately; whereas, the Sunburn instancing example uses the boneweights and bonetransforms to manipulate the instances.
I believe I have managed to get a group of instanced, animated, sunburned gremlins on the screen. I will post a video and explanation (which is basically what you have said) as soon as I am sure it is working as expected.
Thanks for your help. It was instrumental in getting this working.
So here is the video I promised:
http://www.youtube.com/watch?v=nQHSaJToE4k&hd=1
These are the basic steps in order to reproduce it.
I think there is some room for optimization. Currently, I recreate the SceneObject each time a new instance is added/removed. This is because any changes to the vertex buffers require a new sceneobject. Maybe there is a more efficient way to do this?
Another concern is that the Vertex Shader method is run for each deferred pass. Maybe I could run it only for the final pass? It would be fine if the shadows did not reflect the animation transformations. Is it possible to run a dummy (or just a cheap) VS method for all but the final shader technique?
Looks awesome!
khayman218:Currently, I recreate the SceneObject each time a new instance is added/removed. This is because any changes to the vertex buffers require a new sceneobject.
Usually with instancing there are several instances (objects) packed into the same SceneObject and the SkinBones property provides individual world transforms for each of the instances.
I didn't have an opportunity to read over the referenced blog, but maybe it's providing only gpu animation (via textures)? Either way the number of characters you're pushing animated and on-screen is impressive, so it sounds like a great technique.
khayman218:Another concern is that the Vertex Shader method is run for each deferred pass. Maybe I could run it only for the final pass?
For shadows this might work ok (though it will look out-of-sync with the characters), but both the Gbuffer and Final passes must use the same vertex data, otherwise the lighting and materials will not align on-screen.
However it might be possible to preprocess the animation information in a full screen pass to another render target, to reduce the amount of vertex shader processing.
Out of interest, did you have this final example working with XNA 4.0?
I've been trying for some time to get skinned instanced rendering working (as have many people) and have come across the same source many times, but I've always been defeated trying to port it over to 4.0 given my lack of understanding as a graphics coder.
I've got it to the point now where Phelp's example is compiling, but I run in to issues with ReplicateIndexData - specifically this line:
this.indexCount = mesh.IndexBuffer.SizeInBytes / size;
The only thing I can see that's anywhere near close is something like omitting the size calculation and going for:
this.indexCount = mesh.MeshParts[0].IndexBuffer.IndexCount;
Throwing back an exception at runtime, I gave it some closer inspection. My indexbuffer and effects are all null. Perhaps I'm not understanding something in the process and this rings a bell with you? If I can get this working I'll make the converted 4.0 source available to all :)
Cheers,
Steve
I'm assuming that you mean this example...http://ionixxgames.wordpress.com/2009/03/25/xna-sample-skinned-model-instancing/
I managed to get this working some time ago on windows & xbox (using shader instancing since it's the only common technique) and am still hopefull to submit it to IGF at some point, however there are some caveats with my model importing code at the moment.
I've bundled all the files as they stand here
http://dl.dropbox.com/u/9584488/Skinned.rar
The only real remaining issue is that the vertex duplication code assumes that the model you are using has a specific vertex format (The supplied model should work).
Additionally if you are using 3rd party models with a lot of animations it is possible to overflow the maximum texture size for the animation data.
I'm in the process of creating a content pipeline importer which will deal with both these issues but have been distracted recently onto other aspects of my project.
If you have any problems getting things to work just ask and i'll do my best to help out.
nb - I should also mention that i converted the example to work with Sunburns deferred rendering rather than forward rendering as per the original and to also mirror the way that static instancing works in IGF so you use the SkinnedInstanceFactory to create instances of your model which gives you a SkinnedInstanceEntity which you can then use to position the instance and select animations.
oops - The code as supplied uses a modified version of Phillipe's InstancingManager class (which i've now included) however you don't need to use it if you don't use IGF - just get rid of the internal modifier on the constructor of the SkinnedInstanceFactory and you can create one directly. (I've no idea why i also marked it as protected so remove that too - it should break it but doesn't appear to)
Thanks for taking the time to put this together! Sorry I didn't reply sooner, I've only just caught up on forum stuff.
Seems I'm building up quite a backlog of debts to the community!
Anyway, I've grabbed that rar file now, much appreciated - I'll let you know how far I get and as ever, probably write some pseudo forum based documentation for any 'gotchas' I encounter to give other folk an easier time...:-)