High quality line drawing / illustration mode

Started by
4 comments, last by manishfusion 14 years, 7 months ago
Dear forum, Adobe PDF allows the inclusion of 3D objects and can display them using various different drawing methods, i.e. wireframe, solid shading and "illustration": The "illustration" drawing mode looks very clean and has a high quality and accuracy. I want to reproduce this. Currently, I'm doing this via adjacency information, normals and some planar calculations to find creases in the object (C#, Managed DirectX). This produces a line list, which I draw with DrawPrimitive, and it looks like this:

protected static float DistanceOfPointToPlane(Vector3 planeA, Vector3 planeB, Vector3 planeC, Vector3 point)
{
	Matrix matrix = new Matrix();
	matrix.M11 = point.X - planeA.X;
	matrix.M12 = point.Y - planeA.Y;
	matrix.M13 = point.Z - planeA.Z;

	matrix.M21 = point.X - planeB.X;
	matrix.M22 = point.Y - planeB.Y;
	matrix.M23 = point.Z - planeB.Z;

	matrix.M31 = point.X - planeC.X;
	matrix.M32 = point.Y - planeC.Y;
	matrix.M33 = point.Z - planeC.Z;

	matrix.M44 = 1;

	return matrix.Determinant / 25f;
}

protected static bool IsEdge(Vector3 normalA, Vector3 normalB)
{
	return (normalA - normalB).Length() > 0.01f;
}

protected static bool IsAdjacent(Vector3 positionA, Vector3 positionB)
{
	return positionA == positionB;
}

protected static bool IsAdjacentEdge(CustomVertex.PositionNormal vertexA, CustomVertex.PositionNormal vertexB)
{
	return (IsAdjacent(vertexA.Position, vertexB.Position) && IsEdge(vertexA.Normal, vertexB.Normal));
}

protected static CustomVertex.PositionOnly[] GenerateEdges(Mesh mesh, int[] adjacency)
{
	CustomVertex.PositionNormal[] vertices = null;
	ushort[] indices = null;
	try
	{
		vertices = mesh.VertexBuffer.Lock(0, typeof(CustomVertex.PositionNormal),
			LockFlags.ReadOnly, mesh.NumberVertices) as CustomVertex.PositionNormal[];

		indices = mesh.IndexBuffer.Lock(0, typeof(ushort), LockFlags.ReadOnly,
			mesh.IndexBuffer.Description.Size / (mesh.IndexBuffer.Description.Is16BitIndices ? 2 : 4)) as ushort[];

		List<CustomVertex.PositionOnly> lineList = new List<CustomVertex.PositionOnly>();
		for (int nIndex = 0; nIndex < indices.Length; nIndex += 3)
		{
			for (int nEdge = 0; nEdge < 3; nEdge++)
			{
				int nEdge1 = nEdge;
				int nEdge2 = ((nEdge + 1) % 3);

				if (adjacency[nIndex + nEdge] != -1)
				{
					bool hasEdge = false;
					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge1]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 0]]);
					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge1]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 1]]);
					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge1]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 2]]);

					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge2]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 0]]);
					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge2]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 1]]);
					hasEdge |= IsAdjacentEdge(vertices[indices[nIndex + nEdge2]], vertices[indices[(adjacency[nIndex + nEdge] * 3) + 2]]);

					if (!hasEdge)
					{
						bool vertex1Adj = IsAdjacent(vertices[indices[nIndex + nEdge1]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 0]].Position) ||
										IsAdjacent(vertices[indices[nIndex + nEdge2]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 0]].Position);

						bool vertex2Adj = IsAdjacent(vertices[indices[nIndex + nEdge1]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 1]].Position) ||
										IsAdjacent(vertices[indices[nIndex + nEdge2]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 1]].Position);

						bool vertex3Adj = IsAdjacent(vertices[indices[nIndex + nEdge1]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 2]].Position) ||
										IsAdjacent(vertices[indices[nIndex + nEdge2]].Position,
											vertices[indices[(adjacency[nIndex + nEdge] * 3) + 2]].Position);

						if ((vertex1Adj ? 1 : 0) + (vertex2Adj ? 1 : 0) + (vertex3Adj ? 1 : 0) == 2)
						{
							Vector3 testVertex = new Vector3();
							if (!vertex1Adj) testVertex = vertices[indices[(adjacency[nIndex + nEdge] * 3) + 0]].Position;
							else if (!vertex2Adj) testVertex = vertices[indices[(adjacency[nIndex + nEdge] * 3) + 1]].Position;
							else if (!vertex3Adj) testVertex = vertices[indices[(adjacency[nIndex + nEdge] * 3) + 2]].Position;

							float distance = Math.Abs(DistanceOfPointToPlane(
								vertices[indices[nIndex + 0]].Position,
								vertices[indices[nIndex + 1]].Position,
								vertices[indices[nIndex + 2]].Position,
								testVertex));

							float perimeter = (float)(
								(vertices[indices[nIndex + 0]].Position - vertices[indices[nIndex + 1]].Position).Length() +
								(vertices[indices[nIndex + 1]].Position - vertices[indices[nIndex + 2]].Position).Length() +
								(vertices[indices[nIndex + 2]].Position - vertices[indices[nIndex + 0]].Position).Length());
							if (distance < perimeter / 5000)
								continue;
						}
					}
				}

                if (vertices[indices[nIndex + nEdge1]].Position != vertices[indices[nIndex + nEdge2]].Position)
                {
                    lineList.Add(new CustomVertex.PositionOnly(vertices[indices[nIndex + nEdge1]].Position));
                    lineList.Add(new CustomVertex.PositionOnly(vertices[indices[nIndex + nEdge2]].Position));
                }
			}

			continue;
		}
		if (lineList.Count == 0)
			return null;

		return lineList.ToArray();
	}
	finally
	{
		if (vertices != null) mesh.VertexBuffer.Unlock();
		if (indices != null) mesh.IndexBuffer.Unlock();
	}
}

However, with my method, you won't see the outline of a completely round object, like a simple sphere, because no creases can be found. How could one reproduce the effect of the "illustration" mode with the same quality? Thank you in advance, Alexander
Advertisement
You need to find and draw silhouette edges. A silhouette edge is an edge shared by two faces, where one of them is front facing the camera and the other is not.
Quote:Original post by hirez
You need to find and draw silhouette edges. A silhouette edge is an edge shared by two faces, where one of them is front facing the camera and the other is not.


Thank you, I understand. Any other ideas, maybe example code or a project which already does this, preferably open source? My edge-finding algorithm looks more like a dirty hack and not like a reasonable solution.
You could draw it (in monochrome?), and then use a 2D edge detection filter.
Quote:Original post by Adam_42
You could draw it (in monochrome?), and then use a 2D edge detection filter.


I also looked into this solution, however, you don't get the same clean look from image filters, you don't get edges with very similar or completely similar colored faces and edges appear and disappear when the object, camera or lightning is moved around.
hey thanks for it i really like it and i understand it clearly .

This topic is closed to new replies.

Advertisement