Sign in to follow this  
graealex

High quality line drawing / illustration mode

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this