Transparency blended into Toon

I am working on a public art installation with a client where they require characters to subtly shift from a wireframe representation to a toon shaded appearance. I have found multiple posts on this forum about rendering wireframes using mesh filters and some opengl trickery, but it was my idea from the beginning to simply use a texture (which may or may not match the mesh of the character vertex for vertex; ie: imagine a low-poly mesh on a high-poly model, or vice versa).

I figure the best way to accomplish this was to combine the Particles/Additive shader and the Toon/Basic shader in some way utilizing the Blend2Textures.shader on unify: http://www.unifycommunity.com/wiki/index.php?title=Blend_2_Textures. This would allow me to write a script that adjusts the blend value based on the characters distance from the camera. Simple enough, right?

However, the trick comes in fading in the Toon pass over the Transparency pass. Why use separate passes? The Transparency pass requires culling and zwrite to be turned off so you can see the wireframe (or whatever it might be) on the other side of the object. The Toon pass you obviously want culling and zwrite on so that it doesn’t look weird. It also has to be blended somehow, either with a specific Blend mode, or with some alpha adjustment within SetTexture, which I think will ultimately be the way to go. I just don’t know what that is.

The image attached shows the progression from “wireframe” to toon. The additive effect of the translucent toon character with culling off will hopefully be eliminated.

TLDR:
(1) How do I apply a normalized value as a multiplier to my toon shader’s alpha?
(2) How do I do that while keeping my culling/zwrite different?

And of course, here is the code:

Shader "Toon/Blended" {

	Properties {
		_Blend ("Blend", Range (0, 1) ) = 0.5
		_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex ("Underlying Texture", 2D) = "white" {}
		_Color ("Toon Tint Color", Color) = (.5,.5,.5,1)
		_MainToonTex ("Main Toon Texture", 2D) = "white" {}
		_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "white" { Texgen CubeNormal }
	}
	
	SubShader {
		
		Pass {
			Blend One One
			AlphaTest Greater .01
			Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
	
			BindChannels {
				Bind "Color", color
				Bind "Vertex", vertex
				Bind "TexCoord", texcoord
			}
			SetTexture [_MainTex] {
				constantColor [_TintColor]
				combine constant * primary
			}
			SetTexture [_MainTex] {
				combine texture * previous DOUBLE
			}
		}
		
		Pass {
			Blend One One
			ColorMask RGBA
			ZWrite On
			
			SetTexture [_MainToonTex] {
				constantColor [_Color]
				combine texture * constant
			}
			SetTexture [_ToonShade] {
				combine texture * previous DOUBLE, previous
			}
		}
		
	}
	
}

360422--12507--$wireframe_to_toon_402.jpg

Ok, I figured out how to apply the transparency value using the constantColor command, but I don’t know how to apply both the color selected in the inspector as well as the alpha value.

SetTexture [_MainToonTex] {
	//constantColor [_Color]
	constantColor (0, 0.5, 1, [_Blend])
	combine texture * constant
}

Ideally I would like the transparent blending effect to work most similarly to the Alpha VertexLit with Z shader on the unify wiki: http://www.unifycommunity.com/wiki/index.php?title=AlphaVertexLitZ, so that there is no additive effect from the mesh behind it… but that is a separate issue.

If you can post a unitypackage, I’ll have a look at it. What exactly is the type of blending you want? If the lines are white, then additive blending works perfectly and is easy. What about for the toon version, though? You do not want additive blending on that, but instead, alpha blending, right? And I don’t see a need for vertex colors. Are you just including that because of copy/paste from the particles shader, or are you really utilizing them?

The transparent wireframe texture (basically the underlying layer) just uses additive blending. I do want to have the option of adjusting its alpha/color with a tint.

The toon shader I want to be alpha blended, simply fading in on top of the underlying transparency layer.

I am able to apply the _Blend value when I specify the constantColor myself in the shader, but I want to be able to use the color from the inspector combined with the alpha value from the _Blend property.

Sorry, I don’t know how to make a unity package.

The idea is to transition from wireframe to toon. That is what the _Blend property is for, so it can be accessed from a script. The rest of the Blend functionality within each pass is just stuff I’ve been trying out in the hopes of getting something to work the way I expect it.

If you can suggest a different route for going accomplishing this, I would love to hear it.

I realize that. You’re okay with that blending mode? Unless it’s white, then it’s going to pick up whatever was already rendered.

What does that mean?

Why aren’t you just using the alpha of the color instead of the Range value then?

http://unity3d.com/support/documentation/Manual/HOWTO-exportpackage.html

And again, what about my vertex colors question?

Well, probably not in the long run. I’ll want to be able to apply different tints and alpha value to that texture.

I’m just referring to the texture that will appear when the _Blend value is set to 0. I have been thinking of it as the underlying layer because it is the first pass.

Because clearly I don’t know what I’m doing. I figured it would be easier to modify a range value from a script than the color value. That’s probably far from the truth, but I am being forced to learn this stuff as I go about trying to accomplish very specific goals.

I don’t know what you’re referring to. What is the vertex color portion of the shader?

BindChannels {...}

combine constant * primary

I have a definite solution for you using four passes, but I think it can be done in three. I don’t have anything good to test with, though, so I need you to make that package.

I actually did a little more research and managed to get it working the way I like it (so far, though I can still imagine improving it quite a bit from what Jessy has mentioned). I have removed the vertex color bit, but had to keep the BindChannels call in place with the bing color bit. I also removed the _Blend property in favor of using the toon color’s alpha value.

Shader "Toon/Blended" {

	Properties {
		_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex ("Underlying Texture", 2D) = "white" {}
		_Color ("Toon Tint Color", Color) = (.5,.5,.5,1)
		_MainToonTex ("Main Toon Texture", 2D) = "white" {}
		_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "white" { Texgen CubeNormal }
	}
	
	SubShader {
		
		Pass {
			Blend One One
			AlphaTest Greater .01
			Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
	
			BindChannels {
				Bind "Color", color
			}
			SetTexture [_MainTex] {
				constantColor [_TintColor]
				combine constant * primary
			}
			SetTexture [_MainTex] {
				combine texture * previous DOUBLE
			}
		}
		
		Pass {
			Blend SrcAlpha OneMinusSrcAlpha
			ColorMask RGBA
			ZWrite On
			
			SetTexture [_MainToonTex] {
				constantColor [_Color]
				combine texture * constant
			}
			SetTexture [_ToonShade] {
				combine texture * previous DOUBLE, previous
			}
		}
		
	}
	
}

And here is a simple C# script for controlling the alpha value based on the objects distance from the camera:

using UnityEngine;
using System.Collections;

public class BlendShaderScript : MonoBehaviour {
	
	int fullDistance = 2;
	int opaqueRange = 3;
	Color toonColor;
	float alpha = 1;

	void Start () {
	
	}
	
	void Update () {
		float dist = Vector3.Distance(Camera.main.transform.position, transform.position);
		if(dist > opaqueRange  dist < opaqueRange+fullDistance){
			alpha = 1 - ((dist-opaqueRange) / fullDistance);
		} else  if (dist < opaqueRange){
			alpha = 1;
		} else if (dist > opaqueRange+fullDistance){
			alpha = 0;
		}
		
		for(int i=0; i<renderer.materials.Length; i++){
			toonColor = renderer.materials[i].GetColor("_Color");
			toonColor.a = alpha;
			renderer.materials[i].SetColor("_Color", toonColor);
		}
	}
}

Right now I’m SUPER happy with this, but here’s a list of things that could be improved:
(1) The first pass should apply a tint color/alpha value.
(2) The first pass should not be additive so it can utilize more than just white.
(3) The toon effect should be written to the buffer before being blended, similar to this transparency shader: http://www.unifycommunity.com/wiki/index.php?title=AlphaVertexLitZ

BTW, I made a package of this but it’s 80mb. :shock: I started working from within the Locomotion_System project while I was developing it, so there’s a lot more than is necessary for viewing the effect.

I haven’t read the last post yet. Just the model and textures are all I need (I know I can get that model but I don’t feel like using the bandwidth for that whole tutorial just for the model). Feel free to downsample the texture to like 128 first if you want.

Edit:

What? The shader you have now shouldn’t even be able to render the first pass. If you don’t bind the vertex positions then the pass is useless. You don’t need a BindChannels block, but unless you account for primary being black, then yeah, it will be broken if you don’t change them to white, with the binding.

I was reading more about this just now and further simplified the code, which now also allows the first pass to be tinted properly:

Shader "Toon/Blended" {

	Properties {
		_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
		_MainTex ("Underlying Texture", 2D) = "white" {}
		_Color ("Toon Tint Color", Color) = (.5,.5,.5,1)
		_MainToonTex ("Main Toon Texture", 2D) = "white" {}
		_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "white" { Texgen CubeNormal }
	}
	
	SubShader {
		
		Pass {
			Blend One One
			AlphaTest Greater .01
			Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
	
			SetTexture [_MainTex] {
				constantColor [_TintColor]
				combine constant
			}
			SetTexture [_MainTex] {
				combine texture * previous DOUBLE
			}
		}
		
		Pass {
			Blend SrcAlpha OneMinusSrcAlpha
			ColorMask RGBA
			ZWrite On
			
			SetTexture [_MainToonTex] {
				constantColor [_Color]
				combine texture * constant
			}
			SetTexture [_ToonShade] {
				combine texture * previous DOUBLE, previous
			}
		}
		
	}
	
}

I’ll try simplifying everything in a new project for packaging.

I made a new project with all of the hero assets removed (that model and it’s animations is 135mb alone). I instead have made a new scene with a sphere using the same shader (not as interesting, but hey, it’s there). If you use the arrow keys to move the sphere closer to the camera, it turns from the wireframe to the toon shader.

361046–12523–$toon_blend_100.unitypackage (99.7 KB)

Problem with testing with a sphere is lack of concavity. I’ve got characters lying around, but a sphere is no good! 8)

Yeah, I realized that as soon as I made the post. :?
I tried taking a hint from that AlphaVertexLitZ shader and adding a pass in the middle with the ColorMask 0 call, but it didn’t seem to make a difference at the time.

you should add this to the wiki, probably so that someone can see it and maybe add a version that blends to diffuse.

I prefer the results of alpha testing to any kind of blending, for the wireframe. So that’s what I’ve done here. Regardless of if you want to change that, you should be putting your wireframe texture in the alpha channel of the toon texture. I also optimized/cleaned up your script, but you have to set up your variables before you hit play.

There are some other lines I might add, in the way of ZWrites, AlphaTests, and ZTests, if I knew they were actually going to have a useful effect, but in my experience, that requires testing on the target hardware. I’d most likely split this up into two materials, if I were doing this on an iOS/Android device, too, to avoid alpha testing and blending in the same render queue.

361439–12543–$toonyblend_143.cs (899 Bytes)
361439–12547–$toony_blend_182.shader (823 Bytes)

Really not sure what I’m doing wrong, but for some reason it just appears to show the first texture, with no transparency. When I run away from the camera it blends into an emissive wireframe tint color, with no toon effect. I’m also seeing bits of the wireframe tint color revealed underneath the wireframe texture as the character mesh is moved around.

I set the material property to the material used on my character, the transform property to my character’s transform (which the script is applied to), and the camera to the main camera transform.

You just dragged the wireframe texture onto the Main Toon Texture (A = Wireframe) slot. Won’t work. I just uploaded a new version of the shader using the label Wireframe Color instead of Wireframe Tint Color, because that’s what it is.

Now that I think about it, not only did you not supply a non-wireframe texture in your package, but your screenshots don’t show any. Do you even want a UV-mapped texture??

There is a wire-tile texture that I’ve been simply repeating across it, because we do not have a nicely unwrapped high-res UV texture yet (we are working on finding a plugin for 3DS Max to do this, but haven’t found the perfect solution yet). Eventually we will have a low-poly generic model of a human figure with a wireframe appearance which will be used to transition into more unique characters. We’re working right now on creating the morph target animations to accompany this shader.

I’m also currently not using a texture for the underlying toon shader, but rather a default white and relying on the constantColor to tint it. The art direction simply calls for a two-tone cel shaded effect.

How are you planning on getting these animations into Unity?

You don’t need to tint white; white is 1, and “tinting” is multiplying. Hence what I did with the wireframe. You ought to fix the last pass so that it isn’t a jumble of clutter for your purposes.

The wireframe still needs to be an alpha channel though; put it in something else’s alpha channel so you don’t waste the RGB channels or waste memory on an 8 bit alpha.