Making textureless 3D work

Textureless pure 3D

Creating a textureless “pure3D”  look

Introduction

(This is a post about Oberon’s Court; a fantasy RTS/RPG game being developed by Tomas Sala (@littlechicken01) who is also one of the co-founders of Little Chicken Game Company. However the game is not an official Little Chicken Production, you can keep up to date on the game at http://facebook.com/oberonscourt)

One of ways I decided to challenge myself when starting Oberon’s Court was to create a visual style that does not use any textures. There’s two reasons for this.

First of all, it looks beautiful. Having grown up with pixel games and the advent of 3D games has instilled in me a deep appreciation for 3D graphics. But the advent of pixel art has shown me that you can take any visual tech and distill it to its purest form. Creating 3D art without the use of textures does exactly this; it distills your modelling, animation and visual skills. Henceforth I shall call this style “pure3D” (feel free to add that your technobabble jargon).

Secondly, it’s very efficient, taking away the need to unwrap and texture a model removes a significant chunk from the development process. However, you will need to adjust your models and shaders to compensate for the lack of definition, seeing as you’re also losing an aspect or tool in your pallet.

This post is a how-to on approaching this style. This is by no means a “one-and-only” guide, as there are many artists and indie-devs using wildly different approaches to creating a stylized and purist 3D aesthetic, but it is how I did it for Oberon’s Court.

In this first post I’ll write about the general setup, and going through the process step by step.

Regarding shader code:

I’ll do an in-depth look into each shader and how to create them yourself in Shaderforge or Strumpy as a part two of this post. For those eager to experiment here’s a copy of my entire shader library that I used for Oberon’s Court:
http://oberonscourt.com/downloads/someshaders.zip

In the meantime, please do download / purchase these two awesome shader tools for Unity3D. I highly recommend them, as I use these as essential tools for my development process.

Download Strumpy for unity3d
Purchase Shaderforge for unity3d 

Lets get started!

Evolving the aesthetic

When I started creating the visuals of Oberon’s Court I was very much into compensating for the lack of textures, adding lights and accents to maximize the impact of the style. However, I quickly found that increasing contrast, when you have nothing but gradients, edges and solid colors to work with, does not improve the clarity of a game. Even though visually striking, it was hard to discern units and foreground items from the background.

First true textureless unity3D test. Very striking, but sadly not very well readable from in-game perspective.

As development progressed I found myself removing and subtracting visual effects to create a visual style where the player could easily discern the “shadow” units from their environment. Additionally, I found it a very pleasing experience to subtract effects, instead of adding more and more.
If you compare the earlier screens with the latest screens you can see that some of the initial tests where visually more striking, however they weren’t very suitable for game play. I’m not saying that you can’t make a game using a more high-contrast approach, but it was not the game I was designing.

Cluttered and hard to read.

The final style.

The ingredients

To create the pure3D style I used a couple of recurring ingredients and themes. I’ll iterate on those here, explaining what techniques are involved and how I achieved the ingame results.

In essence the style is very, very simple. There is nothing new here for most game artists. But combining these ingredients can lead to striking result when you remove the texturing from the process.

  • using smoothing groups as shape definition
  • using additional dark/light vertex color data, such as radiosity solutions
  • using UV coordinates to create color gradients and color mixing
  • using shaders to add shape enhancing effects(fresnel etc.) to the model
  • using unlit shaders (without lights), but with realtime shadows
  • using select post-fx (bloom) to enhance/soften the look

1. Using smoothing groups as shape definition

When working without textures, you lose an important part used to help enhance the depth of your 3D model. Especially for mobile development, where textures are often used as main way to enhance the visual complexity and shape of an otherwise very lo-poly model.

In Oberon’s Court I used smoothing groups to enhance the definition of my 3D models. Usually smoothing groups are discarded, as normal maps and other techniques are used for describing the angles of faces. Nowadays we’re more used to creating high poly models and distilling that angular (normal) data in a texture. When you have the full dataset of a hi-res texture, smoothing groups seem quaint and only of passing interest. But using them effectively can provide a beautiful tool of enhancing the shape of your model. Both as it’s being lit as well as for use in shaders.

Some notes:

  • Unity does accept multiple normals per vertex, so your smoothing groups will transfer to Unity3d’s lighting models still intact. The same does not count for vertex colors (more on that later)
  • Edge triangulation: when creating smooth surfaces you’ll need to occasionally re-triangulate to create the smoothest look. Just make sure you’re modelling in basic shaded preview mode
  • Create smoothing groups that enhance the edges of your model
  • Break up over-convex shapes, if the angle between two faces is too big (too sharp) do not try to smooth over it, break it up in two smoothing groups
  • Create enough faces so you can create interesting edges and protrusions, which the smoothing groups can enhance.
  • Use a limited number of smoothing groups, just for practicality. You can repeat use smoothing groups, as long as they don’t touch another set of faces with the same smoothing group ID
Creating an interesting hill shape by extruding faces

Creating an interesting hill shape by extruding faces.

selecting faces to create smoothing groups, and the end result in preview shading

Selecting faces to create smoothing groups, and the end result in preview shading.

2. Using additional dark / light vertex color data, such as radiosity solutions

When working with shaders you want to be able to add as much additional information as possible into a 3D model. Most shader models work with data stored in the textures (normal maps, diffuse maps etc), but also with data stored in the geometry and vertices (points). The simplest form is the position: the normals and basic data required to display the model. But you can keep adding more data to each vertex. This can be physics data for physical materials, but also additional lighting data. In Oberon’s Court I used 3D Studio Max’ ability to render a radiosity solution on vertex level and save this to vertex colors. Basically creating a dark-light shading for an object based on a complex lighting solution. This allowed me to darken and lighten the model based on a pre-determined lighting scheme.

Some notes

  • Using skylights is a quick way of creating a soft outdoor look with radiosity
  • Object and vertex colors will influence the color of other faces, as light literally gets bounced off the geometry, and thus radiates color
  • Don’t worry if the radiosity is not very precise, you can add extra tesselation and vertices to improve quality. We only need a hint of dark-light to give depth to a model. It doesn’t need to be realistic, nor perfect
  • Only add radiosity after you’ve done the smoothing groups, as smoothing is taken into account during the calculations
  • Radiosity is found in the scanline renderer of 3D Studio Max and is an advanced lighting method. (Similar features are available in Maya, I believe)
  • You can assign the radiosity to be permanent, by using the vertex colors modifier, and use the “assign vertex colors” function in the roll-out
  • Detach each smoothing group! Unity does not retain multiple vertex colors per vertex over the same channel, so if you want to retain the radiosity solution and the sharp smooth edges, you must detach each smoothing group to separate elements.
First detach all the smoothing groups, so they're seperated, then perform radiosty calculations.

First detach all the smoothing groups, so they’re seperated, then perform radiosty calculations.

 

First results of the radiosity solution, and then assigned to the vertex colors and graded. Notice how the smoothing groups really pop-out the shape of the models.

First results of the radiosity solution, and then assigned to the vertex colors and graded. Notice how the smoothing groups really pop out the shape of the models.

3. Using UV coordinates to create color gradients and color mixing

In order to diffirentiate height in the environment, I used color gradients. The easiest way of implementing these would be to create a texture. However, seeing as I committed to not using textures, this wasn’t an option. Also, gradients are something shaders can do quickly, without much fuzz. In order to create a gradient we need information on both the direction and length.
Initially I used world position data for this, to create a true height-map type effect. However, this approach is calculation-heavy, as the shader needs to retrieve the world position of each individual vertex. Therefore, this approach is not very suitable for mobile use. After coming to this conclusion, I decided to use UV coordinates to achieve the same results.

Simple shader that turns UV  Coordinates into gradient.

Simple shader that turns UV Coordinates into gradient.

You can even use UV coordinates to create not just gradients but entire dynamic gauges and bars, using shaders.

Creating the shader (using the Strumpy shader editor):

  1. Create a model with a simple UV set (in 3dsmax, simple planar mapping)
  2. Create a gradient by lerping between two colors based on either U or V component
  3. Floor or ceil the gradient value (makes it a hard line, either zero or one)
  4. Add a sine deform based on time, to the gradient value (sinetime)
  5. Add an offset to make the gradient rise or fall (add float value)

You end up with this:

This stuff will stay sharp at any resolutuion and zoomed  -in. All the way until your floating point unit will go BLERGH!

This stuff will stay sharp at any resolutuion and zoomed -in. All the way until your floating point units go BLERGH!

 

 

 

 

 

 

 

4. Using shaders to add shape enhancing effects (fresnel etc.) to the model

When writing a shader you can combine gradients, vertex color data, and finally some shader specific effects to create a nice unlit shading that is quick to render and accentuates the geometry, instead of hiding it.

The shader algorithm I used is really simple, and can be summed up as such:

  • Create a top down gradient for basic colors
  • Add a fresnel effect, (shiny rim effect based on the normals of your model)
  • Mask the fresnel effect with another gradient (so the shiny rim only applies on the top of the model, not the flat floors)
  • Multiply all with the radiosity vertex colors (adding a dark / light shading to everything)
  • Add a realtime shadow layer to the unlit shader
  • Tweak until right

Here’s how this looks in strumpy shader editor, and here’s a link to the strumpy shader file 

Creating two gradients from Uv coords, 1 to make a color gradient, and one to mask the fresnel effect.

Creating two gradients from Uv coords, 1 to make a color gradient, and one to mask the fresnel effect.

Mixing the fresnel, color gradient and vertex colors and outputting to emmisive (unlit)

Mixing the fresnel, color gradient and vertex colors and outputting to emmisive (unlit).

At one point I even multiplied the gradient with the Y component of the vertex normal, in order to have one color on flat horizontal surfaces and another on vertical surfaces, creating a true height-map look, however I discarded this as being to cluttering in the scene.

Here’s a link to a decent description of what “fresnel” means: http://www.3drender.com/glossary/fresneleffect.htm

No fresnel effect on the left, and a red fresnel applied on the right, with it masking off towards the bottom.

No fresnel effect on the left, and a red fresnel applied on the right, with it masking off towards the bottom.

5. Using unlit shaders, without lights, but with real-time shadows

Nowadays in Unity3D 4+ you can use real-time directional shadows on mobile platforms, which is great. However, shadows in shaderlab (the in-between system Unity uses for cross-platform compatibility) are part of the lighting calculations. This means that if you make an unlit shader, you have to add shadows in a separate pass, as making shaders unlit or emmisive excludes the effect of lamps or lights on the model, and thus the ability to cast or receive shadows.

Luckily I’ve already done an entire post on this topic , which you can read here: http://blog.littlechicken.nl/programmers-weekly-so-you-like-them-shadows/

6. Using select post-fx (bloom) to enhance / soften the look.

Post-fx are effects that take the entire final rendered image of your game and apply different effects to it. Unity Pro ships with a few post-fx shaders that are optimized for mobile platforms. For example: the Depth of Field shader uses a black and white depth image to blur the 3D world, based on depth. This makes everything in the foreground sharper and at the same time blurs the background.

One of the post-fx I’d like to point out is Mobile Bloom and Fast Bloom. The bloom shader is simple: it makes a copy of your final renderview, blurs it and multiplies the blurred result back onto the original, thus creating a soft glow that is most pronounced at very light areas such as the sky.

Without and with the standard fastBloom shader. The effect can be subtle, but be aware of high contrast visuals where it can really go overboard.

Without (left )and with (right) the bloom shader.

End of Part 1

I hope this post provided some insight into making a pure3D or textureless 3D game. It’s not all that difficult, but it does require you to use your tools differently. That being said, much of what I wrote here also applies to more “normal” 3D art assets.

Please let me know on twitter or facebook what you think, or if there’s anything you’d like to see explained or shared. Just hit me up and i’ll do my best. I’ll try and get into specific shaders in the near future.

Cheers,
Tomas Sala

 

74,205 total views, 48 views today

Programmer’s Weekly: So you like them shadows?

Written by: Tomas Sala

So you like them shadows?

As you might know Unity3D now supports realtime shadows on mobile devices, woot!  For those interested here’s the official announcement of 4.2: http://blogs.unity3d.com/2013/07/22/unity-4-2-has-arrived/

But, like us, your first response is probably: “That’s never going to fly in my mobile game, it’ll suck down frame-rate like nobody’s business!”, and you would be partly right. But it’s not the whole story. If used correctly and carefully it is possible to add a whole new layer of visual depth to your game.

First of we need to talk about what type of shadows unity3D allows on mobile devices. This blog post is not about shadow-maps, but about real-time shadows. This means shadows projected from a directional light onto your scene. Shadows that are rendered every frame, and thus dynamically respond to your scene and light..

Some basic rules

  • Only hard shadows are supported, (we can soften them up in the shader pass I’ll discuss later, but that’s not advisable processing wise)
  • Only 1 directional light is allowed in the scene.  
  • Basically, only low-resolution shadows are practical on most devices (even the nexus 7 (2nd gen, 2013) will take a substantial hit from medium resolution shadows)

Now a couple of main problems pop up, specific to most mobile games.

  • Most mobile games don’t use lights due to the added rendering cost, or are based on unlit atlassed textures, vertex colors or shaders optimized to work without light sources.
  • Draw Calls and batching, these related issues are a pain when doing mobile development. It just got a whole lot nastier with real-time shadows.

So in this week’s blog post we’ll be looking at solutions for each of these two problems.

Solution 1:  Differentiate between what needs to cast a shadow and what needs to receive one.

First off all the most basic rule is: Don’t use shadows where it’s not required, and know the difference between a receiver and a caster.

A shadow caster is an object that casts a shadow. You need to minimize the amount of these. The fewer casters, the fewer shadows and the faster the calculations are. This works down to detail. So if you have a building, make sure only the base structure casts a shadow. Don’t do the chimneys, windows, doors, and other details! They have no need to cast a shadow. Make sure they are turned of in the mesh render component in Unity3D.

shadow-caster

 

A shadow receiver is the object that receives the shadows. First of all, make sure that an object that receives shadows does NOT cast shadows. So separate ceilings from floors for instance, and have the floors be shadow receivers, but not the ceiling. (Or not even the walls. Remember, because you have only 1 directional light, it’s practical to make it a top-lit scene).

Now splitting up your objects might result in additional draw calls,but we’ll deal with those later.

Solution 2: Adding real-time shadows to an Unlit or custom-lit scene.  

So a basic trick transferred from the days of yore to modern mobile game development is the use of vertex colors. Vertex colors allow you to not just paint a model, but actually light it (with, for instance, a radiosity solution) and then save it or bake it into the vertex data of the model..

That will look something like this. A basic vertex colored lighting solution, merged with a custom shader.

vc_colors

This gives the Illusion of lights without using any. Add to this light-maps or pre-lighted textures and you get  a static object, that looks like its lit. Additionally, you can shape the visual style to be unrealistic, cartoony or anything else.

 

Now that your game artists have gone through all the effort to make something look nice without light just to save performance, it’s un-logical to simply add a light to have shadows. Basically a double whammy, the shadows need to be rendered, and the light needs to be calculated.

So why not just turn on shadows? 

Here at Little Chicken Game Company our artists write their own shaders in tools like the Strumpy Shader Editor. The disadvantage of this is that you export surface shaders. To turn on shadows, you need to create a shader that has an output to diffuse. This causes the shader to become lit: lighting your object and at the same time creating shadows. The moment you connect an input to emmisive to create an unlit shader, your shadows disappear. And we don’t want to light the object, we only want the shadows.

Solution: Add a renderpass to your unlit surface shader.

If you really want shadows, you’ll need to find a way to add shadows to your emmisive surface shader. (fragment shaders might be faster and simpler, but when you’re stuck to strumpy and don’t know how to code fragment shaders, this will do the trick)

So we’re going to add a shadow pass to an emmisive shader. I’ll be quick and just give the shader code for the second pass.

—————code————————————————————————————————–

Pass
{
Blend DstColor Zero
Fog
{ Mode Off
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#pragma fragmentoption ARB_precision_hint_fastest
#include “UnityCG.cginc“
#include “AutoLight.cginc“
struct appdata
{
fixed4 vertex : POSITION;
fixed4 color : COLOR;
};

struct v2f

{
fixed4 pos : SV_POSITION;
fixed4 color : TEXCOORD0;
LIGHTING_COORDS(1, 2)
};
v2f vert (appdata v)
{
v2f o;
o.pos = mul( UNITY_MATRIX_MVP, v.vertex);
o.color = 1;
TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
}
fixed4 frag(v2f i) : COLOR

{

fixed atten = LIGHT_ATTENUATION(i);

fixed4 c = i.color;
c.rgb *= atten;
return c;
}
ENDCG
}

—————code ends——————————————————————————————-

 Just paste this after the ENDCG of your regular shader code, in the un-compiled unity3d shader. Make sure you use  FallBack “VertexLit” at the end of your shader. (FallBack “VertexLit” will also enable shadows in your fragment shaders, it’s way easier)

Example, here I’ve only used this additional shading pass on the ground surface. On nothing else. All the shading and colors you see are not created by the directional light, but by the shader, combining fresnel effects, ramps and vertex colors. And finally a shadow pass.

sample_shadows

 

Solution 3:  Batching and drawcalls.

So we now have two things: shadows in our unlit scene and a selection of objects that cast shadows, and an even more limited number of objects that receive shadows.

Doing this separation tightly, will already decrease your drawcalls substantially compared to blindly turning on your directional light with shadows.

But there is an additional problem, materials that are still shadow capable are not batched. In the documentation it states that objects that cast or receive shadows are not batched. But the problem goes deeper, any material capable of casting shadows (or possibly, that is also used for shadows) is not batched!  So in Oberon’s Court I made the mistake of having one solid black material that could cast shadows.

This shader:

—————code————————————————————————————————–

Shader “Oberonscourt/Color”

{

Properties {
_Color (“Color“, Color) = (1,1,1)
}

SubShader {
Color [_Color]
Pass {}
}
Fallback “ VertexLit“, 1
}

—————code ends——————————————————————————————-

Now I stupidly assumed that using this shader on an object that did NOT cast or receive shadows would allow the object to be batched. This is not true! Even objects that do not cast or receive shadows but have shadows enabled in the shader will not batch. (so it seems, I could be wrong, but I’ve got the drawcalls to back it up)

So the final trick was to take any object that receives or casts no shadows, and make sure it had a material that had no shadow capabilities. In the sample case I removed the fallback vertexlit code from the shader

To optimize even further I changed the shader on materials that where on a shadow caster, but did not need to cast a shadow to a non-shadowed version of the same shader.  Practically this means the eyes, the mouth and other small props that where part of the skinned mesh. These are part of a shadow-casting mesh, but now no longer cast any shadows, and reduce the drawcalls. This is especially true for characters with many small un-skinned sub-parts.

Its probably more logical that any material with a shadow capable shader that is also used in an object that does NOT cast or receive shadow(so used also in non shadow, and shadowed objects). Causes the instance of the non casting material to be NOT batched.. (A hunch),,  The solution is the same no matter what the cause. Do not reuse a material you’ve used on a shadow caster or receiver, on an object that is not casting or receiving a shadow. Otherwise the non casting/receiving object will not be batched.

 

Conclusion

Having done these steps, and making sure that only a few objects actually cast shadows, I was able to create the environment for the game and have it run on most android 4.1+devices.  An added advantage by skipping the lighting and keeping the shadow reception shader unlit, I can now turn off the directional light and shadows, and it will look exactly the same (without shadows).  Which is great for an ingame settings menu for instance.

Do remember that shadows are now possible and performance can be maintained, but still if you expect anything less than a doubling of your drawcalls, you will be disappointed.

To finish it off here’s a side by side of with and without real-time shadows. I hope the above solutions and workflow will help you out in implementing shadows on mobile devices.

without-shadows


Cheers,
Tomas

208,774 total views, 56 views today