Fresnel Outline Shader for Picking up Items

Published May 24, 2018
Advertisement

Subscribe to our subreddit to get all the updates from the team!

Idea

We wanted units in our game to be able to pick up items, including the player. But how can the player know which item will be picked up when he executes that action? Traditionally, video game developers and designers do this by using an outline shader. However, in our game, we thought that it was too "normal", cartoonish and not enough A E S T H E T I C.

Solution

The solution was to use Fresnel optics to simulate a cooler outline. The Fresnel Effect is used, approximated actually because it is extremely complex, in shaders such as a reflection shader for specular colors. In real life, this visual effect occurs on surfaces that reflect and refract light. An object that refracts light lets a fraction of the incoming light pass through its body. A good example for this is water.

Examples and Tutorials

Here are examples and tutorials for the Fresnel Effect in computer graphics programming :

How to Do It

Here's how I did it with the jMonkeyEngine, which is a Java 3D game engine.

Firstly, you need to create a material definition that will describe what will be the shaders and their inputs.

Material Definition

MaterialDef Fresnel Outline {

    MaterialParameters {
        Color FresnelOutlineColor
        Float FresnelOutlineBias
        Float FresnelOutlineScale
        Float FresnelOutlinePower
    }

    Technique {
        VertexShader GLSL100:   shader/vertex/fresnel_outline/fresnel_outline_vertex_shader.vert
        FragmentShader GLSL100: shader/fragment/fresnel_outline/fresnel_outline_fragment_shader.frag

        WorldParameters {
            WorldViewProjectionMatrix
            WorldMatrix
            CameraPosition
        }
    }
}

Material Parameters

As you can see, there are 4 uniforms that we need to specify to the shaders for them to function properly :

  1. FresnelOutlineColor - The color of the Fresnel Effect. Usually, it is an environment map that deals with that.
  2. FresnelOutlineBias - Acts like a diffuse color for the specular component.
  3. FresnelOutlineScale - How far the Fresnel Effect affects the model. The smaller the angle between I and N (the surface's normal), the less Fresnel Effect there is and the more scale is needed for that surface to be lit by it.
  4. FresnelOutlinePower - Exponentially increases the Fresnel Effect's color but decreases the scale.

We will need to either set them in the material or in the code. You'll see about that later in the article.

Technique

The technique describes what shaders to execute and their engine's uniforms.

There's no need to use a recent version of OpenGL / GLSL for this shader. The first GLSL version will do.

For the Fresnel shader, we need to the following uniforms to be supplied by the game engine :

  1. WorldViewProjectionMatrix - The MVP matrix is needed to compute each vertex' position
  2. WorldMatrix - The world matrix is needed to compute the position and normal for each vertex in world coordinates
  3. CameraPosition - The camera position is needed to calculate the I (incident) vector

Material

The material uses a material definition and then applies render states and parameters to it. It is instantiable codewise.

Material Fresnel Outline : material_definition/fresnel_outline/fresnel_outline_material_definition.j3md { MaterialParameters { FresnelOutlineColor : 1.0 0.0 0.0 1.0 FresnelOutlineBias : 0.17 FresnelOutlineScale : 2.0 FresnelOutlinePower : 1.0 } }

As you can see, we can set the uniforms right here instead of doing so in the code, which saves us programmers from dealing with design components.

Vertex Shader

#import "Common/ShaderLib/GLSLCompat.glsllib"

uniform vec3 g_CameraPosition;
uniform mat4 g_WorldMatrix;
uniform mat4 g_WorldViewProjectionMatrix;

uniform float m_FresnelOutlineBias;
uniform float m_FresnelOutlineScale;
uniform float m_FresnelOutlinePower;

attribute vec3 inPosition;
attribute vec3 inNormal;

varying float fresnelOutlineR;

void main() {
    vec3 worldPosition = (g_WorldMatrix * vec4(inPosition, 1.0)).xyz;
    vec4 worldNormal = normalize(g_WorldMatrix * vec4(inNormal, 0.0));

    vec3 fresnelI = normalize(worldPosition - g_CameraPosition);

    fresnelOutlineR = m_FresnelOutlineBias + m_FresnelOutlineScale * pow(1.0 + dot(fresnelI, worldNormal.xyz), m_FresnelOutlinePower);

    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}

This is how each vertex position, normal, color, texture coordinates [...] are transferred into the vertex shader by the jMonkeyEngine.

attribute vec3 inPosition

The same procedure is applied onto g_ and m_ variables. g_ variables are definied by the engine, whilst m_ variables are defined by the material definition.

Here's how to do the Fresnel outline shader on the vertex side :

  1. Compute the world position and normal
  2. Compute the eye to vertex direction
  3. Compute the Fresnel Effect R variable (description reference)

Quote

R is a Fresnel term describing how strong the Fresnel effect is at a specific point

Fragment Shader

#import "Common/ShaderLib/GLSLCompat.glsllib"

uniform vec4 m_FresnelOutlineColor;

varying float fresnelOutlineR;

void main() {
    gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), m_FresnelOutlineColor, fresnelOutlineR);
}

All that's left to do is a linear interpolation between the black color and the desired color with R being the interpolation value between the two.

Because this is a simple tutorial, it only shows how to compute the Fresnel outline specular color.

Result

Game_Main1.png.4235e3f6dffbb03961231ebc27f4574e.png

And with a bias of 1.0.

Game_Main1.png.bf6bc58a3add87235984f3bfc2de1de3.png

Teal!

Game_Main1.png.fb99537c93626799435eb693179ce738.png

Tested on a pickable item

Game_Main1.png.7807a0d6a4808413a7d371a879090ac3.png

1 likes 3 comments

Comments

Awoken

So what exactly is this game going to be about anyways?

May 28, 2018 10:57 PM
bdubreuil
3 hours ago, Awoken said:

So what exactly is this game going to be about anyways?

That's a good question actually ahah!

We weren't exactly sure and the game design has changed since we started. Originally (and still today), we wanted a 3D rogue-lite procedural vaporwave game.

The game consists of different worlds which will have randomly generated levels that consist of semi-random rooms and totally random rooms.

For example, we had two worlds in mind : the savanna and the jungle.

  1. The savanna is outdoor and there is no direct path to the end. We didn't define what is the end. We had a zone planned for that with a giant rock with a tons of lions.
  2. The jungle is a classic dungeon crawler level.

The savanna was discarded because it wasn't enough rogue-lite (I actually wanted it to be included in the game, sad reacts only ? ). It was just too open.

As of how it will look like, think of Heavy Bullets.

Anyway, here's the gameplay

  • Attack enemies, which will be the fauna, the flora, mobs, mini-bosses, bosses and legendary bosses, with your weapon.
  • There's a stats mechanic similar to The Binding of Isaac.
  • Equip items : armor, charms, weapons and relics. Those have stats modifiers.
  • Gain money and equipment from killing enemies.
  • Gain relics from killing bosses. They offer permanent effects like in The Binding of Isaac.
  • Beware of traps!
  • And much more! Like a secret gameplay mechanic that we're not ready to announce just yet ;)
May 29, 2018 02:28 AM
Awoken

vapour wave, Heavy Bullets, The Binding of Isaac.  Very interesting indeed.

May 29, 2018 02:39 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement