Gamestudio Links
Zorro Links
Newest Posts
Data from CSV not parsed correctly
by EternallyCurious. 04/18/24 10:45
StartWeek not working as it should
by Zheka. 04/18/24 10:11
folder management functions
by VoroneTZ. 04/17/24 06:52
lookback setting performance issue
by 7th_zorro. 04/16/24 03:08
zorro 64bit command line support
by 7th_zorro. 04/15/24 09:36
Zorro FIX plugin - Experimental
by flink. 04/14/24 07:48
Zorro FIX plugin - Experimental
by flink. 04/14/24 07:46
AUM Magazine
Latest Screens
The Bible Game
A psychological thriller game
SHADOW (2014)
DEAD TASTE
Who's Online Now
1 registered members (SBGuy), 652 guests, and 3 spiders.
Key: Admin, Global Mod, Mod
Newest Members
EternallyCurious, howardR, 11honza11, ccorrea, sakolin
19047 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Normal Mapping Without Precomputed Tangents #420882
04/05/13 13:48
04/05/13 13:48
Joined: Jul 2001
Posts: 6,904
H
HeelX Offline OP
Senior Expert
HeelX  Offline OP
Senior Expert
H

Joined: Jul 2001
Posts: 6,904
Hi,

when you are doing per-pixel shading and you use precomputed tangents, you are bending a vertex normal probably like this:

Code:
float3x3 matTangent;

struct VS_IN
{
	//...
	float3 Normal : NORMAL;
	float4 Tangent : TEXCOORD2;
	//...
};

struct VS_OUT
{
	//...
	float3 Normal : TEXCOORD2;
	float3 Tangent : TEXCOORD3;
	float3 Binormal : TEXCOORD4;
	//...
};

VS_OUT VS (VS_IN In)
{
	//...
	Out.Normal = mul(In.Normal.xyz, (float3x3)matWorld);
	Out.Tangent = mul(In.Tangent.xyz, (float3x3)matWorld);
	Out.Binormal = mul(cross(In.Tangent.xyz, In.Normal.xyz) * In.Tangent.w, (float3x3)matWorld);
	//...
}

float4 PS (VS_OUT In): COLOR
{
	//...
	float3 tsBump = (tex2D(smpNormal, In.Texcoord.xy).rgb * 2 - 1);	
	float3 wsBumpNormal = normalize(In.Tangent * tsBump.r + In.Binormal * tsBump.g + In.Normal * tsBump.b);
	//...
}



This is fast, easy and the classic approach, but requires tangent precomputation. If this is not feasible (too many texcoords used or for whatever reason it is not desired), you can compute the tangent frame on the fly in the pixel shader just with the vertex normal, the view direction to the pixel's surface position and the texcoord, like this:

Code:
struct VS_IN
{
	//...
	float3 Normal : NORMAL;
	//...
};

struct VS_OUT
{
	//...
	float3 Normal : TEXCOORD2;
	float3 ViewDir : TEXCOORD3;
	//...
};

VS_OUT VS (VS_IN In)
{
	//...
	Out.Normal = mul(In.Normal.xyz, (float3x3)matWorld);
	Out.ViewDir = vecViewPos - mul(In.Pos.xyz, (float3x3)matWorld);
	//...
}

// Calculates a cotangent frame without precomputed tangents by Christian Schüler
// ported from GLSL to HLSL; see: http://www.thetenthplanet.de/archives/1180
float3x3 calcWsCotangentFrame (float3 wsNormal, float3 wsInvViewDir, float2 tsCoord)
{
    // get edge vectors of the pixel triangle
    float3 dp1 = ddx(wsInvViewDir);
    float3 dp2 = ddy(wsInvViewDir);
    float2 duv1 = ddx(tsCoord);
    float2 duv2 = ddy(tsCoord);
 
    // solve the linear system
    float3 dp2perp = cross(dp2, wsNormal);
    float3 dp1perp = cross(wsNormal, dp1);
    float3 T = dp2perp * duv1.x + dp1perp * duv2.x;
    float3 B = dp2perp * duv1.y + dp1perp * duv2.y;
 
    // construct and return a scale-invariant cotangent frame
    float invmax = rsqrt(max(dot(T,T), dot(B,B)));
    return float3x3(T * invmax, B * invmax, wsNormal);
}

float4 PS (VS_OUT In): COLOR
{
	//...
	float3 tsBump = (tex2D(smpNormal, In.Texcoord.xy).rgb * 2 - 1);	
	float3 wsBumpNormal = normalize(mul(tsBump, calcWsCotangentFrame(In.Normal, -In.ViewDir, In.Texcoord)));
	//...
}


Of course this is slower than with precomputed tangents, but only about ~14 instructions and requires shader model 3.0. It is in particular useful for procedural geometry. The code was ported from GLSL and is from Christian Schüler (see his blog).

I hope this is useful for some people out there.

Last edited by HeelX; 04/05/13 13:48.
Re: Normal Mapping Without Precomputed Tangents [Re: HeelX] #420883
04/05/13 14:02
04/05/13 14:02
Joined: Jun 2009
Posts: 2,210
Bavaria, Germany
Kartoffel Offline
Expert
Kartoffel  Offline
Expert

Joined: Jun 2009
Posts: 2,210
Bavaria, Germany
Thanks for sharing this.
But aren't the tangents usually computed on the cpu?
So this should be faster when using a lot of geometry (especially for animated models).


POTATO-MAN saves the day! - Random
Re: Normal Mapping Without Precomputed Tangents [Re: Kartoffel] #420889
04/05/13 15:38
04/05/13 15:38
Joined: Jul 2001
Posts: 6,904
H
HeelX Offline OP
Senior Expert
HeelX  Offline OP
Senior Expert
H

Joined: Jul 2001
Posts: 6,904
I am not 100% sure, but I guess so. When you clip pixels with an early z-pass, have GPU animated models and lots of geometry, I guess you can benefit from this technique. By the way, the author claims that the visual results are also more pleasing than the standard approach; I guess this comes in handy on large deformed body parts.

Re: Normal Mapping Without Precomputed Tangents [Re: HeelX] #420925
04/06/13 18:14
04/06/13 18:14
Joined: Mar 2006
Posts: 2,252
Hummel Offline
Expert
Hummel  Offline
Expert

Joined: Mar 2006
Posts: 2,252
Thanks for sharing. Have you done some stability testing? As far as I remember, the old ShaderX4 version wasn't that stable.

EDIT:
Code:
Out.ViewDir = vecViewPos - mul(In.Pos.xyz, (float3x3)matWorld);

-> shouldn't this be:
Code:
Out.ViewDir = vecViewPos - mul(float4(In.Pos.xyz, 1), (float4x4)matWorld);

instead?

Last edited by Hummel; 04/06/13 18:18.
Re: Normal Mapping Without Precomputed Tangents [Re: Hummel] #420926
04/06/13 18:31
04/06/13 18:31
Joined: Jul 2001
Posts: 6,904
H
HeelX Offline OP
Senior Expert
HeelX  Offline OP
Senior Expert
H

Joined: Jul 2001
Posts: 6,904
Originally Posted By: Hummel
Have you done some stability testing? As far as I remember, the old ShaderX4 version wasn't that stable.
No. To be honest, I forgot the handedness of the precomputed tangent frame which is stored in the tangent .w component and tried this approach instead - and it worked in an instant! After I noticed the missed handedness factor (to be multiplied with the crossed' binormal) I switched back to precomupted tangents and skipped the pixelshader approach because of improved performance.

Originally Posted By: Hummel
shouldn't this be ... instead?
Hm. I did it that way because position- and direction vectors are for me always XYZ... why should I use the full homogenous transform? However, I see no noticeable difference if I use the full 4x4 homogenous transform and cast back to the XYZ vector. And last but not least, in the default.fx the world matrix is being casted to a 3x3, too, if a XYZ vector is passed. So.. I guess this shouldn't be too wrong, I guess smile

Last edited by HeelX; 04/06/13 18:32.
Re: Normal Mapping Without Precomputed Tangents [Re: HeelX] #420927
04/06/13 18:58
04/06/13 18:58
Joined: Mar 2006
Posts: 2,252
Hummel Offline
Expert
Hummel  Offline
Expert

Joined: Mar 2006
Posts: 2,252
But you are missing the translation this way. Might very well be that for the cotangent frame calculation it is not relevant since you are using only derivatives.

To be precise it should be:
Code:
Out.ViewDir.xyz = vecViewPos.xyz - mul(float4(In.Pos.xyz, 1), (float4x4)matWorld).xyz;


Last edited by Hummel; 04/06/13 19:01.

Moderated by  Blink, Hummel, Superku 

Gamestudio download | chip programmers | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1