Cabin Orange

View Original

Blending and Translating Two Textures in an Unlit Shader in Unity

Texture Blending transitions

See this content in the original post

Above is a demo. Just use the slider to transition between the two backgrounds. If you cannot see the demo you may want to try a different browser.

Problem Summary

Unity version: Unity 2020.1.5.f1

In my game, each level is split into two parts with a mid-level and end of level boss. The mid-level boss is supposed to be fairly easy and perhaps a bit dumb simply to give the player a bit of a breather. The second half of each level is another ramp up in pace and enemy types. I wanted the user to know that they’d progressed by the fact that the background had also changed. What I wanted to happen was for the initial scrolling background to fade out and a new scrolling background to fade in. It would work perfectly for fading between the colours of nebulas. Do do this I’ve used an unlit shader in Unity with two textures that can be programmatically modified from outside of the shader code.

Introduction

I am going to be the first to admit that I know almost nothing about Unity Shader or more specifically, the Shader language which looks like pseudo C. This seems to work for me but if someone has a better, more efficient way of producing similar results I’d love to hear it.

Tutorial

I’m starting with a Quad mesh to hold my material:

Right-click in the Unity Hierarchy and select 3D Object->Quad from the resulting menus

In the Inspector, click the vertical ellipses (the three dots …) at the top of the Transform section and selected Reset Position

The Quad comes with a Mesh Renderer, a Mesh Collider and a default material.

My game is 2D and I’m using an orthographic camera so the distance for the background and other objects doesn’t really matter. The game is a vertical shooter with the player ship typically as the bottom of the screen and enemies dropping down from the top. To give the illusion of travel the Quad is twice the height of the viewport and the wrap mode is set to Repeat on each of the background textures. So the background will automatically circle around as time goes on once the BackgroundScroller script is written. You may have other requirements so set your sizes and texture properties as you see fit.

Properties for a Game Background Image

With the Quad selected in the Scene, you can simply drop a suitable texture straight onto it. This will replace the default material and likely end with an incorrect shader for your needs. For my game, it’s in space and so it shouldn’t take on any lighting so an Unlit Shader is perfect for this job.

Right-click inside an appropriate folder inside Assets and select Create->Shader->Unlit Shader

Name the shader something appropriate such as BackgroundBlendShader

Double click the newly created Shader to open it up in your IDE, for me that’s Visual Studio

Here’s what the current version creates for us.

See this content in the original post

In the Properties section I made the following modifications:

See this content in the original post

_MainTex is the default property that holds a texture such as the initial game background we want to display in the game. “Background One” is the display name as the texture will eventually be referred to in Unity’s Material property in the Quad. You can name it however you want as long as it makes sense when you get back to Unity. To use another texture I simply replicated the _MainTex property and named it _SecondaryTex with the display name of “Background Two”.

The _Blend property allows us to control the amount of blending between the first texture and the second texture using Range() values between 0.0 and 1.0 where 0.0 displays 100% of the first texture and 1.0 displays 100% of the second texture. The default value is set to 0.0.

I made the following modifications to the v2f structure:

See this content in the original post

The v2f or Vertex Shader to Fragment Shader structure stores Vertex Shader derived data in readiness for per-pixel calculations in the Fragment Shader. We need to add a variable to store the second textures coordinates (float2 uv2 : TEXCOORD1:) as well as increase the number of coordinates values for UNITY_FOG_COORDS() function to handle to 2.

There is some useful information at the Unity3D Wiki if you’re interested to learn more about Shader properties and code.

Next, I need to add variables to store the secondary texture and blend amount to link the properties to the code:

See this content in the original post

I simply replicated the _MainTex variables and named them _SecondaryTex. I also added a _Blend float variable to link to our _Blend property. I don’t think I needed to replicate the _MainTex_ST because I never needed to use it but I added it for code consistency.

Now we’re going to apply the _SecondaryTex variable to Vertex Shader coordinate system :

See this content in the original post

By adding,

o.uv2 = TRANSFORM_TEX(v.uv, _SecondaryTex);

we’ve tied the second texture coordinate calculations into the Vertex Shader code which allows us to move both the first and second textures independently.

Now for the blending functionality:

See this content in the original post

Again, it’s a single line addition:

fixed4 col = lerp(tex2D(_MainTex, i.uv), tex2D(_SecondaryTex, i.uv2), _Blend);

Using the lerp() function with the _Blend variable we have control over the blending between the two textures at the Shader level.

Back in Unity, all being well, the Material should look something like this:

Quad Material using our Unlit Shader

This particular version is directly from my game so there may be some minor differences.

Now we simply click Select on each of the above image thumbnails to apply our image backgrounds or drag and drop the images from the Assets folder.

Sliding the Blend Amount slider will demonstrate the blending mode between the two textures in the Game and Scene Viewports of Unity. Changing the Y Offset value on Background One and Two will move the texture up or down dependant on the value entered. As an example, set the Blend slider to around 50% and change each of the Y Offsets and you should see the results in the Game and Scene viewports.

Changing the Y Offset works for my vertical shooter but changing the X Offset would work better in a side scroller.

Now I’d like to tie this into my Unity code and I do this by adding a script to the Quad.

Click Add Component in the Inspector for the Quad and select New Script

Name it something appropriate, I called it BackgroundScroller

Here’s my code:

See this content in the original post

Essentially, I’ve exposed the following to the Unity UI:

  • The blending duration between the first and second textures through _backgroundTransitionDuration

  • The X and Y material offsets for each texture via the Vector2 _textureMaterial1Offset and _textureMaterial2Offset

  • The speed at which the texture offsets move via _backgroundScrollSpeed

    • This variable is exposed to other source code via the C# property BackgroundScrollSpeed

Both textures exist at all times whilst the game level is running and both are being moved (translated) by the offset via the calculations that occur during each Update() cycle. The speed of the translation is defined by the Unity exposed value Background Scroll Speed multiplied by the difference in time between now and the previous frame.

The blending is controlled via a source code controlled flag (bool) named BackgroundTransitionRequested which is defaulted to false. When another source code sets this flag to true the Update() cycle calculates the blend amount by dividing the difference in time from the last frame by the Background Transistion Duration value exposed in Unity. The resulting value is restricted to something between 0.0 and 1.0 via Mathf.Clamp01() and then applied to the Blend value exposed on the Quad material.

I think the results look pretty neat and help to let the player know that progress is being made in the game as well as help tell the story in a minor way.

Here’s an example of the full BlendBackgroundsShader.shader code I use in the game.

See this content in the original post

In Summary

Using a transition shader is an effective way of switching between moving backgrounds. You could use it to switch between night and day cycles on a platformer game. The downside to this technique is that you need to load in and move two backgrounds throughout the game runtime even though only one is visible for the most part.