Drawing with HLSL + Texture Problem

Started by
6 comments, last by chibitotoro0_0 14 years, 5 months ago
Hi everyone, I'm trying to draw using standard effects using my own diffuse/specular shader. I got everything to work even with one texture mapping to the default p1_wedge model provided by microsoft. However this model has parts that are not UV mapped to the texture. This means that the dome of the plane has only diffuse and specular material properties but no texture properties. Using HLSL and its blending function, I get the texture wrapping the entire model, even parts without UV coordinates. I've been reading around the forums and it says that places with no UVs will be automatically generated? here is an example of the problem: http://www.stromcode.com/2008/04/03/an-introduction-to-hlsl-part-ii/127/ notice how the texture wraps into the dome. If someone knows how to fix this or why please let me know. Thank you!
Advertisement
Isn't the model divided into separate sub-meshes?
It should be. Usually you want to draw parts with different materials in separate draw-calls. That is, first you draw the part of the model that uses a texture, then you change to different shaders, and draw the part that shouldn't use textures. You might even want to create a reflection or refraction in that part of the model, which would require a separate shader.
Alternatively the model could be made so that the texture is single-colored at the correct parts, and specular maps etc. could specify reflectivity and material. The easiest way is to divide the model into parts though.
Thanks for the tip. When I was making my own engine in OpenGL I did stuff like that but because I don't know the whole XNA Model class I couldn't do the same. As far as I know when I imported the fbx into Maya, It treated the whole thing as one mesh but it used different materials for the body and the dome. So I'm not quite sure if it separates it into two logical units in the original fbx but it doesnt seem so.
I see, then the materials should be stored somewhere.. Try using the Model.Meshes Property, and see if it contains more than one mesh.
I managed to get into the model and found that there was only 1 mesh but 3 meshparts (XNA classes)

I guess I could change each part manually with its own effects, but shouldn't their diffuse / specular material information be stored in the .fbx itself? It feels weird having to do this for every model manually instead having it determined automatically or able to be referenced thru the HLSL effects
That depends on the file-format, as well as the model-loading class. You could certainly construct such a system, but whether it's worth it depends on your usage scenario. It might be best to simply have each model in a folder, together with X number of shaders, and then let each material in the model have an index number which corresponds to the correct shader to use in that scenario. That way the designer of your models can also make a different shader for each material.
On the other hand, if all your models use the same shaders, then those shaders could be global. In the case where each material is the same but only have different constants that specify the color and/or properties, then a model-format containing that information would be the best, and only a single shader could be used.

Read the documentation on the XNA Model class to see exactly what it supports. Since it's designed to be pretty general I doubt it contains too much specific information, but I don't know the details about it.
I found the following with Google, which seems to talk about this issue: http://www.ziggyware.com/readarticle.php?article_id=146.
Right now it renders each component separately with my code, but every iteration it seems to draw previous components on top of existing one with the current iteration's material.

For Example we have 3 materials and 3 meshparts
1st loop: draw meshpart 1 with material 1 // everything fine so far
2nd loop: draw meshpart 2 (AND 1?) with material 2 // meshpart 1 redraws again on top

3rd loop: draw meshpart 3 (AND 2 AND 1) with material 3 //and it repeats itself


I feel like im very close but ive been spending way too much time on this problem.

Thank you for any input. I hope it's not too much code for people to look over.

My draw function:


graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;

Matrix[] transforms = new Matrix[mAirplane.ModelObject.Bones.Count];
mAirplane.ModelObject.CopyAbsoluteBoneTransformsTo(transforms);


// Use the SpecularLighttechnique from Shader.fx. You can have multiple techniques in a effect file. If you don't specify
// what technique you want to use, it will choose the first one by default.
effect.CurrentTechnique = effect.Techniques["SpecularLight"];



Matrix renderMatrix, objectMatrix, worldMatrix, viewMatrix, projMatrix;

worldMatrix = Matrix.Identity;
objectMatrix = mAirplane.Transform();
renderMatrix = Matrix.CreateScale(1.5f) * objectMatrix;
projMatrix = mCamera.Projection();
viewMatrix = mCamera.View();




// Begin our effect
effect.Begin();
// A shader can have multiple passes, be sure to loop trough each of them.
System.Diagnostics.Debug.WriteLine("start");

foreach (ModelMesh mesh in mAirplane.ModelObject.Meshes)
{

BasicEffect currentMaterial;

for (int k = 0; k < mesh.MeshParts.Count; k++)
{
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
// Begin current pass
pass.Begin();

//currentMaterial = (BasicEffect)mesh.MeshParts[(k+1)%3].Effect;
currentMaterial = (BasicEffect)mesh.MeshParts[k].Effect;
//System.Diagnostics.Debug.WriteLine(k+" "+currentMaterial.TextureEnabled);

// calculate our worldMatrix..
worldMatrix = transforms[mesh.ParentBone.Index] * renderMatrix;

Vector4 vecEye = new Vector4(mCamera.Position.X, mCamera.Position.X, mCamera.Position.Z, 0);
Vector4 vLightDiffuse = new Vector4(0.8f, 0.0f, 0.0f, 1.0f);
Vector4 vLightSpecular = new Vector4(0.5f, 0.5f, 0.0f, 1.0f);
Vector4 vLightAmbient = new Vector4(0.5f, 0.5f, 0.5f, 1.0f);



Vector4 vMaterialDiffuse = new Vector4(currentMaterial.DiffuseColor.X,
currentMaterial.DiffuseColor.Y, currentMaterial.DiffuseColor.Z, 1.0f);
Vector4 vMaterialSpecular = new Vector4(currentMaterial.SpecularColor.X,
currentMaterial.SpecularColor.Y, currentMaterial.SpecularColor.Z, 1.0f);
Vector4 vMaterialAmbient = new Vector4(currentMaterial.AmbientLightColor.X,
currentMaterial.AmbientLightColor.Y, currentMaterial.AmbientLightColor.Z, 1.0f);

// .. and pass it into our shader.
// To access a parameter defined in our shader file ( Shader.fx ), use effectObject.Parameters["variableName"]
Matrix worldInverse = Matrix.Invert(worldMatrix);
Vector4 vLightDirection = new Vector4(0.0f, 1.0f, 0.0f, 1.0f);


effect.Parameters["HasTexture"].SetValue(currentMaterial.TextureEnabled);
effect.Parameters["matWorldViewProj"].SetValue(worldMatrix * viewMatrix * projMatrix);
effect.Parameters["matWorld"].SetValue(worldMatrix);
effect.Parameters["vecEye"].SetValue(vecEye);
effect.Parameters["vecLightDir"].SetValue(vLightDirection);
effect.Parameters["vLightDiffuse"].SetValue(vLightDiffuse);
effect.Parameters["vLightSpecular"].SetValue(vLightSpecular);
effect.Parameters["vLightAmbient"].SetValue(vLightAmbient);
effect.Parameters["vMaterialDiffuse"].SetValue(vMaterialDiffuse);
effect.Parameters["vMaterialSpecular"].SetValue(vMaterialSpecular);
effect.Parameters["vMaterialAmbient"].SetValue(vMaterialAmbient);


effect.Parameters["ColorMap"].SetValue(colorMap);


// Render our meshpart

graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, total, mesh.MeshParts[k].VertexStride);
graphics.GraphicsDevice.Indices = mesh.IndexBuffer;

graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
mesh.MeshParts[k].BaseVertex, 0, mesh.MeshParts[k].NumVertices,
mesh.MeshParts[k].StartIndex, mesh.MeshParts[k].PrimitiveCount);



pass.End();
}

}

}
// Stop using this effect
effect.End();








And my Shader:
// Global variables
// Can be accessed from outside the shader, using Effect->Parameters["key"] where key = variable name
float4x4 matWorldViewProj; // The projected and tranformed matrix
float4x4 matWorld; // The world matrix
float4 vecLightDir; // Light direction
float4 vecEye; // Eye position
float4 vLightDiffuse; // Diffuse color
float4 vLightSpecular; // Specular color
float4 vLightAmbient; // Ambient light

float4 vMaterialDiffuse; //material diffuse
float4 vMaterialSpecular; //material spec
float4 vMaterialAmbient; //material ambient

bool HasTexture;
texture ColorMap;

struct OUT
{
float4 Pos : POSITION; // Position
float3 L : TEXCOORD0; // Light dir
float3 N : TEXCOORD1; // Normal
float3 V : TEXCOORD2; // View/Eye
float2 Tex:TEXCOORD3; //texture color
};



OUT VS(float4 Pos : POSITION, float3 N : NORMAL,float2 Tex:TEXCOORD)
{
OUT Out = (OUT)0;

Out.Pos = mul(Pos, matWorldViewProj);
Out.N = mul(N, matWorld);

// Just pass textures trough
Out.Tex = Tex;

// Tranform Pos with matWorld in order to get the correct view vector.
float4 PosWorld = mul(Pos, matWorld);

Out.L = vecLightDir;

// Eye position - vertex position returns the view direction from eye to the vertex.
Out.V = vecEye - PosWorld;

return Out;
}

// Create a sampler for the ColorMap texture using leanear filtering and clamping
sampler ColorMapSampler = sampler_state
{
Texture = <ColorMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};

float4 PS(float3 L: TEXCOORD0, float3 N : TEXCOORD1,
float3 V : TEXCOORD2,float2 Tex: TEXCOORD3) : COLOR
{
// normalize our vectors.
float3 Normal = normalize(N);
float3 LightDir = normalize(L);
float3 ViewDir = normalize(V);

// Get the color from ColorMapSampler using the texture coordinates in Tex.
float4 Color;
if(HasTexture==true) Color = tex2D(ColorMapSampler, Tex);
else Color=vMaterialDiffuse;

// calculate diffuse light
float Diff = saturate(dot(Normal, LightDir));

// Create our reflection shader
// R = 2 * (N.L) * N – L
float3 Reflect = normalize(2 * Diff * Normal - LightDir);

// Calculate our specular light
float Specular = pow(saturate(dot(Reflect, ViewDir)), 20); // R.V^n


// return our final light equation
// I = A + Dcolor * Dintensity * N.L + Scolor * Sintensity * (R.V)n
//return Color*vLightAmbient + Color*vLightDiffuse * Diff + Color*vLightSpecular * Specular;
return Color*vLightAmbient + Color*vLightDiffuse * Diff + Color*vLightSpecular * Specular;
}


technique SpecularLight
{
pass P0
{

ZENABLE = TRUE;
ZWRITEENABLE = TRUE;

CULLMODE = CCW;

// compile shaders
VertexShader = compile vs_1_1 VS();
PixelShader = compile ps_2_0 PS();
}
}

This topic is closed to new replies.

Advertisement