Version: 2018.1
Tutoriales de Gráficas
Shaders: programas vertex y fragment

Shaders: ShaderLab y shaders con función fija

Este tutorial le enseña a usted los primeros pasos de crear sus propios shaders, para ayudarle a controlar el aspecto de su juego y optimizar el rendimiento de los gráficos.

Unity is equipped with a powerful shading and material language called ShaderLab. In style it is similar to CgFX and Direct3D Effects (.FX) languages - it describes everything needed to display a Material.

Shaders describe properties that are exposed in Unity’s Material Inspector and multiple shader implementations (SubShaders) targeted at different graphics hardware capabilities, each describing complete graphics hardware rendering state, and vertex/fragment programs to use. Shader programs are written in the high-level Cg/HLSL programming language.

In this tutorial we’ll describe how to write very simple shaders using so-called “fixed function” notation. In the next chapter we’ll introduce vertex and fragment shader programs. We assume that the reader has a basic understanding of OpenGL or Direct3D render states, and has some knowledge of HLSL, Cg, GLSL or Metal shader programming languages.

Empezando

Para crear un nuevo shader, elija Assets > Create > Shader > Unlit Shader del menú principal, o duplique un shader existente y trabaje desde ese. El nuevo shader se puede editar haciendo doble clic en él en el Project View.

Unity has a way of writing very simple shaders in so-called “fixed-function” notation. We’ll start with this for simplicity. Internally the fixed function shaders are converted to regular vertex and fragment programs at shader import time.

Comenzaremos con un shader muy básico:

Shader "Tutorial/Basic" {
    Properties {
        _Color ("Main Color", Color) = (1,0.5,0.5,1)
    }
    SubShader {
        Pass {
            Material {
                Diffuse [_Color]
            }
            Lighting On
        }
    }
}

This simple shader demonstrates one of the most basic shaders possible. It defines a color property called Main Color and assigns it a default pink color (red=100% green=50% blue=50% alpha=100%). It then renders the object by invoking a Pass and in that pass setting the diffuse material component to the property _Color and turning on per-vertex lighting.

Para probar este shader, cree un nuevo material, seleccione el shader desde el menú desplegable (Tutorial->Basic) y asigne el Material a algún objeto. Ajuste el color en el Inspector del Material y mire los cambios. Tiempo de moverse a cosas más complicadas!

Iluminación de vértice básica

Si abre un shader complejo existente, puede ser un poco difícil obtener una buena visión general. Para empezar, diseccionaremos el shader VertexLit integrado que se suministra con Unity. Este shader utiliza una pipeline de función fija para hacer la iluminación estándar por vértice.

Shader "VertexLit" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }

    SubShader {
        Pass {
            Material {
                Diffuse [_Color]
                Ambient [_Color]
                Shininess [_Shininess]
                Specular [_SpecColor]
                Emission [_Emission]
            }
            Lighting On
            SeparateSpecular On
            SetTexture [_MainTex] {
                constantColor [_Color]
                Combine texture * primary DOUBLE, texture * constant
            }
        }
    }
}

Todos los shaders comienzan con la palabra clave Shader seguido de un string que representa el nombre del shader. Este es el nombre que se muestra en el Inspector. Todo el código para este shader debe colocarse dentro de los corchetes: { } (llamado bloque).

  • El nombre debe ser corto y descriptivo. No tiene que coincidir con el nombre del archivo .Shader.
  • Para poner shaders en submenús en Unity, utilice barras inclinadas -e.g. MyShaders/Test se mostraría como Test en un submenú llamado MyShaders o MyShaders->Test.

El shader está compuesto de un bloque de Properties seguido por bloques SubShader. Cada uno de estos se describe en una sección de abajo.

Propiedades

Al principio del bloque de shader puede definir las propiedades que los artistas pueden editar en el Inspector del Material. En el ejemplo de VertexLit las propiedades se ven así:

Las propiedades se enumeran en líneas separadas dentro del bloque de Propiedades. Cada propiedad comienza con el nombre interno (Color, MainTex). Después de esto entre paréntesis viene el nombre mostrado en el inspector y el tipo de la propiedad. Después de eso, el valor predeterminado de esta propiedad aparece listada como:

La lista de los tipos posibles se encuentra en la Referencia de Propiedades. El valor predeterminado depende del tipo de propiedad. En el ejemplo de un color, el valor por defecto debe ser un vector de cuatro componentes.

Ahora tenemos nuestras propiedades definidas, y estamos listos para comenzar a escribir el shader real.

El cuerpo del shader

Antes de seguir, definamos la estructura básica de un archivo shader.

Diferente hardware gráfico tiene diferentes capacidades. Por ejemplo, algunas tarjetas gráficas admiten fragment programs y otros no; Algunos pueden configurar cuatro texturas por pass, mientras que los otros pueden hacer sólo dos o una; Etc. Para permitirle hacer uso completo de cualquier hardware que tenga su usuario, un shader puede contener varios SubShaders. Cuando Unity procesa un shader, pasará por todos los subshaders y utilizará el primero que soporte el hardware.

Shader "Structure Example" {
    Properties { /* ...shader properties... }
    SubShader {
        // ...subshader that requires DX11 / GLES3.1 hardware...
    }
    SubShader {
        // ...subshader that might look worse but runs on anything :)
    }
}

Este sistema le permite a Unity soportar todo el hardware existente y maximiza la calidad de cada uno. Sin embargo, resulta en algunos shaders largos.

Dentro de cada bloque SubShader se configura el estado de rendering compartido por todos los passes; Y definen los passes de rendering ellos mismos. Se puede encontrar una lista completa de los comandos disponibles en la referencia SubShader.

Passes

ada subshader es una colección de passes. Para cada pass, la geometría del objeto es renderizada, por lo que debe haber al menos un pass. Nuestro shader VertexLit tiene sólo un pass:

// ...snip...
Pass {
    Material {
        Diffuse [_Color]
        Ambient [_Color]
        Shininess [_Shininess]
        Specular [_SpecColor]
        Emission [_Emission]
    }
    Lighting On
    SeparateSpecular On
    SetTexture [_MainTex] {
        constantColor [_Color]
        Combine texture * primary DOUBLE, texture * constant
    }
}
// ...snip...

Cualquier comando definido en un pass configura el hardware de gráficos para renderizar la geometría de una manera específica.

En el ejemplo anterior tenemos un bloque Material que une nuestros valores de propiedad a los ajustes de material de la iluminación de función fija. El comando Lighting On enciende la iluminación estándar del vértice, y SeparateSpecular On permite el uso de un color separado para el resaltado especular.

Todos estos comandos hasta ahora asignan muy directamente a la función fija del modelo de hardware OpenGL/Direct3D. Consulte OpenGL red book para obtener más información al respecto.

The next command, SetTexture, is very important. These commands define the textures we want to use and how to mix, combine and apply them in our rendering. SetTexture command is followed by the property name of the texture we would like to use (_MainTex here) This is followed by a combiner block that defines how the texture is applied. The commands in the combiner block are executed for each pixel that is rendered on screen.

Within this block we set a constant color value, namely the Color of the Material, _Color. We’ll use this constant color below.

En el siguiente comando especificamos cómo mezclar la textura con los valores de color. Hacemos esto con el comando Combine que especifica cómo mezclar la textura con otra o con un color. Generalmente se ve así: Combine ColorPart, AlphaPart

Aquí ColorPart y AlphaPart definen la mezcla de componentes de color (RGB) y alfa (A), respectivamente. Si se omite AlphaPart, utilice la misma mezcla que ColorPart.

En nuestro ejemplo VertexLit: Combine texture * primary DOUBLE, texture * constant

Here texture is the color coming from the current texture (here _MainTex). It is multiplied (*) with the primary vertex color. Primary color is the vertex lighting color, calculated from the Material values above. Finally, the result is multiplied by two to increase lighting intensity (DOUBLE). The alpha value (after the comma) is texture multiplied by constant value (set with constantColor above). Another often used combiner mode is called previous (not used in this shader). This is the result of any previous SetTexture step, and can be used to combine several textures and/or colors with each other.

Resumen

Nuestro shader VertexLit configura la iluminación de vértices estándar y configura los combinadores de textura para que la intensidad de iluminación renderizada se duplique.

Podríamos poner más passes en el shader, se obtendrían uno tras otro. Por el momento, sin embargo, eso no es necesario ya que tenemos el efecto deseado. Solo necesitamos un SubShader ya que no hacemos uso de ninguna característica avanzada - este shader en particular funcionará en cualquier tarjeta gráfica que soporte Unity.

El shader VertexLit es uno de los shaders más básicos que podemos imaginar. No usamos ninguna operación específica de hardware, ni utilizamos ninguno de los comandos más especiales y buenos que ShaderLab y Cg/HLSL tienen para ofrecer.

En el capítulo siguiente procederemos explicando cómo escribir programas personalizados de vertex & fragment usando el lenguaje Cg/HLSL.

Tutoriales de Gráficas
Shaders: programas vertex y fragment