Hello MonoGame Community,
I’ve been trying to render a 3D model using a custom effect:
float4x4 World;
float4x4 View;
float4x4 Projection;
float4 AmbientColor;
float AmbientIntensity;
struct VertexShaderInput
{
float4 Position : POSITION0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
};
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
return AmbientColor*AmbientIntensity;
}
technique Technique1
{
pass Pass1
{
VertexShader = compile vs_2_0 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
This effect works perfectly when rendering a cube from a vertex buffer, however when drawing with a model loaded through the content pipeline the model seems to fill the entire screen (it doesn’t with BasicEffect). I think I am rendering it right (I got the code from an rb whitaker tutorial) because the ambient colour appears to be applied.
Any ideas why this might be?
markus
September 30, 2021, 1:35pm
2
Try SV_POSITION instead of POSTION0 in the vertex shader output.
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
};
markus
September 30, 2021, 2:31pm
3
I’m actually not sure if GL really needs SV_POSITION. Maybe your are just missing the internal matrices that are inside the model class?
Thanks for your replies!
The change to the shader didn’t help and I think the matrices are being accounted for but this is the drawing code just in case:
private void DrawModelWithEffect(Model model, Matrix world, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect = epicEffect;
epicEffect.Parameters["World"].SetValue(world * mesh.ParentBone.Transform);
epicEffect.Parameters["View"].SetValue(view);
epicEffect.Parameters["Projection"].SetValue(projection);
}
mesh.Draw();
}
}
Just worked it out, removing the * mesh.ParentBone.Transform
, from where world matrix is set seemed to fix it. I think this will prevent animations, but that is not a concern with this project.
markus
October 1, 2021, 12:20pm
6
Those bone transforms can have parent transforms, this might be why the scale is off.
The model class has a draw function, which you could probably use. Here’s the code for it, see how it’s treating the bone transforms differently:
public void Draw(Matrix world, Matrix view, Matrix projection)
{
int boneCount = this.Bones.Count;
if (sharedDrawBoneMatrices == null ||
sharedDrawBoneMatrices.Length < boneCount)
{
sharedDrawBoneMatrices = new Matrix[boneCount];
}
// Look up combined bone matrices for the entire model.
CopyAbsoluteBoneTransformsTo(sharedDrawBoneMatrices);
// Draw the model.
foreach (ModelMesh mesh in Meshes)
{
foreach (Effect effect in mesh.Effects)
{
IEffectMatrices effectMatricies = effect as IEffectMatrices;
if (effectMatricies == null) {
throw new InvalidOperationException();
}
effectMatricies.World = sharedDrawBoneMatrices[mesh.ParentBone.Index] * world;
effectMatricies.View = view;
effectMatricies.Projection = projection;
}
mesh.Draw();
}
}
It’s using CopyAbsoluteBoneTransformsTo, which also takes parent transforms into account:
public void CopyAbsoluteBoneTransformsTo(Matrix[] destinationBoneTransforms)
{
if (destinationBoneTransforms == null)
throw new ArgumentNullException("destinationBoneTransforms");
if (destinationBoneTransforms.Length < this.Bones.Count)
throw new ArgumentOutOfRangeException("destinationBoneTransforms");
int count = this.Bones.Count;
for (int index1 = 0; index1 < count; ++index1)
{
ModelBone modelBone = (this.Bones)[index1];
if (modelBone.Parent == null)
{
destinationBoneTransforms[index1] = modelBone.transform;
}
else
{
int index2 = modelBone.Parent.Index;
Matrix.Multiply(ref modelBone.transform, ref destinationBoneTransforms[index2], out destinationBoneTransforms[index1]);
}
}
}