Jump to content


per pixel displacement mapping not working with curved basis space


4 replies to this topic

#1 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 04 January 2009 - 07:44 AM

Ive implemented per pixel displacement mapping, it seems to be working when the basis is straight flat out of the triangle, but when i curve it (so it can connect to other triangles in a curving mesh) it seems to jumble up the middle.

Is this supposed to happen, or is curving the normals allowed and im just screwing something up...

Posted Image

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4782 posts
  • LocationBellevue, WA

Posted 04 January 2009 - 07:57 AM

Can we see the code for doing the displacement mapping?
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 04 January 2009 - 08:02 AM

Ive got something to say,
I actually hijacked it out of the november 2007 dx10 sdk displacement mapping sample, so some of the code I didnt write.
Its the biggest shader ive ever put together so far, per pixel displacement mapping is complicated.

cbuffer cb

{

 matrix wvp;

 float3 ldir;

 float3 eye;

};


Texture2D normal;

Texture2D displace;


SamplerState sstate

{

 Filter=ANISOTROPIC;

 AddressU = Clamp;

 AddressV = Clamp;

};



struct VS_INPUT

{

 float4 pos          : POSITION;         

 float2 uv           : TEXCOORD0;

 float3 normal       : NORMAL;

 float3 tangent      : TANGENT;

 uint   VertID : SV_VertexID;	//verTex ID, used for consistent tetrahedron generation

};


struct GS_INPUT

{

 float4 Pos	: POSITION;			//Position

 float3 vPos : POSVIEW;		//view pos

 float3 Norm : NORMAL;		//Normal

 float3 Tex	: TEXCOORD0;	//Texture coordinate

 float3 Tangent : TANGENT;	//Normalized Tangent vector

 uint   VertID : VertID;	    //verTex ID, used for consistent tetrahedron generation

};


struct PS_INPUT

{

 float4 Pos : SV_Position;

 float4 planeDist : TEXCOORD0;

 float3 vPos : TEXCOORD1;

    

 float3 Norm : TEXCOORD2;		// Normal of the first vert

 float3 TanT : TEXCOORD3;		// Tangent of the first vert

 float3 Tex : TEXCOORD4;		// Texture Coordinate of the first vert

 float3 pos0 : TEXCOORD5;			// Position of the first vert

    

 float4 GtxNx : TEXCOORD6;			// Gradient of the tetrahedron for X texcoord

 float4 GtyNx : TEXCOORD7;			// Gradient of the tetrahedron for Y texcoord

 float4 GtzNx : TEXCOORD8;			// Gradient of the tetrahedron for Z texcoord

    

 float4 GTxNy : TEXCOORD9;			// Gradient of the tetrahedron for X Tangent

 float4 GTyNy : TEXCOORD10;		// Gradient of the tetrahedron for Y Tangent

 float4 GTzNy : TEXCOORD11;		// Gradient of the tetrahedron for Z Tangent

 

 float3 GNz : TEXCOORD12;		// Gradient of the tetrahedron for X Normal

};


GS_INPUT vs(VS_INPUT input)

{

 GS_INPUT output = (GS_INPUT)0; 

 output.Pos.xyz = input.pos;

 output.Pos.w=1;

 output.vPos=input.pos;

 output.Tex.xy  = input.uv.xy;

 output.Tex.z=0.0f;

 output.Norm = input.normal;

 output.Tangent = input.tangent;

 output.VertID = input.VertID;


 return output;

}



float RayDistToPlane( float3 vPoint, float3 vDir, float3 A, float3 planeNorm )

{	

    float Nom = dot( planeNorm, float3(A - vPoint) );

    float DeNom = dot( planeNorm, vDir );

    return Nom/DeNom;

}


void CalcGradients( inout PS_INPUT V0, inout PS_INPUT V1, inout PS_INPUT V2, inout PS_INPUT V3, float3 N0, float3 N1, float3 N2, float3 N3 )

{

    float dotN0 = dot(N0, V0.vPos - V3.vPos);

    float dotN1 = dot(N1, V1.vPos - V2.vPos);

    float dotN2 = dot(N2, V2.vPos - V1.vPos);

    float dotN3 = dot(N3, V3.vPos - V0.vPos);

    

    //Tex

    float3	Gtx =  ( V0.Tex.x / dotN0 )*N0;

            Gtx += ( V1.Tex.x / dotN1 )*N1;

            Gtx += ( V2.Tex.x / dotN2 )*N2;

            Gtx += ( V3.Tex.x / dotN3 )*N3;

    

    float3	Gty =  ( V0.Tex.y / dotN0 )*N0;

            Gty += ( V1.Tex.y / dotN1 )*N1;

            Gty += ( V2.Tex.y / dotN2 )*N2;

            Gty += ( V3.Tex.y / dotN3 )*N3;

    

    float3	Gtz =  ( V0.Tex.z / dotN0 )*N0;

            Gtz += ( V1.Tex.z / dotN1 )*N1;

            Gtz += ( V2.Tex.z / dotN2 )*N2;

            Gtz += ( V3.Tex.z / dotN3 )*N3;

        

    //Tangent	

    float3	GTx =  ( V0.TanT.x / dotN0 )*N0;

            GTx += ( V1.TanT.x / dotN1 )*N1;

            GTx += ( V2.TanT.x / dotN2 )*N2;

            GTx += ( V3.TanT.x / dotN3 )*N3;

    

    float3	GTy =  ( V0.TanT.y / dotN0 )*N0;

            GTy += ( V1.TanT.y / dotN1 )*N1;

            GTy += ( V2.TanT.y / dotN2 )*N2;

            GTy += ( V3.TanT.y / dotN3 )*N3;

    

    float3	GTz =  ( V0.TanT.z / dotN0 )*N0;

            GTz += ( V1.TanT.z / dotN1 )*N1;

            GTz += ( V2.TanT.z / dotN2 )*N2;

            GTz += ( V3.TanT.z / dotN3 )*N3;

    

    //Normal	

    float3	GNx =  ( V0.Norm.x / dotN0 )*N0;

            GNx += ( V1.Norm.x / dotN1 )*N1;

            GNx += ( V2.Norm.x / dotN2 )*N2;

            GNx += ( V3.Norm.x / dotN3 )*N3;

    

    float3	GNy =  ( V0.Norm.y / dotN0 )*N0;

            GNy += ( V1.Norm.y / dotN1 )*N1;

            GNy += ( V2.Norm.y / dotN2 )*N2;

            GNy += ( V3.Norm.y / dotN3 )*N3;

    

    float3	GNz =  ( V0.Norm.z / dotN0 )*N0;

            GNz += ( V1.Norm.z / dotN1 )*N1;

            GNz += ( V2.Norm.z / dotN2 )*N2;

            GNz += ( V3.Norm.z / dotN3 )*N3;

            

    V0.Norm = V0.Norm;

    V0.TanT = V0.TanT;

    V0.Tex = V0.Tex;

    V0.pos0 = V0.vPos;

    V0.GtxNx.xyz = Gtx;

    V0.GtyNx.xyz = Gty;

    V0.GtzNx.xyz = Gtz;

    V0.GTxNy.xyz = GTx;

    V0.GTyNy.xyz = GTy;

    V0.GTzNy.xyz = GTz;

    V0.GtxNx.w = GNx.x;

    V0.GtyNx.w = GNx.y;

    V0.GtzNx.w = GNx.z;

    V0.GTxNy.w = GNy.x;

    V0.GTyNy.w = GNy.y;

    V0.GTzNy.w = GNy.z;

    V0.GNz = GNz;

    

    V1.Norm = V0.Norm;

    V1.TanT = V0.TanT;

    V1.Tex = V0.Tex;

    V1.pos0 = V0.vPos;

    V1.GtxNx.xyz = Gtx;

    V1.GtyNx.xyz = Gty;

    V1.GtzNx.xyz = Gtz;

    V1.GTxNy.xyz = GTx;

    V1.GTyNy.xyz = GTy;

    V1.GTzNy.xyz = GTz;

    V1.GtxNx.w = GNx.x;

    V1.GtyNx.w = GNx.y;

    V1.GtzNx.w = GNx.z;

    V1.GTxNy.w = GNy.x;

    V1.GTyNy.w = GNy.y;

    V1.GTzNy.w = GNy.z;

    V1.GNz = GNz;

    

    V2.Norm = V0.Norm;

    V2.TanT = V0.TanT;

    V2.Tex = V0.Tex;

    V2.pos0 = V0.vPos;

    V2.GtxNx.xyz = Gtx;

    V2.GtyNx.xyz = Gty;

    V2.GtzNx.xyz = Gtz;

    V2.GTxNy.xyz = GTx;

    V2.GTyNy.xyz = GTy;

    V2.GTzNy.xyz = GTz;

    V2.GtxNx.w = GNx.x;

    V2.GtyNx.w = GNx.y;

    V2.GtzNx.w = GNx.z;

    V2.GTxNy.w = GNy.x;

    V2.GTyNy.w = GNy.y;

    V2.GTzNy.w = GNy.z;

    V2.GNz = GNz;

    

    V3.Norm = V0.Norm;

    V3.TanT = V0.TanT;

    V3.Tex = V0.Tex;

    V3.pos0 = V0.vPos;

    V3.GtxNx.xyz = Gtx;

    V3.GtyNx.xyz = Gty;

    V3.GtzNx.xyz = Gtz;

    V3.GTxNy.xyz = GTx;

    V3.GTyNy.xyz = GTy;

    V3.GTzNy.xyz = GTz;

    V3.GtxNx.w = GNx.x;

    V3.GtyNx.w = GNx.y;

    V3.GtzNx.w = GNx.z;

    V3.GTxNy.w = GNy.x;

    V3.GTyNy.w = GNy.y;

    V3.GTzNy.w = GNy.z;

    V3.GNz = GNz;

}


void GSCreateTetra( in GS_INPUT A, in GS_INPUT B, in GS_INPUT C, in GS_INPUT D,

                    inout TriangleStream<PS_INPUT> DisplaceStream )

{	

    float3 AView = normalize( A.vPos - eye);

    float3 BView = normalize( B.vPos - eye);

    float3 CView = normalize( C.vPos - eye);

    float3 DView = normalize( D.vPos - eye);

    

    PS_INPUT Aout;

    Aout.Pos = A.Pos;

    Aout.vPos = A.vPos;

    Aout.Norm = A.Norm;

    Aout.Tex = A.Tex;

    Aout.TanT = A.Tangent;

    

    PS_INPUT Bout;

    Bout.Pos = B.Pos;

    Bout.vPos = B.vPos;

    Bout.Norm = B.Norm;

    Bout.Tex = B.Tex;

    Bout.TanT = B.Tangent;

    

    PS_INPUT Cout;

    Cout.Pos = C.Pos;

    Cout.vPos = C.vPos;

    Cout.Norm = C.Norm;

    Cout.Tex = C.Tex;

    Cout.TanT = C.Tangent;

    

    PS_INPUT Dout;

    Dout.Pos = D.Pos;

    Dout.vPos = D.vPos;

    Dout.Norm = D.Norm;

    Dout.Tex = D.Tex;

    Dout.TanT = D.Tangent;

    

    float3 AB = C.vPos-B.vPos;

    float3 AC = D.vPos-B.vPos;

    float3 planeNormA = normalize( cross( AC, AB ) );

           AB = D.vPos-A.vPos;

           AC = C.vPos-A.vPos;

    float3 planeNormB = normalize( cross( AC, AB ) );

           AB = B.vPos-A.vPos;

           AC = D.vPos-A.vPos;

    float3 planeNormC = normalize( cross( AC, AB ) );

           AB = C.vPos-A.vPos;

           AC = B.vPos-A.vPos;

    float3 planeNormD = normalize( cross( AC, AB ) );

    

    Aout.planeDist.x = Aout.planeDist.y = Aout.planeDist.z = 0.0f;

    Aout.planeDist.w = RayDistToPlane( A.vPos, AView, B.vPos, planeNormA );

    

    Bout.planeDist.x = Bout.planeDist.z = Bout.planeDist.w = 0.0f;

    Bout.planeDist.y = RayDistToPlane( B.vPos, BView, A.vPos, planeNormB );

    

    Cout.planeDist.x = Cout.planeDist.y = Cout.planeDist.w = 0.0f;

    Cout.planeDist.z = RayDistToPlane( C.vPos, CView, A.vPos, planeNormC );

    

    Dout.planeDist.y = Dout.planeDist.z = Dout.planeDist.w = 0.0f;

    Dout.planeDist.x = RayDistToPlane( D.vPos, DView, A.vPos, planeNormD );

    

    CalcGradients( Aout, Bout, Cout, Dout, planeNormA, planeNormB, planeNormC, planeNormD );

    

    DisplaceStream.Append( Cout );

    DisplaceStream.Append( Bout );

    DisplaceStream.Append( Aout );

    DisplaceStream.Append( Dout );

    DisplaceStream.Append( Cout );

    DisplaceStream.Append( Bout );

    DisplaceStream.RestartStrip();

}





[maxvertexcount(18)]

void gs(triangle GS_INPUT In[3], inout TriangleStream<PS_INPUT> DisplaceStream)

{

    //Don't extrude anything that's facing too far away from us

    //Just saves geometry generation

    float3 AB = In[1].Pos - In[0].Pos;

    float3 AC = In[2].Pos - In[0].Pos;

    float3 triNorm = cross( AB, AC );

    float lenTriNorm = length( triNorm );

    triNorm /= lenTriNorm;

    

    //Extrude along the Normals

    GS_INPUT v[6];

    [unroll] for( int i=0; i<3; i++ )

    {

        float4 PosNew = In[i].Pos;

        float4 PosExt = PosNew + float4(In[i].Norm*   /*this is max displacement*/ 0.5f  ,0);

        v[i].vPos = PosNew.xyz;

        v[i+3].vPos = PosExt.xyz;

        

        v[i].Pos = mul( PosNew, wvp);

        v[i+3].Pos = mul( PosExt, wvp);

        

        v[i].Tex = float3(In[i].Tex.xy,0);

        v[i+3].Tex = float3(In[i].Tex.xy,1);

        

        v[i].Norm = In[i].Norm;

        v[i+3].Norm = In[i].Norm;

        

        v[i].Tangent = In[i].Tangent;

        v[i+3].Tangent = In[i].Tangent;

    }


    // Make sure that our prism hasn't "flipped" on itself after the extrusion

    AB = v[4].vPos - v[3].vPos;

    AC = v[5].vPos - v[3].vPos;

    float3 topNorm = cross( AB, AC );

    float lenTop = length( topNorm );

    topNorm /= lenTop;

    if( 

		lenTriNorm < 0.005f ||						//avoid tiny triangles

        dot( topNorm, triNorm ) < 0.95f ||			//make sure the top of our prism hasn't flipped

        abs((lenTop-lenTriNorm)/lenTriNorm) > 10.0f//11.0f	//make sure we don't balloon out too much

        

        )

    {

        [unroll] for( int i=0; i<3; i++ )

        {

            float4 PosNew = In[i].Pos;

            float4 PosExt = PosNew + float4(In[i].Norm* /*g_MinDisplacement*/ -0.5f,0);

            v[i].vPos = PosNew.xyz;

            v[i+3].vPos = PosExt.xyz;

            

            v[i].Pos = mul( PosNew, wvp);

            v[i+3].Pos = mul( PosExt, wvp);

        }

    }

    

    int index[6] = {0,1,2,3,4,5};

        

    // Create 3 tetrahedra

    GSCreateTetra( v[index[4]], v[index[5]], v[index[0]], v[index[3]], DisplaceStream );

    GSCreateTetra( v[index[5]], v[index[0]], v[index[1]], v[index[4]], DisplaceStream );

    GSCreateTetra( v[index[0]], v[index[1]], v[index[2]], v[index[5]], DisplaceStream );


}


#define MAX_DIST 1000000000.0f

#define MAX_STEPS 64

#define MIN_STEPS 4

#define STEP_SIZE (1.0f/4096.0f)

#define OFFSET_MAX 0.1f	//maximum we can cover is 1/10th of the entire texture in one march


float4 ps(PS_INPUT input) : SV_Target

{

    float4 modDist = float4(0,0,0,0);

    modDist.x = input.planeDist.x > 0 ? input.planeDist.x : MAX_DIST;

    modDist.y = input.planeDist.y > 0 ? input.planeDist.y : MAX_DIST;

    modDist.z = input.planeDist.z > 0 ? input.planeDist.z : MAX_DIST;

    modDist.w = input.planeDist.w > 0 ? input.planeDist.w : MAX_DIST;

    

    // find distance to the rear of the tetrahedron

    float fDist = min( modDist.x, modDist.y );

    fDist = min( fDist, modDist.z );

    fDist = min( fDist, modDist.w );

    

    // find the texture coords of the entrance point

    float3 texEnter;

    float3 relPos = input.vPos-input.pos0;

    texEnter.x = dot( input.GtxNx.xyz,relPos ) + input.Tex.x;

    texEnter.y = dot( input.GtyNx.xyz,relPos ) + input.Tex.y;

    texEnter.z = dot( input.GtzNx.xyz,relPos ) + input.Tex.z;

    

    // find the exit position

    float3 viewExitDir = normalize( input.vPos - eye)*fDist;

    float3 viewExit = input.vPos + viewExitDir;

    

    // find the texture coords of the exit point

    float3 texExit;

    relPos = viewExit-input.pos0;

    texExit.x = dot( input.GtxNx.xyz,relPos ) + input.Tex.x;

    texExit.y = dot( input.GtyNx.xyz,relPos ) + input.Tex.y;

    texExit.z = dot( input.GtzNx.xyz,relPos ) + input.Tex.z;

	

    // March along the Texture space view ray until we either hit something

    // or we exit the tetrahedral prism

    float3 tanGrad = texExit - texEnter;

    float fTanDist = length( float3(tanGrad.xy,0) );	//length in 2d texture space

    if( fTanDist > OFFSET_MAX )

		discard;

        

    int iSteps = min( ceil( fTanDist / STEP_SIZE ), MAX_STEPS-MIN_STEPS ) + MIN_STEPS;

    tanGrad /= iSteps-1.0f;

    float3 TexCoord = float3(0,0,0);

    bool bFound = false;

	

    float height = 0;

    int i = 0;

    for( i=0; (i<iSteps && !bFound); i++ )

    {

        TexCoord = texEnter + i*tanGrad;

        height = displace.SampleLevel(sstate, float2(TexCoord.xy), 0 );

        height = max( height, -0.5f /*min displacement*/);

        

        if( TexCoord.z <= height )

        {

            bFound = true;

        }

    }

    if( !bFound )

		discard;

	

    // lookup the normal from the normal map

    float3 texNormal = normal.Sample(sstate, TexCoord.xy );

    texNormal *= 2.0;

    texNormal -= float3(1,1,1);

    

    float3 foundPos = input.vPos + viewExitDir*((float)i/(float)iSteps);

    relPos = foundPos - input.pos0;

    float3 nTanT;

    nTanT.x = dot( input.GTxNy.xyz,relPos ) + input.TanT.x;

    nTanT.y = dot( input.GTyNy.xyz,relPos ) + input.TanT.y;

    nTanT.z = dot( input.GTzNy.xyz,relPos ) + input.TanT.z;

    

    float3 nNormT;

    float3 GNx = float3( input.GtxNx.w, input.GtyNx.w, input.GtzNx.w );

    float3 GNy = float3( input.GTxNy.w, input.GTyNy.w, input.GTzNy.w );

    nNormT.x = dot( GNx,relPos ) + input.Norm.x;

    nNormT.y = dot( GNy,relPos ) + input.Norm.y;

    nNormT.z = dot( input.GNz,relPos ) + input.Norm.z;

    

    //float3 nBiNormT = normalize( cross( nNormT, nTanT ) );

    //float3x3 BTNMatrix = float3x3( nBiNormT, nTanT, nNormT );

    //texNormal = normalize( mul( texNormal, BTNMatrix ) ); //world space bump

    

    // Move the light orientation into Texture space

    float3 lightDir = ldir;

    float3 viewDir = normalize( -viewExitDir );

    

    // dot with Texture space light vector

    float lightAmt = saturate( dot( lightDir, texNormal ) );

    float4 lightColor = float4(1,1,1,1);

    

    // Get the Diffuse and Specular Textures

    float4 diffuse = float4(1,1,1,1);

    float specular = diffuse.a;


    // Calculate specular power

    float3 halfAngle = normalize( viewDir + lightDir );

    float4 spec = pow( saturate(dot( halfAngle, texNormal )), 64 );

    

    float4 col=lightColor*diffuse*lightAmt + spec*specular;

    

    col.a=1.0f;

    

    // Return combined lighting

    return col;

}




technique10 vdm

{

 pass P0

 {

  SetVertexShader(CompileShader(vs_4_0, vs()));

  SetGeometryShader(CompileShader(gs_4_0, gs()));

  SetPixelShader(CompileShader(ps_4_0, ps()));       

 }

}




So if it was functioning correctly, should I be able to curve the basis like that and it should still work?
I dont think its the code, Maybe im not calculating the basis space right... anyway ill figure it out...

#4 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 04 January 2009 - 08:48 AM

Disregard this post, ive recoded it myself and ive got different problems, but the basis is working. thankyou...

#5 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 04 January 2009 - 11:14 AM

another picture just for fun, when i add curved basis now it warps too much, but im on it.
Posted Image





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users