HLSL instancing problem

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

HLSL instancing problem

Post by kevinsbro »

When trying to use hardware instancing with an HLSL shader from slavik262 I run into a problem. The normals don't seem to rotate along with the vertex manipulation. I'm not very good with the math so all my attempts to fix the normals have failed :(

Code: Select all

float4x4 viewProjection;
#define NUM_BATCH_INSTANCES 60
float4x4 instanceWorldArray[NUM_BATCH_INSTANCES];
 
struct VertexInput 
{ 
   float3 position: POSITION; 
   float3 normal : NORMAL; 
   float2 uv : TEXCOORD0; 
   float2 uv2 : TEXCOORD1; 
   float4 color : COLOR0; 
}; 
 
struct VertexOutput 
{ 
   float4 screenPos : POSITION; 
   float4 color : COLOR0; 
   float2 uv : TEXCOORD0; 
}; 
 
VertexOutput vs_main(VertexInput IN) 
{ 
   VertexOutput OUT = (VertexOutput)0;
   int index = IN.uv2.x;
   float4x4 WVP = mul(instanceWorldArray[index], viewProjection);
   OUT.screenPos = mul(float4(IN.position,1), WVP); 
   OUT.color = IN.color;
   OUT.uv = IN.uv;
 
   return OUT; 
} 
How do I change the code to change the normals orientation as well?
Last edited by kevinsbro on Fri Feb 17, 2012 7:28 pm, edited 1 time in total.
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Re: HLSL instancing problem

Post by serengeor »

The output lacks normal, is that how it is supposed to be? (I'm pretty sure it needs it too if you need normal in the output)
And to translate your normal to vertex position you simply would do something like:

Code: Select all

normalPos=vertexPos+normalPos;
Working on game: Marrbles (Currently stopped).
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: HLSL instancing problem

Post by hendu »

Translation doesn't affect normals, anyway?
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

I'm sorry, I didn't really mean translation. I meant rotation specifically. Don't the normals need to be altered in order to match the rotation?
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Re: HLSL instancing problem

Post by serengeor »

overlooked the part ._.
well, It should be identical to the rotation of the vertex position then I think.
Probably should be like:

Code: Select all

OUT.normalPos = mul(float4(IN.normalPos,1), WVP); 
Don't know for sure, haven't worked with shaders in a long time.
Working on game: Marrbles (Currently stopped).
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

No, I tried that but it didn't work. The multiplication doesn't even return a normalized vector and even if I normalize it after it still isn't right.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: HLSL instancing problem

Post by Mel »

The normals are independent from the vertex position, but not from the camera, to transform properly the normals you have to pass the "worldViewInverse" matrix (this already includes the rotation) and multiply the normals by it, which suposes a problem in the shader: it is dependant on each instance, and you can't pass viewInverse, and after multiply it by the world matrix, because it doesn't work like that. So either you pass it per instance, which decreases the amount of instances you can render, or you can calculate it inside, which decreases the eficiency of the instancing.

It is much better to render less instances this way and perform the proper transformations on the CPU (once per instance) than to lose time calculating the matrices per vertex, because there is no way to save that time, and the more vertices a instance has, the more time this would lose.

So... something like this might work...

Code: Select all

 
#define NUM_BATCH_INSTANCES 30
float4x4 ViewProjection;
float4x4 instanceWorldArray[NUM_BATCH_INSTANCES];
float4x4 instanceWorldViewInverseArray[NUM_BATCH_INSTANCES];
float3 lightPos;
 
struct VertexInput
{
   float3 position: POSITION;
   float3 normal : NORMAL;
   float2 uv : TEXCOORD0;
   float2 uv2 : TEXCOORD1;
   float4 color : COLOR0;
};
 
struct VertexOutput
{
   float4 screenPos : POSITION;
   float4 color : COLOR0;
   float2 uv : TEXCOORD0;
};
 
VertexOutput vs_main(VertexInput IN)
{
   VertexOutput OUT;
 
   int index = IN.uv2.x;
 
   float4x4 WVP = mul(instanceWorldArray[index],ViewProjection);
 
   OUT.screenPos = mul(IN.position,WVP);
   OUT.uv = IN.uv;
 
   float3 normal = mul(IN.normal,instanceWorldViewInverseArray[index]);
   
   float3 worldPos = mul(IN.position,instanceWorldArray[index]).xyz;
   float3 lightDir = worldPos - lightPos;
   
   lightDir = normalize(lightDir);
   normal = normalize(normal);  
   
   float ndl = saturate(dot(normal,lightDir));
   OUT.color = IN.color*ndl;
 
   return OUT;
}
 
(Needs some fixing and testing for sure, but as a draft i think it is clear :P)
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

Well, maybe there is an easier way around this.
In my particular shader, the normals aren't really important but rather the dot product of the normal and a normalized light direction vector.

I currently multiply the light direction by the inverse world before I pass it to the shader, but I figure instead of worrying about multiplying both normal and light direction by the inverse world matrix I can just leave them both alone and the dot product will be the same. But this still doesn't work :( What am I doing wrong?
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

I've been at this a few days now with no success. Just a bunch of improperly shaded instanced cubes.

@Mel, what exactly would I pass to the shader for the "instanceWorldViewInverseArray"? What do I multiply and where do I find it in irrlicht exactly?

Has anyone else had this problem while instancing?
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: HLSL instancing problem

Post by Mel »

Well, for the worldviewinverse array you would need the array of world matrices you already have, and the view matrix from the current active camera, or the video driver, which use to be the same.

Then, you multiply the view matrix for each of the world matrices, and get the inverse of these matrices, once you have this, pass them to the shader, and you should be done.

The problem is that the space for the shader constants is limited; at least in DX you can have as much as the equivalent of 64 matrices or so, which is okay for the vast majority of the shaders out there but it isn't unlimited. So, you have to decrease the number of instances, in this case, to half of them.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

And you were saying that it's probably not the best idea to simply run the matrix multiplication in the shader itself, because the multiplication would occur for every vertex, right?

And theoretically, couldn't I adjust the light direction before its passed to the shader so that I don't even need to alter the normals in terms of the view? I only need to keep the integrity of the dot product between the light direction and normal. That would eliminate the need for the extra array.

And thank you so much for your help so far. It's really hard to try and teach yourself everything.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: HLSL instancing problem

Post by Mel »

No, because you need then to invert it, so you can multiply the normals for the resulting matrix. Think that you should also invert the matrix inside the shader, and i don't know how long would that take, i was just being on the safer side.

With regard to the other question, i don't know. The normals are expressed in object space, so, you should transform the light to object space for every instance too and pass it to the shader.I can't tell for sure if it would work or not. Maybe, if it works, cool! :D
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

So I have a working solution now. I realized its the light direction that needs to be multiplied by the inverse world matrix. This is simple if all the instances have a uniform scale because I can simply transpose the world matrix to get the equivalent inverse. But Im still gonna try and find an efficient solution even with non uniform scaling.

I'm thinking that the dot product of (lightDirection X inverseWorldMatrix, normal) should equal the dot product of (lightDirection, normal X worldMatrix), but it's not :(
What am I missing? ***by multiplication "X" I also mean normalized as well.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: HLSL instancing problem

Post by Mel »

No, ((lightDirection*inverseWorldMatrix)·normal) equals (lightDirection·(normal*worldViewInverse)) because in the first place, you convert the light to object space, that is the same in which the normal is expressed, and on the other equation you dot product the light direction expressed in world space by the normal transformed to world space.

normal*worldMatrix gives you the normal in object space rotated, but not converted to the right equivalent in world space. For that you need the WorldViewInverse matrix.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
kevinsbro
Posts: 51
Joined: Fri Nov 05, 2010 8:18 pm

Re: HLSL instancing problem

Post by kevinsbro »

so then, worldViewInverse = viewInverse*worldMatrix ?
If this is true than all I need to do is pass the viewInverse and I can multiply it by the particular instances world matrix.



or is it worldInverse*view, bringing me no where :(
Post Reply