• Advertisement

Recommended Posts

https://en.wikipedia.org/wiki/Cel_shading in this Wiki it mentions:  "A Sobel filter or similar edge-detection filter is applied to the normal/depth textures to generate an edge texture. Texels on detected edges are black, while all other texels are white:"

How would I do this with Unity?

I know about the reverse mesh trick but because of strict polygon limits it is no longer a option. I have been looking for a Sobel filter shader for Unity to learn from but all I found was a very old one, that doesn't work.

Edited by Scouting Ninja

Share this post

Link to post
Share on other sites
3 hours ago, GoliathForge said:

I see you've tagged mobile but did you consider [ this ] from the standard assets package?

Thanks, but I can't use anything from the Unity asset store.

The composing would have to be adjusted to work with the special way that our scene is lighted. This means either altering or reverse engineering the shader. Both these things are forbidden by the Unity asset store licence; you are only allowed small tweaks. 

As this is for a commercial game we can't have any legal problems and as a result can't use one of the shaders or effects from the Unity asset store. Also can't afford to hire a pro.

I have found some sobel descriptions and have started to work this out. At the moment I get only the outside outline, it's not reading my normal map, depth or contours.


Basically I need to learn how to do this from scratch. It isn't that I can't do it but it will take me some time so any advice on this will help me.

It also has to be optimized for mobile, that I have no idea how to do.

Share this post

Link to post
Share on other sites

If you use mirror-ball image based lighting you talked about recently, you could experiment with drawing a black circle around the ball :) (Or achieve the same effect by darkening if per pixel normal is tangent to view direction within some soft threshold - less false positives and faster than edge detection by post process, but probably very bad for flat geometry at shallow angles.)

Edit: to fix the problem of large dark areas on flat geometry e.g. a cube, you could use a precalculated texture the is black where you allow the darkening to happen, and white otherwise. So for the cube only the edges would be black and for a sphere the entire texture would be black. Finally you would have good control of the stroke width. Baking of this texture should be driven by mesh curvature.


Edited by JoeJ

Share this post

Link to post
Share on other sites
20 minutes ago, JoeJ said:

If you use mirror-ball image based lighting you talked about recently

With my matcap experiments I know that this won't work exactly. Matcaps takes the camera angle so sometimes the outline will be thin around the edge and other times it would cover the whole model when you look from a angle. Matcaps mostly work from fixed viewpoints.


I am making progress, I learned how to get the normal and depth pass from Unity and have a outline. I just need to learn how to use the normal map to also apply lines on the mesh.

Share this post

Link to post
Share on other sites
1 hour ago, Scouting Ninja said:

Matcaps takes the camera angle so sometimes the outline will be thin around the edge and other times it would cover the whole model when you look from a angle.

Yep, that's why i thought about curvature maps to get control. I quickly tried it out - seems an interesting idea maybe nobody implemented yet. I did it only per face, here are some images.

As expected i get rid of 'whole side view black' (the side of the cube would be black without the curvature map).

But the stroke i get is too thin in comparison to the sphere :( and there is of course no stroke on the front of the cube :( and avoiding too sharp edges also does not help enough :(

Maybe with more work it becomes acceptable, but no break-through. Can't help with edge detection method either.







Share this post

Link to post
Share on other sites
6 minutes ago, JoeJ said:

I quickly tried it out

Looks very interesting. Will keep it in mind, never know when something like this could be handy.


I am having problems with Unity screen effects on mobile so I think I should just abandon this idea and re-work my meshes so I can use the flipped mesh effect. It will take hours but this has taken me two days with very little progress.

Some times I feel like Unity just hates me and the feeling is mutual.

Share this post

Link to post
Share on other sites

... got the idea to blur normals, so flat / round stuff isn't that different.

Also previously i used manual setting for eye position which made sphere stroke even wider.

Looks much better now, also no popping under camera movement. But the need for second normal channel hurts, or you'd accept that smooth look for everything :)

Edit: The blurred normals do all the trick now. curvature map might not be necessary anymore.


static bool visCelShading = 0; ImGui::Checkbox("visCelShading", &visCelShading);
		if (visCelShading)
			static float radius = 0.19f;
			ImGui::DragFloat("radius", &radius, 0.01f);

			static std::vector<vec> vertexCurvatureDirectionsBoth;
			static std::vector<float> vertexConeAngles;

			if (ImGui::Button("Update Curvature") || vertexConeAngles.size()==0)
				mesh.BuildVertexCurvatureDirections (
						&vertexCurvatureDirectionsBoth, 0, 0,
						&vertexConeAngles, 0, 0, 
						0, radius, mesh.mVertexNormals, mesh.mPolyNormals);

			static float curvScale = 15.0f;
			ImGui::DragFloat("curvScale", &curvScale, 0.01f);

			std::vector<float> vertexMap;
			for (int i=0; i<mesh.mVertices.size(); i++)
				//vertexMap[i] = vertexCurvatureDirectionsBoth[i].Length() * curvScale;
				vertexMap[i] = fabs(vertexConeAngles[i]) * curvScale;

			std::vector<float> blurredVertexMap;
			mesh.BlurVertexMap (blurredVertexMap, vertexMap);
			std::vector<float> polyCurvatureMap;
			mesh.VertexMapToPolyMap (polyCurvatureMap, vertexMap);

			static int blurIter = 10;
			ImGui::DragInt("blurIter", &blurIter, 0.01f);

			std::vector<vec> blurredVertexNormals1 = mesh.mVertexNormals;
			std::vector<vec> blurredVertexNormals2;
			for (int i=0; i<blurIter; i++)
				mesh.BlurVertexMap (blurredVertexNormals2, blurredVertexNormals1);
				mesh.BlurVertexMap (blurredVertexNormals1, blurredVertexNormals2);

			std::vector<vec> smoothPolyNormals;
			mesh.VertexMapToPolyMap (smoothPolyNormals, blurredVertexNormals1);
			for (int i=0; i<smoothPolyNormals.size(); i++) smoothPolyNormals[i].Normalize();

			static bool visCurvatureMap = 0; ImGui::Checkbox("visCurvatureMap", &visCurvatureMap);
			if (visCurvatureMap)
				for (int i=0; i<mesh.mPolys.size(); i++) 
					float c = polyCurvatureMap[i];
					float col[3] = {c,c,c}; VisPolyFilled (mesh, i, col);
			static bool visShading = 1; ImGui::Checkbox("visShading", &visShading);
			if (visShading)
				vec lightPos (4,5,-3);
				//static vec eyePos (8,4,-2);
				//ImGui::SliderFloat3 ("eyePos", (float*)&eyePos, -10,10);
				static float strokeT = 0.2f;
				ImGui::DragFloat("strokeT", &strokeT, 0.01f);

				static float strokeS = 2.0f;
				ImGui::DragFloat("strokeS", &strokeS, 0.01f);

				RenderPoint (eyePos, 1,1,1);

				for (int i=0; i<mesh.mPolys.size(); i++) 
					vec pos = mesh.mPolyCenters[i];
					//vec normal = mesh.mPolyNormals[i];
					vec normal = smoothPolyNormals[i];
					vec LightD = vec(pos - lightPos).Unit();
					float NdotL = max (0, normal.Dot(LightD));
					float ambient = 0.4f;
					float rec = NdotL + ambient;

					float strokefactor = max (0, min (1, polyCurvatureMap[i] ));
					vec eyeD = vec(pos - eyePos).Unit();
					float NdotE = max (0, normal.Dot(eyeD));
					NdotE = (NdotE*0.98f + NdotL + 0.02f); // wider stroke in shadow
					float tangF = 1.0f - sqrt(NdotE);
					float darken = max (0, min (1, tangF*strokeS - strokeT)) * strokefactor;

					vec diff (0.5f, 0.2f, 0.7f);
					diff *= (1.0f-darken);
					vec lit = diff * rec;

					VisPolyFilled (mesh, i, &lit[0]);



Edited by JoeJ

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

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By EddieK
      Hi i'm trying incorporate sounds into my game and I have ran into troubles with performance. Whenever I call:
      soundPool = new SoundPool.Builder().setAudioAttributes(new AudioAttributes.Builder().build()).build(); I immediately notice lagging and the framerate drops drastically. I don't even have to actually play the sounds for the framerate drop to occur.
      Is there anything I can do to fix this issue?
      Thanks in advance.
      P.S. Never mind, I figured out that restarting the phone got rid of the lagg and it wasn't because of the sound.
    • By RoKabium Games
      Another one of our new UI for #screenshotsaturday. This is the inventory screen for showing what animal fossils you have collected so far. #gamedev #indiedev #sama
    • By eldwin11929
      We're looking for programmers for our project.
      Our project is being made in Unity
      -Skills in Unity
      We're looking for programmers who can perform a variety of functions on our project.
      Project is a top-down hack-and-slash pvp dungeon-crawler like game. Game is entirely multiplayer based, using randomized dungeons, and a unique combat system with emphasis on gameplay.
      We have a GDD to work off of, and a Lead Programmer you would work under.
      Assignments may include:
      -Creating new scripts of varying degrees specific to the project (mostly server-side, but sometimes client-side)
      -Assembling already created monsters/characters with existing or non-existing code.
      -Creating VFX
      -Assembling already created environment models
      If interested, please contact: eldwin11929@yahoo.com
      This project is unpaid, but with royalties.
      Additional Project Info:
      Bassetune Reapers is a Player-verus-Player, competitive dungeon crawler. This basically takes on aspects of dungeon crawling, but with a more aggressive setting. Players will have the option to play as the "dungeon-crawlers" (called the 'Knights', or "Knight Class", in-game) or as the "dungeon" itself (literally called the 'Bosses', or "Boss Class", in-game). What this means is that players can choose to play as the people invading the dungeon, or as the dungeon-holders themselves.
      Key Features:
      -Intense, fast-paced combat
      -Multiple skills, weapons, and ways to play the game
      -Tons of different Bosses, Minibosses, creatures and traps to utilize throughout the dungeon
      -Multiple unique environments
      -Interesting, detailed lore behind both the game and world
      -Intricate RPG system
      -Ladder and ranking system
      -Lots of customization for both classes s of customization for both classes
    • By RoKabium Games
      Custom coffee mugs have arrived... More caffeine!
      Have a great weekend everyone! 
      #gamedev #indiedev #sama #caffeine
    • By Marina Musetescu
      Hello! We are currently developing a real-time strategy mobile game and this is the concept art for one of our characters.
      Her description is: „She blends upper class sophistication with very sharp blades and even sharper fangs.”
      What do you think? 

  • Advertisement