WorldViz User Forum

WorldViz User Forum (https://forum.worldviz.com/index.php)
-   Vizard (https://forum.worldviz.com/forumdisplay.php?f=17)
-   -   Normal Map, Tangent Binormal on Mirrored Objects (https://forum.worldviz.com/showthread.php?t=4656)

shivanangel 06-21-2013 12:15 PM

Normal Map, Tangent Binormal on Mirrored Objects
 
1 Attachment(s)
Dear Support,

I had a question regarding your calculation of tangent and binormal attributes for shaders.

On my 3ds objects, to conserve texture space I am using symmetries. This causes the UV coordinates to be shared, but mirrored.

When I bring the object into Vizard and use a Normal Map shader, I get every other symmetry (mirrored) element with reversed tangent and binormal attributes.

I was wondering if you know of a fix for reversing the orientation of the tangent attribute, or if there might be a way for you to check in your automatic calculation ( I am guessing you use the texture coordinates for this) to determine if the sign of the normal is in the proper direction?

Attached is an image of the problem, if I go into max and mirror the UVs back, it fixes the alternating problem. However, there are some assets that can not be mirrored (not symmetrical).

Thanks,
George

farshizzo 06-21-2013 01:38 PM

How are you mirroring the UVs in Max? Are you using the Coordinates rollout in the material properties or are you mirroring the actual coordinates through the Edit UVWs dialog of the mesh?

The tangent/binormal calculation is based on the raw UV values, and does not take the texture tiling mode into account. So you should try to use the second method if you are not already doing so.

shivanangel 06-21-2013 01:50 PM

1 Attachment(s)
The mirroring takes place when I use the symmetry modifier on the polygonal mesh. In this case, I created a high resolution mesh for 1/8th the roof of a cylindrical building.
I then use the symmetry modifier 3 times (rotating the modifier pivot each time), the first creates 1/4 of the roof, and then 1/2 and finally the entire roof. I had already UV Mapped the roof so that each symmetry would have the same UV coordinates, increasing the space available in the texture map.

There is no tiling actually occurring, all UVs are within the 0 to 1 area.

Attached is an image of the UVs, with a selection made to show how they are mirrored across the 3D object.

shivanangel 06-21-2013 02:15 PM

After reading a while on Normal maps for other engines, I found that this is a common problem when the attributes do not contain a 4th 'W' component indicating mirrored portions of the mesh. Apparently, other engines such as Unreal and CryEngine are able to determine the flipped normals and have them flipped by multiplying by the W coordinate ( +1 or -1).

Fixes for other engines (Unity in this case) referred to exporting the binormal and tangent vectors through the FBX file format.
In this case, I don't see anything that can be similarly done with the OSG exporter.

farshizzo 06-21-2013 03:25 PM

Do you mind posting your vertex/fragment shader code? I tried applying a normal map to a model with mirrored UVs and it seemed to work correctly.

shivanangel 06-21-2013 04:50 PM

1 Attachment(s)
I went ahead and attached the model and textures.

Thanks,
George

Vertex:
Code:

#version 120

uniform int NumLights;

varying vec3 vPosition;
varying vec3 tPosition;
varying vec3 tView;

varying vec3 tang;
varying vec3 binorm;
varying vec3 normal;

varying vec3 tLightPosition[gl_MaxLights];
varying vec3 tLightDirection[gl_MaxLights];

attribute vec3 Tangent;
attribute vec3 Binormal;

void main()
{
        //Calculate components in view space
        vec3 position = vec3(gl_ModelViewMatrix * gl_Vertex);
        normal = normalize(gl_NormalMatrix * gl_Normal);
        tang = normalize(gl_NormalMatrix * Tangent);
        binorm = normalize(gl_NormalMatrix * Binormal);
       
        mat3 mToTangentSpace = mat3(
        tang.x, binorm.x, normal.x,
        tang.y, binorm.y, normal.y,
        tang.z, binorm.z, normal.z);
       

        tView = mToTangentSpace * (position * -1.0);
        tPosition = mToTangentSpace * position;
        //Iterate over every light and transition position into tangent space
        for( int i = 0 ; i < NumLights ; i++)
        {
                tLightPosition[i] = mToTangentSpace * gl_LightSource[i].spotDirection.xyz;
                tLightPosition[i] = mToTangentSpace * gl_LightSource[i].position.xyz;
        }

        //Pass and transform texture coordinates
        gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

        //Pass Perspecitive Coordinates
        gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
       
        //Compute Fog
        gl_FogFragCoord = gl_FogCoord;
}

Fragment:
Code:

#version 120

uniform int NumLights;

varying vec3 vPosition;
varying vec3 tPosition;
varying vec3 tView;

varying vec3 normal;
varying vec3 tang;
varying vec3 binorm;

varying vec3 tLightPosition[gl_MaxLights];
varying vec3 tLightDirection[gl_MaxLights];

uniform sampler2D Albedo;
uniform sampler2D NormalMap;
uniform sampler2D AmbientOcclusionMap;

//Calculates and returns the contribution of a directional light
void DirectionalLight(in int i,
                                                in vec3 position,
                                                in vec3 view,
                                                in vec3 normal,
                                                inout vec4 ambient,
                                                inout vec4 diffuse,
                                                inout vec4 specular)
{       
        float nDotL; //normal dotted with light vector
        float nDotH; //normal dotted with half vector
        float pf;
       
        nDotL = max(0.0, dot(normal, normalize(tLightPosition[i])));
               
        nDotH = max(0.0, dot(normal, normalize( normalize(tLightPosition[i]) + view) ));
       
        if( nDotL > 0.0)
                pf = pow(nDotH, gl_FrontMaterial.shininess);
        else
                pf = 0.0;
       
        ambient += gl_LightSource[i].ambient;
        diffuse += gl_LightSource[i].diffuse * nDotL;
        specular += gl_LightSource[i].specular * pf;
       
}

//Caculates and returns the contribution of a spotlight
void PointLight( in int i,
                                        in vec3 position,
                                        in vec3 view,
                                        in vec3 iNormal,
                                        inout vec4 ambient,
                                        inout vec4 diffuse,
                                        inout vec4 specular)
{
        float nDotL;
        float nDotH;
        float pf;
        float attenuation;
        float d;
        vec3 LP;
        vec3 halfVector;

        LP = tLightPosition[i] - position;
        d = length(LP); // distance from surface to light for attenuation
        vec3 tLP = normalize(tLightPosition[i]);
       
        attenuation = 1.0 / (gl_LightSource[i].constantAttenuation
                                                + gl_LightSource[i].linearAttenuation * d
                                                + gl_LightSource[i].quadraticAttenuation * d * d);

        halfVector = normalize(tLP + view);

        nDotL = max(0.0, dot(iNormal, tLP));
        nDotH = max(0.0, dot(iNormal, halfVector));
       
        if (nDotL > 0.0)
                pf = pow(nDotH, gl_FrontMaterial.shininess);
        else
                pf = 0.0;
       
        ambient += gl_LightSource[i].ambient * attenuation;
        diffuse += gl_LightSource[i].diffuse * nDotL * attenuation;
        specular += gl_LightSource[i].specular * pf * attenuation;
}

void SpotLight(in int i,
                                in vec3 position,
                                in vec3 view,
                                in vec3 normal,
                                inout vec4 ambient,
                                inout vec4 diffuse,
                                inout vec4 specular)
{
        float nDotL;
        float nDotH;
        float pf;
        float spotDot;
        float spotAttenuation;
        float attenuation;
        float d;
        vec3 LV;
        vec3 halfVector;
       
        LV = tLightPosition[i] - position;
        d = length(LV);
        LV = normalize(LV);
       
        attenuation = 1.0 / (gl_LightSource[i].constantAttenuation +
                                                gl_LightSource[i].linearAttenuation * d +
                                                gl_LightSource[i].quadraticAttenuation * d * d);
       
        spotDot = dot(LV, normalize( tLightDirection[i]));
       
        if(spotDot < gl_LightSource[i].spotCosCutoff)
                spotAttenuation = 0.0;
        else
                spotAttenuation = pow(spotDot, gl_LightSource[i].spotExponent);
               
        attenuation *= spotAttenuation;
       
        halfVector = normalize(LV + view);
       
        nDotL = max(0.0, dot(normal, LV));
        nDotH = max(0.0, dot(normal, halfVector));
       
        if(nDotL == 0.0) pf = 0.0;
        else pf = pow(nDotH, gl_FrontMaterial.shininess);
       
        ambient += gl_LightSource[i].ambient * attenuation;
        diffuse += gl_LightSource[i].diffuse * nDotL * attenuation;
        specular += gl_LightSource[i].specular * pf * attenuation;
}

void main()
{
        vec4 albedo =                texture2D(Albedo , gl_TexCoord[0].xy);
        vec3 texNormal =        normalize(texture2D(NormalMap, gl_TexCoord[0].xy).rgb);
        vec3 ao =                        texture2D(AmbientOcclusionMap, gl_TexCoord[0].xy).rgb;
       
        vec4 ambient = vec4(0.0);
        vec4 diffuse = vec4(0.0);
        vec4 specular = vec4(0.0);
       
        vec3 ntView = normalize(tView);
       
        for(int i = 0; i < NumLights; i++)
        {
                if (gl_LightSource[i].position.w == 0.0)
                        DirectionalLight(i, tPosition, ntView, texNormal, ambient, diffuse, specular);
                       
                else if (gl_LightSource[i].spotCutoff == 180.0)
                        PointLight(i, tPosition, ntView, texNormal, ambient, diffuse, specular);
                       
                else
                        SpotLight(i, tPosition, ntView, texNormal, ambient, diffuse, specular);
        }
       
        //Linear fog calculation
        float dist = abs(vPosition.z);
        float fogFactor =(gl_Fog.end - dist) * gl_Fog.scale;
        fogFactor = clamp(fogFactor, 0.0, 1.0);
       
        vec4 color = (ambient * gl_FrontMaterial.ambient + diffuse * gl_FrontMaterial.diffuse) * albedo; //Albedo
        color = color + (specular * gl_FrontMaterial.specular);//Spec
        //color = color * vec4(ao,1.0);
        color = mix( gl_Fog.color, color, fogFactor); //Fog
       
        gl_FragColor = color;
}


farshizzo 06-24-2013 10:37 AM

Thanks. I think the problem is with your normal map lookup code. The texture color values range from 0 to 1. However, the normals need to range from -1 to 1. Also, it appears the green channel in your normal map is flipped. Try modifying your normal map lookup to the following:
Code:

vec3 texNormal = normalize(texture2D(NormalMap, gl_TexCoord[0].xy).rgb * 2.0 - 1.0) * vec3(1.0,-1.0,1.0);
I tried running your shader with this modification and it appeared to render the normals correctly.

shivanangel 06-25-2013 01:33 PM

Wow, I must have hit undo at some point in my code because I remember renormalizing the normal map from -1 to +1 ... but it sure isn't in that code...

I'll give your tips a try.

Not sure why the green channel is flipped, I'll look into that as well.

Thanks for your great support,

~George


All times are GMT -7. The time now is 11:54 AM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Copyright 2002-2023 WorldViz LLC