Shaders and Materials

What is a Shader

From wikipedia:

In the field of computer graphics, a shader is a computer program that is used to do shading: the production of appropriate levels of color within an image, or, in the modern era, also to produce special effects or do video post-processing. A definition in layman's terms might be given as "a program that tells a computer how to draw something in a specific and unique way".

In other words, it is a piece of code that runs on the GPU (not CPU) to draw the different Cocos2d-x Nodes.

Cocos2d-x uses the OpenGL ES Shading Language v1.0 for the shaders. But describing the GLSL language is outside the scope of this document. In order to learn more about the language, please refer to: OpenGL ES Shading Language v1.0 Spec.

In Cocos2d-x, all Node objects that are renderable use shaders. As an example Sprite uses optimized shaders for 2d sprites, Sprite3D uses optimized shaders for 3d objects, and so on.

Customizing Shaders

Users can change the predefined shaders from any Cocos2d-x Node by calling:

sprite->setGLProgramState(programState);
sprite3d->setGLProgramState(programState);

The GLProgramState object contains two important things:

  • A GLProgram: Basically this is the shader. It contains a vertex and fragment shader.
  • And the state, which basically are the uniforms of the shader.

In case you are not familiar with the term uniform and why it is needed, please refer to the OpenGL Shading Language Specification

Setting uniforms to a GLProgramState is as easy as this:

glProgramState->setUniformFloat("u_progress", 0.9);
glProgramState->setUniformVec2("u_position", Vec2(x,y));
glProgramState->setUniformMat4("u_transform", matrix);

You can even set callbacks as a uniform value:

glProgramState->setUniformCallback("u_progress", [](GLProgram* glProgram, Uniform* uniform)
{
    float random = CCRANDOM_0_1();
    glProgram->setUniformLocationWith1f(uniform->location, random);
}
);

And although it is possible to set GLProgramState objects manually, an easier way to do it is by using Material objects.

What is a Material

Assume that you want to draw a sphere like this one:

The first thing that you have to do is to define its geometry, something like this:

...and then define the brick texture, like:

  • But what if you want to use a lower quality texture when the sphere is far away from the camera?
  • or what if you want to apply a blur effect to the bricks?
  • or what if you want to enable or disable lighting in the sphere ?

The answer is to use a Material instead of just a plain and simple texture. In fact, with Material you can have more than one texture, and much more features like multi-pass rendering.

Material objects are created from .material files, which contain the following information:

  • Material can have one or more Technique objects
  • each Technique can have one more Pass objects
  • each Pass object has:
    • a RenderState object,
    • a Shader object including the uniforms

As an example, this is how a material file looks like:

// A "Material" file can contain one or more materials
material spaceship
{
    // A Material contains one or more Techniques.
    // In case more than one Technique is present, the first one will be the default one
    // A "Technique" describes how the material is going to be renderer
    // Techniques could:
    //  - define the render quality of the model: high quality, low quality, etc.
    //  - lit or unlit an object
    // etc...
    technique normal
    {
        // A technique can contain one or more passes
        // A "Pass" describes the "draws" that will be needed
        //   in order to achieve the desired technique
        // The 3 properties of the Passes are shader, renderState and sampler
        pass 0
        {
            // shader: responsible for the vertex and frag shaders, and its uniforms
            shader
            {
                vertexShader = Shaders3D/3d_position_tex.vert
                fragmentShader = Shaders3D/3d_color_tex.frag

                // uniforms, including samplers go here
                u_color = 0.9,0.8,0.7
                // sampler: the id is the uniform name
                sampler u_sampler0
                {
                    path = Sprite3DTest/boss.png
                    mipmap = true
                    wrapS = CLAMP
                    wrapT = CLAMP
                    minFilter = NEAREST_MIPMAP_LINEAR
                    magFilter = LINEAR
                }
            }
            // renderState: responsible for depth buffer, cullface, stencil, blending, etc.
            renderState
            {
                cullFace = true
                cullFaceSide = FRONT
                depthTest = true
            }
        }
    }
}

And this is how to set a Material to a Sprite3D:

Material* material = Material::createWithFilename("Materials/3d_effects.material");
sprite3d->setMaterial(material);

And if you want to change between different Techniques, you have to do:

material->setTechnique("normal");

Techniques

Since you can bind only one Material per Sprite3D, an additional feature is supported that's designed to make it quick and easy to change the way you render the parts at runtime. You can define multiple techniques by giving them different names. Each one can have a completely different rendering technique, and you can even change the technique being applied at runtime by using Material::setTechnique(const std::string& name). When a material is loaded, all the techniques are loaded ahead too. This is a practical way of handling different light combinations or having lower-quality rendering techniques, such as disabling bump mapping, when the object being rendered is far away from the camera.

Passes

A Technique can have one or more passes That is, multi-pass rendering. And each Pass has two main objects:

  • RenderState: contains the GPU state information, like depthTest, cullFace, stencilTest, etc.
  • GLProgramState: contains the shader (GLProgram) that is going to be used, including its uniforms.

Material file format in detail

Material uses a file format optimized to create Material files. This file format is very similar to other existing Material file formats, like GamePlay3D's and OGRE3D's.

Notes:

  • Material file extensions do not matter. Although it is recommended to use .material as extension
  • id is optional for material, technique and pass
  • Materials can inherit values from another material by optionally setting a parent_material_id
  • Vertex and fragment shader file extensions do not matter. The convention in Cocos2d-x is to use .vert and frag
// When the .material file contains one material
sprite3D->setMaterial("Materials/box.material");
// When the .material file contains multiple materials
sprite3D->setMaterial("Materials/circle.material#wood");
material material_id : parent_material_id    
{    
renderState {} [0..1] block
technique id {} [0..*] block
}    
technique technique_id    
{    
renderState {} [0..1] block
pass id {} [0..*] block
}    
pass pass_id    
{    
renderState {} [0..1] block
shader {} [0..1] block
}    
renderState    
{    
blend = false [0..1] bool
blendSrc = BLEND_ENUM [0..1] enum
blendDst = BLEND_ENUM [0..1] enum
cullFace = false [0..1] bool
depthTest = false [0..1] bool
depthWrite = false [0..1] bool
}    
  frontFace = CW | CCW [0..1] enum
  depthTest = false [0..1] bool
  depthWrite = false [0..1] bool
  depthFunc = FUNC_ENUM [0..1] enum
  stencilTest = false [0..1] bool
  stencilWrite = 4294967295 [0..1] uint
  stencilFunc = FUNC_ENUM [0..1] enum
  stencilFuncRef = 0 [0..1] int
  stencilFuncMask = 4294967295 [0..1] uint
  stencilOpSfail = STENCIL_OPERATION_ENUM [0..1] enum
  stencilOpDpfail = STENCIL_OPERATION_ENUM [0..1] enum
  stencilOpDppass = STENCIL_OPERATION_ENUM [0..1] enum
shadershader_id    
{    
vertexShader = res/colored.vert [0..1] file path
fragmentShader = res/colored.frag [0..1] file path
defines = semicolon separated list [0..1] string
     
uniform_name = scalar | vector [0..*] uniform
uniform_name = AUTO_BIND_ENUM [0..*] enum
sampler uniform_name {} [0..*] block
}    
sampler uniform_name    
{    
path = res/wood.png | @wood [0..1] image path
mipmap = bool [0..1] bool
wrapS = REPEAT | CLAMP [0..1] enum
wrapT = REPEAT | CLAMP [0..1] enum
minFilter = TEXTURE_MIN_FILTER_ENUM [0..1] enum
magFilter = TEXTURE_MAG_FILTER_ENUM [0..1] enum
}    

Enums:

TEXTURE_MIN_FILTER_ENUM  
NEAREST Lowest quality non-mipmapped
LINEAR Better quality non-mipmapped
NEAREST_MIPMAP_NEAREST Fast but low quality mipmapping
LINEAR_MIPMAP_NEAREST  
NEAREST_MIPMAP_LINEAR  
LINEAR_MIPMAP_LINEAR Best quality mipmapping
TEXTURE_MAG_FILTER_ENUM  
NEAREST Lowest quality
LINEAR Better quality
BLEND_ENUM  
ZERO ONE_MINUS_DST_ALPHA
ONE CONSTANT_ALPHA
SRC_ALPHA ONE_MINUS_CONSTANT_ALPHA
ONE_MINUS_SRC_ALPHA SRC_ALPHA_SATURATE
DST_ALPHA  
CULL_FACE_SIDE_ENUM
BACK Cull back-facing polygons.
FRONT Cull front-facing polygons.
FRONT_AND_BACK Cull front and back-facing polygons.
FUNC_ENUM
NEVER ALWAYS
LESS GREATER
EQUAL NOTEQUAL
LEQUAL GEQUAL
STENCIL_OPERATION_ENUM
KEEP REPLACE
ZERO INVERT
INCR DECR
INCR_WRAP DECR_WRAP

Types:

  • scalar is float, int or bool.
  • vector is a comma separated list of floats.

Predefined uniforms

The following are predefined uniforms used by Cocos2d-x that can be used in your shaders:

  • CC_PMatrix: A mat4 with the projection matrix
  • CC_MVMatrix: A mat4 with the Model View matrix
  • CC_MVPMatrix: A mat4 with the Model View Projection matrix
  • CC_NormalMatrix: A mat4 with Normal Matrix
  • CC_Time: a vec4 with the elapsed time since the game was started
    • CC_Time[0] = time / 10;
    • CC_Time[1] = time;
    • CC_Time[2] = time * 2;
    • CC_Time[3] = time * 4;
  • CC_SinTime: a vec4 with the elapsed time since the game was started:
    • CC_SinTime[0] = time / 8;
    • CC_SinTime[1] = time / 4;
    • CC_SinTime[2] = time / 2;
    • CC_SinTime[3] = sinf(time);
  • CC_CosTime: a vec4 with the elapsed time since the game was started:
    • CC_CosTime[0] = time / 8;
    • CC_CosTime[1] = time / 4;
    • CC_CosTime[2] = time / 2;
    • CC_CosTime[3] = cosf(time);
  • CC_Random01: A vec4 with four random numbers between 0.0f and 1.0f
  • CC_Texture0: A sampler2D
  • CC_Texture1: A sampler2D
  • CC_Texture2: A sampler2D
  • CC_Texture3: A sampler2D

results matching ""

    No results matching ""