Vizard 7 » Reference » Shaders » Effects » Custom Effects
7.6

Custom Effects

The effect framework makes it easy to write custom shader code that combines with existing shaders. There is no need to re-implement the entire shader pipeline anymore (vertex transformation, lighting, fog, etc..). You just write a few lines of code and Vizard will automatically combine it with existing effects to generate the full shader.

Effect Syntax

Shader effects are defined using a simple syntax (sections within brackets [] are optional):

Effect [Name] {

    [Properties]

    Shader {
        [Units]
    }
}

[Name]

This is an optional string that allows assigning an arbitrary name to the effect. The name does not affect the functionality, but can be accessed in the script using the <effect>.getName command.

[Properties]

This section allows defining optional properties and input uniforms of the effect.

Type
Type "name"

This declares the type of the effect. The type name can be an arbitrary string. When Vizard generates the full shader code using all the applied effects, it will only use one effect per unique type name. This allows both the use of multiple effects by using different type names and the ability to override existing effects by using the same type name. If no type is specified, then default will be used. The built-in generated material effects use the default type name, so it is recommended to use the default type if you want to use the effect to set the base material values. The effect type name can be accessed in the script using the <effect>.getTypeName command.

 

The table below lists some type names used by other built-in effects. You can use these type names if you wish to override the corresponding effect:

Type Descripton
AmbientLight Used by the default ambient light effect. Use this type name if you wish to create a custom ambient lighting model.
LightingModel Used by the default lighting model effect. Use this type name if you wish to create a custom lighting model.
Fog Used by the default fog effect. Use this type name if you wish to create a custom fog effect.
Texture2D
Texture2D "name" {
    [coordUnit 0]
    [unit -1]
}

Creates a 2D texture property.

TextureCube
TextureCube "name" {
    [coordUnit -1]
    [unit -1]
}

Creates a cubemap texture property.

Float
Float "name" {
    [value x y z w]
}

Creates a float property.

Int
Int "name" {
    [value x y z w]
}

Creates an integer property.

Bool
Bool "name" {
    [value x y z w]
}

Creates a boolean property.

Matrix
Matrix2 "name" {
    [value v1 v2 v3 v4]
}

Matrix3 "name" {
    [value v1 v2 v3 ... v9]
}

Matrix4 "name" {
    [value v1 v2 v3 ... v16]
}

Creates a matrix property.

TangentCoordUnit
TangentCoordUnit unit

This declares the texture coordinate unit to use to generate tangent/binormal values for bump mapping. This should only be specified if a tangent space normal map is being used by the effect.

[Units]

This section is where the actual shader code is defined for the effect. Multiple shader units can be defined, allowing an effect to insert code into multiple stages of the shader pipeline. Each shader unit is defined using the following syntax:

BEGIN Name
Code
END
Name

The section name of the shader pipeline to insert the code into. The default effect composer created by the vizfx library provides the following sections for inserting code into:

Name Shader Description
FragmentHeader Fragment Allows declaring optional functions or varying/uniform inputs for the fragment shader.
FragmentInit Fragment Called at the beginning of the fragment shader. Can be used to discard fragments based on custom clip volumes.
Material Fragment

Material properties of the pixel are applied to the material structure m. The material structure contains the following fields:

  • vec3 diffuse

  • vec3 specular

  • vec3 emission

  • vec3 normal

  • float shininess

  • float alpha

PostMaterial Fragment Post-processing of material properties can be applied to the material structure m.
Ambient Fragment Global ambient color is added to g.ambient variable.
Lighting Fragment

Lighting is applied here to the light structure light. The light structure contains the following fields:

  • vec3 direction

  • vec3 color

  • float attenuation

Once the light fields are set, the light must be applied using the following code:

vizfx_AddLight(light);

DiffuseModel Fragment Global diffuse light color is added to g.diffuse variable. This is called once for each light within a separate function that takes as input the material structure m, light structure l, and view direction viewDir.
SpecularModel Fragment Global specular light color is added to g.specular variable. This is called once for each light within a separate function that takes as input the material structure m, light structure l, and view direction viewDir.
FinalColor Fragment Called at the end of the fragment shader to allow making adjustments to the final pixel color by modifying the gl_FragColor value.
VertexHeader Vertex Allows declaring optional functions or varying/uniform inputs for the vertex shader.
PreTransform Vertex

Called before the vertex data is transformed by the ModelViewProjection matrix. The following vertex values can be modified:

  • vec4 eyeVertex

  • vec3 eyeNormal

  • vec4 eyeTangent // Only if VIZFX_TANGENT_SPACE is defined

PostTransform Vertex Called after vertex data is transformed by the ModelViewProjection matrix.
Code

The actual shader code to insert into the specified section of the shader pipeline. See the table above for variables that can be accessed/modified for each stage. Additionally, the default effect composer created by the vizfx library provides the following commonly used values:

Name Type Description
viewDir vec3 Normalized direction vector of vertex/pixel to view. This will be in the same coordinate system as the material normal vector (m.normal) and the light direction vector (light.direction). The coordinate system may be in eye space or tangent space, depending on whether VIZFX_TANGENT_SPACE is defined.
vizfx_VertexColor vec4 The vertex/material color.
vizfx_ViewDistance float

Distance between the view and vertex/pixel.

vizfx_WorldViewDir vec3 Normalized direction vector of the vertex/pixel to view in world coordinates.
vizfx_WorldPos vec3 Position of vertex/pixel in world coordinates.
vizfx_WorldNormal vec3

Normalized normal vector of vertex/pixel in world coordinates.

vizfx_WorldReflect vec3 Normalized view/normal reflection vector in world coordinates.
vizfx_EyeViewDir vec3 Normalized direction vector of the vertex/pixel to view in eye coordinates.
vizfx_EyePos vec3 Position of vertex/pixel in eye coordinates.
vizfx_EyeNormal vec3 Normalized normal vector of vertex/pixel in eye coordinates.
vizfx_EyeReflect vec3

Normalized view/normal reflection vector in eye coordinates.

vizfx_ScreenPos vec2 Position of pixel in normalized screen coordinates.
vizfx_PixelPos vec2 Position of pixel in viewport coordinates.
vizfx_CubeNormal vec3 World normal vector in cubemap texture coordinates.
vizfx_CubeReflect vec3 World reflection vector in cubemap texture coordinates.
vizfx_SphereReflect vec2 2D sphere map texture coordinates.
vizfx_TangentWorldMatrix mat3 Matrix to convert tangent space vector to world coordinates.
vizfx_TangentEyeMatrix mat3 Matrix to convert tangent space vector to eye coordinates.
VIZFX_TANGENT_SPACE #define Defined when the shader normal is in tangent space coordinates.
VIZFX_LIGHTING #define Defined when lighting is enabled.
VIZFX_FOG #define Defined when fog is enabled.

Effect Commands

The viz.addEffect command is used to load an effect from code or a file. Here we load an effect from code:

code = """
Effect {
    Shader {
        BEGIN Material
        m.diffuse = vizfx_VertexColor.rgb;
        END
    }
}
"""
effect = viz.addEffect(code)

Once an effect is created, it can be applied to a model using the <node>.apply command:

model.apply(effect)

The effect can also be added to a composer object. All objects with the composer applied, will also inherit the effect. If an effect of the same type is added to an object and the composer, the effect applied to the object will take precedence over the effect added to the composer. Here we add an effect to the default composer of the vizfx library:

vizfx.getComposer().addEffect(effect)

Effects can be dynamically disabled and enabled using the <effect>.setEnabled command. Effects are enabled by default. Here we disable an effect:

effect.setEnabled(False)

The <effect>.setProperty command can be used to dynamically change the value of an effect property. For example, the following effect defines a MyColor property that is initialized to white. After the effect is loaded, the property value is changed to red.

code = """
Effect {
    Float MyColor {
        value 1 1 1
    }
    Shader {
        BEGIN Material
        m.diffuse = MyColor;
        END
    }
}
"""
effect = viz.addEffect(code)
effect.setProperty('MyColor', viz.RED)

The above code changes the color for all objects using the effect. However, effect properties can be overridden on a per model basis. Using the <node>.setUniformFloat command, we can override the effect property value with a custom value:

code = """
Effect {
    Float MyColor {
        value 1 1 1
    }
    Shader {
        BEGIN Material
        m.diffuse = MyColor;
        END
    }
}
"""
effect = viz.addEffect(code)

# Model will inherit MyColor property value from effect
model_1.apply(effect)

# Model will override MyColor property value with green color
model_2.apply(effect)
model_2.setUniformFloat('MyColor', viz.GREEN)

An effect can be unapplied from all previous nodes and composers using the <effect>.unapply command:

model.apply(effect)
.
.
.

# Will no longer affect the model
effect.unapply()

Effect Basics

Lighting Effects

Projector Effects

Custom Effects

Example Effects