// http://www.terathon.com/code/tangent.html public static void GenerateTangentSpaceFor(Model <VertexPositionNormalTextureTangent, RealtimeMaterial> model) { int numberOfMeshes = model.MeshCount; for (int j = 0; j < numberOfMeshes; j++) { var mesh = model.GetMesh(j); var vertices = mesh.Vertices; var indicies = mesh.Indices; var numberOfIndicies = indicies.Length; var numberOfVertices = vertices.Length; var tangentCountPerVertex = new uint[numberOfVertices]; // Calculate Tangent & Bitangent for (int i = 0; i < numberOfIndicies; i += 3) { var indicie_0 = indicies[i]; var indicie_1 = indicies[i + 1]; var indicie_2 = indicies[i + 2]; ref VertexPositionNormalTextureTangent v0 = ref vertices[indicie_0]; ref VertexPositionNormalTextureTangent v1 = ref vertices[indicie_1]; ref VertexPositionNormalTextureTangent v2 = ref vertices[indicie_2];
public static VertexPositionNormalTextureTangent[] CalculateTangentArray(VertexPositionNormalTexture[] vertices, int[] indices) { Vector3[] tan1 = new Vector3[vertices.Length]; Vector3[] tan2 = new Vector3[vertices.Length]; Vector4[] tangents = new Vector4[vertices.Length]; for (int i = 0; i < indices.Length; i += 3) { int i1 = indices[i]; int i2 = indices[i + 1]; int i3 = indices[i + 2]; Vector3 v1 = vertices[i1].Position; Vector3 v2 = vertices[i2].Position; Vector3 v3 = vertices[i3].Position; Vector2 w1 = vertices[i1].TextureUV; Vector2 w2 = vertices[i2].TextureUV; Vector2 w3 = vertices[i3].TextureUV; float x1 = v2.X - v1.X; float x2 = v3.X - v1.X; float y1 = v2.Y - v1.Y; float y2 = v3.Y - v1.Y; float z1 = v2.Z - v1.Z; float z2 = v3.Z - v1.Z; float s1 = w2.X - w1.X; float s2 = w3.X - w1.X; float t1 = w2.Y - w1.Y; float t2 = w3.Y - w1.Y; float r = 1.0f / (s1 * t2 - s2 * t1); Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } VertexPositionNormalTextureTangent[] newVertices = new VertexPositionNormalTextureTangent[vertices.Length]; for (int i = 0; i < vertices.Count(); i++) { Vector3 n = vertices[i].Normal; Vector3 t = tan1[i]; Vector3 tmp = Vector3.Normalize(t - n * Vector3.Dot(n, t)); float w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f; tangents[i] = new Vector4(tmp.X, tmp.Y, tmp.Z, w); Vector3 p = vertices[i].Position; Vector2 tx = vertices[i].TextureUV; newVertices[i] = new VertexPositionNormalTextureTangent(p, n, tx, tangents[i]); } return(newVertices); }
// https://gamedev.stackexchange.com/questions/150191/opengl-calculate-uv-sphere-vertices // https://www.opengl.org/discussion_boards/showthread.php/170225-Sphere-tangents /// <summary> /// Returns a Sphere Mesh with the corresponing vertex struct /// Extremely inefficient in OpenGL und MacOS /// </summary> public static Mesh <VertexPositionNormalTextureTangent> GenerateSphereTangent(int numLatitudeLines, int numLongitudeLines, float radius) { // One vertex at every latitude-longitude intersection, // plus one for the north pole and one for the south. // One meridian serves as a UV seam, so we double the vertices there. int numVertices = numLatitudeLines * (numLongitudeLines + 1) + 2; VertexPositionNormalTextureTangent[] vertices = new VertexPositionNormalTextureTangent[numVertices]; List <ushort> indices = new List <ushort>(); // North pole. vertices[0].Position = new Vector3(0, radius, 0); vertices[0].TextureCoordinates = new Vector2(0, 0); // South pole. vertices[numVertices - 1].Position = new Vector3(0, -radius, 0); vertices[numVertices - 1].TextureCoordinates = new Vector2(0, 1); // +1.0f because there's a gap between the poles and the first parallel. float latitudeSpacing = 1.0f / (numLatitudeLines + 1.0f); float longitudeSpacing = 1.0f / (numLongitudeLines); // start writing new vertices at position 1 int v = 1; for (int latitude = 0; latitude < numLatitudeLines; latitude++) { for (int longitude = 0; longitude <= numLongitudeLines; longitude++) { // Scale coordinates into the 0...1 texture coordinate range, // with north at the top (y = 0). vertices[v].TextureCoordinates = new Vector2( longitude.ToFloat() * longitudeSpacing, (latitude.ToFloat() + 1.0f) * latitudeSpacing ); // Convert to spherical coordinates: // theta is a longitude angle (around the equator) in radians. // phi is a latitude angle (north or south of the equator). float theta = vertices[v].TextureCoordinates.X * 2.0f * Math.PI.ToFloat(); float phi = vertices[v].TextureCoordinates.Y * Math.PI.ToFloat(); // This determines the radius of the ring of this line of latitude. // It's widest at the equator, and narrows as phi increases/decreases. // float c = Math.Sin(phi).ToFloat(); // Usual formula for a vector in spherical coordinates. // You can exchange x & z to wind the opposite way around the sphere. vertices[v].Position = new Vector3( Math.Sin(phi).ToFloat() * Math.Sin(theta).ToFloat(), Math.Cos(phi).ToFloat(), Math.Sin(phi).ToFloat() * Math.Cos(theta).ToFloat() ) * radius; vertices[v].Normal = Vector3.Normalize(vertices[v].Position); if (latitude < numLatitudeLines - 1) { indices.Add(v.ToUnsignedShort()); indices.Add((v + 1).ToUnsignedShort()); indices.Add((v + 1 + numLongitudeLines).ToUnsignedShort()); indices.Add((v + 1).ToUnsignedShort()); indices.Add((v + 2 + numLongitudeLines).ToUnsignedShort()); indices.Add((v + numLongitudeLines + 1).ToUnsignedShort()); } var position = vertices[v].Position; var normal = vertices[v].Normal; // derivative wrt. phi vertices[v].Tangent = Vector3.Normalize(new Vector3( Math.Cos(phi).ToFloat() * Math.Sin(theta).ToFloat(), -Math.Sin(phi).ToFloat(), Math.Cos(phi).ToFloat() * Math.Cos(theta).ToFloat() )); if (vertices[v].Tangent.Length() == 0) { Console.WriteLine("Warning, Tagent is 0"); } // Proceed to the next vertex. v++; } } // North pole indices for (int longitude = 1; longitude < numLongitudeLines; longitude++) { indices.Add(0); indices.Add((longitude + 1).ToUnsignedShort()); indices.Add(longitude.ToUnsignedShort()); } indices.Add(0); indices.Add(1); indices.Add(numLongitudeLines.ToUnsignedShort()); v -= numLongitudeLines + 1; // southpole for (int longitude = 0; longitude <= numLongitudeLines; longitude++) { indices.Add((v + longitude).ToUnsignedShort()); indices.Add((v + longitude + 1).ToUnsignedShort()); indices.Add((numVertices - 1).ToUnsignedShort()); } indices.Add((numVertices - 2).ToUnsignedShort()); indices.Add((v + 1).ToUnsignedShort()); indices.Add((numVertices - 1).ToUnsignedShort()); return(new Mesh <VertexPositionNormalTextureTangent>(vertices, indices.ToArray())); }