DX11 add binormals and tangents to objetcts

Hi, I open this post again since it did not let me reply to it.

did anyone do such compute or geometry shader node to add tangents and binormals as the normal dx9 node will do ?

I don’t have it as a single node, but in SuperPhysical I use this snippt to calculate binormals and tangents:

// compute derivations of the world position
float3 p_dx = ddx(;
float3 p_dy = ddy(;
// compute derivations of the texture coordinate
float2 tc_dx = ddx(In.TexCd.xy);
float2 tc_dy = ddy(In.TexCd.xy);
// compute initial tangent and bi-tangent
float3 t = normalize( tc_dy.y * p_dx - tc_dx.y * p_dy );
float3 b = normalize( tc_dy.x * p_dx - tc_dx.x * p_dy ); // sign inversion
// get new tangent from a given mesh normal
float3 n = normalize(In.NormW);
float3 x = cross(n, t);
t = cross(x, n);
t = normalize(t);
// get updated bi-tangent
x = cross(b, n);
b = cross(n, x);
b = normalize(b);

Could be implemented in a geometry shader.



thank you for the code snipppet. what is ddx and ddy in the your code ?

float3 p_dx = ddx(;
float3 p_dy = ddy(;


Not sure what it does tho… never inderstood thouse two…

@antokhio ahh cool thanks, so dx and dy are internal funtions.

Hi, How can you subtract tangent and binormals from dx9 ? or they are caluclated and save in normals and texcoords again somehow?

BinormalTangent_dx9_Get.v4p (8.9 KB)

Hello ; i have started the patch and structure of the geometry shader in case someone more fluent in geomtry can help on it .

i presumed the calculation have to be made in pixel shader as for example DX DY only work in pixel shader as far as i read in last link. (13.4 KB)

Should have pre defined semantics TANGENT and BINORMAL

I know collada and fbx supports that, x and obj seems not.
I think assimp should be adding that by default when you import mesh, but i’m not sure
There also some c# command in dx3d but I can’t find it…

Yeah first thing is you are going to need to start w/ geometry that has them (built in geom nodes don’t afaik). Maybe try w/ FBX? What do you actually want to do in the end? Any reason you can’t do them on the fly as Mr Burk suggested?

Hi @antokhio and @everyoneishappy thanks for replies , yes basicly i want to have a node that does the same as the dx9 normals node i,m trying what @mburk suggested imagen

so i have started a geomFX where i add the layout with tangent and binormals and streamout

since according to this you need to apply dx dy in a pixel shader

how can i add that function in a geomfx and i think you can not add pixe shader inside geom right , so this could be totally wrong.

float4x4 tWV: WORLDVIEW ;

float4x4 tTex <string uiname="Texture Transform";>;

struct psInput
    float4 cpoint : SV_Position;
	float4 TexCd : TEXCOORD0;

struct psOutput
  float3 binormal : BINORMAL;
  float3 tangent : TANGENT;

struct VS_IN
	float3 cpoint : POSITION;
	float3 norm : NORMAL;
	float4 TexCd : TEXCOORD0;


struct VS_OUTPUT
	float4 cpoint : SV_Position;
	float3 norm : NORMAL;
	float4 TexCd : TEXCOORD0;
	//float3 binormal : BINORMAL;
	//float3 tangent : TANGENT;

	//Here we simply passtrough the vertex data
    VS_OUTPUT output;
    output.cpoint = float4(input.cpoint,1);
	output.norm = input.norm;
	output.TexCd = mul(input.TexCd,tTex);
	//output.binormal = input.binormal;
	//output.tangent = input.tangent;
    return output;

//float4 PS(psInput input): SV_Target
	// compute derivations of the world position
//float3 p_dx = ddx(;
//float3 p_dy = ddy(;
// compute derivations of the texture coordinate
//float2 tc_dx = ddx(In.TexCd.xy);
//float2 tc_dy = ddy(In.TexCd.xy);
// compute initial tangent and bi-tangent
//float3 t = normalize( tc_dy.y * p_dx - tc_dx.y * p_dy );
//float3 b = normalize( tc_dy.x * p_dx - tc_dx.x * p_dy ); // sign inversion
// get new tangent from a given mesh normal
//float3 n = normalize(In.NormW);
//float3 x = cross(n, t);
//t = cross(x, n);
//t = normalize(t);
// get updated bi-tangent
//x = cross(b, n);
//b = cross(n, x);
//b = normalize(b);	
 //   return psOutput;



void GS(triangle VS_OUTPUT input[3], inout TriangleStream<VS_OUTPUT>GSOut)

	for(uint i=0;i<3;i++)

GeometryShader StreamOutGS = ConstructGSWithSO( CompileShader( gs_5_0, GS() ), ";;TEXCOORD0.xy;;" );

technique11 MeshSplit
	pass P0
		SetVertexShader( CompileShader( vs_5_0, VS() ) );
	   // SetPixelShader( CompileShader( ps_5_0, PS() ) );
		SetGeometryShader( CompileShader( gs_5_0, GS() ) );
	    SetGeometryShader( StreamOutGS );

} (13.2 KB)

@antokhio As I understand it, ddx, ddy are the difference between, one texel and the next, hence you can get the slope between them. And why they only work in ps. I’ll have a look at the gfx later…

Yes pixel shader process “quads” (blocks of 2x2 pixels), so ddx/ddy compute gradient between those.

This is how Sample works internally (it uses gradient to choose appropriate mip level)

ddx or gs are the same, but note that you will have “flat tangents”, so for many cases it’s not ideal.

Computing smooth tangents is doable, but that can be done in 2 ways

  • For box/quads, they are constant, so could add a table for it
  • For objects like sphere, you can calculate those as a function (or use derivatives)
  • For other you can perform accumulation (as the dx9 node does)

I don’t see having a “Normals” node s per dx9 (as it would involve a readback), but option to generate tangent could definitely be added on the primitive node itself. is there already, I’ll have a look to add it when possible
(2d primitives like quad/roundrect/segment are easy as it’s just x/y vector, others need another treatment)

1 Like

Hi @vux @catweasel , thanks for help and feeeback.

Here is a GeomFX which adds tangents and normals (thanks and credits to @unc ) but somehow although the values in readback seems correct it does not work as expected, maybe some geomfx internal issue ?

All this need came since i was porting some dx9 shaders and dx9 patches similar, like the bumpglosse example i attached, and most of them used the old normal dx9 node to add tangents and binormals.

please check the attached patch with the Geom and the bumpglosse example, toggle the from file model and after geomfx model . (1.2 MB)

Here are two more code snippets from how I use it:

The quick and dirty way in the vertex shader:

//tangents and binormals approximation
vec3 tangent;
vec3 binormal;
vec3 c1 = cross(n, vec3(0.0, 0.0, 1.0));
vec3 c2 = cross(n, vec3(0.0, 1.0, 0.0));
if (length(c1) > length(c2))
tangent = c1;
tangent = c2;
tangent = normalize(tangent);
binormal = normalize(cross(n, tangent));

Or The clean way which involves texture coordinates and is aequivalent to what mburk posted:

vec3 pos_dx = dFdx(v_Position);
vec3 pos_dy = dFdy(v_Position);
vec3 tex_dx = dFdx(vec3(v_UV, 0.0));
vec3 tex_dy = dFdy(vec3(v_UV, 0.0));
vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);

t = normalize(t - ng * dot(ng, t));
vec3 b = normalize(cross(ng, t));
mat3 tbn = mat3(t, b, ng);

I think either way it is better to do it in your final shader and not in an geometry shader

Hi, @tekcor , thanks for your snippets.

Yes for particular cases like the bump shader above adding this in the code it works :

float3 Q1 = ddx(;
float3 Q2 = ddy(;
float2 st1 = ddx(IN.TexCd.xy);
float2 st2 = ddy(IN.TexCd.xy);

float3 Tn = normalize(Q1st2.y - Q2st1.y);
float3 Bn = normalize(-Q1st2.x + Q2st1.x);

anyway it would be great to find out why the geometry shader in the example before is not working as expected visually but output what seems right values in the streamout buffers.


matcap requires tangent and binormal, did the discussion render a usable geometry shader with that function ?

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.