|
Project OverviewThe goal of this project is to combine the two topics of procedural texturing and non-photorealistic rendering to model veins for "cartoon"-like models. In particular, this sort of algorithm can be applied to draw objects with visible arteries (such as varicose veins), but there are also additional applications to general crackled surfaces such as volcanic lava. However, instead of applying this to realistic objects, I plan to first run them through a toon shader and then apply the procedural texture, augmenting a non-realistic scene with a realistic effect.Algorithm DetailsThe first part of this algorithm is a straightforward toon shader. Essentially, we threshold the color of the object in the fragment shader using an intensity value that is based on the normal of the object and the direction of the light dotted together. This creates a cartoon-like segmented gradient of color, where shadows appear to be drawn as block colors.The second part of the algorithm will be the main focus. I plan to apply a random walk algorithm to select fragments to color as the veins. One should be able to apply a procedure here which either generates the walk, or alternatively the walk could be passed in as input to the vertex/fragment shader. The shader could then change the color to some vein color, or apply a bump to make the veins appear above the surface of the object. One example of the effect I would like to generate is at this site, in particular the image above step 5 (middle of page) and the images at the bottom of the page. I have not at this time fully decided what sort of random walk will be used. I plan to explore the following various options. One possibility is to apply some sort of fractal pattern as this walk. Using some sort of space-filling curve would probably be the best choice here. Another possibility is to simply make an algorithm that takes a random step (perhaps of random length) either selected randomly from a fixed set of directions or a random direction. Finally, one other idea would be to apply an algorithm called the rapidly-exploring random tree, which essentially generates a tree in the following way:
T <- buildRRT() {
repeat() {
x <- random point
nn <- nearest_neighbor(T, x)
n <- take a step from nn towards x
addNode(T, n)
}
}
Essentially, the algorithm works by taking fixed steps from the nearest node in the current RRT, T, toward a randomly generated point in the space. This algorithm has been used in a variety of motion planning applications.
One final issue is that the majority of these techniques produce walks that are jagged; however veins generally transition in a smooth manner. This problem could potentially be handled by smoothing the direction, using a very tiny step size, or in the case of the RRT, using a different equation of motion as done in this example. Task DistributionI plan to work by myself---all algorithms and supporting documentation will be authored by me. In addition, any project proposals, reports, etc. will be my work. This excludes places where I adapt code/ideas/examples from other sites, but of course in all of those cases the references will be properly cited.ImplementationThe implementation of the veining required a number of steps. Initially, I wanted to test out the vertex and fragment shaders, so I chose to use the program OpenGL Shader Designer by Typhoon Labs. I developed a sample shader program here which took as input three textures, the first was a texture for the surface itself, the second was a (handmade) texture for the "veins", and the third was a dot3 bumpmap generated by NormalMapGenerator, a free tool by ATI. The resulting image did give an interesting vein-like effect:![]() However, there were a number of issues with this method. While it produced a desired result, it lacked the procedural nature of the vein texture which I desired. In addition, the bump-mapping was done using dot3 bump-mapping, which I had to generate using the external tool. Thus, this was an unsatisfactory result. I opted for an alternative approach to generate the veins procedurally; one immediate issue I ran into was that I could not apply a procedure inside the vertex of fragment shader to accomplish this. The shaders lacked the ability to implement the a satisfactory number of instructions to grow a random walk. As a result, I ended up organizing my code in the following way. First, I choose to make the vein-texture in software and then pass it into the shader. Next, within the shader I wanted to handle a toon-shading effect, and potentially some sort of effect on the edge of the veins to make them appear above the surface. Thus my implementation involved the following steps:
![]() Within the vertex/fragment shaders I use a simple toon shader, adapted from the Lighthouse GLSL Tutorial. This simply thresholds the color value based on the intensity of the light. I also wanted to give an edge effect that was similar to bumping, without the overhead. The compromise I found was examine the texture color within the fragment shader. If the color was a blend from the vein color to the texture color, then I brightened the color around the edges. This did not effectively give the desired affect, but since I was calculating the vein texture in software, I did not implement a bump map texture as well. I also ran into issues using multiple textures. ResultsBelow we show the results we achived on a teapot object:![]() In particular, this image has no edge effects applied to it. However, it does give a sense of a cartoon with a vein-line structure. I ran into some issues because the RRT was growing from the center of the texture, but veins tend to grow more linearly in one direction and stretch across a surface. When I added the edge affects, it tended to cause a glow around the vein: ![]() Essentially, I use the shader to determine if the fragment is an edge. Then, I scale the value by a factor of 1.5 to brighten the effect. The resulting effect really does not add much, it does help the vein blend into the surface to look more. It looks better furhter away, when zoomed in the result looks pixelated. This is a result of the way I grow the structure in the texture; which gives a discretized walk. I also applied the same algorithm to a cracked, volcanic surface model. The results here were additionally varied: ![]() Again the real problem here is the edge effects. It was harder to discriminate in the fragment shader where the "lava" was blending into the surface, hence harder to highlight: ![]() Some additional applications that I did not implement were trying to draw veins on leaf-objects or various other cracked techniques. The results from this project were of mixed success. In some ways, since the objects were draw in a non-realistic way, the effect looked perhaps the way an artist would have drawn veins/cracks in a surface. However, the realistic effect of veining was lost because the surface still looked flat, and in some cases, pixelated. Another flaw was that both the colors and random walk required a large amount of hand-tuning specific to the application. ReferencesBelow is an informal summary of sites where ideas were borrowed or were referred to above: Typhoon Labs, OpenGL Shader Designer. http://www.typhoonlabs.com/index.php?action=shaderdesigner.htm (for testing vertex/fragment shader ideas).ATI Developer Tools. http://www.ati.com/developer/sdk/radeonSDK/html/Tools/ToolsPlugIns.html (for dot3 Bump map generation). OpenGL @ Lighthouse 3D, GLSL Tutorial. http://www.lighthouse3d.com/opengl/glsl/index.php?minimal (for toon-shading and general tutorial information). The Rapidly-Exploring Random Tree (RRT). http://msl.cs.uiuc.edu/rrt/ (for RRT / random walk algorithm). Bezier Curve and Surfaces. http://www.gamedev.net/reference/programming/features/curvessurfaces/ (for Bezier curve code). http://users.ox.ac.uk/~orie1381/code.html (for Loading bitmaps for textures). |