TEXTURED CLOTH AND DEFERRED RENDERING.


This project showcases the use of collision responce among nodes and using the penalty method for collision response. Simulated springs between bodies, which push each other apart through force and accelereation. This method is used for soft body and cloth simulation.

Physics :

  • Collision Responce - Penality Methods.
  • Spring - Soft body.

Graphics :

  • Textured Cloth.
  • Deferred Rendering.
  • Particle System (Main class implemented by Richard Davison, Newcastle University)
  • Lighting calculation - Torch/Spot Light.

Cloth - Structure :

The cloth is a 2D array structure, and acts of type Physics Node which already encapsulates the physical attributes (such as mass, force and collision detection and radius). Each individual node is connected to the nearby node and its tries to pull the connected node with a certain amount of force – hence ultimately the forces cancel out and all the connected nodes come to rest. Force is calculated by directional vector multiplied by the resting length.

                            F = KS * directional_Normal * Vector3(distance_apart);
                        

Where KS = Elasticity by Hook's law.
directional_Normal = p1 - p2
distance_apart = Minimum distance between node when at rest.

In order to make this 2D array structure into a mesh object - I make use of the HeightMap class. As a heightmap is just a bunch of nodes (based on perlin noise) with textures and indices. By rebuffering the nodes, the mesh shape could be altered.

void Cloth::ReBufferValue(){
	for (int x = 0; x < CLOTH_WIDTH ; ++ x ) {
		for (int y = 0; y < CLOTH_HEIGHT ; ++ y ) {			
			int offset = ( x * CLOTH_WIDTH ) + y;
			vertices[offset] = pNode[x][y].m_position;
		}
	}

	GenerateTangents();
	GenerateNormals();
	RebufferNormal();

	glBindBuffer(GL_ARRAY_BUFFER,bufferObject[VERTEX_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER,numVertices*sizeof(Vector3),vertices,GL_DYNAMIC_DRAW);

	glBindBuffer (GL_ARRAY_BUFFER , bufferObject [NORMAL_BUFFER]);
	glBufferData (GL_ARRAY_BUFFER , numVertices * sizeof ( Vector3 ) ,normal , GL_DYNAMIC_DRAW );

	glBindBuffer(GL_ARRAY_BUFFER,bufferObject[TANGENT_BUFFER]);
	glBufferData(GL_ARRAY_BUFFER,numVertices*sizeof(Vector3),tangents,GL_DYNAMIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,bufferObject[INDEX_BUFFER]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,numIndices*sizeof(GLuint),indices,GL_DYNAMIC_DRAW);
}
                        

Cloth Surface Normal :

In order to make the torch/spotlight work properly - I regenerate the normals as I can't use the ones based on collision. Collision is a “sphere sphere” collision among nodes, the collision normal will be wrong as compared to that cloth normal. So, each particle node has an additionally variable to store a surface normal.

After the position of the particle node is moved, generate normal function (originally called in Mesh class) will be called. The normal will be stored into the particle node; also, the normal will be calculated from the nearest neighbour to smooth it out. Therefore instead of using the collision normal, the normal stored with the particle node will be used.

void Cloth::RebufferNormal(){
for (int i = 0; i < CLOTH_WIDTH -1; ++ i ) {
	for (int j = 0; j < CLOTH_HEIGHT-1 ; ++ j ) {
			int origin = (i * ( CLOTH_WIDTH )) + j ;
			int top = ((i +1) * ( CLOTH_WIDTH )) + j ; 	
			int bottom = ((i -1) * ( CLOTH_WIDTH )) + j ; 
			int left = ( i * ( CLOTH_WIDTH )) + ( j +1);
			int right = ( i * ( CLOTH_WIDTH )) + ( j -1);

			Vector3 surfaceNormal = normal[origin];
			if(top >= 0 && top < numVertices){
				surfaceNormal += normal[top];
			}
			if(bottom >= 0 && bottom < numVertices){
				surfaceNormal += normal[bottom];
			}
			if(left >= 0 && left < numVertices){
				surfaceNormal += normal[left];
			}
			if(right >= 0 && right < numVertices){
				surfaceNormal += normal[right];
			}
			surfaceNormal.Normalise();
			pNode[i][j].collisionNormal = surfaceNormal;
		}
	}
}

                        

Spot Light :

In order to draw spot light - A normal light was taken and the trick was to limit the amount of light being drawn. The spot light is attached to the camera as a torch, and it moves according to the player movement. To aceive the desired effect, the forward vector was used in prder to calcualte the direction of ligt. Forward vector was taken from the rotation matrix of the camera in 8,9,10 indexpostion of the matrix. This was passed to the spotlight shader.

void main ( void ) {

	vec3 spotDir = vec3(0,0,0);

	 spotDir = normalize(spotDirIn);

	 vec3 pos = vec3 (( gl_FragCoord .x * pixelSize .x),( gl_FragCoord .y * pixelSize .y), 0.0);
	 pos .z = texture ( depthTex , pos .xy ).r;

	 vec3 normal = normalize ( texture ( normTex , pos .xy ). xyz *2.0 - 1.0);

	 vec4 clip = inverseProjView * vec4 (pos * 2.0 - 1.0 , 1.0);
	 pos = clip .xyz / clip .w;

	 float dist = length ( lightPos - pos );
	 float angleAtten = dot(normalize(pos - lightPos), spotDir);

	float cutoff = cos((degreeToSpot*3.14)/360);
	if(angleAtten >= cutoff) {

		float atten = 1.0 - clamp ( dist / lightRadius , 0.0 , 1.0);
		atten = atten*((angleAtten - cutoff)/(1-cutoff));
		if( atten == 0.0) {
			discard ;
		}

		vec3 incident = normalize ( lightPos - pos );
		vec3 viewDir = normalize ( cameraPos - pos );
		vec3 halfDir = normalize ( incident + viewDir );

		float lambert = clamp (dot ( incident , normal ) ,0.0 ,1.0);
		float rFactor = clamp (dot ( halfDir , normal ) ,0.0 ,1.0);
		float sFactor = pow ( rFactor , 33.0 ); 

		gl_FragColor [0] = vec4 ( lightColour .xyz * lambert * atten , 1.0);
		gl_FragColor [1] = vec4 ( lightColour .xyz * sFactor * atten *0.33 ,1.0);

	}
}
                        

Deferred Rendering :

To acheive deferred rendering for it to use the same shaders, another FBO is created to store the colour. The second pass requires the first pass to generate two textures namely colour and normal, but the shader just output the colour. As the result, the second pass cannot calculate the light correctly. By using another FBO, all the colour of the objects are draw into this buffer using their original shader. The non textured world is draw into the original first pass buffer and that is used to calculate the light effect.
In the third pass, the diffuse colour is sampled from the colour texture attached to the new buffer and added with the light from second pass.