parallax mapping

Here is some code from my test application
fragment shader:

uniform sampler2D NMAP;
 
varying vec4 L;
varying vec4 H;
varying vec2 P;
varying float q;
 
const int ns = 40;
 
void main(void)
{
    gl_TexCoord[0].s /= q;

    float dB = 1.0 / ns;
 
    vec2 dC = P * dB;
    vec2 C = gl_TexCoord[0].st + dC;
     
    float cH = texture2D(NMAP, C.st).a;
    float pH = 0.0;
    float B = 1.0 - dB;
    
    while (cH < B)
    {
        pH = cH;
        B -= dB;
        C += dC;
        cH = texture2D(NMAP, C.st).a;
    }
   
    float t = (B + dB - pH) / (cH - pH + dB);
    
    C += P * (dB * t - dB);

    vec4 N = texture2D(NMAP, C.st);
    
    vec3 Nt = N.xyz;
    Nt = Nt * 2.0 - 1.0;
    Nt = normalize(Nt);
    
    float diff = max(dot(Nt, normalize(L.xyz)), 0.0);
    float spec = max(dot(Nt, normalize(H.xyz)), 0.0);
    
    spec = pow(spec, 512.0);

    gl_FragColor = gl_FrontMaterial.ambient + (diff *
gl_FrontLightProduct[0].diffuse + spec *
gl_FrontLightProduct[0].specular);
    gl_FragColor.a = smoothstep(0.0, 1.0, N.a);
}

vertex shader:

const vec4 EYE = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 SRC = vec4(0.0, 0.0, 0.0, 1.0);

varying vec4 L;
varying vec4 H;
varying vec2 P;
varying float q;

uniform float s;

void main(void)
{

    vec3 T;
    vec3 B;
    vec3 N;

    N = normalize(gl_Normal);
    T = normalize(vec3(N.z, 0.0, -N.x));
    B = cross(N, T);

    mat4 iTBN = mat4(
        T.x, B.x, N.x, 0.0,
        T.y, B.y, N.y, 0.0,
        T.z, B.z, N.z, 0.0,
        0.0, 0.0, 0.0, 1.0
    );

    vec4 E = gl_ModelViewMatrixInverse * EYE - gl_Vertex;
    E = iTBN * E;
    
    L = gl_ModelViewMatrixInverse * SRC - gl_Vertex;
    L = iTBN * L;
    
    H = E + L;
    
    P = -E.xy * s / E.z;
    
    q = sqrt(1.0 - gl_Vertex.y * gl_Vertex.y);
        
    gl_Position = ftransform();
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_TexCoord[0].s *= q;

} 

rendering routine:

glBegin(GL_QUADS);
    for (int i = 0; i < T; ++i)
    for (int j = 0; j < P; ++j)
    {
    
float3 N = 0.25f * (
    vertices[(i + 0) * (P + 1) + j + 0] +
    vertices[(i + 0) * (P + 1) + j + 1] +
    vertices[(i + 1) * (P + 1) + j + 1] +
    vertices[(i + 1) * (P + 1) + j + 0]);
    
glNormal3fv(N);

glTexCoord2f(0.0f, 0.0f);
glVertex3fv(vertices[(i + 0) * (P + 1) + j + 0]);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(vertices[(i + 0) * (P + 1) + j + 1]);
glTexCoord2f(1.0f, 1.0f);
glVertex3fv(vertices[(i + 1) * (P + 1) + j + 1]);
glTexCoord2f(0.0f, 1.0f);
glVertex3fv(vertices[(i + 1) * (P + 1) + j + 0]);
    }
glEnd();

Here verices represents array of points on unit sphere (normal vectors
are the same in fact)

float3 N = 0.25f * … - flat normal calculation.

caffeine_rv

As I see, now the problem is that you calculate your texture coordinates with some perspective hacks, BUT you don’t consider this hacks in other interpolators (L, H, P; especially P, because as I understood, P is texture-space parallax direction, and it has to follow the same rules, as texture coordinates). So it is clear, why picture is wrong.

Could you please post simple test program - so we can play a little with you pixel shaders to see what we can fix. It is so lazy to write test program with your shaders.