Well, might as well post here.
I’ve started to learn more about shaders, and in an attempt to write model instancing, I wrote a drawing code with the help of XNA sample.
Problem is, all I get is black void, and I’m not sure what and where I did wrong. I’ve checked all code multiple times, and tried tinkering with it, nothing helped. So… I need some guidance. Again.
Here’s drawing code (I wrote it simplest so that I get something on screen first, and will refactor it later so that it’s more efficient):
private void Draw3D(GameTime gameTime)
{
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
var box = CalculateBoundingBox(_ground);
var height = Math.Abs(box.Max.Y - box.Min.Y);
var width = Math.Abs(box.Max.X - box.Min.X);
Tile tile;
var tileTypes = new Dictionary<string, List<Matrix>>();
// Count all the instances of tiles
for (int i = 0; i < Map.Instance.Grid.Count; i++)
for (int j = 0; j < Map.Instance.Grid[i].Count; j++)
{
tile = Map.Instance.Grid[i][j];
var tileName = tile.Height.ToString();
if (tile.Height >= LandHeight.Plains)
tileName = tile.Biome.ToString() + tileName /*+ (tile.Resource == Resource.None ? "" : tile.Resource.ToString()) + (tile.Enhancement == Enhancement.None ? "" : tile.Enhancement.ToString())*/;
if (!tileTypes.ContainsKey(tileName))
tileTypes.Add(tileName, new List<Matrix>());
var tileTransform = Matrix.CreateScale(0.01f) * Matrix.CreateRotationY(MathHelper.PiOver2) * Matrix.CreateTranslation(new Vector3(i * height * 0.75f, 0, j * width + (i % 2) * (width / 2)));
tileTypes[tileName].Add(tileTransform);
}
// Draw map
foreach (var key in tileTypes.Keys)
{
var instanceVertexBuffer = new DynamicVertexBuffer(GraphicsDevice, InstanceVertexDeclaration, tileTypes[key].Count, BufferUsage.WriteOnly);
instanceVertexBuffer.SetData(tileTypes[key].ToArray(), 0, tileTypes[key].Count, SetDataOptions.Discard);
var model = key.Contains("Water") ? _ground : _tileModels[key];
var modelBones = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(modelBones);
foreach (var mesh in model.Meshes)
{
foreach (var meshPart in mesh.MeshParts)
{
GraphicsDevice.SetVertexBuffers(
new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
new VertexBufferBinding(instanceVertexBuffer, 0, 1)
);
GraphicsDevice.Indices = meshPart.IndexBuffer;
Effect effect = _instancingShader;
effect.CurrentTechnique = effect.Techniques["Instancing"];
effect.Parameters["World"].SetValue(modelBones[mesh.ParentBone.Index]);
effect.Parameters["View"].SetValue(Matrix.CreateLookAt(_cameraPosition, _cameraTarget, Vector3.Up));
effect.Parameters["Projection"].SetValue(_projection);
effect.Parameters["Color"].SetValue(Vector4.One);
/*effect.Parameters["AmbientLight"].SetValue(Vector3.One);
effect.Parameters["DiffuseLight"].SetValue(Vector3.Zero);
effect.Parameters["LightDirection"].SetValue(Vector3.One);*/
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, meshPart.StartIndex, meshPart.PrimitiveCount, tileTypes[key].Count);
}
}
}
}
}
Here’s shader code:
#if OPENGL
#define SV_POSITION POSITION
#define VS_SHADERMODEL vs_3_0
#define PS_SHADERMODEL ps_3_0
#else
#define VS_SHADERMODEL vs_4_0_level_9_1
#define PS_SHADERMODEL ps_4_0_level_9_1
#endif
// Camera settings.
float4x4 World;
float4x4 View;
float4x4 Projection;
// This sample uses a simple Lambert lighting model.
float3 LightDirection;
float3 DiffuseLight;
float3 AmbientLight;
float4 Color;
texture Texture;
sampler CustomSampler = sampler_state
{
Texture = (Texture);
};
struct VertexShaderInput
{
float4 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TextureCoordinate : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR0;
float2 TextureCoordinate : TEXCOORD0;
};
VertexShaderOutput VertexShaderFunction(VertexShaderInput input, float4x4 instanceTransform : BLENDWEIGHT)
{
VertexShaderOutput output;
// Apply the world and camera matrices to compute the output position.
float4x4 instancePosition = mul(World, transpose(instanceTransform));
float4 worldPosition = mul(input.Position, instancePosition);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
// Compute lighting, using a simple Lambert model.
//float3 worldNormal = mul(input.Normal, instanceTransform);
//float diffuseAmount = max(-dot(worldNormal, LightDirection), 0);
//float3 lightingResult = saturate(diffuseAmount * DiffuseLight + AmbientLight);
output.Color = Color;
// Copy across the input texture coordinate.
output.TextureCoordinate = input.TextureCoordinate;
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
return tex2D(CustomSampler, input.TextureCoordinate) * input.Color;
}
// Hardware instancing technique.
technique Instancing
{
pass Pass1
{
VertexShader = compile VS_SHADERMODEL VertexShaderFunction();
PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
}
}
I have a general idea of what both the code and shaders are doing (there’s always a possibility of me misunderstanding something), so it looks to me like it should work, which is why I’m confused.
EDIT: Solved it. The issue was because my effect didn’t use the texture contained in the mesh parts due to me using my own effect instead of BasicEffect
already present. Copying the texture to my effect did the trick.