My Account


RSS
oZone3D.Net RSS Feeds»RSS 2.0 Feeds

Blogs
»Demoniak3D Blog
»JeGX's Infamous Lab

Sponsors


Link to Us

oZone3D.Net 100% Realtime 3D

»All Site's Network

Search
Google
Web
oZone3D.Net

Visitors Map

 
Mesh Deformers with the GLSL

By Jerome Guinot aka 'JeGX' - jegx_AT_ozone3d(dot)net

Initial draft: June 1, 2006


[ Index ]

Introduction | Page 2 | Page 3 | Conclusion

»Next Page



2 - Surface Deformer

Let's start with the chapter 42 of GPU Gems #1. This chapter, quite mathematical, gives an overview of the polygonal surface deformation theory. But as always with real time shaders, the problem lies in the calculation of the per vertex normal vector. Indeed, the change of the vertex position is a simple operation while its normal calculation maybe quite a complex or even an impossible task in some cases. The author (Eugene d'Eon) shows us a solution based on Jacobian matrices. In a word, if the first-order partial derivatives of the surface deformer function are continuous, we can then compute for each vertex a Jacobian 3x3 matrix that will help us calculating the new normal vector.

If the surface deformer is given by f(x, y, z)=(fx, fy, fz) where fx, fy and fz are three functions of the x, y, and z variables, the per vertex Jacobian matrix will look like:

∂fx/∂x∂fx/∂y∂fx/∂z
∂fy/∂x∂fy/∂y∂fy/∂z
∂fz/∂x∂fz/∂y∂fz/∂z

where ∂fx/∂x is the derivative of the fx function regarding x.

This is the global idea. For more explanations, please refer to the GPU Gems #1. With this Jacobian matrix, the calculation of the normal is given by the following relation:

n' = normalize( (J(v)*t) cross (J(v)*b))

where J(v) is the Jacobian matrix at the vertex v, t is the vertex tangent vector, b the binormal vertex vector (the same used in bump mapping) and cross the cross product operator.

The goal of the following demo is to deform a mesh plane (or a plane polygonal surface) as shown in the next image:

The function we are going to use to deform this mesh is:

f(x, y, z) = (x, y+sin(f*sqrt(x2+z2) + p), z)

where f is the wave frequency and p its phase. For this function, the Jacobian matrix at each vertex is:

100
jac_coef * v.x1jac_coef * v.z
001

where v.x and v.z are the vertex x and z coordinates. jac_coef is equal to:

jac_coef = cos(f*dist + p) / (dist+0.00001);
with
dist = sqrt(v.x2+v.z2)

Software such as Maple can help you to find the literal expressions of the derivatives.

Okay, now the pseudo-theory is assumed, let's go the the shader GLSL implementation. Only the vertex shader code will be shown, the pixel shader one showing less interest in our case. You can however refer to the Phong Lighting tutorial for more details about the pixel shader.

uniform float time;
uniform float frequency;

varying vec3 vNormal;
varying vec4 vVertex;

vec3 computeNormal( vec3 pos, 
                    vec3 tangent, 
                    vec3 binormal, 
                    float phase, 
                    float freq )
{
	mat3 J;
	
	float dist = sqrt(pos.x*pos.x + pos.z*pos.z);
	float jac_coef = cos(freq*dist + phase) / (dist+0.00001);
	
	// A matrix is an array of column vectors so J[2] is 
	// the third column of J.
	
	J[0][0] = 1.0;
	J[0][1] = jac_coef * pos.x;
	J[0][2] = 0.0;
	
	J[1][0] = 0.0;
	J[1][1] = 1.0;
	J[1][2] = 0.0;

	J[2][0] = 0.0;
	J[2][1] = jac_coef * pos.z;
	J[2][2] = 1.0;
	
	vec3 u = J * tangent;
	vec3 v = J * binormal;
	
	vec3 n = cross(v, u);
	return normalize(n);
}

vec4 displaceVertexFunc( vec4 pos, float phase, float frequency ) 
{
	vec4 new_pos;
	
	new_pos.x = pos.x;
	new_pos.z = pos.z;
	new_pos.w = pos.w;
	
	float dist = sqrt(pos.x*pos.x + pos.z*pos.z);
	new_pos.y = pos.y + 20.0 * sin( frequency * dist + phase );
	
	return new_pos;
}

void main(void)
{
	vec4 displacedVertex;
	vec3 displacedNormal;
	
	// 1 - Compute the diplaced position.
	//	
	displacedPosition = displaceVertexFunc(gl_Vertex, time*2.0, frequency );
   	
	gl_Position = gl_ModelViewProjectionMatrix * displacedPosition;	   	
	vVertex = gl_ModelViewMatrix * displacedPosition;
	
	
	// 2 - Compute the displaced normal
	//
	
	// if the engine does not provide the tangent vector you 
	// can compute it with the following piece of of code:
	//
	vec3 tangent; 
	vec3 binormal; 
	
	vec3 c1 = cross(gl_Normal, vec3(0.0, 0.0, 1.0)); 
	vec3 c2 = cross(gl_Normal, vec3(0.0, 1.0, 0.0)); 
	
	if(length(c1)>length(c2))
	{
		tangent = c1;	
	}
	else
	{
		tangent = c2;	
	}
	
	tangent = normalize(tangent);
	
	binormal = cross(gl_Normal, tangent); 
	binormal = normalize(binormal);

	displacedNormal = computeNormal( displacedPosition.xyz, 
	                                 tangent.xyz, 
	                                 binormal, 
	                                 time*2.0, 
	                                 frequency );
   	
   	vNormal = normalize(gl_NormalMatrix * displacedNormal);
}

Tangent Space Computing:

This vertex shader is a direct implemtation of the previous equations. The little trick, because there is always a tick, concerns the tangent vector calculation. Usually, we use the texture coordinates variations to determine accuratly the tangent vector. But in our situation, the following routine provides quite good results:

vec3 tangent; 
	vec3 binormal; 
	
	vec3 c1 = cross(gl_Normal, vec3(0.0, 0.0, 1.0)); 
	vec3 c2 = cross(gl_Normal, vec3(0.0, 1.0, 0.0)); 
	
	if(length(c1)>length(c2))
	{
		tangent = c1;	
	}
	else
	{
		tangent = c2;	
	}
	
	tangent = normalize(tangent);
	
	binormal = cross(gl_Normal, tangent); 
	binormal = normalize(binormal);
}

The mesh density has to be taken into account to get a good visual quality. The previous image showed a 245,000 triangles mesh plane while in the next image, the mesh plane has only 5,000 triangles:

The main interest to perform the mesh deformation through the GPU capabilities is that we can apply real time deformations on very dense meshes with an acceptable FPS. On With my 7800GT, the results are the following:

  • 5,000 faces: 580 fps
  • 245,000 faces: 170 fps
  • 500,000 faces: 90 fps
  • 720,000 faces: 64 fps
  • 1,280,000 faces: 37 fps

And with a Radeon X700:

  • 5,000 faces: 142 fps
  • 245,000 faces: 57 fps
  • 500,000 faces: 37 fps
  • 720,000 faces: 28 fps
  • 1,280,000 faces: 17 fps

More than one million faces deformed in real time, only a GPU process can achieve such a job!

At the Demoniak3D demo level, the number of mesh-plane's faces can be set by modifying the following piece of code in the Surface_Deformer.xml file:

<mesh name="mesh_plane" shape_type="PLANE" render="TRUE" 
	lighting="TRUE" use_vbo="TRUE" >
	<position x="0.0" y="25.0" z="0.0" />
	<plane x_size="400.0" z_size="400.0" num_segs_x="350" num_segs_z="350" />
	<attach_material name="mesh_mat" />
</mesh>

The num_segs_x and num_segs_z attributes allow specifying the number of mesh subdivisions along the X and Z axis.

At the RaptorGL/Win32 demo level, go in the Surface_Deformer project. In the cDemo.cpp file, find the following line:

m_pMeshPlane->buildPlane( 400.0, 400.0, 800, 800 ); // 1 280 000 faces

The two last parameters of the buildPlane() method specify the number of mesh subdivisions along the X and Z axis.

More generally, the number of triangles is given by the following relation:

num_triangles = x_subdiv * z_subdiv * 2;

If x_subdiv=350 and z_subdiv=350 then the mesh plane will have 245,000 faces.





[ Index ]

Introduction | Page 2 | Page 3 | Conclusion

»Next Page







Language:

The Geeks Of 3D



Demoniak3D
Current Version: 1.23.0
»Demoniak3D
»Download
»Libraries and Plugins
»Demos
»Online Help - Reference Guide
»Codes Samples


GPU Caps Viewer
Current Version: 1.6.2
»GPU Caps Viewer
»GPU DB Submissions


FurMark
Current Version: 1.4.0
»FurMark
»Benchmark Submissions


PhysX FluidMark
Current Version: 1.0.0
»PhysX FluidMark
»Benchmark Submissions


Geeks3D News


Misc
»Texture DataPack #1
»Asus Silent Knight CPU Cooler


Announces

Shop at CheckCost UK.Find Software products like Windows, Antivirus Software, Video Editing & more.


Modul8: real time video mixing and compositing

Page generated in 0.072895050048828 seconds.