WorldViz User Forum  

Go Back   WorldViz User Forum > Vizard

Reply
 
Thread Tools Rating: Thread Rating: 2 votes, 5.00 average. Display Modes
  #1  
Old 06-21-2013, 12:15 PM
shivanangel shivanangel is offline
Member
 
Join Date: Feb 2006
Location: New Jersey
Posts: 180
Normal Map, Tangent Binormal on Mirrored Objects

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
Attached Thumbnails
Click image for larger version

Name:	Normal_Mirror.png
Views:	750
Size:	208.2 KB
ID:	600  
Reply With Quote
  #2  
Old 06-21-2013, 01:38 PM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
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.
Reply With Quote
  #3  
Old 06-21-2013, 01:50 PM
shivanangel shivanangel is offline
Member
 
Join Date: Feb 2006
Location: New Jersey
Posts: 180
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.
Attached Thumbnails
Click image for larger version

Name:	UVMirror.png
Views:	659
Size:	625.0 KB
ID:	601  
Reply With Quote
  #4  
Old 06-21-2013, 02:15 PM
shivanangel shivanangel is offline
Member
 
Join Date: Feb 2006
Location: New Jersey
Posts: 180
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.
Reply With Quote
  #5  
Old 06-21-2013, 03:25 PM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
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.
Reply With Quote
  #6  
Old 06-21-2013, 04:50 PM
shivanangel shivanangel is offline
Member
 
Join Date: Feb 2006
Location: New Jersey
Posts: 180
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;
}
Attached Files
File Type: zip Church.zip (390.0 KB, 564 views)
Reply With Quote
  #7  
Old 06-24-2013, 10:37 AM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
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.
Reply With Quote
  #8  
Old 06-25-2013, 01:33 PM
shivanangel shivanangel is offline
Member
 
Join Date: Feb 2006
Location: New Jersey
Posts: 180
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
Reply With Quote
Reply

Tags
binormal, normal, tangent

Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Per vertex attributes in OTF objects? stefs Vizard 1 10-23-2009 03:36 PM
Semi-circle array containing target and distractor objects ptjt255 Vizard 3 08-04-2009 03:09 AM
Normal Map Workflow for Vizard shivanangel Vizard 2 03-10-2009 06:54 PM
Could not find plugin to load objects... halley Vizard 1 05-30-2006 11:01 AM


All times are GMT -7. The time now is 10:03 PM.


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