Está en la página 1de 132

Tabla de contenido

Unity 5.x Shaders y Efectos Cookbook


Copyright © 2016 Packt Publishing
Todos los derechos reservados. Ninguna parte de este libro puede ser reproducida,
almacenada en un sistema de recuperación, O transmitida en cualquier forma o por cualquier
medio, sin el permiso previo por escrito del Editor, excepto en el caso de citas breves incluidas
en artículos críticos o revisiones. Se ha hecho todo lo posible en la preparación de este libro
para asegurar la Información presentada. Sin embargo, la información contenida en este libro
se vende sin Garantía, ya sea expresa o implícita. Ni los autores, ni Packt Publishing, y sus
Distribuidores y distribuidores se hará responsable de los daños causados o presuntamente
causados Directa o indirectamente por este libro. Packt Publishing se ha esforzado por
proporcionar información de marcas sobre Empresas y productos mencionados en este libro
por el uso apropiado de capitales. Sin embargo, Packt Publishing no puede garantizar la
exactitud de esta información.
Primera publicación: febrero de 2016
Referencia de producción: 1220216
Publicado por Packt Publishing Ltd.
Lugar de entrega
35 Livery Street
Birmingham B3 2PB, Reino Unido.
ISBN 978-1-78528-524-0
Www.packtpub.com

Créditos

Authors
Alan Zucconi
Kenneth Lammers
Reviewer
Kenneth Lammers
Commissioning Editor
Priya Singh
Acquisition Editors
Rahul Nair
Erol Staveley
Content Development Editor
Mehvash Fatima
Technical Editors
Pranil Pathare
Danish Shaikh
Copy Editor
Tasneem Fatehi
Project Coordinator
Kinjal Bari
Proofreader
Safis Editing
Indexer
Monica Ajmera Mehta
Graphics
Kirk D’Penha
Disha Haria
Production Coordinator
Nilesh Mohite
Cover Work
Nilesh Mohite

Sobre los autores


Sobre los autores
Alan Zucconi es un desarrollador apasionado, autor y orador motivacional, reconocido como
Uno de los "30 bajo 30" de Develop. Su experiencia se ha construido en los últimos 10 años,
mientras que
Dedicó su tiempo al mundo académico ya la industria del juego. Empezó su carrera
independiente
Carrera para explorar plenamente su creatividad, derribando la pared entre el arte y el juego.
Anterior
A eso, trabajó en el Imperial College de Londres, donde descubrió su pasión por
Enseñanza y escritura. Sus títulos incluyen el rompecabezas de gravedad, 0RBITALIS, y el
próximo
Tiempo de juego de plataformas, Still Time.
Kenneth Lammers tiene más de 15 años de experiencia en la industria del juego, trabajando
Artista de carácter, artista técnico, director de arte técnico y programador. A través de su
Ha trabajado en títulos como Call of Duty 3, Crackdown 2, Alan Wake y
Kinect Star Wars. Actualmente posee y opera Ozone Interactive junto con su
Socio de negocios, Noah Kaarbo. Juntos, han trabajado con clientes como Amazon,
Eline Media, IGT y Microsoft.
Kenny ha trabajado para Microsoft Games Studios, Activision y Surreal, y ha
Salió por su cuenta, operando CreativeTD y Ozone Interactive.
Kenny fue el autor de la primera versión de Unity Shaders and Effects Cookbook de Packt
Editorial, y estaba muy contento de ser parte de la redacción, actualización y revisión de este
libro.
Www.PacktPub.com

Libros electrónicos, ofertas de descuento y más


Libros electrónicos, ofertas de descuento y más
¿Sabía usted que Packt ofrece versiones eBook de cada libro publicado, con PDF y
Archivos ePub disponibles? Puede actualizar a la versión de eBook en www.PacktPub.com y
como
Un cliente del libro de la impresión, usted tiene derecho a un descuento en la copia del eBook.
Estar en contacto con
Nosotros en <customercare@packtpub.com> para más detalles.
En www.PacktPub.com, también puede leer una colección de artículos técnicos gratuitos,
regístrese
Para una gama de boletines de noticias libres y recibir descuentos y ofertas exclusivas en los
libros de paquete Y libros electrónicos.
Https://www2.packtpub.com/books/subscription/packtlib
¿Necesita soluciones instantáneas para sus preguntas de TI? PacktLib es el formato digital en
línea de Packt
Biblioteca de libros Aquí, puede buscar, acceder y leer toda la librería de libros de Packt.

¿Por qué suscribirse?


Completamente buscable en todos los libros publicados por Packt
Copiar y pegar, imprimir y marcar contenido
A petición y accesible a través de un navegador web

Prefacio
Unity 5.x Shaders and Effects Cookbook es su guía para familiarizarse con los
Creación de shaders y efectos posteriores en Unity 5. Iniciará su viaje en el
Empezando, creando los shaders más básicos y aprendiendo cómo se estructura el código de
shader.
Este conocimiento fundamental le armará con los medios para progresar más a través de
Cada capítulo, aprendiendo técnicas avanzadas como explosiones volumétricas y sombreado
de pieles.
Esta edición del libro está escrita específicamente para Unity 5 y le ayudará a dominar
Renderizado físicamente e iluminación global para acercarse al fotorrealismo como
posible.
Al final de cada capítulo, habrá ganado nuevos conjuntos de habilidades que aumentarán la
Calidad de sus shaders e incluso hacer su proceso de la escritura del sombreador más eficiente.
Estas
Capítulos se han adaptado para que pueda saltar en cada sección y aprender un
Habilidad de principiante a experto. Para aquellos que son nuevos para escribir sombreado en
Unity, puede
Avance en cada capítulo, uno a la vez, para construir sobre su conocimiento. De cualquier
manera, usted
Aprenderá las técnicas que hacen que los juegos modernos se vean como lo hacen.
Una vez que haya completado este libro, tendrá un conjunto de sombreadores que puede
utilizar en
Sus juegos de Unity 3D, así como la comprensión de añadir a ellos, lograr nuevos efectos,
Y atender las necesidades de rendimiento. ¡Entonces empecemos!

Lo que este libro cubre


Capítulo 1, Creando su primer Shader, le presenta al mundo de la codificación de sombreado
en Unidad 4 y 5.

El Capítulo 2, Shaders de Superficie y Mapeo de Textura, cubre las más comunes y útiles
Técnicas que puede implementar con Shaders de superficie, incluyendo cómo usar texturas
Y mapas normales para sus modelos.

El capítulo 3, Descripción de los modelos de iluminación, le ofrece una explicación


Los shaders modelan el comportamiento de la luz. El capítulo le enseña cómo crear
Modelos de iluminación utilizados para simular efectos especiales tales como toon shading.
El Capítulo 4, Rendimiento basado físicamente en Unity 5, muestra que las Rendering es la
tecnología estándar utilizada por Unity 5 para llevar el realismo a tus juegos. Esta
Capítulo explica cómo sacar el máximo provecho de ello, dominar las transparencias,
Superficies e iluminación global.

Capítulo 5, Vertex Functions, le enseña cómo los shaders se pueden utilizar para alterar la
geometría de un objeto; Este capítulo introduce modificadores de vértices y los utiliza para
traer volumetría Explosiones, sombreadores de nieve, y otros efectos a la vida.
El capítulo 6, Fragment Shaders y Grab Passes, explica cómo usar los pases grab para hacer
Materiales que emulan las deformaciones generadas por estos materiales semitransparentes.

Capítulo 7, Ajuste de sombreado móvil, le ayuda a optimizar sus sombreadores para sacar el
máximo provecho De cualquier dispositivo.

Capítulo 8, Efectos de pantalla con Unity Render Textures, muestra cómo crear
Efectos y efectos visuales que de otro modo serían imposibles de lograr.

Capítulo 9, Efectos de juego y de pantalla, le explica cómo se pueden


Utilizado para complementar su juego, simulando, por ejemplo, un efecto de visión nocturna.

Capítulo 10, Técnicas avanzadas de sombreado, introduce las técnicas más avanzadas en
Este libro, como sombreado de pieles y renderizado de mapa de calor.

Lo que necesitas para este libro


La siguiente es una lista del software necesario y opcional para completar las recetas en este
libro:
 Unidad 5
 Una aplicación 3D como Maya, Max o Blender (opcional)
 Una aplicación de edición de imágenes 2D como Photoshop o Gimp (opcional)

Para quién es este libro


Este libro está escrito para desarrolladores que desean crear sus primeros shaders en Unity 5 o
Desean llevar su juego a un nivel completamente nuevo agregando post-procesamiento
profesional
Efectos. Se requiere una sólida comprensión de unity.

Secciones
En este libro, encontrará varios títulos que aparecen con frecuencia (Preparándose, Cómo
hacerlo, Cómo funciona, Hay más y Vea también).
Para dar instrucciones claras sobre cómo completar una receta, usamos estas secciones de la
siguiente manera:

Preparándose
Esta sección le dice qué esperar en la receta y describe cómo configurar
Software o cualquier configuración preliminar requerida para la receta.

Cómo hacerlo…
Esta sección contiene los pasos necesarios para seguir la receta.

Cómo funciona…
Esta sección generalmente consiste en una explicación detallada de lo ocurrido en el
sección.

Hay más…
Esta sección consiste en información adicional sobre la receta para que el lector tenga más
conocimiento sobre la receta.

Ver también
Esta sección proporciona enlaces útiles a otra información útil para la receta.

Convenciones
En este libro, encontrará una serie de estilos de texto que distinguen entre diferentes tipos de
información. Aquí hay algunos ejemplos de estos estilos y una explicación de su significado.
Codifique palabras en texto, nombres de tablas de bases de datos, nombres de carpetas,
nombres de archivos, extensiones de archivos,
Los nombres de ruta, las URL falsas, la entrada de usuario y las descripciones de Twitter se
muestran de la siguiente manera: "Enter El siguiente código en el bloque de propiedades de su
shader. "

Un bloque de código se establece de la siguiente manera:

void surf (Input IN, inout SurfaceOutput o)


{
float4 c;
c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}

Cuando queremos llamar su atención sobre una parte particular de un bloque de código, las
líneas o elementos relevantes se definen en negrita:

void surf (Input IN, inout SurfaceOutputStandard o) {


fixed4 c = pow((_Color + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}

Los nuevos términos y palabras importantes se muestran en negrita. Palabras que usted ve en
la pantalla, Por ejemplo, en menús o cuadros de diálogo, aparecen en el texto como este: "En
la pestaña Proyecto de Su editor Unity, haga clic con el botón derecho en la carpeta Activos y
seleccione Crear | Carpeta."

Nota
Las advertencias o notas importantes aparecen en un cuadro como este.

Consejo
Consejos y trucos aparecen así.

Comentarios del lector


Comentarios del lector
Los comentarios de nuestros lectores son siempre bienvenidos. Háganos saber lo que piensa
acerca de esto Libro-lo que te gustó o disgustó. La retroalimentación del lector es importante
para nosotros, ya que nos ayuda Desarrollar títulos de los que realmente sacarás el máximo
provecho.
Para enviarnos comentarios generales, simplemente envíe un mensaje de correo electrónico a
<feedback@packtpub.com> y mencione el Título del libro en el tema de su mensaje.
Si hay un tema en el que usted tiene experiencia y está interesado en escribir o Contribuyendo
a un libro, vea nuestra guía del autor en www.packtpub.com/authors.

Atención al cliente
Ahora que usted es el dueño orgulloso de un libro de Packt, tenemos un número de cosas a
ayudar Usted para sacar el máximo provecho de su compra.

Descargando el código de ejemplo


Puede descargar los archivos de código de ejemplo de este libro desde su cuenta en
Http://www.packtpub.com. Si ha comprado este libro en otro lugar, puede visitar
Http://www.packtpub.com/support y registrarse para que los archivos se envíen por correo
electrónico directamente a usted.
Puede descargar los archivos de código siguiendo estos pasos:
1. Inicie sesión o regístrese en nuestro sitio web utilizando su dirección de correo
electrónico y contraseña.
2. Coloque el puntero del mouse en la pestaña SUPPORT en la parte superior.
3. Haga clic en Código Descargas & Erratas.
4. Introduzca el nombre del libro en el cuadro Buscar.
5. Seleccione el libro para el que desea descargar los archivos de código.
6. Elija en el menú desplegable donde adquirió este libro.
7. Haga clic en Descargar código.
Una vez que el archivo se descarga, asegúrese de descomprimir o extraer la carpeta usando
La última versión de:
 WinRAR / 7-Zip para Windows
 Zipeg / iZip / UnRarX para Mac
 7-Zip / PeaZip para Linux

Descarga de las imágenes en color de este libro


También le proporcionamos un archivo PDF que tiene imágenes en color de las capturas de
pantalla / diagramas
Utilizado en este libro. Las imágenes en color le ayudarán a comprender mejor los cambios
salida. Puede descargar este archivo desde
Https://www.packtpub.com/sites/default/files/downloads/Unity5xShadersAndEffectsCookb

Errata
Aunque hemos tomado todas las precauciones para garantizar la exactitud de nuestro
contenido, los errores se producen. Si encuentras un error en uno de nuestros libros -tal vez un
error en el texto o el código- estaríamos agradecidos si pudieras informarnos esto. Al hacerlo,
puede salvar a otros lectores de la frustración y ayudarnos a mejorar las versiones posteriores
de este libro. Si encuentra alguna errata, infórmenos visitando
http://www.packtpub.com/submit-errata, seleccionando su libro, haciendo clic en el enlace
Formulario de Envío de Erratas e ingresando los detalles de sus erratas. Una vez verificadas sus
erratas, se aceptará su envío y las erratas se cargarán en nuestro sitio web o se agregarán a
cualquier lista de erratas existentes en la sección Erratas de ese título.
Para ver las erratas presentadas anteriormente, vaya a
https://www.packtpub.com/books/content/support e ingrese el nombre del libro en el campo
de búsqueda. La información requerida aparecerá en la sección Errata.

Piratería
La piratería de material protegido por derechos de autor en Internet es un problema continuo
en todos los medios de comunicación. En Packt, tomamos muy en serio la protección de
nuestros derechos de autor y licencias. Si encuentra alguna copia ilegal de nuestras obras en
cualquier forma en Internet, sírvase proporcionarnos la dirección de la ubicación o el nombre
del sitio web inmediatamente para que podamos buscar un remedio. Póngase en contacto con
nosotros en <copyright@packtpub.com> con un enlace al material pirateado sospechoso.
Agradecemos su ayuda en la protección de nuestros autores y nuestra capacidad para
ofrecerle contenido valioso.

Preguntas
Si tiene algún problema con cualquier aspecto de este libro, puede ponerse en contacto con
nosotros en <Questions@packtpub.com>, y haremos todo lo posible para resolver el
problema.

1. Creando su primer Shader


Este capítulo cubrirá algunas de las técnicas difusas más comunes que se encuentran en las
Juego de desarrollo de sombreado de tuberías. En este capítulo, aprenderá sobre lo siguiente
recetas:
 Creación de un Shader estándar básico
 Migración de Shaders heredados de Unity 4 a Unity 5
 Adición de propiedades a un sombreado
 Uso de propiedades en un Shader de Superficie

Introducción
Imaginemos un cubo que ha sido pintado de blanco de manera uniforme. Incluso si el color
utilizado es el mismo en cada cara, todos tendrán diferentes tonos de blanco dependiendo de
la dirección de la que proviene la luz y el ángulo que estamos mirando. Este nivel adicional de
realismo se logra en gráficos 3D por shaders, programas especiales que se utilizan
principalmente para simular cómo funciona la luz. Un cubo de madera y uno de metal pueden
compartir el mismo modelo 3D, pero lo que los hace parecer diferentes es el sombreado que
utilizan. Receta tras receta, este primer capítulo le presentará la codificación de shader en
Unity. Si tiene poca o ninguna experiencia previa con shaders, este capítulo es lo que necesita
para entender qué son los shaders, cómo funcionan y cómo personalizarlos.
Al final de este capítulo, habrás aprendido a construir shaders básicos que realizan
operaciones básicas. Armado con este conocimiento, usted será capaz de crear casi cualquier
superficie Shader.
Creación de un Shader estándar básico
Cada desarrollador de juegos de Unity debe estar familiarizado con el concepto de
componentes. Todos los objetos que forman parte de un juego contienen una serie de
componentes que afectan su apariencia y comportamiento. Mientras que los scripts
determinan cómo deben comportarse los objetos, los renderers deciden cómo deben aparecer
en la pantalla. Unity viene con varios procesadores, dependiendo del tipo de objeto que
estamos tratando de visualizar; Cada modelo 3D normalmente tiene MeshRenderer. Un objeto
debe tener sólo un renderizador, pero el propio procesador puede contener varios materiales.
Cada material es una envoltura para un solo shader, el anillo final en la cadena alimenticia de
3D gráficos. Las relaciones entre estos componentes se pueden ver en los siguientes diagrama:

Comprender la diferencia entre estos componentes es esencial para entender cómo


Trabaja los Shaders.

Preparándose
Para comenzar con esta prescripción, necesitará tener Unity 5 en funcionamiento y debe tener
Creado un nuevo proyecto. También habrá un proyecto de Unity incluido con este cookbook,
por lo que Usted puede utilizar ese también y apenas agregue sus propios shaders de encargo
a él como usted step A través de cada receta. ¡Con esto completado, ya está listo para entrar
en el maravilloso Mundo del shaders en tiempo real!

Cómo hacerlo…
Antes de entrar en nuestro primer shader, vamos a crear una pequeña escena para trabajar
con nosotros. Esto puede Se puede hacer navegando a GameObject | Crear vacío en el editor
Unity. De aquí, Usted puede crear un plano para actuar como un terreno, un par de esferas a
las que aplicaremos nuestro Shader, y una luz direccional para dar a la escena alguna luz. Con
nuestra escena generada, Puede pasar a los pasos de escritura de shaders:

1. En la pestaña Proyecto de su editor Unity, haga clic con el botón derecho en la carpeta
Crear Carpeta.
Nota
Si está utilizando el proyecto Unity que viene con el libro de cocina, puede saltar al
paso 4.
2. Cambie el nombre de la carpeta que creó a Shaders haciendo clic con el botón derecho en
Cambie el nombre de la lista desplegable o seleccione la carpeta y pulse F2 en la teclado.
3. Cree otra carpeta y cambie el nombre a Materiales.
4. Haga clic con el botón derecho del ratón en la carpeta Shaders y seleccione Crear | Shader.
Luego haga clic con el botón derecho en el Materiales y seleccione Crear | Material.
5. Cambie el nombre del sombreador y el material a StandardDiffuse.
6. Inicie el shader StandardDiffuse en MonoDevelop (el editor de secuencias de comandos
predeterminado para Unity) haciendo doble clic en él. Esto iniciará automáticamente el
editor para usted y Mostrar el código de sombreado.
Nota
Verá que Unity ya ha poblado a nuestro shader con un código básico. Esta, Por defecto,
obtendrá un shader difuso básico que acepta una textura. Estaremos Modificando este
código base para que pueda aprender a comenzar rápidamente a desarrollar su Propios
shaders de encargo.
7. Ahora vamos a dar a nuestro sombreador una carpeta personalizada de la que está
seleccionada. La primera línea De código en el shader es la descripción personalizada que
tenemos que dar al shader para que Unity puede hacer que esté disponible en la lista
desplegable de sombreado al asignar a los materiales.
Hemos cambiado el nombre de nuestro camino a Shader "CookbookShaders /
StandardDiffuse", pero Puedes nombrarlo a lo que quieras y renombrarlo en cualquier
momento. Así que no te preocupes Sobre cualquier dependencia en este punto. Guardar el
sombreador en MonoDevelop y volver a El editor Unity. Unity compilará automáticamente
el sombreador cuando reconozca que El archivo se ha actualizado. Esto es lo que su
sombreador debe verse en este punto:

Shader "CookbookShaders/StandardDiffuse" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on
all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
8. Técnicamente hablando, este es un Surface Shader basado en física Que la unity 5 ha
adoptado como su nuevo estándar. Como sugiere su nombre, este Tipo de sombreador
logra realismo simulando cómo la luz se comporta físicamente cuando Golpear objetos. Si
está utilizando una versión anterior de Unity (como Unity 4), su Código se verá muy
diferente. Antes de la introducción de shaders físicamente basados, Unity 4 utilizó técnicas
menos sofisticadas. Todos estos tipos diferentes de shader serán Explorado en los
próximos capítulos de este libro.
9. Después de crear su shader, necesitamos conectarlo a un material. Seleccione el material
Llamada StandardDiffuse que creamos en el paso 4 y mira la pestaña Inspector. En la lista
desplegable Shader, seleccione CookbookShaders | StandardDiffuse. (Su ruta de
sombreado puede ser diferente si decide utilizar un nombre de ruta de acceso diferente).
Asignará su sombreador a su material y lo preparará para asignar a un objeto.
Nota
Para asignar un material a un objeto, simplemente puede hacer clic y arrastrar su material
desde La pestaña Proyecto al objeto de la escena. También puede arrastrar un material
Inspector de un objeto en el editor de Unity para asignar un material. La captura de
pantalla de un ejemplo es la siguiente:

No hay mucho que ver en este punto, pero nuestro entorno de desarrollo de sombreado está
configurado y ahora podemos comenzar a modificar el sombreador para satisfacer nuestras
necesidades.

Cómo funciona…
Unity ha hecho la tarea de conseguir que su entorno de sombreado funcione, lo cual es muy
fácil para usted. Es simplemente una cuestión de unos pocos clics y usted es bueno para ir. Hay
muchos elementos que trabajan en el fondo con respecto al Shader superficial sí mismo. Unity
ha tomado el lenguaje de shader Cg y lo ha hecho más eficiente para escribir al hacer un
montón de la pesada Cg código de elevación para usted. El lenguaje Surface Shader es una
forma más basada en componentes de escribir sombreadores. Tareas como el procesamiento
de sus propias coordenadas de textura y matrices de transformación ya se han hecho para
usted, por lo que no tiene que empezar de cero más. En el pasado, tendríamos que empezar
un nuevo shader y reescribir una gran cantidad de código una y otra vez. A medida que gane
más experiencia con Surface Shaders, naturalmente querrá explorar más funciones
subyacentes del lenguaje Cg y cómo Unity está procesando todas las tareas de la unidad de
procesamiento gráfico (GPU) de bajo nivel.
Nota
Todos los archivos de un proyecto de Unity se hacen referencia independientemente de la
carpeta en la que se encuentran. Podemos mover shaders y materiales desde dentro del editor
sin el riesgo de breakingany conexión. Los archivos, sin embargo, nunca deben moverse desde
fuera del editor, ya que Unity no podrá actualizar sus referencias.
¡Así que, simplemente cambiando el nombre de la ruta de acceso del shader a un nombre de
nuestra elección, tenemos nuestro shader difuso básico trabajando en el entorno de Unity, con
luces y sombras y todo eso con sólo cambiar una línea de código!

Ver también
El código fuente de los shaders incorporados suele estar oculto en Unity 5. No se puede abrir
Esto del editor como lo haces con tus propios shaders.
Para obtener más información sobre dónde encontrar una gran parte de las funciones
integradas de Cg para Unity, vaya al directorio de instalación de Unity y vaya a Unity45 \
Editor \ Data \ CGIncludes. En esta carpeta, puede encontrar el código fuente de la Shaders
enviados con Unity. Con el tiempo, han cambiado mucho; UNITY DESCARGAR
ARCHIVE (https://unity3d.com/get-unity/download/archive) es el lugar a donde ir si
Necesidad de acceder a los códigos fuente de un shader utilizado en una versión diferente de
Unity. Después Seleccionando la versión correcta, seleccione Construido en shaders en la lista
desplegable, como se muestra en La siguiente imagen. Hay tres archivos que son de la nota en
este punto-UnityCG.cginc, Lighting.cginc y UnityShaderVariables.cginc. Nuestro shader actual
está haciendo uso de Todos estos archivos en este momento:

El Capítulo 10, Técnicas avanzadas de sombreado, explorará en profundidad cómo usar


GcInclude para un enfoque modular de la codificación de sombreado.

Migración de Shaders heredados de Unity 4 a Unity 5


Es innegable que los gráficos en videojuegos han cambiado masivamente durante los últimos
10 años. Cada nuevo juego viene con técnicas de vanguardia que nos están acercando al logro
fotorrealismo en tiempo real. No debemos sorprendernos por el hecho de que los shaders
mismos han cambiado masivamente durante la vida de unity. Esta es una de las principales
fuentes de confusión al acercarse a los shaders por primera vez. Antes de la unity 5, se
adoptaron principalmente dos shaders diferentes: Difuso y Especular. Como sugieren los
nombres, se usaron para materiales mate y brillante, respectivamente. Si ya está utilizando
Unity 5, puede omitir esta receta. Esta receta explicará cómo replicar estos efectos usando
Unity 5.
Preparándose
El punto de partida de esta receta es tener un espacio de trabajo hecho en Unity 4, que utiliza
De los shaders incorporados que fueron proporcionados originalmente. Si va a comenzar un
nuevo juego, No hay duda de que debe utilizar la última versión de Unity. Sin embargo, si su
proyecto es Ya en las últimas etapas de desarrollo con una versión anterior, deberías estar muy
Cuidado antes de migrar. Muchas cosas han cambiado detrás de las cortinas del motor, y
Incluso si los shaders integrados probablemente funcionarán sin ningún problema, sus scripts
tal vez no. Si va a migrar todo su espacio de trabajo, lo primero que debe hacer es
Tomar respaldo Es importante recordar que salvar activos y escenas no es suficiente
La mayor parte de la configuración de Unity se almacena en sus archivos de metadatos. La
opción más segura para Sobrevivir a una migración es duplicar toda la carpeta que contiene su
proyecto. la mejor manera de hacerlo es copiando físicamente la carpeta desde el Explorador
de archivos (Windows) o Finder (Mac).

Cómo hacerlo…
Hay dos opciones principales si desea migrar los shaders integrados: actualizar su
Proyecto automáticamente o cambiar a Shaders estándar en su lugar.

Actualización automática
Esta opción es la más fácil. Unity 5 puede importar un proyecto realizado con una versión
anterior Y actualizarlo. Usted debe notar que una vez que la conversión se hace, usted no será
capaz de Utilice Unity 4; Incluso si ninguno de sus activos puede haber cambiado
directamente, los metadatos de Unity han Han sido convertidos. Para continuar con esto, abra
Unity 5 y haga clic en OPEN OTHER para seleccionar La carpeta de su antiguo proyecto. Se le
preguntará si desea convertirlo; haga clic en Actualizar para continuar. Unity reimportará
todos sus activos y recompilará todos sus guiones. El proceso puede durar varias horas si su
proyecto es grande. Una vez realizada la conversión Se hace, los shaders integrados de Unity 4
deberían haber sido reemplazados con su legado equivalente. Usted puede comprobar esto
del inspector de sus materiales que deben tener Cambiado (por ejemplo) de Bumped Diffuse a
Legacy Shader / Bumped Diffuse.
Nota
Incluso si Diffuse, Specular y los otros shaders integrados de Unity 4 están ahora obsoletos,
Unity 5 los mantiene compatibles con versiones anteriores. Se pueden encontrar en el menú
desplegable Menú de un material bajo la carpeta Legacy Shaders.
Uso de shader estándar En lugar de usar el Legacy Shaders, usted podría decidir reemplazarlos
por los nuevos Shaders estándar de Unity 5. Antes de hacer esto, debe tener en cuenta que
como ellos Se basan en un modelo de iluminación diferente, sus materiales muy
probablemente se verán diferentes.
Unity 4 viene con más de ochenta diferentes shaders integrados divididos en seis diferentes
Familias (Normal, Transparente, Recorte Transparente, Auto-Iluminado y Reflexivo). En
Unity 5, todos ellos son reemplazados por el Shader estándar introducido en la receta anterior.
Desafortunadamente, no hay ninguna receta mágica para convertir tus shaders directamente.
Sin embargo, puede Utilice la siguiente tabla como punto de partida para entender cómo
puede usarse el Shader estándar
Configurado para simular Unity 4 Legacy Shaders:
Puede cambiar el sombreado utilizado por su material antiguo mediante el menú desplegable
Shader En Inspector. Todo lo que necesitas hacer es simplemente seleccionar el Shader
estándar adecuado. Si tu El shader utiliza texturas, colores y mapas normales, se utilizarán
automáticamente en el Nuevo Shader estándar. Es posible que tenga que configurar los
parámetros del estándar Shader para obtener lo más cerca posible de su modelo de
iluminación original. La siguiente foto Muestra el omnipresente conejo de Stanford prestado
con un Shader Diffuse Legacy (derecha), Shader estándar convertido (izquierda) y Shader
estándar con suavidad ajustado a cero
(medio):

Migración de sombreadores personalizados


Si ha escrito shaders personalizados en Unity 4, es probable que esto funcione
Inmediatamente en la Unidad 5. A pesar de esto, Unity ha hecho algunos cambios
Shaders trabajo, que puede causar errores e inconsistencias. Los más relevantes y
Importante es la intensidad de la luz. Las luces en Unity 5 son dos veces más brillantes. Todos
Legacy Shaders han sido reescritos para tener esto en cuenta; Si ha actualizado su
Shaders o cambiar a Shaders estándar, no notará ninguna diferencia. Si usted tiene
Escrito su propio modelo de iluminación, tendrá que estar seguro de que la intensidad de la luz
es
No multiplicado por dos más. El siguiente código se utiliza para garantizar esto:
// Unidad 4
C.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
// Unidad 5
C.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
Si aún no ha escrito un sombreado, no entre en pánico: los modelos de iluminación serán
ampliamente
Explicado en el Capítulo 3, Comprensión de los modelos de iluminación.
Nota
Hay varios otros cambios en la forma en que Unity 5 maneja shaders en comparación con
Unity 4.
Puedes ver todos ellos en Shaders en Unity 5.0 en
Http://docs.unity3d.com/Manual/UpgradeGuide5-Shaders.html.

Cómo funciona…
Escribir shaders es siempre un trade-off entre el realismo y la eficiencia; Shaders realistas
Requieren una computación intensiva, lo que potencialmente introduce un desfase
significativo. Es importante Utilice únicamente aquellos efectos estrictamente necesarios: si un
material no necesita Reflexiones, entonces no hay necesidad de usar un sombreador que los
calcula. Esta ha sido la Razón principal por la que Unity 4 ha sido enviado con tantos shaders
diferentes. El nuevo Shader estándar de Unity 5 potencialmente puede reemplazar todos los
shaders anteriores ya que Incorpora mapeo normal, transparencia y reflexión. Sin embargo, ha
sido hábilmente Optimizado para que sólo se calculen los efectos realmente necesarios. Si su
estándar El material no tiene reflejos, no serán calculados.
A pesar de esto, el Shader estándar está diseñado principalmente para materiales realistas. El
legado Los shaders difusos y especulares, en comparación, no fueron realmente diseñados
para realistas Materiales. Esta es la razón por la que cambiar de Shaders Legacy a Standard
Introduzca cambios ligeros en la forma en que se renderizan sus objetos.

Ver también
 El capítulo 3, Entendiendo los Modelos de Iluminación, explora en profundidad cómo
Los shaders especulares funcionan. Incluso si está obsoleto en la Unidad 5, entenderlas
es Esencial si desea diseñar nuevos modelos de iluminación.
 Capítulo 4, Rendimiento con base física en Unity 5, le mostrará cómo desbloquear el
Potencial del Shader estándar en Unity 5.

Adición de propiedades a un shader


Las propiedades de un shader son muy importantes para la canalización de shader, ya que son
el método que utiliza para permitir que el artista o usuario del sader asigne texturas y ajuste
sus valores de shader. Las propiedades le permiten exponer los elementos GUI en la pestaña
Inspector de un material sin tener que usar un editor independiente, que proporciona formas
visuales para ajustar un shader.
Con el sombreado abierto en MonoDevelop, mire el bloque de las líneas 2 a 7. Esto se llama el
bloque de propiedades. Actualmente, tendrá una propiedad en ella llamada _MainTex. Si
observa su material que tiene este shader aplicado a él, notará que hay un elemento GUI de
textura en la ficha Inspector. Estas líneas de código en nuestro shader están creando este
elemento GUI para nosotros.
Una vez más, Unity ha hecho este proceso muy eficiente en términos de codificación y la
cantidad de tiempo que tarda en iterar a través del cambio de sus propiedades.
Preparándose
Vamos a ver cómo funciona esto en nuestro actual shader llamado StandardDiffuse, creando
nuestro Propiedades y aprender más sobre la sintaxis involucrada. Para este ejemplo,
volveremos a colocar el sombreador creado anteriormente. En lugar de utilizar una textura,
sólo usará su color y otras propiedades que podremos cambiar directamente desde la pestaña
Inspector. Comience duplicando el shader StandardDiffuse. Puede hacerlo seleccionándolo en
la ficha Inspector y presionando Ctrl + D. Esto creará una copia llamada StandardDiffuse2.
Nota
Puede dar un nombre más amigable a su shader en su primera línea. Por ejemplo, Shader
"CookbookShaders / StandardDiffuse" le dice a Unity que llame a este sombreado
StandardDiffuse Y muévalo a un grupo llamado CookbookShaders. Si duplica un sombreado
usando Ctrl + D, su nuevo archivo compartirá el mismo nombre. Para evitar confusiones,
asegúrese de cambiar la primera línea de cada nuevo sombreador para que utilice un alias
único.

Cómo hacerlo…
Una vez que el shader StandardDiffuse2 esté listo, podemos empezar a cambiar sus
propiedades:
1. En nuestro bloque de propiedades de nuestro shader, elimine la propiedad actual
Siguiente código de nuestro shader actual:
_MainTex ("Albedo (RGB)", 2D) = " white " {}
2. Como hemos eliminado una propiedad esencial, este sombreador no compilará hasta
que el otro Las referencias a _MainTex se eliminan. Vamos a eliminar esta otra línea:
Sampler2D _MainTex;
3. El shader original utilizó _MainTex para colorear el modelo. Cambiemos esto
reemplazando La primera línea de código de la función surf () con esto:
fixed4 c = _Color;
4. Cuando guarde y vuelva a Unity, el shader compilará y verá que Ahora la pestaña
Inspector de nuestro material ya no tiene una muestra de textura. A Completar el
reajuste de este shader, vamos a agregar una propiedad más y ver qué pasa.
Introduzca el código siguiente:
_AmbientColor("Ambient Color", Color) = (1,1,1,1)
5. Hemos añadido otra muestra de color a la pestaña Inspector del material. Ahora
vamos a agregar Uno más para tener una idea de otros tipos de propiedades que
podemos crear. Añade el Siguiente código al bloque de propiedades:
_MySliderValue("This is a Slider", Range(0,10)) = 2.5
6. Ahora hemos creado otro elemento GUI que nos permite interactuar visualmente con
nuestro Shader Esta vez, creamos un control deslizante con el nombre This is a Slider,
como se muestra en la Siguiente captura de pantalla:

Las propiedades le permiten crear una forma visual de ajustar los shaders sin tener
que cambiar Valores en el propio código de shader. La siguiente fórmula le mostrará
cómo estas propiedades pueden En realidad se utiliza para crear un shader más
interesante.
Nota
Mientras que las propiedades pertenecen a shaders, los valores asociados con ellas se
almacenan en Materiales. El mismo shader se puede compartir de forma segura entre
muchos materiales diferentes. Sobre el Por otro lado, cambiar la propiedad de un
material afectará la apariencia de todos los objetos que Lo están utilizando
actualmente.
Cómo funciona…
Cada shader Unity tiene una estructura incorporada que está buscando en su código. El bloque
de propiedades es una de esas funciones que se esperan de Unity. La razón detrás de esto es
darle, el programador del sombreador, medios de crear rápidamente los elementos de la GUI
que atan directamente en su código del sombreador. Estas propiedades que se declaran en el
bloque Propiedades se pueden utilizar en su código de sombreado para cambiar valores,
colores y texturas. La sintaxis para definir una propiedad es la siguiente:

Echemos un vistazo a lo que está pasando bajo el capó aquí. Cuando empiece a escribir una
nueva propiedad, necesitará darle un nombre de variable. El nombre de variable va a ser el
nombre que su código de shader va a utilizar para obtener el valor del elemento GUI. Esto nos
ahorra mucho tiempo porque no tenemos que configurar este sistema nosotros mismos.
Los siguientes elementos de una propiedad son el Inspector GUI Nombre y Tipo de la
propiedad, que se encuentra entre paréntesis. El Inspector GUI Name es el nombre que va a
aparecer en la pestaña Inspector del material cuando el usuario está interactuando y
ajustando el sombreado. El Tipo es el tipo de datos que esta propiedad va a controlar.
Hay muchos tipos que podemos definir para propiedades dentro de los shaders de Unity. la
siguiente tabla describe los tipos de variables que podemos tener en nuestros shaders:

Tipos de propiedades de Surface Shader


Range (min, Esto crea una propiedad flotante como un deslizador desde el valor
max) mínimo hasta el valor máximo
Color
Esto crea una muestra de color en la pestaña Inspector que abre un
selector de color = (float,float,float,float)
2D
Esto crea una muestra de textura que permite al usuario arrastrar una
textura en el shader
Rect
Esto crea una muestra de textura (no-power-of-2) y funciona igual que el
elemento 2D GUI
Cube
Esto crea una muestra de mapa de cubo en la pestaña Inspector y
permite al usuario arrastrar y soltar un mapa de cubo en el sombreado
Float
Esto crea un valor flotante en la ficha Inspector pero sin un control
deslizante
Vector
Esto crea una propiedad de cuatro flotadores que le permite crear
direcciones o colores

Finalmente, está el valor predeterminado. Esto simplemente establece el valor de esta


propiedad en el valor que coloca en el código. Por lo tanto, en la imagen de ejemplo anterior,
el valor predeterminado para la propiedad denominada _AmbientColor, que es del tipo de
color, se establece en un valor de 1,1,1,1. Como esta es una propiedad de color que espera un
color que es RGBA o float4 o r, g, b, a = x, y, z, w, esta propiedad de color, cuando se crea por
primera vez, se establece en blanco.
Ver también
Las propiedades están documentadas en el manual de Unity en
http://docs.unity3d.com/Documentation/Components/SL-Properties.html.

Uso de propiedades en un Shader de Superficie


Ahora que hemos creado algunas propiedades, vamos a conectarlas al shader para que
podamos usarlas como ajustes en nuestro shader y hacer que el proceso de material sea
mucho más interactivo.
Podemos usar los valores de las propiedades de la pestaña Inspector del material porque
tenemos Adjunto un nombre de variable a la propiedad en sí, pero en el código de sombreado,
tiene que configurar un par de cosas antes de que pueda empezar a llamar al valor por su
nombre de variable.

Cómo hacerlo…
Los pasos siguientes muestran cómo usar las propiedades en un Shader de superficie:
1. Para comenzar, vamos a eliminar las siguientes líneas de código, ya que eliminamos la
propiedad llamada _MainTex en la receta de Shader estándar básico de este capítulo:
_MainTex ("Albedo (RGB)", 2D) = "white" {}
sampler2D _MainTex;
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
2. A continuación, agregue las siguientes líneas de código al shader, debajo de la línea
CGPROGRAM:
float4 _AmbientColor;
float _MySliderValue;
3. Con el paso 2 completo, ahora podemos usar los valores de las propiedades de nuestro
shader.
Hagamos esto añadiendo el valor de la propiedad _Color a la _AmbientColor Y dando
el resultado de esto a la línea de código de o.Albedo. Por lo tanto, vamos a añadir el
Siguiente código al shader en la función surf ():
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = pow((_Color + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
4. Por último, su sombreador debe ser similar al siguiente código de sombreado. Si
guarda su Shader en MonoDevelop y vuelva a entrar en Unity, su shader compilará. Si
hubiera Sin errores, ahora tendrá la posibilidad de cambiar los colores ambientales y
emisivos de El material, así como aumentar la saturación del color final utilizando el
valor deslizante. Muy bonito, ¿eh?

Shader "CookbookShaders/Chapter01/StandardDiffuse3" {
// We define Properties in the properties block
Properties {
_Color ("Color", Color) = (1,1,1,1)
_AmbientColor("Ambient Color", Color) = (1,1,1,1)
_MySliderValue("This is a Slider", Range(0,10)) = 2.5
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

// We need to declare the properties variable type inside of the


// CGPROGRAM so we can access its value from the properties block.
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0

struct Input {
float2 uv_MainTex;
};

fixed4 _Color;
float4 _AmbientColor;
float _MySliderValue;

void surf (Input IN, inout SurfaceOutputStandard o) {


// We can then use the properties values in our shader
fixed4 c = pow((_Color + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

Propina
Descargando el código de ejemplo
Puede descargar los archivos de código de ejemplo de todos los libros Packt que haya
comprado Su cuenta en http://www.packtpub.com. Si ha comprado este libro en otro lugar,
puede Visite http://www.packtpub.com/support y regístrese para que los archivos se envíen
por correo electrónico directamente a su mail.
Nota
La función pow (arg1, arg2) es una función incorporada que realizará la matemática
equivalente Función del poder. Así, el argumento 1 es el valor que queremos elevar a una
potencia y El argumento 2 es el poder al que queremos elevarlo.
Para obtener más información sobre la función pow (), consulte el tutorial de Cg. Es una gran
libertad Recurso que puede utilizar para aprender más sobre sombreado y obtener un glosario
de Funciones disponibles en el lenguaje de sombreado Cg:
Http://http.developer.nvidia.com/CgTutorial/cg_tutorial_appendix_e.html
La siguiente captura de pantalla muestra el resultado obtenido usando nuestras propiedades
para controlar Los colores y la saturación de nuestro material desde la pestaña Inspector del
material:
Cómo funciona…
Cuando declara una nueva propiedad en el bloque Propiedades, está proporcionando un
shader para recuperar el valor tweaked de la pestaña Inspector del material. Este valor se
almacena en la parte de nombre de variable de la propiedad. En este caso, _AmbientColor,
_Color y _MySliderValue son las variables en las que almacenamos los valores tweaked. Para
que pueda utilizar el valor en el bloque SubShader {}, debe crear tres nuevas variables con los
mismos nombres que el nombre de la variable de la propiedad. Esto establece
automáticamente un enlace entre estos dos para que sepan que tienen que trabajar con los
mismos datos. Además, declara el tipo de datos que queremos almacenar en nuestras
variables subshader, lo que resultará útil si analizamos la optimización de los shaders en un
capítulo posterior.
Una vez que haya creado las variables subshader, puede utilizar los valores de la función surf
(). En este caso, queremos agregar las variables _Color y _AmbientColor juntas y llevarla a una
potencia de lo que la variable _MySliderValue sea igual en la pestaña Inspector del material.
La gran mayoría de los shaders comienzan como Shaders estándar y se modifican hasta que
coinciden con el aspecto deseado. Ahora hemos creado la base para cualquier sombreado de
superficie que vaya a crear que requiera un componente difuso.
Nota
Los materiales son activos. Esto significa que cualquier cambio hecho a ellos mientras su juego
es Que se ejecutan en el editor son permanentes. Si ha cambiado el valor de una propiedad
por Error, puede deshacerlo usando Ctrl + Z.

Hay más…
Como cualquier otro lenguaje de programación, Cg no permite errores. Como tal, su shader no
funcionará si tiene un error tipográfico en su código. Cuando esto sucede, los materiales se
representan en magenta sin sombrear:

Cuando un script no compila, Unity evita que su juego se exporte o incluso se ejecute. Por el
contrario, los errores en los shaders no impiden que su juego se ejecute.
Si uno de sus shaders aparece como magenta, es hora de investigar dónde está el problema. Si
selecciona el sombreador incriminado, verá una lista de errores que se muestran en su pestaña
Inspector:
A pesar de mostrar la línea que planteó el error, rara vez significa que esta es la línea que tiene
que ser fijo. El mensaje de error mostrado en la imagen anterior se genera al eliminar la
variable sampler2D _MainTex del bloque SubShader {}. Sin embargo, el error se plantea por la
primera línea que intenta tener acceso a dicha variable.
Encontrar y corregir lo que está mal con el código es un proceso llamado depuración. El más
Los errores comunes que debe buscar son los siguientes:

 Un soporte faltante. Si se olvidó de agregar un paréntesis para cerrar una sección, es


probable que el compilador genere errores al final del documento, al principio o en
una nueva sección.

 Un punto y coma faltante. Uno de los errores más comunes, pero afortunadamente
uno de los más fáciles de detectar y arreglar. Los errores se plantean a menudo por la
línea siguiente.

 Una propiedad que se ha definido en la sección Propiedades pero que no se ha


acoplado con una variable en el bloque SubShader {}.

 A la inversa, a lo que podría estar acostumbrado en los scripts C #, los valores de coma
flotante en Cg no necesitan el seguido por un f: es 1.0, no 1.0f.
Los mensajes de error generados por shaders pueden ser muy engañosos, especialmente
debido a sus estrictas restricciones sintácticas. Si tiene dudas sobre su significado, es mejor
buscar en Internet. Los foros de Unity están llenos de otros desarrolladores que
probablemente hayan encontrado (y arreglado) su problema antes.

Ver también
Más información sobre cómo dominar los Shaders de Superficie y sus propiedades se puede
encontrar en el Capítulo 2, Shaders de Superficie y Mapeo de Textura. Si tiene curiosidad de
ver lo que los shaders pueden hacer realmente cuando se usan con todo su potencial, eche un
vistazo al Capítulo 10, Técnicas avanzadas de sombreado, para algunas de las técnicas más
avanzadas que se incluyen en este libro.
2. Shaders de Superficie y Mapeo de Textura
Capítulo 2. Shaders de Superficie y Mapeo de Textura
En este capítulo, exploraremos Shaders de Superficie. Comenzaremos con un material mate
muy simple y terminaremos con proyecciones holográficas y mezclas de terrenos avanzados.
También podemos usar texturas para animar, mezclar y manejar cualquier otra propiedad que
deseemos. En este capítulo, aprenderá sobre los siguientes métodos:
 Sombreado difuso
 Uso de matrices empaquetadas
 Adición de una textura a un sombreado
 Desplazamiento de las texturas mediante la modificación de los valores UV
 Mapeo normal
 Creación de un material transparente
 Creación de un sombreador holográfico
 Embalaje y mezcla de texturas
 Crear un círculo alrededor de su terreno

Introducción
Shaders de Surface se han introducido en el capítulo 1, Crear su primer Shader, como el tipo
principal de sombreado utilizado en Unity. Este capítulo mostrará en detalle lo que realmente
son y cómo funcionan. En general, hay dos pasos esenciales en cada Shader de Surface. En
primer lugar, debe especificar ciertas propiedades físicas del material que desea describir,
como su color difuso, suavidad y transparencia. Estas propiedades se inicializan en una función
llamada función de Surface y se almacenan en una estructura llamada salida de Surface. En
segundo lugar, la salida Surface se pasa a un modelo de iluminación. Esta es una función
especial que también tendrá información sobre las luces cercanas en la escena. Ambos
parámetros se utilizan para calcular el color final de cada píxel de su modelo. La función de
iluminación es donde tienen lugar los cálculos reales de un shader, ya que es el código que
determina cómo debe comportarse la luz cuando toca un material.
El siguiente diagrama resume de forma flexible cómo funciona un Shader de superficie. Los
modelos de iluminación personalizada se explorarán en el Capítulo 3, Comprensión de los
modelos de iluminación, mientras que el Capítulo 5, Funciones de vértice, se centrará en los
modificadores de vértices:
Sombreado difuso
Antes de comenzar nuestro viaje en el mapeo de textura, es importante entender cómo
funcionan los materiales difusos. Ciertos objetos pueden tener un color uniforme y una
superficie lisa, pero no lo suficientemente suave como para brillar en la luz reflejada. Estos
materiales mate se representan mejor con un sombreado difuso. Mientras que, en el mundo
real, los materiales difusos puros no existen; Los shaders difusos son relativamente fáciles de
implementar y encontrar una aplicación grande en juegos con baja estética de poliuretano.

Preparándose
Hay varias formas en las que puede crear su propio shader difuso. Una forma rápida es
comenzar con el Shader estándar en Unity 5 y editarlo para eliminar cualquier textura, de
manera similar a lo que se hizo anteriormente en el Capítulo 1, Creación de su primer Shader.

Cómo hacerlo…
Comencemos con nuestro Shader estándar y apliquemos los siguientes cambios:
1. Quite todas las propiedades excepto _Color:
_Color ("Color", Color) = (1,1,1,1)
2. En la sección SubShader {}, quite el _MainTex, _Glossiness y _Metallic
Variables. No debe quitar la referencia a uv_MainTex como Cg no permite
La estructura de entrada está vacía. El valor será simplemente ignorado.
3. Elimine el contenido de la función surf () y cámbielo por lo siguiente:
o.Albedo = _Color.rgb;
4. Su sombreador debe mirar como sigue:

Shader "CookbookShaders/Chapter02/Diffuse" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0

struct Input {
float2 uv_MainTex;
};

fixed4 _Color;

void surf (Input IN, inout SurfaceOutputStandard o) {


o.Albedo = _Color.rgb;
}
ENDCG
}
FallBack "Diffuse"
}

Dado que este sombreador ha sido reajustado de un Shader estándar, utilizará componentes
físicos Rendering para simular cómo se comporta la luz en sus modelos. Si está tratando de
lograr un No-fotorrealista, puede cambiar la primera directiva #pragma para que use Lambert
En lugar de Estándar. Si lo hace, también debe reemplazar SurfaceOutputStandard con
SurfaceOutput.
Cómo funciona…
La forma en que los shaders le permiten comunicar las propiedades de renderizado de su
material a su modelo de iluminación es a través de una salida de superficie. Es básicamente
una envoltura alrededor de todos los parámetros que necesitan el modelo actual de
iluminación. No debe sorprendernos que los diferentes modelos de iluminación con
estructuras de salida de superficie diferentes. La tabla siguiente muestra las tres estructuras de
salida usadas en Unity 5 y cómo se puede usar:
Tipo de shaders Unity 4 Unity 5
Diffuse (difuso) Cualquier Surface Shader Standard
Que carece de claridad SurfaceOutput SurfaceOutputStandard
Estándar (configuración
Cualquier Surface Shader
Specular (De espejo) especular)
SurfaceOutput
SurfaceOutputStandardSpecular

La SurfaceOutput struct tiene las siguientes propiedades:


 fixed3 Albedo;: Este es el color difuso del material
 fixed3 Normal;: Este es el espacio tangente normal, si está escrito
 fixed3 Emission;: Este es el color de la luz emitida por el material (este Propiedad se
declara como la half3 en el estándar Shaders)
 Fixed Alpha;: Esta es la transparencia del material
 half Specular;: Es la potencia especular de 0 a 1
 fixed Gloss;: Esta es la intensidad especular
La SurfaceOutputStandard struct tiene las siguientes propiedades:
 fixed3 Albedo;: Este es el color base del material (si es difuso o especular)
 fixed3 Normal;
 half3 Emission;: Esta propiedad se declara como media3, mientras que se definió como
fixed3 en SurfaceOutput
 fixed Alpha;
 half Occlusion;: Esta es la oclusión (predeterminado 1)
 half Smoothness;: Esta es la suavidad (0 = rough, 1 = smooth) // rough: áspero/
smooth: suave
 half Metallic;: 0 = non-metal, 1= metal
La SurfaceOutputStandardSpecular struct tiene las siguientes propiedades:
 fixed3 Albedo;.
 fixed3 Normal;.
 half3 Emission;.
 fixed Alpha;.
 half Occlusion;.
 half Smoothness;.
 fixed3 Specular;: Este es el color especular. Esto es muy diferente de la propiedad
Specular en SurfaceOutput, ya que permite especificar un color en lugar de un solo
valor.
Usar un Surface Shader correctamente es una cuestión de inicializar la salida de la superficie
con los valores correctos.
Uso de matrices empaquetadas
Hablando francamente, el código dentro de un shader tiene que ser ejecutado por lo menos
cada píxel en su pantalla. Esta es la razón por la cual las GPUs están altamente optimizadas
para la computación paralela.
Esta filosofía también es evidente en el tipo estándar de variables y operadores disponibles en
Cg. Comprenderlos es esencial no sólo para usar shaders correctamente, sino también para
escribir altamente optimizado.

Cómo hacerlo…
Hay dos tipos de variables en Cg: valores únicos y matrices empaquetadas. Este último puede
ser identificado porque su tipo termina con un número tal como float3 o int4. Como sugieren
sus nombres, estos tipos de variables son similares a las estructuras, lo que significa que cada
una contiene varios valores individuales. Cg los llama arrays llenos, aunque no son
exactamente arreglos en el sentido tradicional.
Los elementos de una matriz empaquetada se pueden acceder como una estructura normal.
Normalmente se les llama x, y, z, y w. Sin embargo, Cg también le proporciona otro alias para
ellos, es decir, r, g, b, y a. A pesar de que no hay diferencia entre el uso de x o r, puede hacer
una gran diferencia para los lectores. La codificación Shader, de hecho, a menudo implica el
cálculo con posiciones y colores. Podrías haber visto esto en las Shaders Estándar:
o.Alpha = _Color.a;
Aquí, era una estructura y era una matriz empaquetada. Es por eso Que Cg prohíbe el uso de
estas dos sintaxis: no puede utilizar _Color.xgz. También hay otra característica importante de
matrices empaquetadas que no tiene equivalente en C #: swizzling. Cg permite direccionar y
reordenar elementos dentro de matrices empaquetadas en una sola línea. Una vez más, esto
aparece en el Shader estándar:
o.Albedo = _Color.rgb;
Albedo es fixed3, lo que significa que contiene tres valores del tipo fijo. Sin embargo, _Color se
define como fixed4. Una asignación directa resultaría en un error de compilador como _Color
es más grande que Albedo. La forma C # de hacer esto sería la siguiente:
o.Albedo.r = _Color.r;
o.Albedo.g = _Color.g;
o.Albedo.b = _Color.b;
Sin embargo, se puede comprimir en Cg:
o.Albedo = _Color.rgb;
Cg también permite reorganizar elementos, por ejemplo, utilizando _Color.bgr para
intercambiar los canales rojo y azul. Por último, cuando se asigna un valor único a una matriz
empaquetada, se copia en todos sus campos:
o.Albedo = 0; // Black =(0,0,0)
o.Albedo = 1; // White =(1,1,1)
Esto se conoce como manchas.
Swizzling también se puede usar en el lado izquierdo de una expresión, permitiendo que sólo
se sobrescriban ciertos componentes de una matriz empaquetada:
o.Albedo.rg = _Color.rg;
En cuyo caso, se denomina enmascaramiento.

Matrices empaquetadas
Donde swizzling realmente muestra su pleno potencial es cuando se aplica a matrices
empaquetadas. Cg permite tipos como float4x4, que representa una matriz de flotantes con
cuatro filas y cuatro columnas. Puede acceder a un solo elemento de la matriz usando la

notación _mRC, donde R es la fila y C es la columna:

float4x4 matrix;
// ...
float first = matrix._m00;
float last = matrix._m33;
La notación _mRC también se puede encadenar:
float4 diagonal = matrix._m00_m11_m22_m33;
Se puede seleccionar una fila entera utilizando corchetes:
float4 firstRow = matrix[0];
// Equivalent to
float4 firstRow = matrix._m00_m01_m02_m03;
Ver también
Las matrices empaquetadas son una de las características más agradables de Cg. Puedes
descubrir más sobre ellos aquí:
http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter02.html

Adición de una textura a un shader


Las texturas pueden llevar a nuestros shaders a la vida muy rápidamente en términos de
alcanzar muy realista Efectos. Con el fin de utilizar con eficacia texturas, tenemos que
entender cómo una imagen 2D es Mapeado a un modelo 3D. Este proceso se denomina mapeo
de textura, y requiere Trabajo a realizar en el shader y el modelo 3D que queremos utilizar. Los
modelos, de hecho, están hechos de triángulos; Cada vértice puede almacenar datos a los que
los shaders pueden acceder. Una de las informaciones más importantes almacenadas en los
vértices son los datos UV. Consta de dos coordenadas, U y V, que van de 0 a 1. Representan la
posición XY del píxel en la imagen 2D que se asignará a los vértices. Los datos UV sólo están
presentes para los vértices; Cuando los puntos internos de un triángulo tienen que ser
mapeados por textura, la GPU interpola los valores UV más cercanos para encontrar el píxel
correcto en la textura a utilizar. La siguiente imagen muestra cómo se asigna una textura 2D a
un triángulo desde un modelo 3D:

Los datos UV se almacenan en el modelo 3D y requieren un software de modelado para ser


editado. Algunos modelos carecen del componente UV, por lo tanto no pueden soportar el
mapeo de textura. El conejo de Stanford, por ejemplo, no fue provisto originalmente de uno.
Preparándose
Para esta fórmula, necesitará un modelo 3D con datos UV y su textura. Ambos necesitan ser
importados a Unity antes de comenzar. Puede hacerlo simplemente arrastrándolos al editor.
Como Standard Shader admite el mapeado de texturas de forma predeterminada, usaremos
esto y luego explicaremos en detalle cómo funciona.

Cómo hacerlo…
Agregar una textura a su modelo usando el Shader estándar es increíblemente simple, como
sigue:
1. Cree un nuevo Shader estándar denominado TexturedShader.
2. Cree un nuevo material llamado TexturedMaterial.
3. Asigne el sombreado al material arrastrándolo.
4. Después de seleccionar el material, arrastre su textura al rectángulo vacío llamado
Albedo (RGB). Si ha seguido correctamente todos estos pasos, su pestaña de Inspector
de materiales
Debe verse así:

El Shader estándar sabe cómo asignar una imagen 2D a un modelo 3D utilizando sus datos UV.

Cómo funciona…
Cuando el shader estándar se utiliza del inspector de un material, el proceso detrás del
trazamiento de la textura es completamente transparente a los reveladores. Si queremos
entender cómo funciona, es necesario echar un vistazo más de cerca de TexturedShader.
Desde la sección Propiedades, podemos ver que la textura de Albedo (RGB) es en realidad
referida en el código como _MainTex:
_MainTex ("Albedo (RGB)", 2D) = "white" {}
En la sección CGPROGRAM, esta textura se define como sampler2D, el tipo estándar para
texturas 2D:
sampler2D _MainTex;
La siguiente línea muestra una estructura llamada Entrada. Este es el parámetro de entrada
para la función de superficie y contiene una matriz empaquetada llamada uv_MainTex:
struct Input {
float2 uv_MainTex;
};
Cada vez que se llama a la función de superficie surf (), la estructura de entrada contendrá el
UV de _MainTex para el punto específico del modelo 3D que necesita ser procesado. El Shader
estándar reconoce que el nombre uv_MainTex se refiere a _MainTex y lo inicializa
automáticamente. Si está interesado en comprender cómo se mapea realmente la radiación
UV desde un espacio 3D a una textura 2D, puede consultar el Capítulo 3, Descripción de los
modelos de iluminación. Finalmente, los datos UV se usan para muestrear la textura en la
primera línea de la función superficial:
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
Esto se hace usando la función tex2D () de Cg; Toma una textura y UV y devuelve el color del
píxel en esa posición.
Nota
Las coordenadas U y V van de 0 a 1, donde (0,0) y (1,1) corresponden a dos esquinas opuestas.
Diferentes implementaciones asocian UV con diferentes esquinas; Si su textura aparece
invertida, trate de invertir el componente V.

Hay más…
Cuando importa una textura a Unity, está configurando algunas de las propiedades que
utilizará sampler2D. El más importante es el modo de filtro, que determina cómo se interpolan
los colores cuando se muestrea la textura. Es muy improbable que los datos UV apunten
exactamente al centro de un píxel; En todos los demás casos, es posible que desee interpolar
entre los píxeles más cercanos para obtener un color más uniforme. A continuación, se
muestra la captura de pantalla de la pestaña Inspector de un ejemplo de textura:

Para la mayoría de las aplicaciones, Bilinear proporciona una manera barata pero eficaz de
alisar la textura. Si está creando un juego 2D, sin embargo, Bilinear podría producir azulejos
borrosos. En este caso, puede utilizar Point para eliminar cualquier interpolación del muestreo
de textura. Cuando se ve una textura desde un ángulo pronunciado, es probable que el
muestreo de textura produzca artefactos visualmente desagradables. Puede reducirlos
estableciendo Aniso Level a un valor más alto. Esto es particularmente útil para texturas de
suelo y techo, donde los fallos pueden romper la ilusión de continuidad.

Ver también
Si desea saber más sobre el funcionamiento interno de cómo las texturas se asignan a una
superficie 3D, puede leer la información disponible en
http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter03.html.
Para obtener una lista completa de las opciones disponibles al importar una textura 2D, puede
consultar el siguiente sitio web:
Http://docs.unity3d.com/Manual/class-TextureImporter.html

Desplazamiento de las texturas mediante la modificación de los valores UV


Una de las técnicas de textura más comunes utilizadas en la industria del juego de hoy en día
es el proceso de permitirle desplazar las texturas sobre la superficie de un objeto. Esto le
permite crear efectos como cascadas, ríos, caudales de lava, etc. También es una técnica que
es la base para crear efectos animados de sprite, pero cubriremos esto en una fórmula
posterior de este capítulo. Veamos primero cómo crearemos un efecto de desplazamiento
simple en un Shader de superficie.

Preparándose
Para comenzar esta fórmula, necesitará crear un nuevo archivo y material de shader. Esto nos
pondrá con un bonito shader limpio que podemos usar para estudiar el efecto de
desplazamiento por sí mismo.

Cómo hacerlo…
Para empezar, lanzaremos nuestro nuevo archivo de sombreado que acabamos de crear e
ingresaremos el código mencionado en los siguientes pasos:

1. El shader necesitará dos nuevas propiedades que nos permitan controlar la velocidad
de desplazamiento de la textura. Por lo tanto, vamos a agregar una propiedad de
velocidad para la dirección X y una velocidad Propiedad para la dirección Y, como se
muestra en el siguiente código:
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_ScrollXSpeed ("X Scroll Speed", Range(0,10)) = 2.0
_ScrollYSpeed ("Y Scroll Speed", Range(0,10)) = 2.0}
2. Modifique las propiedades de Cg en la sección CGPROGRAM y cree nuevas variables
para que podamos acceder a los valores de nuestras propiedades:
fixed4 _MainTint;
fixed _ScrollXSpeed;
fixed _ScrollYSpeed;
sampler2D _MainTex;
3. Modifique la función de superficie para cambiar los UVs dados a la función tex2D (). A
continuación, utilice la variable _Time incorporada para animar los UVs con el tiempo
cuando se pulsa el botón de reproducción en el editor:
void surf (Input IN, inout SurfaceOutput o)
{
// Crear una variable separada para almacenar nuestros UVs
// antes de pasarlos a la función tex2D ()
fixed2 scrolledUV = IN.uv_MainTex;
// Crear variables que almacenan las variables individuales xyy
// componentes para los UV's escalados por el tiempo
fixed xScrollValue = _ScrollXSpeed * _Time;
fixed yScrollValue = _ScrollYSpeed * _Time;
// Aplicar el offset UV final
scrolledUV += fixed2(xScrollValue, yScrollValue);
// Aplicar texturas y tintes
half4 c = tex2D (_MainTex, scrolledUV);
o.Albedo = c.rgb * _MainTint;
o.Alpha = c.a;
}
La siguiente imagen muestra el resultado de la utilización del sistema de desplazamiento UV
para crear un simple movimiento fluvial para sus entornos. Puede notar este efecto en la
escena llamada ScrollingUVs de los archivos de código proporcionados con este libro:

Cómo funciona…
El sistema de desplazamiento comienza con la declaración de un par de propiedades, lo que
permitirá al usuario de este sombreador aumentar o disminuir la velocidad del efecto de
desplazamiento en sí. En su núcleo, son valores flotantes que pasan de la pestaña Inspector del
material a la función de superficie del sombreador. Para obtener más información sobre las
propiedades del sombreador, consulte el Capítulo 1,
Creación de su primer Shader.
Una vez que tengamos estos valores de flotador desde la pestaña Inspector del material,
podemos usarlos para compensar nuestros valores de UV en el sombreado.
Para comenzar este proceso, primero almacenamos los UVs en una variable separada llamada
scrolledUV.
Esta variable tiene que ser float2 / fixed2 porque los valores UV se nos pasan desde la
estructura Input:
struct Input
{
float2 uv_MainTex;
}
Una vez que tengamos acceso a las UV de la malla, podemos compensarlas usando nuestras
variables de velocidad de desplazamiento y la variable _Time incorporada. Esta variable
incorporada devuelve una variable del tipo float4, lo que significa que cada componente de
esta variable contiene diferentes valores de
Tiempo en lo que respecta al tiempo de juego.
Una descripción completa de estos valores de tiempo individuales se describe en el siguiente
enlace: http://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
Esta variable _Time nos dará un valor flotante incrementado basado en el reloj de tiempo de
juego de Unity. Por lo tanto, podemos utilizar este valor para mover nuestros UVs en una
dirección UV y escala que el tiempo con nuestras variables de velocidad de desplazamiento:
// Crear variables que almacenan las variables individuales xyy
// componentes para los uv's escalados por el tiempo
fixed xScrollValue = _ScrollXSpeed * _Time;
fixed yScrollValue = _ScrollYSpeed * _Time;
Con el offset correcto calculado por el tiempo, podemos añadir el nuevo valor de
compensación de nuevo a la posición UV original. Esta es la razón por la que estamos usando
el operador + = en la línea siguiente. Queremos tomar la posición UV original, añadir el nuevo
valor de desplazamiento y pasarlo a la función tex2D () como las nuevas UVs de la textura. Esto
crea el efecto de la textura que se mueve sobre la superficie. Realmente estamos manipulando
los UVs, por lo que estamos fingiendo el efecto de la textura en movimiento:
ScrolledUV + = fixed2 (xScrollValue, yScrollValue);
Half4 c = tex2D (_MainTex, scrolledUV);
Mapeo normal
Cada triángulo de un modelo 3D tiene una dirección de orientación, que es la dirección hacia la
que apunta. A menudo se representa con una flecha colocada en el centro del triángulo y
ortogonal a la superficie. La dirección de enfrentamiento juega un papel importante en la
forma en que la luz se refleja en una superficie. Si dos triángulos adyacentes se enfrentan a
diferentes direcciones, reflejarán luces en ángulos diferentes, por lo que se sombrearán de
manera diferente. Para los objetos curvos, esto es un problema: es obvio que la geometría está
hecha de triángulos planos. Para evitar este problema, la forma en que la luz se refleja en un
triángulo no tiene en cuenta su orientación, sino su dirección normal. Como se indica en la
adición de una textura a una receta de sombreado, los vértices pueden almacenar datos; La
dirección normal es la información más utilizada después de los datos UV. Se trata de un
vector de longitud unitaria que indica la dirección a la que se enfrenta el vértice.
Independientemente de la dirección de enfrentamiento, cada punto dentro de un triángulo
tiene su propia dirección normal que es una interpolación lineal de los almacenados en sus
vértices. Esto nos da la capacidad de falsificar el efecto de la geometría de alta resolución en
un modelo de baja resolución. La siguiente imagen muestra la misma forma geométrica
rendida con diferentes por vértices normales. En la imagen de la izquierda, las normales son
ortogonales a la cara representada por sus vértices; Esto indica que hay una clara separación
entre cada cara. A la derecha, las normales son interpoladas a lo largo de la superficie,
indicando que incluso si la superficie es áspera, la luz debe reflejar como si fuera suave. Es fácil
ver que incluso si los tres objetos de la siguiente imagen comparten la misma geometría,
reflejan la luz de forma diferente. A pesar de estar hecho de triángulos planos, el objeto de la
derecha refleja la luz como si su superficie fuera realmente curvada:
Los objetos suaves con bordes ásperos son una clara indicación de que las normales por vértex
han sido interpoladas. Esto se puede ver si dibujamos la dirección del normal almacenado en
cada vértice, como se muestra en la siguiente imagen. Usted debe notar que cada triángulo
tiene sólo tres normales, pero como triángulos múltiples pueden compartir el mismo vértice,
más de una línea puede salir de ella:

El cálculo de las normales a partir del modelo 3D es una técnica que ha disminuido
rápidamente a favor de una asignación más avanzada de una normalidad. Similar a lo que
sucede con el mapeo de textura, las direcciones normales pueden proporcionarse usando una
textura adicional, normalmente llamada mapa normal o mapa de bump. Los mapas normales
son normalmente imágenes RGB, donde los componentes RGB se utilizan para indicar los
componentes X, Y y Z de la dirección normal. Hay muchas maneras de crear mapas normales
en estos días. Algunas aplicaciones como CrazyBump (http://www.crazybump.com/) y NDO
Painter (http://quixel.se/ndo/) recibirán datos 2D y lo convertirán en datos normales para
usted. Otras aplicaciones como Zbrush 4R7 (http://www.pixologic.com/) y AUTODESK
(http://usa.autodesk.com) tomarán datos 3D esculpidos y crearán mapas normales para usted.
El proceso real de crear mapas normales está definitivamente fuera del alcance de este libro,
pero los enlaces en el texto anterior deberían ayudarlo a empezar.
Unity hace que el proceso de agregar normales a tus shaders sea un proceso muy sencillo en el
reino Surface Shader usando la función UnpackNormals (). Veamos cómo se hace esto.

Preparándose
Cree un nuevo material y un shader y configúrelos en un nuevo objeto en la vista de escena.
Esto nos dará un espacio de trabajo limpio en el que podemos ver sólo la técnica normal de
mapeo. Necesitará un mapa normal para esta fórmula, pero también hay uno en el proyecto
Unity incluido con este libro. Un ejemplo de mapa normal incluido con el contenido de este
libro se muestra aquí:
Cómo hacerlo…
Los siguientes son los pasos para crear un sombreado de mapas normal:
1. Vamos a obtener el bloque de propiedades establecidas con el fin de tener un tono de
color y textura:
Properties
{
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_NormalTex ("Normal Map", 2D) = "bump" {}
}

Nota
Al inicializar la textura como bump, le decimos a Unity que _NormalTex contendrá
Un mapa normal. Si la textura no está establecida, será reemplazada por una textura
gris. El color utilizado (0,5,0,5,0,5,1) indica que no hay ningún golpe en absoluto.

2. Vincule las propiedades al programa Cg declarándolas en SubShader {} debajo de la


instrucción CGPROGRAM:
CGPROGRAM
#pragma surface surf Lambert
// Vincule la propiedad al programa CG
sampler2D _NormalTex;
float4 _MainTint;
3. Debemos asegurarnos de que actualizamos la estructura de entrada con el nombre de
variable adecuado para que podamos usar las UVs del modelo para la textura normal
del mapa:
// Asegúrese de obtener las UVs para la textura en la estructura
struct Input
{
float2 uv_NormalTex;
}
4. Finalmente, extraemos la información normal de la textura normal del mapa utilizando
la función UnpackNormal () incorporada. Entonces, sólo tiene que aplicar estas nuevas
normales a la salida del Surface Shader:

// Obtiene los datos normales de la textura normal del mapa


// utilizando la función UnpackNormal
float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
// Aplicar la nueva norma al modelo de iluminación
o.Normal = normalMap.rgb;
o.Albedo = _MainTint.rgb;
o.Alpha = _MainTint.a;
La siguiente imagen muestra el resultado de nuestro sombreado de mapas normal:

Nota
Shaders puede tener tanto un mapa de textura como un mapa normal. No es raro utilizar los
mismos datos UV para tratar ambos. Sin embargo, es posible proporcionar un conjunto
secundario de UVs en los datos de vértice (UV2) utilizados específicamente para el mapa
normal.

Cómo funciona…
La matemática real para realizar el efecto normal de mapeo está definitivamente más allá del
alcance de este capítulo, pero Unity ya lo ha hecho todo para nosotros. Ha creado las
funciones para que no tengamos que seguir haciéndolo una y otra vez. Esta es otra razón por
la cual los Shaders de Superficie son una manera realmente eficiente de escribir shaders.
Si busca en el archivo UnityCG.cginc que se encuentra en la carpeta Data de su instalación de
Unity
, Encontrará las definiciones de la función UnpackNormal (). Cuando tú
Declare esta función en su Surface Shader, Unity toma el mapa normal proporcionado y lo
procesa para usted y le da el tipo correcto de datos para que pueda usarlo en su función de
iluminación por pixel. ¡Es un gran ahorro de tiempo! Cuando muestre una textura, obtiene
valores RGB de 0 a 1; Sin embargo, las direcciones de un vector normal van de -1 a +1.
UnpackNormal () lleva estos componentes al rango correcto.
Una vez que haya procesado el mapa normal con la función UnpackNormal (), lo devolverá a su
estructura SurfaceOutput para que pueda utilizarse en la función de iluminación. Esto se hace
por o.Normal = normalMap.rgb ;. Veremos cómo se usa realmente la normalidad para calcular
el color final de cada píxel en el Capítulo 3, Comprensión de los modelos de iluminación.

Hay más…
También puede agregar algunos controles a su shader de mapa normal que permite al usuario
ajustar el Intensidad del mapa normal. Esto se hace fácilmente modificando los componentes
xyy de la variable de mapa normal y agregándolos de nuevo juntos. Agregue otra propiedad al
bloque de propiedades y denomínela _NormalMapIntensity:
_NormalMapIntensity("Normal intensity", Range(0,1)) = 1
Multiplique los componentes x e y del mapa normal desempaquetado y vuelva a aplicar este
valor a la variable de mapa normal:
fixed3 n = UnpackNormal(tex2D(_BumpTex, IN.uv_ uv_MainTex)).rgb;
n.x *= _NormalMapIntensity;
n.y *= _NormalMapIntensity;
o.Normal = normalize(n);
Nota
Se supone que los vectores normales tienen longitudes iguales a uno. Multiplicarlos por
_NormalMapIntensity cambia su longitud, haciendo necesaria la normalización.
Ahora, puede permitir que un usuario ajuste la intensidad del mapa normal en la pestaña
Inspector del material. La siguiente imagen muestra el resultado de modificar el mapa normal
con nuestro escalar
valores:

Código Shader Completo Normal map:


Shader "CookbookShaders/NormalMap" {
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_NormalTex ("Normal Map", 2D) = "bump" {}
_NormalIntensity ("Normal Map Intensity", Range(0,20)) = 1
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _NormalTex;
float4 _MainTint;
float _NormalIntensity;
struct Input {float2 uv_NormalTex;};
void surf (Input IN, inout SurfaceOutput o) {
// Obtiene los datos normales de la textura normal del mapa
// utilizando la función UnpackNormal
float3 normalMap = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));
normalMap = float3(normalMap.x * _NormalIntensity, normalMap.y * _NormalIntensity, normalMap.z);
// Aplicar la nueva norma al modelo de iluminación
o.Normal = normalMap.rgb;
o.Albedo = _MainTint.rgb;
o.Alpha = _MainTint.a;
}
ENDCG
}
FallBack "Diffuse"
}

Creación de un material transparente


Todos los shaders vistos hasta ahora tienen algo en común: se utilizan para materiales sólidos.
Si desea mejorar el aspecto de su juego, los materiales transparentes son a menudo una buena
manera de empezar. Se pueden utilizar para cualquier cosa, desde un efecto de fuego a un
cristal de la ventana. Trabajar con ellos, por desgracia, es un poco más complicado. Antes de
representar modelos sólidos, Unity los ordena de acuerdo con la distancia de la cámara
(ordenamiento Z) y omite todos los triángulos que están alejados de la cámara (selección).
Cuando se representan geometrías transparentes, hay casos en los que estos dos aspectos
pueden causar problemas. Esta receta le mostrará cómo resolver algunos de estos problemas
cuando se trata de crear un Surface Shader transparente. Este tema será revisado en
profundidad en el capítulo 6, Fragmento de sombreadores y pases de agarre, donde se
proporcionarán sombreadores de vidrio y agua realistas.

Preparándose
Esta fórmula requiere un nuevo shader, al que llamaremos Transparent, y un nuevo
Material para que pueda unirse a un objeto. Como esto va a ser una ventana de cristal
transparente, un quad o avión es perfecto. También necesitaremos varios otros objetos no
transparentes para probar el efecto. En este ejemplo, usaremos un PNG para la textura del
vidrio. El canal alfa de la imagen se utilizará para determinar la transparencia del vidrio. El
proceso de crear una imagen de este tipo depende del software que esté utilizando. Sin
embargo, éstos son los pasos principales que usted necesitará seguir:
1. Encuentre la imagen del cristal que desea para sus ventanas.
2. Abra con un software de fotoedición, como GIMP o Photoshop.
3. Seleccione las partes de la imagen que desea que sean semitransparentes.
4. Cree una máscara de capa blanca (opacidad completa) en su imagen.
5. Utilice la selección hecha previamente para llenar la máscara de capa con un color más
oscuro.
6. Guarde la imagen e impórtela a Unity.
La imagen del juguete usada en esta receta es una imagen de un vitral de la catedral de Meaux
en Francia (https://en.wikipedia.org/wiki/Stained_glass). Si siguió todos los pasos, su imagen
debería tener este aspecto (canales RGB a la izquierda y canal A a la derecha):

Cómo hacerlo…
Como se mencionó anteriormente, hay algunos aspectos que debemos cuidar al usar un
Shader Transparente:
1. En la sección SubShader {} del sombreador, agregue las siguientes etiquetas que
shader es transparente:
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
2. Como este shader está diseñado para materiales 2D, asegúrese de que la geometría
posterior de su modelo no se dibuja agregando lo siguiente:
Cull Back
3. Dígale al shader que este material es transparente y necesita ser mezclado con lo que
fue dibujado en la pantalla antes:
#pragma surface surf Standard alpha:fade
4. Utilice este Shader de Superficie para determinar el color final y la transparencia del
vidrio:
void surf(Input IN, inout SurfaceOutputStandard o)
{
float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}

Cómo funciona…
Este sombreador introduce varios nuevos conceptos. En primer lugar, las etiquetas se utilizan
para agregar información acerca de cómo se va a mostrar el objeto. El realmente interesante
aquí es cola. Unity, de forma predeterminada, ordenará los objetos según la distancia de la
cámara. Por lo tanto, a medida que un objeto se acerca a la cámara, se va a dibujar sobre
todos los objetos que están más lejos de la cámara. Para la mayoría de los casos, esto funciona
muy bien para los juegos, pero encontrarás ciertas situaciones en las que querrás tener más
control sobre la clasificación de tus objetos en tu escena. Unity nos ha proporcionado algunas
colas de procesamiento predeterminadas, cada una con un valor único que dirige Unity cuando
dibujar el objeto a la pantalla. Estas colas de procesamiento incorporadas se denominan
Fondo, Geometría, AlphaTest, Transparente y Superposición. Estas colas no fueron creadas
arbitrariamente; En realidad sirven a un propósito para hacer nuestra vida más fácil al escribir
shaders e interactuar con el renderizador en tiempo real. Consulte la tabla siguiente para
obtener descripciones sobre el uso de cada una de estas colas de procesamiento individuales:
Render
Render
Render queue descripción queue
queue
value
Backgrou Esta cola de renderizado se procesa primero. Se utiliza para 1000
nd skyboxes y así sucesivamente.
Geometry
Esta es la cola de procesamiento predeterminada. Esto se utiliza 2000
para la mayoría de los objetos. La geometría opaca utiliza esta cola.
La geometría con prueba de alfa utiliza esta cola. Es diferente del
AlphaTes
t Geometry y Queue, ya que es más eficiente renderizar objetos 2450
probados alfa después de dibujar todos los objetos sólidos.
Esta cola de renderizado se procesa después de las filas Geometry y
Transpar AlphaTest en orden de inicio. Cualquier cosa alfa mezclada (es decir, 3000
ent shaders que no escriben en el buffer de profundidad) debe ir aquí,
por ejemplo, efectos de vidrio y partículas.
Esta cola de procesamiento está destinada a efectos de
Overlay superposición. Cualquier cosa rendida por último debe ir aquí, por 4000
ejemplo, bengalas de lente.
Por lo tanto, una vez que sepa a qué fila de procesamiento pertenece su objeto, puede asignar
su etiqueta de cola de procesamiento incorporada. Nuestro shader utilizó la cola Transparent,
por lo que escribimos Tags {"Queue" = "Trasparent"}.
Nota
El hecho de que la cola Transparente se procese después de Geometría no significa que
nuestro vidrio aparecerá encima de todos los demás objetos sólidos. La unidad dibujará el
último cristal, pero no hará píxeles que pertenecen a las piezas de geometría ocultadas detrás
de otra cosa. Este control se realiza mediante una técnica llamada ZBuffering. Puede encontrar
más información sobre cómo se procesan los modelos en
http://docs.unity3d.com/Manual/SLCullAndDepth. Html. La etiqueta IgnoreProjector hace que
este objeto no sea afectado por los proyectores de Unity. Por último, RenderType desempeña
un papel en el reemplazo de sombreado, un tema que se tratará brevemente en el Capítulo 9,
Jugabilidad y Efectos de pantalla. El último concepto introducido es alpha: fade. Esto indica
que todos los píxeles de este material tienen que ser mezclados con lo que estaba en la
pantalla antes de acuerdo a sus valores alfa. Sin esta directiva, los píxeles se dibujarán en el
orden correcto, pero no tendrán ninguna transparencia.

Código de Transparencia
Shader "CookbookShaders/Transparent" {
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags {
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
CGPROGRAM
#pragma surface surf Standard alpha:fade
sampler2D _MainTex;
fixed4 _Color;

struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutputStandard o) {
float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

Creación de un shader holográfico


Cada año se lanzan más y más juegos con temas espaciales. Una parte importante de un buen
juego de ciencia ficción es la forma en que la tecnología futurista se presenta e integra en el
juego. No hay nada que grita futurista más que hologramas. A pesar de estar presentes en
muchos sabores, los hologramas se representan a menudo como proyecciones
semitransparentes y finas de un objeto. Esta fórmula le muestra cómo crear un sombreado
que simula tales efectos. Tome esto como punto de partida: puede añadir ruido, líneas de
exploración animadas y vibraciones para crear un efecto holográfico realmente sobresaliente.
La siguiente imagen muestra un ejemplo de un efecto holográfico:
Preparándose
Como los efectos holográficos muestran sólo los contornos de un objeto, llamaremos a este
sombreador Silueta. Adjuntarlo a un material y asignarlo a su modelo 3D.

Cómo hacerlo…
Los cambios siguientes modificarán nuestro sombreador actual en uno holográfico:
1. Agregue la propiedad siguiente al sombreador:
_DotProduct("Rim effect", Range(-1,1)) = 0.25
2. Agregue su respectiva variable a la sección CGPROGRAM:
float _DotProduct;
3. Como este material es transparente, agregue las siguientes etiquetas:
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Nota
Según el tipo de objeto que va a utilizar, es posible que desee que su parte trasera de
Aparecer. Si este es el caso, agregue Cull Off para que la parte posterior del modelo no sea
Eliminado (sacrificado).
4. Este sombreado no está tratando de simular un material realista, por lo que no hay
necesidad de usar el modelo de iluminación PBR. La reflectancia lambertiana, que es
muy barata, se utiliza en su lugar. Además, debemos desactivar cualquier iluminación
con nolighting y señalar a Cg que se trata de un Shader Transparente usando alpha:
fade:

#pragma surface surf Lambert alpha:fade nolighting

5. Cambie la estructura de entrada para que Unity lo llene con la dirección de la vista real
y la dirección normal del mundo:
struct Input
{
float2 uv_MainTex;
float3 worldNormal;
float3 viewDir;
};
6. Utilice la siguiente función de superficie. Recuerde que como este shader está usando
la reflectancia Lambertiana como su función de iluminación, el nombre de la
estructura de salida de la superficie debe cambiarse en consecuencia a SurfaeOutput
en lugar de SurfaceOutputStandard:
void surf(Input IN, inout SurfaceOutput o)
{
float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
float border = 1 - (abs(dot(IN.viewDir, IN.worldNormal)));
float alpha = (border * (1 - _DotProduct) + _DotProduct);
o.Alpha = c.a * alpha;
}
Ahora puede utilizar el deslizador de efectos Rim para elegir la intensidad del efecto
holográfico.
Cómo funciona…
Como se mencionó anteriormente, este sombreado funciona mostrando sólo la silueta de un
objeto. Si miramos el objeto desde otro ángulo, su contorno cambiará. Geométricamente
hablando, los bordes de un modelo son todos aquellos triángulos cuya dirección normal es
ortogonal (90 grados) a la dirección de la vista actual. La estructura de entrada declara estos
parámetros, worldNormal y viewDir, respectivamente.
El problema de entender cuando dos vectores son ortogonales se puede resolver usando el
producto punto. Es un operador que toma dos vectores y devuelve cero si son ortogonales.
Utilizamos _DotProduct para determinar cuán cerca de cero el producto de punto tiene que
ser para que el triángulo se desvanezca completamente. El segundo aspecto que se utiliza en
este sombreado es el suave desvanecimiento entre el borde del modelo (totalmente visible) y
el ángulo determinado por _DotProduct (invisible). Esta interpolación lineal se realiza de la
siguiente manera:
float alpha = (border * (1 - _DotProduct) + _DotProduct);
Finalmente, el alfa original de la textura se multiplica con el nuevo cálculo Coeficiente para
lograr el aspecto final.

Hay más…
Esta técnica es muy simple y relativamente simple. Sin embargo, puede utilizarse para una
gran variedad de efectos, como los siguientes:
 La atmósfera ligeramente coloreada de un planeta en juegos de ciencia ficción
 El borde de un objeto que ha sido seleccionado o que se encuentra actualmente bajo
el ratón
 Un fantasma o espectro
 Humo que sale de un motor
 La onda de choque de una explosión
 El escudo de la burbuja de una nave espacial bajo ataque

Ver también
El producto punto juega un papel importante en la forma en que se calculan las reflexiones. El
capítulo 3, Descripción de los modelos de iluminación, explicará en detalle cómo funciona y
por qué es ampliamente utilizado en tantos shaders.

Código fuente Holográfico

Shader "CookbookShaders/Silueta" {
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_DotProduct("Rim effect", Range(-1,1)) = 0.25
}
SubShader {
Tags {
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
LOD 200
CGPROGRAM
#pragma surface surf Lambert alpha:fade
sampler2D _MainTex;
fixed4 _Color;
float _DotProduct;
struct Input {
float2 uv_MainTex;
float3 worldNormal;
float3 viewDir;
};
void surf(Input IN, inout SurfaceOutput o) {
float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
float border = 1 - (abs(dot(IN.viewDir, IN.worldNormal)));
float alpha = (border * (1 - _DotProduct) + _DotProduct);
o.Alpha = c.a * alpha;
}
ENDCG
}
FallBack "Diffuse"
}

Embalaje y mezcla de texturas


Las texturas son útiles para almacenar no sólo cargas de datos, no sólo colores de píxeles ya
que generalmente tendemos a pensar en ellos, sino también para múltiples conjuntos de
píxeles en las direcciones xey, y en los canales RGBA. Realmente podemos empaquetar
múltiples imágenes en una sola textura RGBA y utilizar cada uno de los componentes R, G, B y
A como texturas individuales, extrayendo cada uno de estos componentes en el código de
sombreado.
El resultado de empaquetar imágenes individuales en escala de grises en una sola textura
RGBA se puede ver en la siguiente imagen:

¿Por qué es útil? Bueno, en términos de la cantidad de memoria real que su aplicación ocupa,
las texturas son una gran parte del tamaño de su aplicación. Por lo tanto, para comenzar a
reducir el tamaño de su aplicación, podemos ver todas las imágenes que estamos usando en
nuestro shader y ver si podemos combinar estas texturas en una sola textura.
Cualquier textura que esté en escala de grises puede ser empaquetada en uno de los canales
RGBA de otra textura. Esto podría sonar un poco extraño al principio, pero esta receta va a
demostrar uno de los usos de embalar una textura y el uso de estas texturas en un sombreado.
Un ejemplo de uso de estas texturas empaquetadas es cuando se desea mezclar un conjunto
de texturas en una sola superficie. Usted ve esto a menudo en shaders del tipo del terreno,
donde usted necesita mezclarse en otra textura agradable usando una cierta clase de textura
del control o de la textura embalada, en este caso. Esta receta cubre esta técnica y te muestra
cómo puedes construir los inicios de un agradable tono de textura de cuatro texturas

Preparándose
Vamos a crear un nuevo archivo de sombreado en su carpeta Shaders y luego crear un nuevo
material para este sombreado. La convención de nomenclatura es totalmente suya para su
shader y archivos de material, así que intente lo mejor para mantenerlos organizados y fáciles
de hacer referencia más adelante.
Una vez que tengas tu shader y material listo, crea una nueva escena en la que podemos
probar nuestro shader. También tendrá que recoger cuatro texturas que usted desea mezclar.
Éstos pueden ser cualquier cosa, pero para un sombreador agradable del terreno, usted
deseará la hierba, la suciedad, la suciedad rocosa, y las texturas de la roca. Estas son las
texturas de color que vamos a utilizar para esta receta, que se incluyen con este libro. Por
último, también necesitará una textura de mezcla que está repleta de imágenes en escala de
grises. Esto nos dará las cuatro texturas de mezcla que podemos usar para dirigir cómo las
texturas de color se colocarán en la superficie del objeto. Podemos utilizar texturas de mezcla
muy intrincadas para crear una distribución muy realista de texturas de terreno sobre una
malla de terreno, como se ve en la siguiente imagen:

Cómo hacerlo…
Aprendamos cómo usar texturas empaquetadas ingresando el código mostrado en los
siguientes pasos:
1. Tenemos que añadir algunas propiedades a nuestro bloque de propiedades.
Necesitaremos cinco objetos sampler2D, o texturas, y dos propiedades de color:
Properties{
_MainTint ("Diffuse Tint", Color) =(1,1,1,1)
// Agregue las propiedades a continuación para poder ingresar todas nuestras texturas
_ColorA ("Terrain Color A", Color) = (1,1,1,1)
_ColorB ("Terrain Color B", Color) = (1,1,1,1)
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_BlendTex ("Blend Texture", 2D) = ""{}
}
2. Entonces necesitamos crear las variables de sección de SubShader {} que serán nuestro
enlace a los datos en el bloque Properties:
CGPROGRAM
#pragma surface surf Lambert
float4 _MainTint;
float4 _ColorA;
float4 _ColorB;
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _BlendTex;
sampler2D _ATexture;
3. Así que ahora tenemos nuestras propiedades de textura y las estamos pasando a
nuestra función SubShader {}. Con el fin de permitir al usuario cambiar las tarifas de
baldosas en una base por textura, tendremos que modificar nuestra estructura de
entrada. Esto nos permitirá utilizar los parámetros de mosaico y offset en cada textura:
struct Input {
float2 uv_RTexture;
float2 uv_GTexture;
float2 uv_BTexture;
float2 uv_ATexture;
float2 uv_BlendTex;
};
4. En la función surf (), obtenga la información de textura y guárdela en su propio
Variables para que podamos trabajar con los datos de una manera limpia y fácil de
entender:
// Obtener los datos de píxeles de la textura de mezcla
// necesitamos un flotador 4 aquí porque la textura
// devolverá R, G, B y A o X, Y, Z y W
float4 blendData = tex2D(_BlendTex, IN.uv_BlendTex);
// Obtener los datos de las texturas que queremos mezclar
float4 rTexData = tex2D(_RTexture, IN.uv_RTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_GTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_BTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_ATexture);
5. Combinemos cada una de nuestras texturas utilizando la función lerp (). Se necesitan
tres argumentos, lerp (valor: a, valor: b, mezcla: c). La función lerp () toma dos texturas
y las mezcla con el valor de flotador dado en el último argumento:
// No, necesitamos construir un nuevo valor RGBA y agregar todo
// la mezcla de la textura de nuevo juntos
float4 finalColor;
finalColor = lerp(rTexData, gTexData, blendData.g);
finalColor = lerp(finalColor, bTexData, blendData.b);
finalColor = lerp(finalColor, aTexData, blendData.a);finalColor.a =1.0;
6. Finalmente, multiplicamos nuestras texturas mezcladas con los valores de tinte de
color y usamos el color rojo
Canal para determinar dónde se encuentran los dos colores de tinte de terreno
diferentes:
// Añada a nuestros colores tintométricos para el terreno
float4 terrainLayers = lerp(_ColorA, _ColorB, blendData.r);
finalColor *= terrainLayers;
finalColor = saturate(finalColor);
o.Albedo = finalColor.rgb * _MainTint.rgb;
o.Alpha = finalColor.a;
El resultado de combinar cuatro texturas de terreno y crear una técnica de tintado de terreno
se puede ver en la siguiente imagen:

Cómo funciona…
Esto puede parecer como unas pocas líneas de código, pero el concepto detrás de la mezcla es
en realidad bastante simple. Para que la función funcione, hay que emplear la función
incorporada de built-in lerp() de la biblioteca estándar de CgFX. Esta función nos permite
seleccionar un valor entre el argumento uno y el argumento de utilizar el argumento tres como
la cantidad de mezcla:
Función Descripción
Esto implica una interpolación lineal:
(1 - f) * a + b * f
lerp( a , b,f ) Aquí, a y b son tipos vectoriales o escalares coincidentes. El
parámetro f puede ser un escalar o un vector de
Del mismo tipo que a y b.
Así, por ejemplo, si queríamos encontrar el valor medio entre 1 y 2, podríamos alimentar el
valor 0.5 como el tercer argumento a la función lerp () y devolvería el valor 1.5. Esto funciona
perfectamente para nuestras necesidades de mezcla ya que los valores de un canal individual
en una textura RGBA son valores de flotador único, generalmente en el rango de 0 a 1.
En el shader, simplemente tomamos uno de los canales de nuestra textura de mezcla y lo
usamos para
Unidad el color que se recoge en una función lerp () para cada píxel. Por ejemplo, tomamos
nuestra textura de hierba y textura de suciedad, usamos el canal rojo de nuestra textura de
mezcla, y alimentamos esto a una función lerp (). Esto nos dará el resultado de color mezclado
correcto para cada píxel en la superficie. Una representación más visual de lo que está
sucediendo cuando se utiliza la función lerp () se muestra en la siguiente imagen:

El código de sombreado simplemente utiliza los cuatro canales de la textura de mezcla y todas
las texturas de color para crear una textura final mezclada. Esta textura final se convierte
entonces en nuestro color que podemos multiplicar con nuestra iluminación difusa.
Código Mezcla Textura
Shader "CookbookShaders/MesclaTextura" {
Properties {
_MainTint ("Diffuse Tint", Color) =(1,1,1,1)
//Agregue las propiedades a continuación para poder ingresar todas nuestras texturas
_ColorA ("Terrain Color A", Color) = (1,1,1,1)
_ColorB ("Terrain Color B", Color) = (1,1,1,1)
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_BlendTex ("Blend Texture", 2D) = ""{}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert alpha:fade

float4 _MainTint;
float4 _ColorA;
float4 _ColorB;
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _BlendTex;
sampler2D _ATexture;

struct Input {
float2 uv_RTexture;
float2 uv_GTexture;
float2 uv_BTexture;
float2 uv_ATexture;
float2 uv_BlendTex;
};

void surf (Input IN, inout SurfaceOutput o) {


// Obtener los datos de píxeles de la textura de mezcla
// necesitamos un flotador 4 aquí porque la textura
// devolverá R, G, B y A o X, Y, Z y W
float4 blendData = tex2D(_BlendTex, IN.uv_BlendTex);
// Obtener los datos de las texturas que queremos mezclar
float4 rTexData = tex2D(_RTexture, IN.uv_RTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_GTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_BTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_ATexture);
// No, necesitamos construir un nuevo valor RGBA y agregar todo
// la mezcla de la textura de nuevo juntos
float4 finalColor;
finalColor = lerp(rTexData, gTexData, blendData.g);
finalColor = lerp(finalColor, bTexData, blendData.b);
finalColor = lerp(finalColor, aTexData, blendData.a);finalColor.a =1.0;
// Añada a nuestros colores tintométricos para el terreno
float4 terrainLayers = lerp(_ColorA, _ColorB, blendData.r);
finalColor *= terrainLayers;
finalColor = saturate(finalColor);
o.Albedo = finalColor.rgb * _MainTint.rgb;
o.Alpha = finalColor.a;

}
ENDCG
}
FallBack "Diffuse"
}

Crear un círculo alrededor de su terreno


Muchos juegos RTS muestran distancias (ataque de alcance, distancia de movimiento, vista,
etc.) trazando un círculo alrededor de la unidad seleccionada. Si el terreno es plano, esto se
puede hacer simplemente estirando un quad con la textura de un círculo. Si ese no es el caso,
el quad será muy probablemente recortado detrás de una colina u otra pieza de la geometría.
Esta receta le mostrará cómo crear un sombreado que le permite dibujar círculos alrededor de
un objeto de complejidad arbitraria. Si quieres poder mover o animar tu círculo, necesitaremos
tanto un shader como un script C #. La siguiente imagen muestra un ejemplo de dibujo de un
círculo en una región montañosa usando un sombreado:
Preparándose
A pesar de trabajar con cada pieza de geometría, esta técnica está orientada a terrenos.
Por lo tanto, el primer paso es establecer un terreno en Unity.
1. Comencemos creando un nuevo shader llamado RadiusShader y el material respectivo,
Radius.
2. Tenga el personaje preparado para su objeto; Dibujaremos un círculo alrededor de él.
3. En el menú, navegue hasta GameObject | Objeto 3D | Terreno para crear un nuevo
terreno.
4. Cree la geometría para su terreno. Puede importar uno existente o dibujar Su propio
uso de las herramientas disponibles (levantar / bajar terreno, altura de la pintura,
Altura lisa).
5. Las terrazas son objetos especiales en Unity, y la forma en que el mapeo de textura
funciona en ellos es diferente de los modelos 3D tradicionales. No puede proporcionar
_MainTex desde un sombreado, ya que debe proporcionarse directamente desde el
propio terreno. Para ello, seleccione Textura de pintura y haga clic en Añadir
textura ...:
6. Ahora que la textura está ajustada, usted tiene que cambiar el material del terreno de
modo que un shader de encargo pueda ser proporcionado. Desde Configuración de
terreno, cambie la propiedad Material a Personalizado y, a continuación, arrastre el
material Radius al cuadro Material personalizado.
Ahora está listo para crear su shader.
Cómo hacerlo…
Empecemos editando el archivo RadiusShader:
1. En el nuevo sombreador, agregue estas cuatro propiedades:
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius", Float) = 0.5
_RadiusColor("Radius Color", Color) = (1,0,0,1)
_RadiusWidth("Radius Width", Float) = 2
2. Agregue sus respectivas variables a la sección CGPROGRAM:
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
3. La entrada a nuestra función superficial requiere no sólo la UV de la textura, sino
también la posición (en coordenadas del mundo) de cada punto del terreno. Podemos
recuperar este parámetro cambiando la estructura de entrada de la siguiente manera:
struct Input
{
float2 uv_MainTex; // The UV of the terrain texture
float3 worldPos; // The in-world position
};
4. Por último, utilizamos esta función de superficie:
void surf(Input IN, inout SurfaceOutputStandard o)
{
float d = distance(_Center, IN.worldPos);
if (d > _Radius && d < _Radius + _RadiusWidth)
o.Albedo = _RadiusColor;
else
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
Estos pasos son todo lo que se necesita para dibujar un círculo en su terreno. Puede utilizar la
pestaña Inspector del material para cambiar la posición, el radio y el color del círculo.
Moviendo el círculo
Si desea que el círculo siga a su personaje, otros pasos son necesarios:

1. Cree un nuevo script C # llamado Radius.


2. Agregue estas propiedades al script:
public Material radiusMaterial;
public float radius = 1;
public Color color = Color.white;
3. En el método Update (), agregue estas líneas de código:
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetColor("_RadiusColor", color);
4. Adjunte el script a su personaje.
5. Finalmente, arrastre el material Radius a la ranura Material de Radius del script.

Ahora puedes mover tu personaje y esto creará un círculo bonito alrededor de él.
Cambiar las propiedades de la secuencia de comandos Radius también cambiará el radio.

Cómo funciona…
Los parámetros relevantes para dibujar un círculo son su centro, radio y color. Todos ellos
están disponibles en el sombreador con los nombres _Center, _Radius y _RadiusColor. Al
agregar la variable worldPos a la estructura Input, estamos pidiendo a Unity que nos
proporcione la posición del píxel que estamos dibujando expresado en coordenadas
mundiales. Esta es la posición real de un objeto en el editor.
La función surf () es donde se dibuja realmente el círculo. Calcula la distancia desde
El punto que se está dibujando y el centro del radio, entonces comprueba si está entre
_Radius y _Radius + _RadiusWidth; Si este es el caso, utiliza el color elegido. En el otro caso,
sólo muestra el mapa de textura como todos los otros shaders vistos hasta ahora.

Código Dibujo Circulo

Shader "CookbookShaders/RadiusShader" {
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Center("Center", Vector) = (0,0,0,0)
_Radius("Radius", Float) = 0.5
_RadiusColor("Radius Color", Color) = (1,0,0,1)
_RadiusWidth("Radius Width", Float) = 2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows

// Use shader model 3.0 target, to get nicer looking lighting


#pragma target 3.0
sampler2D _MainTex;
fixed4 _Color;
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;

struct Input {
float2 uv_MainTex; // The UV of the terrain texture
float3 worldPos; // The in-world position
};

void surf (Input IN, inout SurfaceOutputStandard o) {


float d = distance(_Center, IN.worldPos);
if (d > _Radius && d < _Radius + _RadiusWidth)
o.Albedo = _RadiusColor;
else
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;

}
ENDCG
}
FallBack "Diffuse"
}

Capítulo 3. Descripción de los modelos de iluminación


En los capítulos anteriores, introdujimos Shaders de superficie y explicamos cómo podemos
cambiar las propiedades físicas (como Albedo y Specular) para simular diferentes materiales.
¿Cómo funciona esto realmente? En el corazón de cada Surface Shader, existe su modelo de
iluminación. Es la función que toma estas propiedades y calcula la sombra final de cada píxel.
La unidad suele ocultar esto a los desarrolladores porque para escribir un modelo de
iluminación, hay que entender cómo la luz refleja y refractan en las superficies. Este capítulo le
mostrará finalmente cómo funcionan los modelos de iluminación y le dará los fundamentos
para crear los suyos.
En este capítulo, aprenderá los siguientes procedimientos:
 Creación de un modelo de iluminación difusa personalizada
 Creación de un Shader Toon
 Creación de un tipo especular de Phong
 Creación de un tipo especular BlinnPhong
 Creación de un tipo especular anisotrópico

Introducción
Simular la forma en que funciona la luz es una tarea muy desafiante y que consume muchos
recursos. Durante muchos años, los videojuegos han utilizado modelos de iluminación muy
sencillos que, a pesar de su falta de realismo, eran muy creíbles. Incluso si la mayoría de los
motores 3D utilizan ahora procesadores físicos, vale la pena explorar algunas técnicas más
sencillas. Los que se presentan en este capítulo son razonablemente realistas y ampliamente
adoptados en dispositivos con bajos recursos como los teléfonos móviles. La comprensión de
estos modelos de iluminación sencilla también es esencial si desea crear uno propio.
Creación de un modelo de iluminación difusa personalizado
Si está familiarizado con Unity 4, puede saber que el sombreado predeterminado que
proporcionó se basó en un modelo de iluminación llamado reflectancia lambertiana. Esta
receta le mostrará cómo es posible crear un sombreado con un modelo de iluminación
personalizado y explicar las matemáticas y la aplicación detrás de él. La siguiente imagen
muestra la misma geometría renderizada con un Shader estándar (derecha) y Lambert difuso
uno (izquierda):

Los shaders basados en la reflectancia lambertiana se clasifican como no fotorrealistas; Ningún


objeto en el mundo real se parece realmente a esto. Sin embargo, Lambert Shaders todavía se
utilizan a menudo en los juegos de bajo poly como producir un contraste limpio entre las caras
de las geometrías complejas. El modelo de iluminación utilizado para calcular la reflectancia
Lambertiana también es muy eficiente, lo que lo hace perfecto para juegos móviles. Unity ya
nos ha proporcionado una función de iluminación que podemos usar para nuestros shaders. Se
llama el modelo Lambertian de iluminación. Es una de las formas más básicas y eficientes de
reflectancia, que se puede encontrar en muchos juegos incluso hoy en día. Dado que ya está
construido en el lenguaje Unity Surface Shader, pensamos que es mejor comenzar con esto
primero y construir sobre él. También puede encontrar un ejemplo en el manual de referencia
de Unity, pero profundizaremos con él y explicaremos de dónde vienen los datos y por qué
está funcionando de la manera que es. Esto le ayudará a obtener una base agradable en la
creación de modelos de iluminación personalizada para que podamos aprovechar este
conocimiento en las recetas futuras de este capítulo.
Preparándose
Comencemos por realizar los siguientes pasos:
1. Crear un nuevo shader y darle un nombre.
2. Cree un nuevo material, déle un nombre y asigne el nuevo shader a su propiedad de
sombreado.
3. Luego, cree un objeto de esfera y colóquelo aproximadamente en el centro de la
escena.
4. Finalmente, vamos a crear una luz direccional para echar algo de luz sobre nuestro
objeto.
Cuando sus activos se hayan configurado en Unity, debe tener una escena similar a la
Siguiente captura de pantalla:
Cómo hacerlo…
La reflectancia lambertiana se puede lograr con los siguientes cambios en el sombreado:
1. Comience agregando las siguientes propiedades al bloque de propiedades del
sombreador:
_MainTex("Texture", 2D) = "white"
2. Cambie la directiva #pragma del sombreador para que, en lugar de Standard, utilice
nuestro modelo de iluminación personalizado:
#pragma surface surf SimpleLambert
3. Utilice una función de superficie muy simple, que sólo muestre la textura según su
Datos UV:
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
4. Agregue una función llamada LightingSimpleLambert () que contendrá lo siguiente
Código para la reflectancia Lambertiana:
half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 1);
c.a = s.Alpha;
return c;
}
Cómo funciona…
Como se vio anteriormente en el capítulo 1, Creación de su primer Shader, la directiva #
pragma se utiliza para especificar qué función de superficie utilizar. La elección de un modelo
de iluminación diferente funciona de manera similar: SimpleLambert obliga a Cg a buscar una
función llamada IluminaciónSimpleLambert (). Nota La iluminación al principio, que se omite
en la directiva. La función de iluminación toma tres parámetros: la salida de la superficie (que
contiene las propiedades físicas tales como el albedo y la transparencia), la dirección de la luz
que viene, y su atenuación. Según la reflectancia lambertiana, la cantidad de luz que una
superficie refleja depende del ángulo entre la luz incidente y la superficie normal. Si has jugado
billar en la piscina, seguramente estás familiarizado con este concepto; La dirección de una
bola depende de su ángulo incidente contra la pared. Si golpeas una pared en un ángulo de 90
grados, la bola volverá a ti; Si lo golpea con un ángulo muy bajo, su dirección será mayormente
sin cambios. El modelo Lambertiano hace la misma suposición; Si la luz golpea un triángulo con
un ángulo de 90 grados, toda la luz se refleja hacia atrás. Cuanto menor sea el ángulo, menos
luz se reflejará hacia usted. Este concepto se muestra en la siguiente imagen:

Este concepto simple tiene que traducirse en una forma matemática. En álgebra vectorial, el
ángulo entre dos vectores unitarios se puede calcular a través de un operador llamado
producto punto. Cuando el producto punto es igual a cero, dos vectores son ortogonales, lo
que significa que hacen un ángulo de 90 grados. Cuando es igual a uno (o menos uno), son
paralelos entre sí. Cg tiene una función llamada dot (), que implementa el producto punto de
manera muy eficiente. La siguiente imagen muestra una fuente de luz (sol) que brilla sobre una
superficie compleja. L indica la dirección de la luz (llamada lightDir en el shader) y N es la
normal a la superficie. La luz se refleja con el mismo ángulo que golpea la superficie:

La reflectancia de Lambertian simplemente utiliza el producto de punto NdotL como un


coeficiente multiplicativo para la intensidad de la luz:
I=N*L
Cuando N y L son paralelos, toda la luz se refleja de nuevo a la fuente, haciendo que la
geometría aparezca más brillante. La variable _LightColor0 contiene el color de la luz que se
calcula.
Nota
Antes de la Unidad 5, la intensidad de las luces era diferentes. Si está utilizando un shader
difuso antiguo basado en el modelo Lambertian, puede observar que NdotL se multiplicó por
dos:
(NdotL * atten * 2) en lugar de (NdotL * atten). Si está importando un sombreado
personalizado desde Unity 4, tendrá que corregirlo manualmente. Legacy Shaders, sin
embargo, ya han sido diseñados teniendo en cuenta este aspecto. Cuando el producto punto
es negativo, la luz viene del lado opuesto del triángulo. Esto no es un problema para las
geometrías opacas ya que los triángulos que no se enfrentan frontalmente a la cámara son
eliminados (descartados) y no renderizados. Este Lambert básico es un gran punto de partida
cuando usted está prototipando sus shaders como usted puede conseguir mucho logrado en
términos de escribir la funcionalidad básica del shader sin tener que preocuparse por las
funciones básicas de iluminación. Unity nos ha proporcionado un modelo de iluminación que
ya ha tomado la tarea de crear una iluminación Lambert para usted. Si observa el archivo
UnityCG.cginc que se encuentra en el directorio de instalación de Unity en la carpeta Datos,
notará que dispone de modelos de iluminación Lambert y BlinnPhong disponibles para su uso.
En el momento en que compile su sombreado con #pragma surf surface Lambert, le está
diciendo al shader que utilice la implementación de Unity de la función de iluminación Lambert
en el archivo UnityCG.cginc para que no tengamos que escribir ese código una y otra vez.
Exploraremos cómo funciona el modelo BlinnPhong más adelante en este capítulo.
Creación de un Shader Toon
Uno de los efectos más utilizados en los juegos es el toon shading, que también se conoce
como cel shading (corto de celuloide). Es una técnica de renderización no fotorrealista que
hace que los modelos 3D parezcan planos. Muchos juegos lo usan para dar la ilusión de que los
gráficos están siendo dibujados a mano en lugar de ser modelados en 3D. Puede ver, en la
siguiente imagen, una esfera renderizada con Shader estándar (derecha) y Toon Shader
(izquierda):
Lograr este efecto utilizando sólo funciones superficiales no es imposible, pero sería
extremadamente costoso y requiere mucho tiempo. La función de superficie, de hecho, sólo
funciona en las propiedades del material, no su condición de iluminación real. Como toon
shader requiere para cambiar la forma en que la luz se refleja, necesitamos crear nuestro
modelo de iluminación personalizada en su lugar.
Preparándose
Preparándose
Comencemos este procedimiento creando un shader y su material e importando una textura
especial, como sigue:
1. Comience creando un nuevo shader; En este ejemplo, extenderemos el que se hizo en
la receta anterior.
2. Cree un nuevo material para el shader y adjúntelo a un modelo 3D. El toon shading
funciona mejor en superficies curvas.
3. Esta receta requiere una textura adicional llamada mapa de rampa. Es importante que
cambie su modo de ajuste a abrazadera. Si desea que los bordes entre los colores sean
nítidos, el modo de filtro también se debe establecer en Punto:

Cómo hacerlo…
La estética toon se puede lograr con los siguientes cambios en el sombreado:
1. Agregue una nueva propiedad para una textura llamada _RampTex:
_RampTex ("Ramp", 2D) = "white" {}
2. Añada su variable relativa en la sección CGPROGRAM:
sampler2D _RampTex;
3. Cambie la directiva # pragma para que señale a una función llamada LightingToon ():
#pragma surface surf Toon
4. Utilice este modelo de iluminación:
fixed4 LightingToon (SurfaceOutput s, fixed3 lightDir, fixed atten){
half NdotL = dot(s.Normal, lightDir);
NdotL = tex2D(_RampTex, fixed2(NdotL, 0.5));
fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * NdotL * atten;
c.a = s.Alpha;
return c;
}

Cómo funciona…
La característica principal del sombreado de toon es la forma en que se hace la luz; Las
superficies no están sombreadas uniformemente. Para lograr este efecto, necesitamos un
mapa de rampa. Su propósito es reasignar la intensidad de luz Lambertian NdotL a otro valor.
Utilizando un mapa de rampa sin gradiente, podemos forzar la iluminación para que sea
renderizada en pasos. La siguiente imagen muestra cómo se utiliza el mapa de rampa para
corregir la intensidad de la luz:

Hay más…
Hay muchas maneras diferentes se puede lograr un efecto de sombreado toon. El uso de
diferentes rampas puede producir cambios dramáticos en la apariencia de sus modelos, por lo
que debe experimentar para encontrar el mejor. Una alternativa a las texturas en rampa es
ajustar la intensidad de luz NdotL de modo que sólo pueda asumir un cierto número de valores
equidistantemente muestreados de 0 a 1:

half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {


half NdotL = dot (s.Normal, lightDir);
half cel = floor(NdotL * _CelShadingLevels) / (_CelShadingLevels -0.5); //
Snap
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * cel * atten;
c.a = s.Alpha;
return c;
}
El código de ajuste multiplica NdotL veces _CelShadingLevels, lo redondea a un entero y luego
lo divide de nuevo. Al hacer esto, la cantidad de cel es obligado a asumir uno de los
_CelShadingLevels valores equidistantes de 0 a 1. Esto elimina la necesidad de una textura
rampa y hace todos los pasos de color del mismo tamaño. Si va para esta implementación,
recuerde agregar una propiedad llamada _CelShadingLevels a su shader.
Código Shader Toon
Shader "CookbookShaders/ShaderToon" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_RampTex ("Ramp", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }

CGPROGRAM
#pragma surface surf Toon

sampler2D _RampTex;
sampler2D _MainTex;

struct Input {
float2 uv_MainTex;
};

void surf(Input IN, inout SurfaceOutput o) {


o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}

fixed4 LightingToon (SurfaceOutput s, fixed3 lightDir, fixed atten){


half NdotL = dot(s.Normal, lightDir);
NdotL = tex2D(_RampTex, fixed2(NdotL, 0.5));

fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * NdotL * atten * 2;
c.a = s.Alpha;

return c;
}

ENDCG
}
FallBack "Diffuse"
}

Creación de un tipo especular de Phong


La especularidad de una superficie de objeto simplemente describe lo brillante que es. Estos
tipos de efectos se refieren a menudo como efectos dependientes de la vista en el mundo del
sombreado. Esto se debe a que, para lograr un efecto especular realista en los shaders, es
necesario incluir la dirección en la que la cámara o el usuario se encuentra frente a la
superficie del objeto. El tipo especular más básico y de rendimiento fácil es el efecto especular
de Phong. Es el cálculo de la dirección de la luz que se refleja fuera de la superficie en
comparación con la dirección de la vista del usuario. Es un modelo Specular muy común
utilizado en muchas aplicaciones, desde juegos a películas. Aunque no es el más realista en
términos de modelar con precisión el Especular reflejado, da una gran aproximación que
funciona bien en la mayoría de las situaciones. Además, si su objeto está más lejos de la
cámara y no hay necesidad de un especular muy preciso, esta es una gran manera de
proporcionar un efecto especular a sus shaders. En esta receta, cubriremos cómo implementar
la versión por vértex del shader y también la versión por píxel usando algunos nuevos
parámetros en la estructura Input de Shader de Surface. Vamos a ver la diferencia y discutir
cuándo y por qué utilizar estas dos implementaciones diferentes para diferentes situaciones.

Preparándose
Para comenzar con esta receta, realice los siguientes pasos:
1. Cree un nuevo shader, material y objeto, y déles nombres apropiados para que pueda
encontrarlos más tarde.
2. Conecte el sombreador al material y al material al objeto. Para terminar tu nueva
escena, crea una nueva luz direccional para que podamos ver nuestro efecto Especular
como lo codificamos.

Cómo hacerlo…
Siga los siguientes pasos para crear un modelo de iluminación Phong:
1. Es posible que esté viendo un patrón en este punto, pero siempre nos gusta comenzar
con nuestra parte más básica del proceso de escritura de sombreado: la creación de
propiedades. Por lo tanto, vamos a agregar las siguientes propiedades al shader:
Properties
{
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_SpecularColor ("Specular Color", Color) = (1,1,1,1)
_SpecPower ("Specular Power", Range(0,30)) = 1
}
2. A continuación, debemos asegurarnos de agregar las variables correspondientes a
nuestro bloque CGPROGRAM en nuestro bloque SubShader {}:
float4 _SpecularColor;
sampler2D _MainTex;
float4 _MainTint;
float _SpecPower;
3. Ahora tenemos que añadir nuestro modelo de iluminación personalizado para que
podamos calcular nuestro propio especular de Phong. No se preocupe si no tiene
sentido en este punto; Cubriremos cada línea de código en la sección Cómo
funciona .... Agregue el siguiente código a la función SubShader {} del shader:
fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){
// Reflection
float NdotL = dot(s.Normal, lightDir);
float3 reflectionVector = normalize(2.0 * s.Normal * NdotL -
lightDir);
// Specular
float spec = pow(max(0, dot(reflectionVector, viewDir)), _SpecPower);
float3 finalSpec = _SpecularColor.rgb * spec;
// Final effect
fixed4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * max(0,NdotL) * atten) +
(_LightColor0.rgb * finalSpec);
c.a = s.Alpha;
return c;
}
4. Finalmente, tenemos que decirle al bloque CGPROGRAM que necesita usar nuestra
función de iluminación personalizada en lugar de una de las incorporadas. Hacemos
esto cambiando la sentencia #pragma a lo siguiente:
CGPROGRAM
#pragma surface surf Phong
La siguiente captura de pantalla muestra el resultado de nuestro modelo de iluminación Phong
personalizado usando nuestro propio vector de reflexión personalizado:

Cómo funciona…
Vamos a desglosar la función de iluminación por sí mismo, como el resto del sombreador debe
ser bastante familiar a usted en este momento. En las recetas anteriores, hemos utilizado una
función de iluminación que proporciona sólo la dirección de la luz, lightDir. Unity viene con un
conjunto de funciones de iluminación que puede utilizar, incluyendo una que proporciona la
dirección de la vista, viewDir. Consulte la tabla siguiente o vaya a
http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html:
Not viewdependent half4 Lighting Name You choose (SurfaceOutput s, half3 lightDir, half atten);
View-dependent half4 Lighting Name You choose (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
En nuestro caso, estamos haciendo un shader Specular, por lo que necesitamos tener la
estructura de función de iluminación dependiente de la vista. Por lo tanto, tenemos que
escribir lo siguiente:
CPROGRAM
#pragma surface surf Pong
fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
// ...
}
Esto le dirá al shader que queremos crear nuestro propio shader dependiente de la vista.
Siempre asegúrese de que su nombre de función de iluminación es el mismo en su declaración
de función de iluminación y la instrucción #pragma, o Unity no podrá encontrar su modelo de
iluminación. Los componentes que juegan un papel en el modelo Phong se describen en la
siguiente imagen. Tenemos la dirección de la luz L (junto con su reflejo perfecto R) y la
dirección normal N. Todos ellos han sido encontrados antes en el modelo de Lambertian, con
la excepción de V, que es la dirección de la vista:

El modelo de Phong supone que la intensidad luminosa final de una superficie reflectante se
da y dos componentes: su color difuso y valor especular, como sigue:
I=D+S
El componente difuso D permanece inalterado desde el modelo de Lambertiano:
D=N*L
El componente especular S se define como sigue:
S=(R*V)P
Aquí, p es la potencia especular definida como _SpecPower en el sombreado. El único
parámetro desconocido es R, que es el reflejo de L de acuerdo con N. En álgebra vectorial, esto
se puede calcular de la siguiente manera:
R=2N*(N*L)-L
Esto es exactamente lo que se calcula en lo siguiente:
float3 reflectionVector = normalize(2.0 * s.Normal * NdotL - lightDir);
Esto tiene el efecto de doblar la normal hacia la luz; Como un vértice normal apunta lejos de la
luz, se ve obligado a mirar a la luz. Consulte la siguiente captura de pantalla para obtener una
representación más visual. La secuencia de comandos que produce este efecto de depuración
se incluye en la página de soporte del libro en
https://www.packtpub.com/books/content/support:

La siguiente captura de pantalla muestra el resultado final de nuestro cálculo de Phong


Specular aislado en el shader:

Creación de un tipo especular BlinnPhong


Blinn es otra forma más eficiente de calcular y estimar la especularidad. Se hace mediante la
obtención de la media vector de la dirección de la vista y la dirección de la luz. Fue introducido
en el mundo de Cg por Jim Blinn. Él encontró que era mucho más eficiente conseguir apenas el
medio vector en vez de calcular nuestros propios vectores de la reflexión. Reduce el código y el
tiempo de procesamiento. Si realmente mira el modelo de iluminación BlinnPhong
incorporado incluido en el archivo UnityCG.cginc, notará que está utilizando el medio vector
también, por lo que se denomina BlinnPhong. Es sólo una versión más simple del cálculo
completo de Phong.

Preparándose
Para comenzar con esta fórmula, realice los siguientes pasos:
1. Esta vez, en lugar de crear una escena totalmente nueva, vamos a usar los objetos y la
escena que tenemos, y crear un nuevo shader y material y nombrarlos BlinnPhong.
2. Una vez que tenga un nuevo shader, haga doble clic en él para lanzar MonoDevelop
para poder comenzar a editar nuestro shader.

Cómo hacerlo…
Realice los siguientes pasos para crear un modelo de iluminación BlinnPhong:
1. En el primer lugar, tenemos nuestras propias propiedades al bloque de propiedades
para controlar la apariencia del restablecimiento Especular:
Properties
{
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_SpecularColor ("Specular Color", Color) = (1,1,1,1)
_SpecPower ("Specular Power", Range(0.1,60)) = 3
}
2. A continuación, debemos asegurarnos de que hemos creado las variables
correspondientes en nuestro bloque CGPROGRAM para poder acceder a los datos de
nuestro bloque de propiedades en nuestro subshader:
sampler2D _MainTex;
float4 _MainTint;
float4 _SpecularColor;
float _SpecPower;
3. Ahora es el momento de crear nuestro modelo de iluminación personalizado que
procesará nuestros cálculos Difuso y Especular. El código es el siguiente:
fixed4 LightingCustomBlinnPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten){
float NdotL = max(0,dot(s.Normal, lightDir));
float3 halfVector = normalize(lightDir + viewDir);
float NdotH = max(0, dot(s.Normal, halfVector));
float spec = pow(NdotH, _SpecPower) * _SpecularColor;
float4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb *
_SpecularColor.rgb * spec) * atten;
c.a = s.Alpha;
return c;
}
4. Para completar nuestro shader, tendremos que decirle a nuestro bloque CGPROGRAM que use nuestro
modelo de iluminación personalizado en lugar de uno incorporado modificando la instrucción #pragma
con el siguiente código:
CGPROGRAM
#pragma surface surf CustomBlinnPhong
La siguiente captura de pantalla muestra los resultados de nuestro modelo de iluminación BlinnPhong:
Cómo funciona…
El Specular de BlinnPhong es casi exactamente como el Specular de Phong, excepto que es más
eficiente porque usa menos código para lograr casi el mismo efecto. Antes de la introducción
de renderizado físicamente, este enfoque fue la opción por defecto para la reflexión especular
en la Unidad 4. El cálculo del vector de reflexión R es generalmente caro. El Specular de
BlinnPhong lo reemplaza con el medio vector H entre la dirección de la vista V y la dirección de
la luz L:

En lugar de calcular nuestro propio vector de reflexión, simplemente vamos a obtener el


vector a medio camino entre la dirección de la vista y la dirección de la luz, simulando
básicamente el vector de reflexión. En realidad, se ha encontrado que este enfoque es más
físicamente preciso que el último enfoque, pero pensamos que era necesario mostrarle todas
las posibilidades:

Según el álgebra vectorial, el medio vector se puede calcular de la siguiente manera:

Aquí, |V+L| Es la longitud del vector V+L . En Cg, simplemente necesitamos agregar la dirección
de la vista y la dirección de la luz juntos y luego normalizar el resultado a un vector unitario:
float3 halfVector = normalize(lightDir + viewDir);
Entonces, simplemente necesitamos puntear el vértice normal con este nuevo medio vector
para obtener nuestro valor especular principal. Después de esto, lo llevamos a un poder de
_SpecPower y lo multiplicamos por la variable de color Specular. Es mucho más ligero en el
código y las matemáticas, pero todavía nos da un buen resaltado especular que trabajará para
un montón de situaciones en tiempo real.

Ver también
Los modelos de luz que se ven en este capítulo son extremadamente simples; Ningún material
real es perfectamente mate o perfectamente especular. Por otra parte, no es infrecuente que
los materiales complejos tales como ropa, madera, y piel requieran el conocimiento de cómo
la luz se dispersa en las capas debajo de la superficie. Use la siguiente tabla para recapitular los
diferentes modelos de iluminación encontrados hasta ahora:

Hay otros modelos interesantes como el modelo de iluminación Oren-Nayar para


Superficies: https://en.wikipedia.org/wiki/Oren%E2%80%93Nayar_reflectance_model
Código:
Shader "CookbookShaders/BlinnPhong" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_SpecColor ("Specular Color", Color) = (1,1,1,1)
_SpecPower ("Specular Power", Range(0,1)) = 0.5
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf CustomBlinnPhong
sampler2D _MainTex;
float4 _MainTint;
float4 _SpecularColor;
float _SpecPower;
struct Input {
float2 uv_MainTex;
};
fixed4 LightingCustomBlinnPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed
atten){
float NdotL = max(0,dot(s.Normal, lightDir));
float3 halfVector = normalize(lightDir + viewDir);
float NdotH = max(0, dot(s.Normal, halfVector));
float spec = pow(NdotH, _SpecPower) * _SpecularColor;
float4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb * _SpecularColor.r
gb * spec) * atten;
c.a = s.Alpha;
return c;
}

void surf (Input IN, inout SurfaceOutput o)


{
half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint;
o.Specular = _SpecPower;
o.Gloss = c.r;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

Creación de un tipo especular anisotrópico


Anisotrópico es un tipo de Especular o reflexión que simula la direccionalidad de las ranuras en
una superficie y modifica / estira el Especular en la dirección perpendicular. Es muy útil cuando
se desea simular metales cepillados, no un metal con una superficie clara, lisa y pulida. Imagine
el Especular que usted ve cuando mira el lado de los datos de un CD o DVD o la forma
Especular se forma en la parte inferior de una olla o sartén. Usted notará que si examina
cuidadosamente la superficie, verá que hay una dirección a las ranuras en la superficie,
generalmente en la manera en que el metal fue cepillado. Cuando se aplica un Especular a esta
superficie, se obtiene un Specular estirado en la dirección perpendicular. Esta receta le
presentará el concepto de aumentar sus reflejos especulares para lograr diferentes tipos de
superficies cepilladas. En las recetas futuras, vamos a mirar formas en las que podemos utilizar
los conceptos de esta receta para lograr otros efectos como reflejos estirados y pelo, pero
aquí, usted va a aprender los fundamentos de la técnica en primer lugar. Usaremos este
sombreador como una referencia para nuestro Shader anisotrópico personalizado:
http://wiki.unity3d.com/index.php?title=Anisotropic_Highlight_Shader La siguiente captura de
pantalla muestra ejemplos de diferentes tipos de efectos especulares que se pueden lograr
usando Shaders anisotrópicos En Unidad:
Preparándose
Comencemos esta receta creando un shader, su material y algunas luces para nuestra escena:
1. Cree una nueva escena con algunos objetos y luces para que podamos depurar
visualmente nuestro sombreado.
2. A continuación, crear un nuevo sombreado y material, y conectarlos a nuestros
objetos.
3. Por último, necesitaremos algún tipo de mapa normal que indicará la direccionalidad
de nuestro resaltado especular anisotrópico.

La siguiente captura de pantalla muestra el mapa normal de anisotropía que usaremos para
esta receta. Está disponible en la página de soporte del libro en
https://www.packtpub.com/books/content/support:

Cómo hacerlo…
Para crear un efecto anisotrópico, necesitamos realizar los siguientes cambios en el
sombreador creado anteriormente:
1. Primero necesitamos agregar las propiedades que vamos a necesitar para nuestro
shader. Estos permitirán un gran control artístico sobre la apariencia final de la
superficie:
Properties
{
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_SpecularColor ("specular Color", Color) = (1,1,1,1)
_Specular ("Specular Amount", Range(0,1)) = 0.5
_SpecPower ("Specular Power", Range(0,1)) = 0.5
_AnisoDir ("Anisotropic Direction", 2D) = "" {}
_AnisoOffset ("Anisotropic Offset", Range(-1,1)) = -0.2
}
2. A continuación, necesitamos hacer la conexión entre nuestro bloque de propiedades y
nuestro bloque SubShader {} para que podamos usar los datos proporcionados por el
bloque de propiedades:
sampler2D _MainTex;
sampler2D _AnisoDir;
float4 _MainTint;
float4 _SpecularColor;
float _AnisoOffset;
float _Specular;
float _SpecPower;
3. Ahora podemos crear nuestra función de iluminación que producirá el efecto
anisotrópico correcto en nuestra superficie. Usaremos el siguiente código para esto:
fixed4 LightingAnisotropic(SurfaceAnisoOutput s, fixed3 lightDir, half3 viewDir, fixed atten){
fixed3 halfVector = normalize(normalize(lightDir) + normalize(viewDir));
float NdotL = saturate(dot(s.Normal, lightDir));
fixed HdotA = dot(normalize(s.Normal + s.AnisoDirection), halfVector);
float aniso = max(0, sin(radians((HdotA + _AnisoOffset) * 180)));
float spec = saturate(pow(aniso, s.Gloss * 128) * s.Specular);
fixed4 c;
c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb *_SpecularColor.rgb * spec)) * atten;
c.a = s.Alpha;
return c;
}
4. Para usar esta nueva función de iluminación, necesitamos decirle a la instrucción
#pragma del subshader que la busque en lugar de usar una de las funciones de
iluminación incorporadas. También le decimos al sombreador que apunte al modelo
de shader 3.0 para que tengamos más espacio para texturas en nuestro programa:
CGPROGRAM
#pragma Surface surf Anisotropic
#pragma target 3.0
5. También hemos dado el mapa normal anisotrópico sus propias UVs declarando el
siguiente código en la estructura de entrada. Esto no es totalmente necesario, ya que
sólo podemos usar los UVs de la textura principal, pero esto nos da un control
independiente sobre el embaldosado de nuestro efecto de metal cepillado para que
podamos escalar a cualquier tamaño que queremos:
struct Input{
float2 uv_MainTex;
float2 uv_AnisoDir;
};
6. También necesitamos agregar la estructura SurfaceAnisoOutput:
struct SurfaceAnisoOutput{
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
fixed3 AnisoDirection;
half Specular;
fixed Gloss;
fixed Alpha;
};
7. Finalmente, necesitamos usar la función surf () para pasar los datos correctos a
nuestra función de iluminación. Por lo tanto, obtendremos la información por píxel de
nuestro mapa normal anisotrópico y estableceremos nuestros parámetros especulares
de la siguiente manera:
void surf(Input IN, inout SurfaceAnisoOutput o){
half4 c = tex2D(_MainTex, IN.uv_MainTex) * _MainTint;
float3 anisoTex = UnpackNormal(tex2D(_AnisoDir, IN.uv_AnisoDir));
o.AnisoDirection = anisoTex;
o.Specular = _Specular;
o.Gloss = _SpecPower;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
El mapa normal anisotrópico nos permite dar la dirección de la superficie y nos ayuda a
dispersar el resalte especular alrededor de la superficie. La siguiente captura de pantalla
muestra el resultado de nuestro Shader anisotrópico:

Cómo funciona…
Vamos a dividir este sombreado en sus componentes básicos y explicar por qué estamos
recibiendo el efecto. En su mayoría cubriremos la función de iluminación personalizada aquí,
ya que el resto del sombreador debería ser bastante autoexplicativo en este punto.
Comenzamos primero declarando nuestra propia estructura SurfaceAnisoOutput. Necesitamos
hacer esto para obtener la información por píxel del mapa normal anisotrópico, y la única
manera que podemos hacer esto en un Shader de superficie es usar una función tex2D () en la
función surf (). El código siguiente muestra la estructura de salida de superficie personalizada
utilizada en nuestro sombreado:
struct SurfaceAnisoOutput{
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
fixed3 AnisoDirection;
half Specular;
fixed Gloss;
fixed Alpha;
};
Podemos utilizar la estructura SurfaceAnisoOutput como una forma de interactuar entre la
función de iluminación y la función de superficie. En nuestro caso, almacenamos la
información de textura por píxel en la variable llamada anisoTex en nuestra función surf () y
luego pasando estos datos a la estructura SurfaceAnisoOutput almacenándola en la variable
AnisoDirection. Una vez que tengamos esto, podemos usar la información por pixel en la
función de iluminación usando
S.AnisoDirection. Con esta conexión de datos establecida, podemos pasar a nuestros cálculos
de iluminación reales. Esto comienza por obtener lo habitual fuera del camino, el medio
vector, por lo que no tenemos que hacer el cálculo de reflexión completa y la iluminación
difusa, que es el vértice normal punteado con el vector de luz o la dirección. Esto se hace en Cg
con las líneas siguientes:
Fixed3 halfVector = normalizar (normalizar (lightDir) + normalizar (viewDir));
Float NdotL = saturate (punto (s.Normal, lightDir));
Entonces, comenzamos la modificación real al Specular para conseguir la mirada derecha.
Primero punteamos la suma normalizada de los vectores normal y per-pixel del vértice de
nuestro mapa normal anisotrópico con medio vector calculado en el paso anterior. Esto nos da
un valor flotante que da un valor de 1 como la superficie normal, que es modificada por el
mapa normal Anisotrópico cuando se convierte en paralelo con medioVector y 0 como es
perpendicular. Finalmente, modificamos este
Valor con una función sin () para que básicamente obtengamos un relieve medio más oscuro y
finalmente un efecto de anillo basado en medioVector. Todas las operaciones mencionadas
anteriormente se resumen en las dos líneas siguientes de código Cg:
Fijo HdotA = punto (normalizar (s.Normal + s.AnisoDirection), medioVector);
Float aniso = max (0, sin (radians ((HdotA + _AnisoOffset) * 180)));
Finalmente, escala el efecto del valor del aniso llevándolo a una potencia de la pérdida de
peso, y después disminuye globalmente su fuerza multiplicándola por s. Especular:
Float spec = saturate (pow (aniso, s.Gloss * 128) * specular);
Este efecto es ideal para crear superficies de metal más avanzadas, especialmente las que se
cepillan y parecen tener direccionalidad para ellas. También funciona bien para el cabello o
cualquier tipo de superficie blanda con la direccionalidad a la misma. La siguiente captura de
pantalla muestra el resultado de mostrar el cálculo de iluminación anisotrópico final:

Shader "CookbookShaders/anisotropico" {
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_SpecularColor ("specular Color", Color) = (1,1,1,1)
_Specular ("Specular Amount", Range(0,1)) = 0.5
_SpecPower ("Specular Power", Range(0,1)) = 0.5
_AnisoDir ("Anisotropic Direction", 2D) = "" {}
_AnisoOffset ("Anisotropic Offset", Range(-1,1)) = -0.2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Anisotropic
#pragma target 3.0
sampler2D _MainTex;
sampler2D _AnisoDir;
float4 _MainTint;
float4 _SpecularColor;
float _AnisoOffset;
float _Specular;
float _SpecPower;

struct SurfaceAnisoOutput{
fixed3 Albedo;
fixed3 Normal;
fixed3 Emission;
fixed3 AnisoDirection;
half Specular;
fixed Gloss;
fixed Alpha;
};

inline fixed4 LightingAnisotropic(SurfaceAnisoOutput s, fixed3 lightDir, half3 viewDir, fixe


d atten){
fixed3 halfVector = normalize(normalize(lightDir) + normalize(viewDir));
float NdotL = saturate(dot(s.Normal, lightDir));

fixed HdotA = dot(normalize(s.Normal + s.AnisoDirection), halfVector);


float aniso = max(0, sin(radians((HdotA + _AnisoOffset) * 180)));

float spec = saturate(pow(aniso, s.Gloss * 128) * s.Specular);

fixed4 c;
c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb * _SpecularColor.rgb
* spec)) * (atten);
c.a = 1.0;
return c;
}

struct Input{
float2 uv_MainTex;
float2 uv_AnisoDir;
};

void surf(Input IN, inout SurfaceAnisoOutput o){


half4 c = tex2D(_MainTex, IN.uv_MainTex) * _MainTint;
float3 anisoTex = UnpackNormal(tex2D(_AnisoDir, IN.uv_AnisoDir));

o.AnisoDirection = anisoTex;
o.Specular = _Specular;
o.Gloss = _SpecPower;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

Capítulo 4. Renderizado físicamente en Unity 5


Uno de los mayores cambios introducidos en Unity 5 es el renderizado físicamente, que
también se conoce como PBR. Los capítulos anteriores han mencionado repetidamente sin
revelar demasiado sobre él. Si quieres entender no sólo cómo funciona PBR, sino cómo sacarle
el máximo partido, este es el capítulo que deberías leer. En este capítulo, aprenderá las
siguientes recetas:
 Comprender la configuración metálica
 Adición de transparencia a PBR
 Creación de espejos y superficies reflectantes
 Luces de hornear en su escena

Introducción
Todos los modelos de iluminación encontrados en el Capítulo 3, Comprensión de los Modelos
de Iluminación, eran descripciones muy primitivas de cómo se comporta la luz. El aspecto más
importante durante su elaboración fue la eficiencia. El sombreado en tiempo real es costoso, y
técnicas como Lambertian o BlinnPhong son un compromiso entre el coste computacional y el
realismo. Tener una unidad de procesamiento gráfico (GPU) más potente nos ha permitido
escribir progresivamente modelos de iluminación y motores de renderización más sofisticados,
con el objetivo de simular cómo la luz se comporta realmente. Esto es, en pocas palabras, la
filosofía detrás de PBR. Como su nombre sugiere, trata de acercarse lo más posible a la física
detrás de los procesos que dan un aspecto único a cada material. A pesar de esto, el término
PBR ha sido ampliamente utilizado en las campañas de marketing y es más un sinónimo de
renderización de vanguardia en lugar de una técnica bien definida. Unity 5 implementa PBR
introduciendo dos cambios importantes. El primero es un modelo de iluminación
completamente nuevo (llamado Estándar). Shaders de superficie permiten a los
desarrolladores especificar las propiedades físicas de un material, pero no imponen
restricciones físicas reales sobre ellos. PBR rellena esta brecha utilizando un modelo de
iluminación que hace cumplir los principios de la física como la conservación de la energía (un
objeto no puede reflejar más luz que la cantidad que recibe), la dispersión de la microescala
(las superficies rugosas reflejan la luz más erráticamente en comparación con las lisas)
Reflexiones aparecen en los ángulos de pastoreo), y la oclusión superficial (el oscurecimiento
de las esquinas y otras geometrías que son difíciles de iluminar). Todos estos aspectos, y
muchos otros, se utilizan para calcular el modelo de iluminación estándar. El segundo aspecto
que hace al PBR tan realista se llama Iluminación Global (GI) y es la simulación del transporte
ligero físicamente. Significa que los objetos no se dibujan en la escena como si fueran
entidades separadas. Todos ellos contribuyen a la interpretación final como la luz puede
reflexionar sobre ellos antes de golpear algo más. Este aspecto no se captura en los shaders
mismos, pero es una parte esencial de cómo funciona el motor de renderizado.
Desafortunadamente, simular con precisión cómo los rayos de luz realmente rebotan sobre las
superficies en tiempo real está más allá de las capacidades de las modernas GPUs. Unity 5 hace
algunas optimizaciones inteligentes que permiten retener la fidelidad visual sin sacrificar el
rendimiento. Algunas de las técnicas más avanzadas (como las reflexiones), sin embargo,
requieren la entrada del usuario. Todos estos aspectos se tratarán en este capítulo. Es
importante recordar que PBR y GI no garantizan automáticamente que su juego sea
fotorrealista. Conseguir el fotorealismo es una tarea muy desafiante y, como cualquier arte,
requiere gran expertize y habilidades excepcionales.

Comprender la configuración metálica


Unity 5 proporciona dos tipos diferentes de sombreadores PBR; Se mencionan en el menú
desplegable de la pestaña Inspector del material como Estándar y Estándar (Configuración
especular). La principal diferencia es que la primera expone la propiedad Metálica, mientras
que la última la reemplaza con Especular. Ambas configuraciones metálicas y especulares
representan diferentes maneras en las cuales uno puede inicializar materiales de PBR. Uno de
los conceptos que ha impulsado PBR es la capacidad de proporcionar significativas, las
propiedades físicamente relacionadas que los artistas y desarrolladores pueden ajustar y jugar.
Las propiedades de algunos materiales son más fáciles de representar indicando cómo son
metálicos, mientras que para algunos, el otro es más importante para definir cómo reflejan las
luces directamente. Si ha utilizado Unity 4 en el pasado, Standard (Specular setup) podría
parecer más familiar para usted. Esta receta le mostrará cómo usar la configuración metálica
de manera efectiva. Es importante recordar que el flujo de trabajo metálico no es sólo para
materiales metálicos; Es una manera de definir cómo se verán los materiales de acuerdo a su
superficie metálica o no metálica. A pesar de presentarse como dos tipos diferentes de
shaders, tanto Metallic y Specular configuraciones son generalmente igualmente expresivo.
Como se muestra en la documentación de Unity en
http://docs.unity3d.com/Manual/StandardShaderMetallicVsSpecular.html, la misma
Los materiales se pueden recrear generalmente con ambas configuraciones (vea la imagen
siguiente):
Preparándose
Preparándose
Esta fórmula utilizará el Shader estándar proporcionado en Unity 5, por lo que no es necesario
crear uno nuevo. Los pasos para iniciar la receta son los siguientes:
1. Crear un nuevo material.
2. Desde su Inspector, asegúrese de que Standard esté seleccionado en su menú
desplegable Shader.
También necesitará un modelo 3D con textura.

Cómo hacerlo…
Hay dos texturas principales que deben configurarse en el Shader estándar: Albedo y Metallic.
Para utilizar eficazmente el flujo de trabajo metálico, debemos inicializar estos mapas
correctamente:
1. El mapa Albedo debe ser inicializado con la textura no iluminada del modelo 3D.
2. Para crear el mapa metálico, comience por duplicar el archivo para su mapa Albedo.
Puede hacerlo seleccionando el mapa en la pestaña Proyecto y presionando Ctrl + D.
3. Utilice blanco (#ffffff) para colorear las regiones del mapa que corresponden a
materiales que están hechos de metal puro. Utilice negro (# 000000) para todos los
demás colores. Los tonos de gris deben usarse para superficies metálicas polvorientas,
resistidas o desgastadas, óxido, pintura rayada, etc. De hecho, Unity utiliza sólo el
canal rojo para almacenar el valor metálico; Los verdes y los azules son ignorados.
4. Utilice el canal alfa de la imagen para proporcionar información sobre la suavidad del
material.
5. Asigne el mapa metálico al material. Los controles deslizantes Metalizado y Suavizado
desaparecerán, ya que estas dos propiedades ahora están controladas por el mapa.
Cómo funciona…
Legacy Shaders permite a los artistas crear materiales que rompen fácilmente la ilusión del
fotorealismo al tener condiciones de iluminación que son imposibles en la realidad. Esto pasa
Porque todas las propiedades de un material expuesto en un Shader Legacy Surface no están
correlacionadas. Al introducir el flujo de trabajo metálico, Unity 5 impone más restricciones en
el aspecto de los objetos, haciendo más difícil para los artistas crear materiales ilógicos. Los
metales son conocidos por la conducción de electricidad; La luz está en forma de ondas
electromagnéticas, lo que significa que casi todos los metales se comportan de manera similar
en comparación con los no conductores (a menudo referidos como aislantes). Los conductores
tienden a reflejar la mayoría de los fotones (70- 100%), lo que resulta en alta reflectancia. La
luz restante es absorbida, en lugar de difundida, lo que sugiere que los conductores tienen un
componente difuso muy oscuro. Los aislantes, por el contrario, tienen una baja reflectancia
(4%); El resto de la luz se dispersa en la superficie, contribuyendo a su aspecto difuso. En el
Shader estándar, los materiales puramente metálicos tienen componentes difusos oscuros y el
color de sus reflejos especulares está determinado por el mapa de Albedo. Por el contrario, el
componente difuso de materiales puramente no metálicos está determinado por el mapa de
Albedo; El color de sus reflejos especulares está determinado por el color de la luz entrante.
Siguiendo estos principios se permite que el flujo de trabajo metálico combine el albedo y el
especular en el mapa de Albedo, imponiendo comportamientos físicamente precisos. Esto
también permite ahorrar más espacio, dando por resultado una aceleración significativa a los
costes de control reducido sobre la mirada de sus materiales.

Ver también
Ver también
Para obtener más información sobre la configuración metálica, puede consultar estos enlaces:
Gráfico de calibración: Cómo calibrar un material metálico
(Http://blogs.unity3d.com/wp-content/uploads/2014/11/UnityMetallicChart.png)
Tabla de materiales: Cómo inicializar los parámetros de Shader estándar para
Materiales (http://docs.unity3d.com/Manual/StandardShaderMaterialCharts.html)
Quixel MEGASCANS: Una vasta biblioteca de materiales, incluyendo texturas y PBR
Parámetros (http://quixel.se/megascans)
Conversión de texturas PBR: Cómo se pueden convertir los shaders tradicionales en shaders
PBR
(Http://www.marmoset.co/toolbag/learn/pbr-conversion)
Diseñador de sustancias: un software basado en nodos para trabajar con PBR
(Https://www.allegorithmic.com/products/substance-designer)
La teoría de la representación físicamente basada: Una guía completa sobre PBR
(Https://www.allegorithmic.com/pbr-guide)

Adición de transparencia a PBR


La transparencia es un aspecto tan importante en los juegos que el Standard Shader admite
tres formas diferentes de hacerlo. Esta receta es útil si necesita tener materiales realistas con
propiedades transparentes o semitransparentes. Los vidrios, las botellas, las ventanas, y los
cristales son buenos candidatos para los shaders transparentes de PBR. Esto es porque usted
todavía puede tener todo el realismo introducido por PBR con la adición de un efecto
transparente o translúcido. Si necesita transparencia para algo diferente, como elementos de
interfaz de usuario o arte de píxeles, hay alternativas más eficientes que se exploran en la
sección Creación de una receta de material transparente en el Capítulo 2, Shaders de
superficie y Mapeado de texturas.
Nota
Para tener un material estándar transparente, el cambio del canal alfa de su propiedad de
color Albedo no es suficiente. A menos que establezca correctamente su modo de renderizado,
su material no aparecerá transparente.

Preparándose
Esta receta utilizará el Shader estándar, por lo que no hay necesidad de crear uno nuevo:
1. Crear un nuevo material.
2. Asegúrese de que la propiedad Shader está establecida en Estándar o Estándar
(configuración especular) en la ficha Inspector del material.
3. Asigne el material recién creado al objeto 3D que desea que sea transparente.
Cómo hacerlo…
El Shader estándar proporciona tres tipos diferentes de transparencias. A pesar de ser muy
similares, tienen diferencias sutiles y encajan contextos diferentes.
Materiales semi-transparentes
Algunos materiales tales como plásticos claros, cristal y vidrio son semitransparentes. Esto
significa que ambos requieren todos los efectos realistas de PBR (como reflejos especulares y
refracción y reflexión de Fresnel) pero permiten ver la geometría detrás. Si esto es lo que
necesita, realice los pasos siguientes:
1. En la pestaña Inspector del material, establezca el Modo de renderizado en
Transparente.
2. La cantidad de transparencia está determinada por el canal alfa del color Albedo o el
mapa Albedo (si existe).
La siguiente imagen muestra la escena de calibración de Unity 5 con cuatro esferas de plástico
muy pulidas. De izquierda a derecha, su transparencia se incrementa. La última esfera es
totalmente transparente, pero conserva todos los efectos añadidos de PBR:

El modo de renderizado transparente es perfecto para ventanas, botellas, joyas y auriculares.


Nota
Debe notar que muchos materiales transparentes no suelen proyectar sombras. Además, las
propiedades metálicas y de suavidad de un material pueden interferir con el efecto de
transparencia. Una superficie similar a un espejo puede tener el alpha puesto a cero, pero si
refleja toda la luz entrante, no aparecerá transparente.

Objetos de decoloración
A veces, desea que un objeto desaparezca completamente con un efecto de atenuación. En
este caso, las reflexiones especulares y la refracción y reflexión de Fresnel también deberían
desaparecer. Cuando un objeto de desvanecimiento es totalmente transparente, también
debe ser invisible. Para ello, realice los pasos siguientes:
1. En la pestaña Inspector del material, establezca el modo de renderizado en Fade.
2. Como antes, use el canal alfa del color o mapa Albedo para determinar la
transparencia final.
La siguiente imagen muestra las esferas de desvanecimiento. Resulta claro de la imagen que
los efectos PBR se desvanecen con la esfera también. Como se puede ver en la siguiente
imagen, la última de la derecha es casi invisible:

Este modo de renderizado funciona mejor para objetos no realistas, como hologramas, rayos
láser, luces falsas, fantasmas y efectos de partículas.

Geometrías sólidas con agujeros


La mayoría de los materiales encontrados en un juego son sólidos, lo que significa que no
permiten que la luz pase a través de ellos. Al mismo tiempo, muchos objetos tienen una
geometría muy compleja (pero plana). Modelar hojas y césped con objetos 3D es a menudo
excesivo. Un enfoque más eficiente es utilizar un cuadrángulo (rectángulo) con una textura de
hoja. Mientras que la hoja en sí es sólida, el resto de la textura debe ser totalmente
transparente. Si esto es lo que desea, realice los pasos siguientes:
1. En la pestaña Inspector del material, establezca el modo de renderizado en Recorte.
2. Utilice el deslizador Cutoff Alpha para determinar el umbral de corte. Todos los píxeles
en el mapa de Albedo con un valor alfa igual o inferior a Cutaff Alfa se ocultará.
La siguiente imagen, tomada de los Tutoriales Oficiales de Unity sobre PBR
(Https://www.youtube.com/watch?v=fD_ho_ofY6A), muestra cómo el efecto de la
El modo de renderizado de corte puede usarse para crear un agujero en la geometría:
Vale la pena notar que Cutout no permite ver la parte posterior de la geometría. En el ejemplo
anterior, no se podía ver el volumen interno de la esfera. Si necesita este efecto, debe crear su
propio shader y asegurarse de que la geometría posterior no se elimine.

Ver también
 Los ejemplos de esta receta se han creado utilizando el Unity 5 ShaderEscena de
calibración, que está disponible gratuitamente en la
Https://www.assetstore.unity3d.com/en/#!/content/25422.

 Más información sobre el albedo y la transparencia se puede encontrar en


http://docs.unity3d.com/Manual/StandardShaderMaterialParameterAlbedoColor.html

Creación de espejos y superficies reflectantes


Los materiales especulares reflejan las luces cuando los objetos se ven desde ciertos ángulos.
Desafortunadamente, incluso la reflexión de Fresnel, que es uno de los modelos más precisos,
no refleja correctamente las luces de objetos cercanos. Los modelos de iluminación
examinados en los capítulos anteriores sólo tenían en cuenta las fuentes de luz, pero
ignoraban la luz que se refleja en otras superficies. Con lo que has aprendido sobre shaders
hasta ahora, hacer un espejo simplemente no es posible. La iluminación global hace esto
posible proporcionando a los shaders PBR información sobre sus alrededores. Esto permite a
los objetos no sólo reflejos especulares, sino también reflexiones reales, que dependen de los
otros objetos a su alrededor. Reflexiones en tiempo real son muy costosas y requieren la
configuración manual y ajustes para trabajar. Cuando se hace correctamente, se pueden
utilizar para crear superficies similares a espejos, como se ve en la siguiente imagen:
Preparándose
Preparándose
Esta receta no contendrá ningún nuevo sombreador. Todo lo contrario; La mayor parte del
trabajo se realiza directamente en el editor. Realice los pasos siguientes:
1. Cree una nueva escena.
2. Crear un quad, que servirá como un espejo.
3. Cree un nuevo material y adjúntelo al espejo.
4. Coloque el quad en una escena con otros objetos.
5. Crear una nueva sonda de reflexión de GameObject | Luz | Sonda de reflexión y
colocarlo en frente del quad.

Cómo hacerlo…
Cómo hacerlo…
Si se han seguido correctamente los pasos anteriores, debe tener un quad en el centro de su
escena, cerca de una sonda de reflexión. Para hacerla en un espejo, algunos cambios
Necesitan hacerse:
1. Cambie el sombreado del material a Estándar y su Modo de renderizado a Opaco.
2. Cambie sus propiedades metálicas y suavidad a una. Usted debe ver el material que
refleja el cielo más claramente.
3. Seleccione la sonda de reflexión y cambie su tamaño y origen de la sonda hasta que
esté delante del cuadrángulo y que encierre todos los objetos que desee reflejar.
4. Finalmente, cambie su Tipo a Real time. Asegúrese de que Culling Mask está
establecido en Everything.
Su sonda de reflexión debe estar configurada, como se muestra en la siguiente imagen:
Si su sonda se utiliza para un espejo real, debe comprobar el indicador de proyección de caja.
Si se utiliza para otras superficies reflectantes, como piezas brillantes de mesas de metal o de
vidrio, puede desmarcarla.

Cómo funciona…
Cuando un sombreador quiere información sobre su entorno, por lo general se proporciona en
la estructura llamada mapas de cubo. Se han mencionado brevemente en el Capítulo 1,
Creación de su primer Shader, como uno de los tipos de propiedades de sombreado, entre
Color, 2D, Float y Vector. Hablando francamente, los mapas de cubos son el equivalente 3D de
las texturas 2D; Representan una vista de 360 grados del mundo, como se ve desde un punto
central. Unity 5 visualiza mapas de cubos con una proyección esférica, como se ve en la
siguiente imagen:

Ver también
Cuando los mapas de cubo se adjuntan con una cámara, se les conoce como skyboxes, ya que
se utilizan para proporcionar una forma de reflejar el cielo. Pueden usarse para reflejar
geometrías que no están en la escena real, como nebulosas, nubes, estrellas, etc. La razón por
la que se llaman mapas de cubo es debido a la forma en que se crean: un mapa de cubo se
compone de seis texturas diferentes, cada uno unido a la cara de un cubo. Puede crear
manualmente un mapa de cubos o delegarlo en una sonda de reflexión. Usted puede imaginar
una sonda de reflexión como una colección de seis cámaras, la creación de un mapa de 360 de
la zona circundante. Esto también le da una idea de por qué las sondas son tan caras. Al crear
uno en nuestra escena, permitimos que la Unidad sepa qué objetos están alrededor del
espejo. Si necesita más superficies reflectantes, puede agregar varias sondas. No necesita más
acción para que las sondas de flexión funcionen. Los Shaders Estándar los utilizarán
automáticamente. Debería notar que cuando se configuran en Real time, representan su mapa
de cubos al principio de cada fotograma. Hay un truco para hacer esto más rápido; Si sabes
que parte de la geometría que quieres reflejar no se mueve, puedes hornear la reflexión. Esto
significa que Unity puede calcular la reflexión antes de iniciar el juego, lo que permite cálculos
más precisos (y computacionalmente caros). Para ello, su sonda de reflexión debe establecerse
en Baked y funcionará sólo para objetos que estén marcados como estáticos. Los objetos
estáticos no pueden moverse o cambiar, lo que los hace perfectos para terrenos, edificios y
accesorios. Cada vez que se mueve un objeto estático, Unity regenerará los mapas de cubo
para sus sondas de reflexión al horno. Esto puede llevar de unos minutos a varias horas. Usted
puede mezclar Realtime y Baked sondas para aumentar el realismo de su juego. Las sondas al
horno proporcionarán reflexiones ambientales de muy alta calidad, mientras que las de tiempo
real se pueden utilizar para mover objetos como coches o espejos. Las siguientes luces de
hornear en su receta escena le explicará en detalle cómo funciona la cocción ligera.
Ver también
Si está interesado en aprender más acerca de las sondas de reflexión, debe consultar estos
enlaces: Unity 5 manual about Reflection Probe:
http://docs.unity3d.com/Manual/class-ReflectionProbe.html

Luces de hornear en su escena


Renderizar la iluminación es un proceso muy costoso. Incluso con GPU de última generación, el
cálculo preciso del transporte ligero (que es la forma en que la luz rebote entre las superficies)
puede llevar horas. Para hacer este proceso factible para los juegos, la representación en
tiempo real es esencial. Los motores modernos ponen en peligro el realismo y la eficiencia; La
mayor parte del cálculo se hace de antemano en un proceso llamado cocción ligera. Esta
receta explicará cómo funciona la cocción ligera y cómo puede obtener el máximo provecho de
ella.

Preparándose
La cocción ligera requiere que usted tenga una escena lista. Debe tener geometrías y,
obviamente, luces. Para esta receta, dependeremos de las características estándar de Unity
para que no haya necesidad de crear shaders o materiales adicionales. Para un mejor control,
es posible que desee acceder a la ventana de iluminación. Si no lo ve, seleccione Ventana |
Iluminación desde el menú y acoplarlo donde es más conveniente para usted.

Cómo hacerlo…
La cocción ligera requiere alguna configuración manual. Hay tres pasos esenciales, pero
independientes, que usted necesita tomar. Configuración de la geometría estática Estos pasos
deben seguirse para la configuración:
1. Identifique todos los objetos de su escena que no cambian de posición, tamaño y
material. Los candidatos posibles son edificios, paredes, terrenos, accesorios, árboles y
otros.
2. Seleccione estos objetos y marque el cuadro Estática de la ficha Inspector, como se
muestra en la siguiente imagen. Si alguno de los objetos seleccionados tiene hijos, Unity
le preguntará si desea que se consideren estáticos también. Si cumplen con los
requisitos (posición fija, tamaño y material), seleccione Sí, cambie los niños en el cuadro
emergente:

3. Si una luz califica como un objeto estático pero ilumina la geometría no estática,
Que su propiedad Baking está establecida en Mixed. Si sólo afectará a objetos estáticos,
establézcalo en Baked.
Configuración de las sondas de luz
Hay objetos en tu juego que se moverán, como el personaje principal, los enemigos y los otros
personajes no jugables (NPCs). Si entran en una región estática que está iluminada, es posible
que desee rodearla con sondas de luz. Para ello, siga los pasos indicados:
1. En el menú, vaya a GameObject | Luz | Grupo de sondas de luz. Un nuevo objeto
llamado Light Probe Group aparecerá en Jerarquía.
1. Una vez seleccionadas, aparecerán cuatro esferas interconectadas. Haz clic y muévelas
por la escena para que encierren la región estática en la que pueden entrar tus
personajes. La siguiente imagen muestra un ejemplo de cómo se pueden usar sondas
de luz para encerrar el volumen de un espacio de oficina estático:

2. Seleccione los objetos en movimiento que entrarán en la región de la sonda de luz.


3. Desde su Inspector, expanda su componente de representación (normalmente
Procesador de malla) y asegúrese de que está activada la opción Usar sondas de luz
(consulte la siguiente imagen):

Decidir dónde y cuándo usar las sondas de luz es un problema crítico; Más información sobre
esto se puede encontrar en la sección Cómo funciona ....
Hornear las luces
Para hornear las luces, siga los pasos indicados:
1. Para finalmente cocer las luces, abra la ventana de iluminación y seleccione su pestaña
Lightmaps.
2. Si la casilla de verificación Auto está activada, Unity ejecutará automáticamente el
proceso de cocción en segundo plano. Si no, haga clic en Construir.
Nota
La cocción ligera puede tardar varias horas incluso en una escena relativamente pequeña. Si
está constantemente moviendo objetos estáticos o luces, Unity reiniciará el proceso desde
cero, causando una severa desaceleración en el editor. Puede desmarcar la casilla de
verificación Auto de la opción Iluminación | Lightmaps para evitar esto para que pueda decidir
cuándo iniciar el proceso manualmente.

Cómo funciona…
La parte más complicada de la representación es el transporte ligero. Durante esta fase, la GPU
calcula cómo los rayos de luz rebotan entre objetos. Si un objeto y sus luces no se mueven,
este cálculo se puede hacer sólo una vez que nunca cambiará durante el juego. Marcar un
objeto como estático es cómo le está diciendo a Unity que se puede hacer una optimización de
este tipo. Hablando francamente, light baking se refiere al proceso de calcular la iluminación
global de un objeto estático y guardarlo en lo que se llama un mapa de luz. Una vez terminada
la cocción, los mapas de luz se pueden ver en la pestaña de Lightmaps de la ventana de
iluminación:

La cocción ligera tiene un gran costo: la memoria. De hecho, cada superficie está retextured de
modo que ya incluye su condición de iluminación. Imaginemos que tienes un bosque de
árboles, todos compartiendo la misma textura. Una vez que se hacen estáticos, cada árbol
tendrá su propia textura. La cocción ligera no sólo aumenta el tamaño de su juego, sino que
puede tomar mucha memoria de textura si se usa indiscriminadamente. El segundo aspecto
introducido en esta receta es la luz de sondeo. La cocción ligera produce resultados de altísima
calidad para geometrías estáticas, pero no funciona en objetos en movimiento. Si su personaje
está entrando en una región estática, puede verse de alguna manera separado del entorno. Su
sombreado no coincide con el entorno, resultando en un resultado estéticamente
desagradable. Otros objetos, como los procesadores de malla de piel, no recibirán iluminación
global incluso si están estáticos. Las luces de cocción en tiempo real no son posibles, aunque
las sondas de luz ofrecen una alternativa efectiva. Cada sonda de luz mide la iluminación global
en un punto específico del espacio. Un grupo de sonda de luz puede tomar muestras de varios
puntos en el espacio, lo que permite interpolar la iluminación global dentro de un volumen
específico. Esto nos permite proyectar una mejor luz sobre objetos en movimiento, incluso a
pesar de que la iluminación global ha sido calculada sólo por unos pocos puntos. Es importante
recordar que las sondas de luz necesitan encerrar un volumen para poder trabajar. Es mejor
colocar sondas de luz en regiones donde hay un cambio repentino en la condición de luz. De
forma similar a los mapas de luz, las sondas consumen memoria y deben colocarse
sabiamente; Recuerde que existen sólo para la geometría no estática. Incluso usando sondas
de luz, hay algunos aspectos que la iluminación global de Unity no puede capturar. Los objetos
no estáticos, por ejemplo, no pueden reflejar luz sobre otros objetos.

Ver también
Puede leer más sobre las sondas de luz en
Http://docs.unity3d.com/Manual/LightProbes.html.

5. Funciones Vertex

Capítulo 5. Funciones Vertex


El término shader se origina del hecho de que Cg se ha utilizado principalmente para simular
condiciones de iluminación realistas (sombras) en modelos 3D. A pesar de esto, los shaders
son ahora mucho más que eso. No sólo definen la forma en que los objetos van a buscar, sino
que también pueden redefinir sus formas enteramente. Si desea aprender a manipular la
geometría de un objeto 3D a través de shaders, este es el capítulo para usted.
En este capítulo, aprenderá las siguientes recetas:
 Acceso a un color de vértice en un Shader de superficie
 Animación de vértices en un Shader de Superficie
 Extruir sus modelos
 Implementación de un sombreador de nieve
 Implementación de una explosión volumétrica

Introducción
En el Capítulo 1, Creando su primer Shader, explicamos que los modelos 3D no son sólo una
colección de triángulos. Cada vértice puede contener datos que son esenciales para
representar correctamente el modelo. Este capítulo explorará cómo acceder a esta
información para usarla en un sombreado. También exploraremos en detalle cómo la
geometría de un objeto puede ser deformada simplemente usando código Cg.

Acceso a un color de vértice en un Shader de superficie


Comencemos este capítulo echando un vistazo a cómo podemos acceder a la información del
vértice de un modelo usando la función de vértice en un Shader de superficie. Esto nos armará
con el conocimiento para comenzar a utilizar los elementos contenidos en el vértice de un
modelo para crear efectos realmente útiles y visualmente atractivos. Un vértice en una función
de vértice puede devolver información sobre sí mismo de la que tenemos que estar
conscientes. De hecho, puede recuperar las direcciones normales de los vértices como un valor
float3, la posición del vértice como float3, e incluso puede almacenar valores de color en cada
vértice y devolver ese color como float4. Esto es lo que vamos a echar un vistazo en esta
receta. Necesitamos ver cómo almacenar información de color y recuperar esta información
de color almacenada dentro de cada vértice de un Shader de Superficie.

Preparándose
Para escribir este shader, necesitamos preparar algunos activos. Los siguientes pasos nos
configurarán para crear este Vertex Shader:
1. Para ver los colores de un vértice, necesitamos tener un modelo que tenga el color
aplicado a sus vértices. Si bien podría utilizar Unity para aplicar colores, tendría que
escribir una herramienta para permitir que un individuo aplicar los colores o escribir
algunos scripts para lograr la aplicación de color. En el caso de esta receta, simplemente
utilizamos Maya para aplicar los colores a nuestro modelo. Este modelo está disponible
en la página de soporte del libro en https://www.packtpub.com/books/content/support.
2. Cree una nueva escena y coloque el modelo importado en la escena.
3. Cree un nuevo sombreador y material. Cuando haya terminado, asigne el sombreado al
material y luego al material al modelo importado.

Su escena ahora debe ser similar a la siguiente captura de pantalla:

Cómo hacerlo…
Con nuestra escena, shader y material creado y listo para comenzar, podemos comenzar a
escribir el código para nuestro shader. Inicie el sombreado haciendo doble clic en él en la
pestaña Proyecto del editor Unity. Realice los pasos siguientes:
1. Como estamos creando un shader muy simple, no necesitaremos incluir ninguna
propiedad en nuestro bloque de propiedades. Todavía incluiremos un color de tinte
global, sólo para mantener la coherencia con los otros shaders de este libro.
Introduzca el código siguiente en el bloque de propiedades de su sombreado:
Properties
{
_MainTint("Global Color Tint", Color) = (1,1,1,1)
}
2. Este siguiente paso le dice a Unity que vamos a incluir una función de vértice en
nuestro sombreado:
CGPROGRAM
#pragma surface surf Lambert vertex:vert
3. Como de costumbre, si hemos incluido propiedades en nuestro bloque de
propiedades, debemos asegurarnos de crear una variable correspondiente en nuestra
declaración CGPROGRAM. Escriba el siguiente código justo debajo de la instrucción
#pragma:
float4 _MainTint;
4. Ahora volvemos nuestra atención a la estructura de entrada. Necesitamos agregar una
nueva variable para que nuestra función surf () pueda acceder a los datos que nos
proporciona nuestra función vert ():
struct Input
{
float2 uv_MainTex;
float4 vertColor;
};
5. Ahora, podemos escribir nuestra función simple vert () para obtener acceso a los
colores almacenados en cada vértice de nuestra malla:
void vert(inout appdata_full v, out Input o)
{
o.vertColor = v.color;
}
6. Finalmente, podemos usar los datos de color de vértice de nuestra estructura de
entrada para ser asignados a los parámetros de o.Albedo en la estructura de
SurfaceOutput incorporada:
void surf (Input IN, inout SurfaceOutput o){
o.Albedo = IN.vertColor.rgb * _MainTint.rgb;
}
7. Con nuestro código completado, podemos volver a entrar en el editor de Unity y dejar
que el shader compile. Si todo va bien, deberías ver algo similar a la siguiente captura
de pantalla:

Cómo funciona…
Cómo funciona…
Unity nos proporciona una forma de acceder a la información de vértice del modelo al que
está conectado un sombreador. Esto nos da el poder de modificar cosas como la posición y el
color de los vértices. Con esta receta, hemos importado una malla de Maya (aunque casi
cualquier aplicación de software 3D se puede utilizar), donde los colores de vértice se
agregaron a Verts. Observará que al importar el modelo, el material predeterminado no
mostrará los colores de vértice. En realidad tenemos que escribir un shader para extraer el
color del vértice y mostrarlo en la superficie del modelo. Unity nos proporciona una gran
cantidad de funcionalidad incorporada al usar Surface Shaders, que hacen que el proceso de
extracción de esta información de vértice sea rápido y eficiente. Nuestra primera tarea es
decirle a Unity que usaremos una función de vértice al crear nuestro shader. Lo hacemos
añadiendo el parámetro vértice: vert a la instrucción #pragma de CGPROGRAM. Esto
automáticamente hace que Unity busque una función vertex llamada vert () cuando vaya a
compilar el hader. Si no encuentra uno, Unity lanzará un error de compilación y le pedirá que
agregue una función vert () a su shader. Esto nos lleva al siguiente paso. Tenemos que
realmente codificar la función vert (), como se ve en el paso 5. Al tener esta función, podemos
acceder a la estructura de datos integrada llamada appdata_full. Esta estructura incorporada
es donde se almacena la información de vértice. Entonces, extraemos la información de color
del vértice pasándola a nuestra estructura de entrada añadiendo el código, o.vertColor =
v.color. La variable o representa nuestra estructura de entrada y la variable v es nuestros datos
de vértice appdata_full. En este caso, simplemente tomamos la información de color de la
estructura appdata_full y la colocamos en nuestra estructura de entrada. Una vez que el color
del vértice está en nuestra estructura de entrada, podemos usarlo en nuestra función surf ().
En el caso de esta receta, simplemente aplicamos el color al parámetro o.Albedo a la
estructura SurfaceOutput incorporada.

Hay más…
También se puede acceder a un cuarto componente a partir de los datos de color verde. Si
observa, la variable vertColor que declaramos en la estructura de entrada es del tipo float4.
Esto significa que también estamos pasando el valor alfa de los colores de vértice. Sabiendo
esto, puede utilizarlo para su ventaja con el fin de almacenar un color de vértice cuarto para
realizar efectos como la transparencia o darse una más máscara para mezclar dos texturas.
Depende de usted y de su producción determinar si realmente necesita usar el cuarto
componente, pero vale la pena mencionarlo aquí. Con Unity 5, ahora tenemos la capacidad de
dirigir shaders a DirectX 11. Esto es genial, pero significa que el proceso de compilación para
los shaders es ahora un poco pickier. Esto significa que necesitamos incluir una línea más de
código en nuestro shader para inicializar la salida de la información de vértice correctamente.
El siguiente código muestra cómo se ve el código de función de vértice, si está utilizando
DirectX 11 en su sombreado:
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
o.vertColor = v.color;
}
Al incluir esta línea de código, el Vertex Shader no lanzará ninguna advertencia, que dice que
no se compilará correctamente en DirectX 11.

Animación de vértices en un Shader de Superficie


Ahora que sabemos cómo acceder a los datos en una base por vértice, vamos a ampliar
nuestro conjunto de conocimientos para incluir otros tipos de datos y la posición de un vértice.
Usando una función de vértice, podemos acceder a la posición de cada vértice en una malla.
Esto nos permite modificar realmente cada vértice individual mientras el sombreado realiza el
procesamiento. En esta receta, crearemos un shader que nos permitirá modificar las
posiciones de cada vértice en una malla con una onda senoidal. Esta técnica se puede utilizar
para crear animaciones para objetos como banderas o ondas en un océano....

Preparándose
Vamos a reunir nuestros activos juntos para que podamos crear el código para nuestro Vertex
Shader:
1. Cree una nueva escena y coloque una malla plana en el centro de la escena.
2. Luego cree un nuevo shader y material.
3. Finalmente, asignar el sombreador al material y al material a la malla plana.
Su escena debe ser similar a la siguiente captura de pantalla:
Cómo hacerlo…

Cómo funciona…
Con nuestra escena lista para salir, vamos a hacer doble clic en nuestro shader recién creado
para abrirlo en MonoDevelop:
1. Comencemos con nuestro sombreado completando el bloque Propiedades:
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_tintAmount ("Tint Amount", Range(0,1)) = 0.5
_ColorA ("Color A", Color) = (1,1,1,1)
_ColorB ("Color B", Color) = (1,1,1,1)
_Speed ("Wave Speed", Range(0.1, 80)) = 5
_Frequency ("Wave Frequency", Range(0, 5)) = 2
_Amplitude ("Wave Amplitude", Range(-1, 1)) = 1
}
2. Ahora necesitamos decirle a Unity que vamos a usar una función de vértice agregando
lo siguiente a la sentencia #pragma:
CGPROGRAM
#pragma surface surf Lambert vertex:vert
3. Para acceder a los valores que nos han dado nuestras propiedades, debemos declarar
una variable correspondiente en nuestro bloque CGPROGRAM:
sampler2D _MainTex;
float4 _ColorA;
float4 _ColorB;
float _tintAmount;
float _Speed;
float _Frequency;
float _Amplitude;
float _OffsetVal;
4. Usaremos la modificación de la posición del vértice como un color verde también. Esto
nos permitirá matizar nuestro objeto:
struct Input
{
float2 uv_MainTex;
float3 vertColor;
};
5. En este punto, podemos realizar nuestra modificación de vértices usando una onda
senoidal y función de vértice. Introduzca el código siguiente después de la estructura
de entrada:
void vert(inout appdata_full v, out Input o)
{
float time = _Time * _Speed;
float waveValueA = sin(time + v.vertex.x * _Frequency) * _Amplitude;
v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA,v.vertex.z);
v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y,v.normal.z));
o.vertColor = float3(waveValueA,waveValueA,waveValueA);
}
6. Finalmente, completaremos nuestro shader realizando una función lerp () entre dos
colores para que podamos teñir los picos y valles de nuestra nueva malla, modificados
por nuestra función de vértice:
void surf (Input IN, inout SurfaceOutput o)
{
half4 c = tex2D (_MainTex, IN.uv_MainTex);
float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb;
o.Albedo = c.rgb * (tintColor * _tintAmount);
o.Alpha = c.a;
}
Después de completar el código de su shader, vuelva a Unity y deje que el shader compile. Una
vez compilado, debería ver algo similar al siguiente creenshot:

Cómo funciona…
Este shader particular utiliza el mismo concepto de la última receta, excepto que esta vez,
estamos modificando las posiciones de los vértices en la malla. Esto es realmente útil si no
desea montar objetos simples, como un indicador, y luego animarlos utilizando una estructura
esquelética o una jerarquía de transformaciones. Simplemente creamos un valor de onda
senoidal utilizando la función sin () que está incorporada en el lenguaje Cg. Después de calcular
este valor, lo añadimos al valor y de cada posición de vértice, creando un efecto parecido a una
onda. También hicimos un poco de modificación a la normal en la malla sólo para darle un
sombreado más realista basado en el valor de onda sinusoidal. Verá lo fácil que es realizar
efectos de vértice más complejos mediante la utilización de los parámetros de vértice
incorporados que Surface Shaders nos proporciona.

Shader "Custom/onda" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_tintAmount ("Tint Amount", Range(0,1)) = 0.5
_ColorA ("Color A", Color) = (1,1,1,1)
_ColorB ("Color B", Color) = (1,1,1,1)
_Speed ("Wave Speed", Range(0.1, 80)) = 5
_Frequency ("Wave Frequency", Range(0, 5)) = 2
_Amplitude ("Wave Amplitude", Range(-1, 1)) = 1

}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Lambert vertex:vert

sampler2D _MainTex;
float4 _ColorA;
float4 _ColorB;
float _tintAmount;
float _Speed;
float _Frequency;
float _Amplitude;
float _OffsetVal;

struct Input {
float2 uv_MainTex;
float3 vertColor;
};
void vert(inout appdata_full v, out Input o){
UNITY_INITIALIZE_OUTPUT(Input,o);
float time = _Time * _Speed;
float waveValueA = sin(time + v.vertex.x * _Frequency) * _Amplitude;
v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA,v.vertex.z + waveValueA);
v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y,v.normal.z));
o.vertColor = float3(waveValueA,waveValueA,waveValueA);
}

void surf (Input IN, inout SurfaceOutput o){


half4 c = tex2D (_MainTex, IN.uv_MainTex);
float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb;
o.Albedo = c.rgb * (tintColor * _tintAmount);
o.Alpha = c.a;
}

ENDCG
}
FallBack "Diffuse"
}

Extruir sus modelos


Uno de los mayores problemas en los juegos es las repeticiones. Crear un nuevo contenido es
una tarea que toma tiempo, y cuando tienes que enfrentar a miles de enemigos, lo más
probable es que todos se vean igual. Una técnica relativamente barata para agregar
variaciones a sus modelos es usar un sombreado que altera su geometría básica. Esta receta le
mostrará una técnica llamada extrusión normal, que puede usarse para crear una versión más
chubbier o skinnier de un modelo, como se muestra en la siguiente imagen con el soldado de
la demostración del campo Unity:
Preparándose
Para esta receta, necesitamos tener acceso al sombreado utilizado por el modelo que desea
modificar. Una vez que lo tiene, lo duplicamos para que podamos editarlo de forma segura. Se
puede hacer de la siguiente manera:
1. Encuentre el sombreado que está usando su modelo y una vez seleccionado, lo
duplicará presionando Ctrl + D.
2. Duplicar el material original del modelo y asignarle el sombreado clonado.
3. Asigne el nuevo material a su modelo y comience a editarlo.
Para que este efecto funcione, su modelo debe tener normales.

Cómo hacerlo…
Cómo hacerlo…
Para crear este efecto, comience modificando el shader duplicado:
1. Comencemos agregando una propiedad a nuestro shader, que se usará para modular
su extrusión. El rango presentado aquí va de -1 a +1, pero puede que tenga que
ajustarlo de acuerdo a sus propias necesidades:
_Amount ("Extrusion Amount", Range(-1,1)) = 0
2. Agrupar la propiedad con su respectiva variable:
float _Amount;
3. Cambie la directiva # pragma para que ahora utilice un modificador de vértice. Puede
hacerlo añadiendo vértice: nombre_función al final del mismo. En nuestro caso, hemos
llamado la función, vert:
#pragma surface surf Lambert vertex:vert
4. Agregue el siguiente modificador de vértice:
void vert (inout appdata_full v) {
v.vertex.xyz += v.normal * _Amount;
}
5. El sombreador está listo; Puede utilizar el control deslizante Extrusión en la pestaña
Inspector del material para que su modelo sea más delgado o más rechoncho.

Cómo funciona…
Surface Shaders funciona en dos pasos. En todos los capítulos anteriores, sólo exploramos su
último: la función superficial. Hay otra función que se puede utilizar: el modificador de vértice.
Toma la estructura de datos de un vértice (que se llama generalmente appdata_full) y aplica
una transformación a él. Esto nos da la libertad de hacer prácticamente todo con la geometría
de nuestro modelo. Señalamos a la unidad de procesamiento gráfico (GPU) que dicha función
existe añadiendo vértice: vert a la directiva # pragma del Surface Shader. Puede consultar el
Capítulo 6, Fragment Shaders y Grab Passes, para saber cómo se pueden definir los
modificadores de vértices en un Vertex y Shader de fragmentos.
Una de las técnicas más simples, pero eficaces, que se pueden utilizar para alterar la geometría
de un modelo se denomina extrusión normal. Funciona proyectando un vértice a lo largo de su
dirección normal. Esto se hace con la siguiente línea de código:
v.vertex.xyz += v.normal * _Amount;
La posición de un vértice se desplaza por _Amount unidades hacia el vértice normal. Si
_Amount llega demasiado alto, los resultados pueden ser bastante desagradables. Con valores
más pequeños, sin embargo, puede agregar una gran cantidad de variaciones a sus modelos.

Hay más…
Si tienes varios enemigos y quieres que cada uno tenga su propio peso, tienes que crear un
material diferente para cada uno de ellos. Esto es necesario ya que los materiales
normalmente se comparten entre los modelos y el cambio uno cambiará todos ellos. Hay
varias maneras en que usted puede hacer esto; El más rápido es crear un script que
automáticamente lo haga por usted. El siguiente script, una vez unido a un objeto con un
Renderer, duplicará su primer material y establecerá automáticamente la propiedad _Amount:
using UnityEngine;
public class NormalExtruder : MonoBehaviour {
[Range(-0.0001f, 0.0001f)]
public float amount = 0;
// Use this for initialization
void Start () {
Material material = GetComponent<Renderer>().sharedMaterial;
Material newMaterial = new Material(material);
newMaterial.SetFloat("_Amount", amount);
GetComponent<Renderer>().material = newMaterial;
}
}

Adición de mapas de extrusión


Esta técnica puede mejorar aún más. Podemos añadir una textura extra (o usar el canal alfa de
la principal) para indicar la cantidad de la extrusión. Esto permite un control mucho mejor
sobre qué partes se suben o bajan. El código siguiente muestra cómo es posible lograr este
efecto:
sampler2D _ExtrusionTex;
void vert(inout appdata_full v) {
float4 tex = tex2Dlod (_ExtrusionTex, float4(v.texcoord.xy,0,0));
float extrusion = tex.r * 2 - 1;
v.vertex.xyz += v.normal * _Amount * extrusion;
}
El canal rojo de _ExtrusionTex se utiliza como coeficiente de multiplicación para la extrusión
normal. Un valor de 0,5 deja el modelo no afectado; Sombras más oscuras o más claras se
utilizan para extrudir vértices hacia dentro o hacia fuera, respectivamente. Usted debe notar
que para probar una textura dentro de un modificador de vértice, tex2Dlod debe utilizarse en
lugar de tex2D.
Nota
En shaders, los canales de color van de 0 a 1, aunque a veces también se necesita representar
valores negativos (como la extrusión hacia adentro). Cuando este es el caso, tratar 0.5 como
cero, teniendo valores más pequeños considerados negativos y valores más altos como
positivos. Esto es exactamente lo que ocurre con las normales, que normalmente están
codificadas en texturas RGB. La función UnpackNormal () se utiliza para asignar un valor en el
rango (0,1) en el rango (-1, + 1). Matemáticamente hablando, esto es equivalente a tex.r * 2 -1.
Los mapas de extrusión son perfectos para zombificar los caracteres reduciendo la piel para
resaltar la forma de los huesos debajo. La siguiente imagen muestra cómo un soldado sano
puede ser transformado en un cadáver usando sólo un mapa de sombreado y extrusión. En
comparación con el ejemplo anterior, puede notar cómo la ropa no se ve afectada. El
sombreado utilizado en la siguiente imagen también oscurece las regiones extruidas para dar
un aspecto aún más demacrado al soldado:
Shader "Custom/deforme" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Amount ("Extrusion Amount", Range(-1,1)) = 0
}
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Standard vertex:vert

struct Input {
float2 uv_MainTex;
};
float _Amount;
void vert (inout appdata_full v) {
v.vertex.xyz += v.normal * _Amount;
}
sampler2D _MainTex;
void surf(Input IN, inout SurfaceOutputStandard o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
FallBack "Diffuse"
}

Implementación de un shader de nieve


La simulación de la nieve siempre ha sido un desafío en los juegos. La gran mayoría de los
juegos simplemente incluye nieve directamente en las texturas del modelo para que sus tops
se vean blancos. Sin embargo, ¿qué pasa si uno de estos objetos comienza a girar? La nieve no
es sólo una capa de pintura en una superficie; Es una acumulación adecuada de material y
debe tratarse como tal. Esta fórmula le muestra cómo dar una mirada de nieve a sus modelos
con sólo un sombreado. Este efecto se consigue en dos pasos. En primer lugar, se utiliza un
color blanco para todos los triángulos que se enfrentan al cielo. En segundo lugar, sus vértices
se extruyen para simular el efecto de la acumulación de nieve. Puede ver el resultado en la
siguiente imagen:
Nota
Tenga en cuenta que esta receta no tiene por objeto crear un efecto de nieve fotorrealista.
Proporciona un buen punto de partida, pero depende de un artista crear las texturas correctas
y encontrar los parámetros adecuados para que encajen en su juego.

Preparándose
Este efecto se basa puramente en shaders. Necesitaremos lo siguiente:
1. Cree un nuevo shader para el efecto de nieve.
2. Cree un nuevo material para el sombreador.
3. Asigne el material recién creado al objeto que desea que esté nevado.

Cómo hacerlo…
Para crear un efecto nevado, abra su sombreador y realice los siguientes cambios:
1. Sustituya las propiedades del sombreador por las siguientes:
_MainColor("Main Color", Color) = (1.0,1.0,1.0,1.0)
_MainTex("Base (RGB)", 2D) = "white" {}
_Bump("Bump", 2D) = "bump" {}
_Snow("Level of snow", Range(1, -1)) = 1
_SnowColor("Color of snow", Color) = (1.0,1.0,1.0,1.0)
_SnowDirection("Direction of snow", Vector) = (0,1,0)
_SnowDepth("Depth of snow", Range(0,1)) = 0
2. Completarlos con sus variables relativas:
sampler2D _MainTex;
sampler2D _Bump;
float _Snow;
float4 _SnowColor;
float4 _MainColor;
float4 _SnowDirection;
float _SnowDepth;
3. Reemplace la estructura de entrada por la siguiente:
struct Input {
float2 uv_MainTex;
float2 uv_Bump;
float3 worldNormal;
INTERNAL_DATA
};
4. Sustituya la función de superficie por la siguiente. Coloreará las partes nevadas de el
modelo blanco:
void surf(Input IN, inout SurfaceOutputStandard o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));
if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) >=
_Snow)
o.Albedo = _SnowColor.rgb;
else
o.Albedo = c.rgb * _MainColor;
o.Alpha = 1;
}
5. Configure la directiva # pragma para que utilice modificadores de vértice:
#pragma surface surf Standard vertex:vert
6. Añada los siguientes modificadores de vértices, que expulsan los vértices cubiertos de
nieve:
void vert(inout appdata_full v) {
float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
if (dot(v.normal, sn.xyz) >= _Snow)
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
}
Ahora puede usar la pestaña Inspector de materiales para seleccionar la cantidad de su
modelo que va a cubrir y la densidad de la nieve.

Cómo funciona…
Este shader funciona en dos pasos.
Colorear la superficie
El primero altera el color de los triángulos que están mirando hacia el cielo. Afecta a todos los
triángulos con una dirección normal similar a _SnowDirection. Como se ha visto antes en el
Capítulo 3, Comprensión de los Modelos de Iluminación, la comparación de los vectores
unitarios se puede hacer usando el producto punto. Cuando dos vectores son ortogonales, su
producto punto es cero; Es uno (o menos uno) cuando están paralelos entre sí. La propiedad
_Snow se utiliza para decidir cómo alineados deben ser considerados frente al cielo. Si observa
de cerca la función de superficie, puede ver que no estamos salpicando la dirección normal y la
dirección de la nieve directamente. Esto se debe a que normalmente se definen en un espacio
diferente. La dirección de la nieve se expresa en coordenadas del mundo, mientras que las
normales del objeto son generalmente relativas al modelo sí mismo. Si giramos el modelo, sus
normales no cambiarán, lo cual no es lo que queremos. Para arreglar esto, necesitamos
convertir las normales de sus coordenadas de objeto a coordenadas del mundo. Esto se hace
con la función WorldNormalVector (), como se ve en el código siguiente:
if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) >= _Snow)
o.Albedo = _SnowColor.rgb;
else
o.Albedo = c.rgb * _MainColor;
Este sombreado simplemente colorea el modelo blanco; Una más avanzada debería inicializar
la estructura SurfaceOutputStandard con texturas y parámetros de un material de nieve
realista.
Alteración de la geometría
El segundo efecto de este sombreador altera la geometría para simular la acumulación de
nieve. En primer lugar, identificamos qué triángulos se han coloreado de blanco probando la
misma condición utilizada en la función superficial. Esta vez, por desgracia, no podemos
confiar en WorldNormalVector () ya que la estructura SurfaceOutputStandard todavía no se ha
inicializado en El modificador de vértices. Utilizamos este otro método en su lugar, que
convierte _SnowDirection a las coordenadas del objeto:
float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
Entonces, podemos extruir la geometría para simular la acumulación de nieve:
if (dot(v.normal, sn.xyz) >= _Snow)
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
Una vez más, este es un efecto muy básico. Uno podría utilizar un mapa de la textura para
controlar la acumulación de la nieve más exactamente o dar una mirada peculiar, desigual.
Ver también
Si necesitas efectos de nieve de alta calidad y accesorios para tu juego, también puedes
consultar estos recursos en Unity Asset Store:
Winter Suite ($ 30): Una versión mucho más sofisticada del sombreador de nieve presentado
En esta receta se puede encontrar en
Https://www.assetstore.unity3d.com/en/#!/content/13927
Winter Pack ($ 60): Un conjunto muy realista de accesorios y materiales para snowy
Pueden encontrarse en
Https://www.assetstore.unity3d.com/en/#!/content/13316

Shader "Custom/shaderNieve" {
Properties {
_MainColor("Main Color", Color) = (1.0,1.0,1.0,1.0)
_MainTex("Base (RGB)", 2D) = "white" {}
_Bump("Bump", 2D) = "bump" {}
_Snow("Level of snow", Range(1, -1)) = 1
_SnowColor("Color of snow", Color) = (1.0,1.0,1.0,1.0)
_SnowDirection("Direction of snow", Vector) = (0,1,0)
_SnowDepth("Depth of snow", Range(0,1)) = 0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Standard vertex:vert

sampler2D _MainTex;
sampler2D _Bump;
float _Snow;
float4 _SnowColor;
float4 _MainColor;
float4 _SnowDirection;
float _SnowDepth;

struct Input {
float2 uv_MainTex;
float2 uv_Bump;
float3 worldNormal;
INTERNAL_DATA
};

void surf(Input IN, inout SurfaceOutputStandard o) {


half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));
if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) >=
_Snow)
o.Albedo = _SnowColor.rgb;
else
o.Albedo = c.rgb * _MainColor;
o.Alpha = 1;
}

void vert(inout appdata_full v) {


float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
if (dot(v.normal, sn.xyz) >= _Snow)
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
}

ENDCG
}
FallBack "Diffuse"
}
Implementación de una explosión volumétrica
El arte del desarrollo del juego es un compromiso inteligente entre el realismo y la eficiencia.
Esto es particularmente cierto para las explosiones; Están en el corazón de muchos juegos, sin
embargo, la física detrás de ellos es a menudo más allá del poder computacional de las
máquinas modernas. Las explosiones son, esencialmente, nada más que bolas de gas muy
calientes; Por lo tanto, la única manera de simularlas correctamente es mediante la
integración de una simulación de fluidos en su juego. Como se puede imaginar, esto es
imposible para la aplicación en tiempo de ejecución, y muchos juegos simulan simplemente
con partículas. Cuando un objeto explota, es común simplemente instanciar muchas partículas
de fuego, humo y escombros que, juntas, pueden lograr resultados creíbles. Este enfoque, por
desgracia, no es muy realista y es fácil de detectar. Hay una técnica intermedia que se puede
utilizar para lograr un efecto mucho más realista: explosiones volumétricas. La idea detrás de
este concepto es que las explosiones no se tratan más como un manojo de partículas; Están
evolucionando objetos 3D, no sólo texturas 2D planas.
Preparándose
Inicie esta receta con los siguientes pasos:
1. Cree un nuevo shader para este efecto.
2. Cree un nuevo material para alojar el sombreador.
3. Conecte el material a una esfera. Puedes crear uno directamente desde el editor,
navegando a GameObject | Objeto 3D | Esfera.
Nota
Esta receta funciona bien con la Unity Sphere estándar, pero si necesita grandes
explosiones, puede que tenga que usar una esfera de mayor polietileno. De hecho, una
función de vértice sólo puede modificar los vértices de una malla. Todos los demás
puntos serán interpolados usando las posiciones de los vértices cercanos. Menos
vértices significan una resolución más baja para sus explosiones.
4. Para esta receta, también necesitará una textura de rampa que tiene, en un gradiente,
todos los colores que sus explosiones tendrán. Puede crear una textura como la
siguiente con GIMP o Photoshop:

5. Una vez que tengas la imagen, importa a Unity. A continuación, desde su inspector,
asegúrese de que el modo de filtro esté ajustado en modo bilineal y de ajuste en
abrazadera. Estos dos ajustes aseguran que la textura de la rampa se muestre sin
problemas.
6. Por último, usted necesitará una textura ruidosa. Puede buscar en Internet texturas de
ruido libremente disponibles. Los más usados son generados usando ruido de Perlin.
Cómo hacerlo…
Este efecto funciona en dos pasos: una función de vértice para cambiar la geometría y la
función de superficie para darle el color correcto. Los pasos son los siguientes:
1. Agregue las propiedades siguientes al sombreador:
_RampTex("Color Ramp", 2D) = "white" {}
_RampOffset("Ramp offset", Range(-0.5,0.5))= 0
_NoiseTex("Noise tex", 2D) = "gray" {}
_Period("Period", Range(0,1)) = 0.5
_Amount("_Amount", Range(0, 1.0)) = 0.1
_ClipRange("ClipRange", Range(0,1)) = 1
2. Agregue sus variables relativas para que el código Cg del sombreador pueda acceder a
ellas:
sampler2D _RampTex;
half _RampOffset;
sampler2D _NoiseTex;
float _Period;
half _Amount;
half _ClipRange;
3. Cambie la estructura de entrada para que reciba los datos UV de la textura de la
rampa:
struct Input {
float2 uv_NoiseTex;
};
4. Agregue la siguiente función de vértice:
void vert(inout appdata_full v) {
float3 disp = tex2Dlod(_NoiseTex, float4(v.texcoord.xy,0,0));
float time = sin(_Time[3] *_Period + disp.r*10);
v.vertex.xyz += v.normal * disp.r * _Amount * time;
}
5. Añada la siguiente función de superficie:
void surf(Input IN, inout SurfaceOutput o) {
float3 noise = tex2D(_NoiseTex, IN.uv_NoiseTex);
float n = saturate(noise.r + _RampOffset);
clip(_ClipRange - n);
half4 c = tex2D(_RampTex, float2(n,0.5));
o.Albedo = c.rgb;
o.Emission = c.rgb*c.a;
}
6. Especificamos la función de vértice en la directiva # pragma, añadiendo el parámetro
nolightmap para evitar que Unity agregue iluminaciones realistas a nuestra explosión:
#pragma surface surf Lambert vertex:vert nolightmap
7. El último paso es seleccionar el material, y de su inspector, adjuntar las dos texturas en
las ranuras relativas. Este es un material animado, lo que significa que evoluciona
a través del tiempo. Puede ver el cambio de material en el editor haciendo clic en
Materiales animados de la ventana Escena:

Cómo funciona…
Si estás leyendo esta receta, ya deberías estar familiarizado con cómo funcionan los
modificadores de superficie y los modificadores de vértices. La idea principal detrás de este
efecto es alterar la geometría de la esfera de una manera aparentemente caótica,
exactamente como ocurre en una explosión real. La siguiente imagen muestra cómo se verá tal
explosión dentro del editor. Puede ver que la malla original ha sido fuertemente deformada:
La función de vértice es una variante de la técnica denominada extrusión normal introducida
en la receta de Extrusión de sus modelos de este capítulo. La diferencia aquí es que la cantidad
de la extrusión se determina tanto por la textura de tiempo como de ruido.
Nota
Cuando necesita un número aleatorio en Unity, puede confiar en la función Random.Range ().
No existe una forma estándar de obtener números aleatorios en un shader, por lo que la forma
más fácil es probar una textura de ruido.
No hay una forma estándar de hacerlo, por lo que tome esto como un ejemplo solamente:
float time = sin(_Time[3] *_Period + disp.r*10);
La variable _Time [3] incorporada se utiliza para obtener el tiempo actual desde el interior del
sombreado y el canal rojo de la dispersión de ruido disp.r se utiliza para asegurarse de que
cada vértice se mueve independientemente. La función sin () hace que los vértices suban y
bajen, simulando el comportamiento caótico de una explosión. Entonces, la extrusión normal
tiene lugar:
v.vertex.xyz += v.normal * disp.r * _Amount * time;
Usted debe jugar con estos números y variables hasta que encuentre un patrón de ovement
Que usted es feliz con. La última parte del efecto se consigue mediante la función superficial.
Aquí, la textura del ruido se utiliza para probar un color aleatorio de la textura de la rampa. Sin
embargo, hay dos aspectos más que vale la pena notar. La primera es la introducción de
_RampOffset. Su uso obliga a la explosión a muestrear colores desde el lado izquierdo o
derecho de la textura. Con valores positivos, la superficie de la explosión tiende a mostrar más
tonos grises; Exactamente lo que sucede cuando se está disolviendo. Puede usar _RampOffset
para determinar cuánto fuego o humo debe haber en su explosión. El segundo aspecto
introducido en la función superficial es el uso de clip (). Lo que clip () hace es clips (elimina)
píxeles de la canalización de renderizado. Cuando se invoca con un valor negativo, el píxel
actual no se dibuja. Este efecto es controlado por _ClipRange, que determina qué píxeles de
las explosiones volumétricas van a ser transparentes.
Controlando tanto _RampOffset como _ClipRange, tiene control total para determinar cómo se
comporta la explosión y se disuelve.

Hay más…
El shader presentado en esta receta hace que una esfera parezca una explosión. Si realmente
quieres usarlo, deberías asociarlo con algunas secuencias de comandos para sacarle el máximo
provecho. Lo mejor es crear un objeto de explosión y convertirlo en un prefabricado para que
pueda reutilizarlo cada vez que lo necesite. Puede hacerlo arrastrando la esfera de nuevo a la
ventana de proyecto. Una vez hecho esto, puede crear tantas explosiones como desee
utilizando la función Instantiate ().
Vale la pena notar, sin embargo, que todos los objetos con el mismo material comparten la
misma mirada. Si tiene múltiples explosiones al mismo tiempo, no debe usar el mismo
material. Cuando está instanciando una nueva explosión, también debe duplicar su material.
Usted puede hacer esto fácilmente con este pedazo de código:
GameObject explosion = Instantiate(explosionPrefab) as GameObject;
Renderer renderer = explosion.GetComponent<Renderer>();
Material material = new Material(renderer.sharedMaterial);
renderer.material = material;
Por último, si va a utilizar este sombreador de una manera realista, debe adjuntar un script que
cambie su tamaño, _RampOffset y _ClipRange según el tipo de explosión que desea volver a
crear.

Ver también
Mucho más se puede hacer para hacer las explosiones realistas. El enfoque presentado en esta
receta sólo crea un shell vacío; Dentro de él, la explosión está realmente vacía. Un truco fácil
para mejorar esto es crear partículas dentro de él. Sin embargo, sólo puede ir tan lejos con
esto. El corto, The Butterfly Effect (http://unity3d.com/pages/butterfly) creado por Unity
Technologies en colaboración con Passion Pictures y Nvidia, es el ejemplo perfecto. Se basa en
el mismo concepto de alterar la geometría de una esfera, pero lo hace con una técnica llamada
fundición de rayo de volumen. En pocas palabras, muestra la geometría como si estuviera
llena. Puede ver un ejemplo en la siguiente imagen:

Si está buscando explosiones de alta calidad, consulte Pyro Technix


(https://www.assetstore.unity3d.com/en/#!/content/16925) en la tienda de activos. Incluye
explosiones volumétricas y las acopla con ondas de choque realistas.

Capítulo 6. Fragmento Shaders y Grab Passes


Hasta ahora, hemos confiado en Shaders de superficie. Han sido diseñados para simplificar la
forma en que funciona la codificación de sombreado, proporcionando herramientas
significativas para los artistas. Si queremos acercar nuestro conocimiento de los shaders,
debemos aventurarnos en el territorio de Vertex y Fragment Shaders.
En este capítulo, aprenderá las siguientes recetas:
 Descripción de los sombreados de vértices y fragmentos
 Usando el pase de agarre
 Implementación de un sombreado de vidrio
 Implementación de un Shader de agua para juegos 2D
Introducción
Comparado con Shaders de superficie, Vertex y Shaders de fragmentos vienen con poca o
ninguna información sobre las propiedades físicas que determinan cómo la luz se refleja en las
superficies. Lo que les falta en expresividad, compensan con poder: Vertex y Fragmento Los
shaders no están limitados por limitaciones físicas y son perfectos para efectos no
fotorrealistas. Este capítulo se enfocará en una técnica llamada agarrar paso, que permite a
estos sombreadores simular deformaciones.

Descripción de los sombreados de vértices y fragmentos


La mejor manera de entender cómo funcionan Vertex y Fragment Shaders es mejor crear uno
mismo. Esta fórmula le mostrará cómo escribir uno de estos shaders, que simplemente
aplicará una textura a un modelo y la multiplicará por un color dado, como se muestra en la
siguiente imagen:

El sombreado presentado aquí es muy simple, y se utilizará como base de partida para todos
los otros Vertex y Fragmento Shaders.

Preparándose
Para esta fórmula, necesitaremos un nuevo shader. Sigue estos pasos:
1. Cree un nuevo shader.
2. Cree un nuevo material y asigne el shader al mismo.

Cómo hacerlo…
En todos los capítulos anteriores, siempre hemos sido capaces de reajustar Shaders de
superficie. Esto ya no es el caso ya que los Shaders de Superficie y Fragmento son
estructuralmente diferentes. Necesitaremos los siguientes cambios:
1. Elimine todas las propiedades del shader, sustituyéndolas por las siguientes:
_Color ("Color", Color) = (1,0,0,1) // Red
_MainTex ("Base texture", 2D) = "white" {}
2. Elimine todo el código del bloque SubShader y reemplácelo por éste:
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 _Color;
sampler2D _MainTex;
struct vertInput {
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
};
struct vertOutput {
float4 pos : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
vertOutput vert(vertInput input) {
vertOutput o;
o.pos = mul(UNITY_MATRIX_MVP, input.pos);
o.texcoord = input.texcoord;
return o;
}
half4 frag(vertOutput output) : COLOR{
half4 mainColour = tex2D(_MainTex, output.texcoord);
return mainColour * _Color;
}
ENDCG
}
Esto también será la base para todos los futuros Vertex y Fragment Shaders.

Cómo funciona…
Como su nombre indica, Vertex y Fragment Shaders trabajan en dos pasos. El modelo se pasa
primero a través de una función de vértice; El resultado se introduce entonces en una función
de fragmento. Ambas funciones se asignan mediante directivas pragma:
#pragma vertex vert
#pragma fragment frag
En este caso, se llaman simplemente vert y frag.
Conceptualmente hablando, los fragmentos están estrechamente relacionados con los píxeles;
El término fragmento se utiliza a menudo para referirse a la recopilación de datos necesarios
para dibujar un píxel. Esta es también la razón por la que Vertex y Fragment Shaders a menudo
se llaman Shaders de píxeles. La función de vértice toma los datos de entrada en una
estructura que se define como vertInput en el shader:

Hay más…
struct vertInput {
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
};
Su nombre es totalmente arbitrario, pero su contenido no lo es. Cada campo de estructura
debe estar decorado con una semántica vinculante. Esta es una característica de Cg que nos
permite marcar variables para que se inicialicen con ciertos datos, como vectores normales y
posición de vértice. La POSICIÓN semántica de enlace indica que cuando vertInput se
introduce en la función de vértice, pos contendrá la posición del vértice actual. Esto es similar
al campo de vértices de la estructura appdata_full en un Shader de superficie. La principal
diferencia es que pos se representa en coordenadas del modelo (relativo al objeto 3D), que
necesitamos convertir para ver las coordenadas manualmente (en relación con la posición en
la pantalla).
Nota
La función de vértice en un Surface Shader se utiliza para alterar la geometría del modelo
solamente. En un Vertex y Shader de fragmentos, en cambio, la función de vértice es necesario
para proyectar las coordenadas del modelo a la pantalla. La matemática detrás de esta
conversión está más allá del alcance de este capítulo. Sin embargo, esta transformación puede
lograrse multiplicando pos por una matriz especial proporcionada por Unity:
UNITY_MATRIX_MVP. A menudo se denomina matriz de proyección-proyección, y es esencial
encontrar la posición de un vértice en la pantalla:
vertOutput o;
o.pos = mul(UNITY_MATRIX_MVP, input.pos);
La otra información inicializada es textcoord, que utiliza la semántica de enlace TEXCOORD0
para obtener los datos UV de la primera textura. No se requiere ningún procesamiento
adicional y este valor se puede pasar directamente a la función de fragmento:
o.texcoord = input.texcoord;
Mientras que Unity inicializará vertInput para nosotros, somos responsables de la inicialización
de vertOutput. A pesar de esto, sus campos aún necesitan ser decorados con semántica
vinculante:
struct vertOutput {
float4 pos : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
Una vez que la función de vértice ha inicializado vertOutput, la estructura se pasa a la función
de fragmento. Este muestra la textura principal del modelo y lo multiplica por el color
proporcionado. Como puede ver, el Vertex y Shader de Fragmento no tiene conocimiento de
las propiedades físicas del material; Comparado con un Shader de superficie, trabaja más cerca
de la arquitectura de la unidad de procesamiento gráfico (GPU).
Hay más…
Uno de los aspectos más confusos de Vertex y Fragment Shaders es la semántica vinculante.
Hay muchos otros que puedes usar y su significado depende del contexto.
Semántica de entrada
La semántica de enlace en la siguiente tabla se puede utilizar en vertInput, que es la estructura
que Unity proporciona a la función de vértice. Los campos decorados con estas semánticas se
inicializarán automáticamente:
Semántica de encuadernación Description
POSITION, SV_POSITION La posición de un vértice en las coordenadas del mundo (espacio del objeto)
NORMAL La normal de un vértice, con relación al mundo (no a la cámara)
COLOR, COLOR0, DIFFUSE, SV_TARGET La información de color almacenada en el vértice
COLOR1, SPECULAR La información de color secundario almacenada en el vértice (generalmente el
especular)
TEXCOORD0, TEXCOORD1, …, TEXCOORDi Los i-ésimo datos UV almacenados en el vértice

Semántica de salida
Cuando se enlaza, la semántica se utiliza en vertOutput; No garantizan automáticamente que
los campos se inicializarán. Todo lo contrario; Es nuestra responsabilidad hacerlo. El
compilador hará todo lo posible para asegurarse de que los campos se inicializan con los datos
correctos:
Semántica de encuadernación Description
POSITION, SV_POSITION, HPOS La posición de un vértice en las coordenadas de la cámara (espacio de clip, de cero a
uno para cada dimensión)
COLOR, COLOR0, COL0, COL, El color primario delantero
SV_TARGET
COLOR1, COL1 El color secundario delantero
TEXCOORD0, TEXCOORD1, …, Los i-ésimo datos UV almacenados en el vértice
TEXCOORDi, TEXi
WPOS La posición, en píxeles, en la ventana (origen en la esquina inferior izquierda)
Si, por alguna razón, necesita un campo que contenga un tipo diferente de datos, puede
decorarlo con uno de los muchos datos TEXCOORD disponibles. El compilador no permitirá que
los campos queden sin decorar.

Ver también
Ver también
Puede consultar el Manual de referencia de NVIDIA para comprobar la semántica de enlace
disponible en Cg:
http://developer.download.nvidia.com/cg/Cg_3.1/Cg-3.1_April2012_ReferenceManual.pdf
Usando el pase de agarre
En la adición de transparencia a la receta PBR del Capítulo 4, Creación de casos de prueba y
escenarios de escritura para el desarrollo impulsado por el comportamiento en Symfony,
hemos visto cómo un material puede ser transparente. Incluso si un material transparente
puede dibujar sobre una escena, no puede cambiar lo que se ha dibujado debajo de ella. Esto
significa que los shaders transparentes no pueden crear distorsiones como las que
normalmente se ven en el vidrio o el agua. Para simularlos, necesitamos introducir otra técnica
llamada grab pass. Esto nos permite acceder a lo que se ha dibujado en la pantalla hasta el
momento para que un sombreador pueda usarlo (o alterarlo) sin restricciones. Para aprender a
usar los pases de agarre, crearemos un material que agarra lo que se muestra detrás de él y lo
dibuja de nuevo en la pantalla. Es un sombreador que, paradójicamente, utiliza varias
operaciones para no mostrar ningún cambio en absoluto.

Preparándose
Preparándose
Esta receta requiere las siguientes operaciones:
1. Cree un shader que inicializaremos más adelante.
2. Cree un material para alojar el shader.
3. Conecte el material a una pieza plana de geometría, como un cuadrante. Colóquelo
delante de algún otro objeto para que no pueda ver a través de él. El quad aparecerá
transparente tan pronto como el shader esté completo.

Cómo hacerlo…
Cómo hacerlo…
Para usar el pase de agarre, debe seguir estos pasos:
1. Quite la sección Propiedades; Este sombreador no usará ninguno de ellos.
2. En la sección SubShader, agregue GrabPass:
GrabPass{ }
3. Después del pase de agarre, tendremos que añadir este pase extra:
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GrabTexture;
struct vertInput {
float4 vertex : POSITION;
};
struct vertOutput {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD1;
};
// Vertex function
vertInput vert(vertexInput v) {
vertexOutput o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uvgrab = ComputeGrabScreenPos(o.vertex);
return o;
}
// Fragment function
half4 frag(vertexOutput i) : COLOR {
fixed4 col = tex2Dproj(_GrabTexture,
UNITY_PROJ_COORD(i.uvgrab));
return col + half4(0.5,0,0,0);
}
ENDCG
}

Cómo funciona…
Esta receta no sólo introduce pases de agarre sino también Vertex y Fragment Shaders; Por
esta razón, tenemos que analizar el shader en detalle. Hasta ahora, todo el código siempre se
ha colocado directamente en la sección de SubShader. Esto se debe a que nuestros shaders
anteriores solo requerían un solo pase. Esta vez, se requieren dos pases. El primero es el pase
grab, que se define simplemente por GrabPass {}. El resto del código se coloca en el segundo
paso, que está contenido en un bloque de paso. El segundo paso no es estructuralmente
diferente del sombreado mostrado en la primera receta de este capítulo; Utilizamos la función
vertex vert para obtener la posición del vértice y luego le damos un color en la fragción frag. La
diferencia es que vert calcula otro detalle importante: los datos UV para el pase de agarre. El
pase de captura crea automáticamente una textura a la que se puede hacer referencia de la
siguiente manera:
sampler2D _GrabTexture;
Para probar esta textura, necesitamos sus datos UV. La función ComputeGrabScreenPos
devuelve datos que podemos usar más tarde para probar la textura de agarre correctamente.
Esto se hace en el Shader de fragmentos utilizando la línea siguiente:
fixed4 col = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
Esta es la manera estándar en la que una textura es agarrada y aplicada a la pantalla en su
posición correcta. Si todo se ha hecho correctamente, este sombreado simplemente clonará lo
que se ha representado detrás de la geometría. Veremos en las siguientes recetas cómo esta
técnica se puede utilizar para crear materiales como el agua y el vidrio.
Hay más…
Cada vez que utilice un material con GrabPass {}, Unity tendrá que renderizar la pantalla a una
textura. Esta operación es muy costosa y limita el número de pases de agarre que puedes usar
en un juego. Cg ofrece una variación ligeramente diferente:
GrabPass {"TextureName"}
Esta línea no sólo le permite dar un nombre a la textura, sino que también comparte la textura
con todos los materiales que tienen un pase grab llamado TextureName. Esto significa que si
usted tiene diez materiales, Unity sólo hará un solo pase de agarre y compartirá la textura con
todos ellos. El principal problema de esta técnica es que no permite efectos que se pueden
apilar. Si está creando un vaso con esta técnica, no podrá tener dos vasos uno tras otro.

Implementación de un shader de vidrio


El vidrio es un material muy complicado; No debería sorprender que otros capítulos ya hayan
creado shaders para simularlo en la sección Adición de transparencia a la receta PBR del
Capítulo 4, Creación de casos de prueba y escenarios de escritura para el desarrollo impulsado
por comportamientos en Symfony. Sin embargo, hay un efecto que la transparencia no puede
reproducir deformaciones. La mayoría de los vidrios no son perfectos, por lo tanto crean
distorsiones cuando miramos a través de ellos. Este procedimiento le enseñará cómo hacerlo.
La idea detrás de este efecto es usar un Vertex y Fragment Shader con un pase de agarre, y
luego muestre la textura grab con un pequeño cambio a sus datos UV para crear una
distorsión. Puede ver el efecto en la siguiente imagen, utilizando las texturas manchadas de
vidrio de los Activos estándar de Unity:
Preparándose
La configuración de esta receta es similar a la presentada en el capítulo anterior:
1. Crear nuevos vértices y shaders de fragmentos. Puede empezar copiando el que se usó
en la receta anterior, utilizando el pase de agarre, como base.
2. Cree un material que utilice el shader.
3. Asigne el material a una cuadrícula u otra geometría plana que simule su cristal.
4. Coloque algunos objetos detrás de él para que pueda ver el efecto de distorsión.

Cómo hacerlo…
Comencemos editando el Vertex y Fragment Shaders:
1. Agregue estas dos propiedades al bloque de propiedades:
_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
_BumpMap("Noise text", 2D) = "bump" {}
_Magnitude("Magnitude", Range(0,1)) = 0.05
2. Añada sus variables en el segundo pase:
sampler2D _MainTex;
sampler2D _BumpMap;
float _Magnitude;
3. Agregue la información de textura en las estructuras de entrada y salida:
float2 texcoord : TEXCOORD0;
4. Transfiera los datos UV de la entrada a la estructura de salida:
o.texcoord = v.texcoord;
5. Utilice la siguiente función de fragmento:
half4 frag(vertOutput i) : COLOR {
half4 mainColour = tex2D(_MainTex, i.texcoord);
half4 bump = tex2D(_BumpMap, i.texcoord);
half2 distortion = UnpackNormal(bump).rg;
i.uvgrab.xy += distortion * _Magnitude;
fixed4 col = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
return col * mainColour * _Colour;
}
6. Este material es transparente por lo que cambia sus etiquetas en el bloque SubShader:
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Opaque" }
7. Lo que queda ahora es establecer la textura para el vidrio y un mapa normal para
desplazar la textura de agarre.
Cómo funciona…
El núcleo que utiliza este sombreador es un pase grab para tomar lo que ya se ha representado
en la pantalla. La parte donde se produce la distorsión se encuentra en la función de
fragmento. Aquí, un mapa normal se desempaqueta y se utiliza para compensar los datos UV
de la textura agarrar:
half4 bump = tex2D(_BumpMap, i.texcoord);
half2 distortion = UnpackNormal(bump).rg;
i.uvgrab.xy += distortion * _Magnitude;
La diapositiva Magnitud se utiliza para determinar la fuerza del efecto.

Hay más…
Este efecto es muy genérico; Agarra la pantalla y crea una distorsión basada en un mapa
normal. No hay ninguna razón por la que no se debe utilizar para simular cosas más
interesantes. Muchos juegos usan distorsiones alrededor de explosiones u otros dispositivos
de ciencia ficción. Este material se puede aplicar a una esfera y, con un mapa normal diferente,
simularía perfectamente la ola de calor de una explosión.

Implementación de un Shader de agua para juegos 2D


El sombreado de vidrio introducido en la receta anterior es estático; Su distorsión nunca
cambia. Se necesita sólo unos pocos cambios para convertirlo en un material animado, por lo
que es perfecto para juegos 2D, que cuentan con agua. Esta receta usa una técnica similar a la
que se muestra en el Capítulo 5, Animando los Vértices en un Shader Superficial:

Preparándose
Esta receta se basa en los vértices y sombreadores de fragmentos descritos en la receta de uso
de pases de agarrar, ya que dependerá en gran medida del paso de agarre.
1. Cree un nuevo shader de paso de captura; Usted puede escribir el suyo propio o
comenzar con el presentado en la receta del uso del paso del asimiento.
2. Cree un nuevo material para su shader.
3. Asigne el material a una geometría plana que represente su agua 2D. Para que este
efecto funcione, debe tener algo representado detrás de él para que pueda ver el
desplazamiento similar al agua.
4. Esta receta requiere una textura de ruido, que se utiliza para obtener valores pseudo-
aleatorios. Es importante que elija una textura de ruido sin fisuras, como las generadas
por el ruido perlable 2D Perlin, como se muestra en la siguiente imagen. Esto asegura
que cuando el material se aplica a un objeto grande, no verá ninguna discontinuidad.
Para que este efecto funcione, la textura debe importarse en el modo Repetir. Si desea
un aspecto suave y continuo para su agua, también debe establecer a Bilinear de
Inspector. Estos ajustes garantizan que la textura se muestrea correctamente desde el
shader:

Cómo hacerlo…
Cómo hacerlo…
Para crear este efecto animado, puede empezar por reajustar el sombreado. Sigue estos pasos:
1. Agregue las siguientes propiedades:
_NoiseTex("Noise text", 2D) = "white" {}
_Colour ("Colour", Color) = (1,1,1,1)
_Period ("Period", Range(0,50)) = 1
_Magnitude ("Magnitude", Range(0,0.5)) = 0.05
_Scale ("Scale", Range(0,10)) = 1
2. Agregue sus respectivas variables a la segunda pasada del shader:
sampler2D _NoiseTex;
fixed4 _Colour;
float _Period;
float _Magnitude;
float _Scale;
3. Defina la siguiente estructura de salida para la función vértice:
struct vertInput {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPos : TEXCOORD1;
float4 uvgrab : TEXCOORD2;
};
4. Este shader necesita saber la posición exacta del espacio de cada fragmento. Para ello,
agregue la línea siguiente a la función vértice:
o.worldPos = mul(_Object2World, v.vertex);
5. Utilice la siguiente función de fragmento:
fixed4 frag (vertInput i) : COLOR {
float sinT = sin(_Time.w / _Period);
float2 distortion = float2(
tex2D(_NoiseTex, i.worldPos.xy / _Scale + float2(sinT, 0) ).r - 0.5,
tex2D(_NoiseTex, i.worldPos.xy / _Scale + float2(0, sinT) ).r - 0.5
);
i.uvgrab.xy += distortion * _Magnitude;
fixed4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
return col * _Colour;
}
Cómo funciona…
Este sombreado es muy similar al que se introdujo en la implementación de una receta de
sombreado de vidrio. La principal diferencia es que se trata de un material animado; El
desplazamiento no se genera a partir de un mapa normal, sino que tiene en cuenta el tiempo
actual para crear una animación constante. El código que desplaza los datos UV de la textura
de agarre parece bastante complicado; Vamos a tratar de entender cómo se ha generado. La
idea detrás de esto es que se utiliza una función sinusoidal para hacer que el agua oscile. Este
efecto necesita evolucionar con el tiempo; Para lograr este efecto, la distorsión generada por
el sombreado depende del tiempo actual que se recupera con la variable incorporada, _Time.
La variable _Period determina el período de la sinusoide, lo que significa la rapidez con que
aparecen las ondas:
float2 distortion = float2( sin(_Time.w/_Period), sin(_Time.w/_Period) ) – 0.5;
El problema con este código es que tiene el mismo desplazamiento en los ejes X e Y; Como
resultado, la textura agarrar entera se girará en un movimiento circular, que no se parece nada
al agua. Obviamente tenemos que añadir algo de aleatoriedad a esto. La forma más común de
agregar comportamientos aleatorios a los shaders es mediante la inclusión de una textura de
ruido. El problema ahora es encontrar una manera de probar la textura en posiciones
aparentemente aleatorias. La mejor manera de evitar ver un patrón sinusoidal obvio es usar
las ondas senoidales como un offset en los datos UV de la textura del ruido:

float sinT = sin(_Time.w / _Period);


float2 distortion = float2( tex2D(_NoiseTex, i.texcoord / _Scale + float2(sinT, 0) ).r - 0.5,
tex2D(_NoiseTex, i.texcoord / _Scale + float2(0, sinT) ).r - 0.5
);
La variable _Scale determina el tamaño de las ondas. Esta solución está más cerca de la versión
final, pero tiene un problema grave: si el cuádruple de agua se mueve, los datos UV lo siguen y
se pueden ver las olas de agua que siguen al material en lugar de anclarse al fondo. Para
resolver esto, necesitamos usar la posición mundial del fragmento actual como posición inicial
para los datos UV:

float sinT = sin(_Time.w / _Period);


float2 distortion = float2( tex2D(_NoiseTex, i.worldPos.xy / _Scale + float2(sinT, 0) ).r - 0.5,
tex2D(_NoiseTex, i.worldPos.xy / _Scale + float2(0, sinT) ).r - 0.5
);
i.uvgrab.xy += distortion * _Magnitude;

El resultado es una distorsión agradable, sin fisuras, que no se mueve en ninguna dirección
clara.
Nota
Como sucede con todos estos efectos especiales, no hay una solución perfecta. Esta receta le
muestra una técnica para crear distorsión similar al agua, pero se le anima a jugar con ella
hasta que encuentre un efecto que se ajuste a la estética de su juego.

Capítulo 7. Ajuste de shader móvil


En los próximos dos capítulos, vamos a echar un vistazo a hacer que los shaders que
escribimos para el rendimiento amigable para las diferentes plataformas. No vamos a hablar
de ninguna plataforma específicamente, pero vamos a romper los elementos de shaders que
podemos ajustar para hacerlos más optimizados para móviles y eficientes en cualquier
plataforma en general. Estas técnicas abarcan desde la comprensión de lo que Unity ofrece en
términos de variables incorporadas que reducen la sobrecarga de la memoria de shaders hasta
el aprendizaje sobre formas en las que podemos hacer que nuestro propio código de shader
sea más eficiente. Este capítulo cubrirá los siguientes procedimientos:
 ¿Qué es un shader fácil?
 Perfilado de tus shaders
 Modificación de nuestros shaders para móviles

Introducción
Aprender el arte de optimizar sus shaders surgirá en casi cualquier proyecto de juego en el que
trabaje. Siempre llegará un punto en cualquier producción donde un shader necesita ser
optimizado, o tal vez necesita usar menos texturas, pero producir el mismo efecto. Como
artista técnico o programador de shader, usted tiene que entender estos fundamentos básicos
para optimizar sus shaders para que pueda aumentar el rendimiento de su juego mientras
sigue obteniendo la misma fidelidad visual. Tener este conocimiento también puede ayudar a
establecer la forma en que se escribe su shader desde el principio. Por ejemplo, al saber que el
juego construido con su shader se reproducirá en un dispositivo móvil, podemos configurar
automáticamente todas nuestras funciones de iluminación para utilizar un medio vector como
dirección de la vista o establecer todos nuestros tipos de variable flotante a fijo o medio. Estas
y muchas otras técnicas contribuyen a que tus shaders funcionen eficientemente en tu
hardware de destino. Comencemos nuestro viaje y comencemos a aprender cómo optimizar
nuestros shaders.

¿Qué es un shader fácil?


Cuando se le preguntó por primera vez, ¿qué es un shader fácil, podría ser un poco difícil de
responder, ya que hay muchos elementos que entran en hacer un shader más eficiente. Podría
ser la cantidad de memoria utilizada por sus variables. Podría ser la cantidad de texturas que
utiliza el shader. También podría ser que nuestro shader esté funcionando bien, pero podemos
producir el mismo efecto visual con la mitad de la cantidad de datos reduciendo la cantidad de
código que estamos usando o los datos que estamos creando. Vamos a explorar algunas de
estas técnicas en esta receta y mostrar cómo se pueden combinar para hacer su shader rápido
y eficiente, pero aun así producir las imágenes de alta calidad que todos esperan de los juegos
de hoy, ya sea en un móvil o PC.

Preparándose
Para conseguir esta receta comenzada, necesitamos recolectar algunos recursos juntos. Así
que realicemos las siguientes tareas:
1. Cree una nueva escena y llénela con un objeto de esfera simple y una luz direccional
única.
2. Cree un nuevo shader y material y asigne el shader al material.
3. Entonces necesitamos asignar el material que acabamos de crear a nuestro objeto de
esfera en nuestra nueva escena.
4. Finalmente, modifique el shader para que utilice una textura difusa y un mapa normal e
incluya su propia función de iluminación personalizada. La siguiente imagen muestra el
resultado de modificar nuestro shader predeterminado que creamos en el paso 1:
Shader "Custom/MovilShader" {
Properties{
_MainTex ("Base (RGB)", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
}
SubShader{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf SimpleLambert
sampler2D _MainTex;
sampler2D _NormalMap;

struct Input{
float2 uv_MainTex;
float2 uv_NormalMap;
};
inline float4 LightingSimpleLambert (SurfaceOutput s, float3 lightDir, float atten){
float diff = max (0, dot (s.Normal, lightDir));
float4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
c.a = s.Alpha;
return c;
}

void surf (Input IN, inout SurfaceOutput o){


float4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
}
ENDCG
}
FallBack "Diffuse"
}
Ahora debe tener una configuración similar a la siguiente imagen. Esta configuración nos
permitirá echar un vistazo a algunos de los conceptos básicos que entran en la optimización de
shaders usando Surface Shaders en Unity:

Cómo hacerlo…
Vamos a construir un sombreado difuso simple para echar un vistazo a algunas formas en las
que puede optimizar sus shaders en general. En primer lugar, optimizaremos nuestros tipos de
variables para que usen menos memoria cuando procesan datos:
1. Comencemos con la estructura Input en nuestro shader. Actualmente, nuestras UVs se
almacenan en una variable del tipo float2. Necesitamos cambiar esto para usar half2
en su lugar:
struct Input{
half2 uv_MainTex;
half2 uv_NormalMap;
};
2. A continuación, podemos pasar a nuestra función de iluminación y reducir la huella de
la memoria de la variable cambiando sus tipos a lo siguiente:
inline fixed4 LightingSimpleLambert (SurfaceOutput s, fixed3 lightDir, fixed atten){
fixed diff = max (0, dot (s.Normal, lightDir));
fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
c.a = s.Alpha;
return c;
}
3. Finalmente, podemos completar este pase de optimización actualizando las variables
en nuestra función surf ():
void surf (Input IN, inout SurfaceOutput o){
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
}
Ahora que tenemos nuestras variables optimizadas, vamos a aprovechar una variable de
función de iluminación integrada para que podamos controlar cómo las luces son procesadas
por este shader. Haciendo esto, podemos reducir en gran medida la cantidad de luces que
procesa el shader. Modifique la instrucción #pragma en su shader con el código siguiente:
CGPROGRAM
#pragma surface surf SimpleLambert noforwardadd
Podemos optimizar esto aún más compartiendo UVs entre el mapa normal y la textura difusa.
Para hacer esto, simplemente cambiamos la búsqueda de UV en nuestra función
UnpackNormal () para usar UVs _MainTex en lugar de UVs de _NormalMap:
void surf (Input IN, inout SurfaceOutput o){
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
}

4. Como hemos eliminado la necesidad de las UV de mapas normales, necesitamos


asegurarnos de que eliminamos el código UV normal del mapa de la estructura de
entrada:
struct Input{
half2 uv_Diffuse;
};
5. Finalmente, podemos optimizar aún más este shader diciéndole al shader que sólo
funciona con ciertos renderizadores:
CGPROGRAM
#pragma surface surf SimpleLambert exclude_path:prepass noforwardadd
El resultado de nuestros pasos de optimización nos muestra que realmente no notamos una
diferencia en la calidad visual, pero hemos reducido la cantidad de tiempo que toma para que
este sombreado sea dibujado a la pantalla. Aprenderá a averiguar cuánto tiempo toma un
sombreador para renderizar en la siguiente receta, pero la idea de centrarse aquí es que
conseguimos el mismo resultado con menos datos. Así que tenga esto en cuenta al crear sus
shaders. La siguiente imagen nos muestra el resultado final de nuestro sombreado:
Cómo funciona…
Ahora que hemos visto las formas en que podemos optimizar nuestros shaders, vamos a
bucear en un poco más profundo y realmente entender por qué todas estas técnicas están
funcionando y mirar un par de otras técnicas que puede probar por sí mismo. Primero
enfocamos nuestra atención en el tamaño de los datos que cada una de nuestras variables
almacena cuando las declaramos. Si está familiarizado con la programación, entonces
comprenderá que puede declarar valores o variables con diferentes tamaños de tipos. Esto
significa que un flotador tiene realmente un tamaño máximo en la memoria. La siguiente
descripción describirá estos tipos de variables con mucho más detalle:
 Float: Un float es un valor de precisión de 32 bits completo y es el más lento de los tres
tipos diferentes que vemos aquí. También tiene sus valores correspondientes de
float2, float3 y float4.

 half: El tipo de medio variable es un valor de punto flotante de 16 bits reducido y es


adecuado para almacenar valores de UV y valores de color y es mucho más rápido que
usar un valor de flotador. Tiene sus valores correspondientes como el tipo de float,
que son half2, half3 y half4.

 fixed: Un valor fixed es el más pequeño en tamaño de los tres tipos, pero se puede
utilizar para cálculos de iluminación y colores y tiene los valores correspondientes de
fixed2, fixed3 y fixed4.

Nuestra segunda fase de optimización de nuestro shader simple fue declarar el valor
noforwardadd a nuestra sentencia # pragma. Esto es básicamente un interruptor que indica
automáticamente a Unity que cualquier objeto con este sombreador particular recibe sólo luz
por píxel de una sola luz direccional. Cualquier otra luz que sea calculada por este sombreado
será forzada a ser procesada como luces por vértice utilizando valores de armónica esférica
producidos internamente por Unity. Esto es especialmente obvio cuando colocamos otra luz
en la escena para iluminar nuestro objeto de esfera porque nuestro sombreador está haciendo
una operación por pixel usando el mapa normal. Esto es genial, pero ¿qué pasa si usted quería
tener un montón de luces direccionales en la escena y el control sobre cuál de estas luces se
utiliza para la luz principal por pixel? Bueno, si te das cuenta, cada luz tiene un menú
desplegable de Render Mode. Si hace clic en este menú desplegable, verá un par de banderas
que se pueden establecer. Estos son Auto, Importante y No Importante. Al seleccionar una luz,
puede decirle a Unity que una luz debe considerarse más como una luz por píxel que una luz
por vértice, estableciendo su modo de renderizado en Importante y viceversa. Si deja una luz
establecida en Auto, entonces dejará que Unity decida el mejor curso de acción. Coloque otra
luz en su escena y eliminar la textura que actualmente está en la textura principal de nuestro
sombreado. Observará que la segunda luz del punto no reacciona con el mapa normal, sólo la
luz direccional que creamos primero. El concepto aquí es que ahorras en operaciones por píxel
simplemente calculando todas las luces adicionales como luces de vértice y ahorrando
rendimiento simplemente calculando la luz direccional principal como una luz por píxel. La
siguiente imagen demuestra visualmente este concepto cuando la luz del punto no reacciona
con el mapa normal:
Finalmente, hicimos un poco de limpieza y simplemente le dijimos a la textura del mapa
normal que usara los valores UV de la textura principal, y nos deshicimos de la línea de código
que tiraba de un conjunto separado de valores UV específicamente para el mapa normal. Esto
siempre es una buena manera de simplificar su código y limpiar los datos no deseados.
También declaramos exclude_pass: prepass en nuestra sentencia #pragma para que este
shader no acepte ninguna iluminación personalizada del renderizador diferido. Esto significa
que realmente podemos usar este sombreado de manera efectiva en el renderizador hacia
adelante solamente, que se establece en la configuración de la cámara principal.
Al tomar un poco de tiempo, usted se sorprenderá de cuánto se puede optimizar un
sombreador. Has visto cómo podemos empaquetar texturas en escala de grises en una sola
textura RGBA así como usar texturas de búsqueda para iluminación falsa. Hay muchas maneras
en que un sombreador puede ser optimizado, por lo que siempre es una pregunta ambigua
para preguntar en primer lugar, pero conociendo estas diferentes técnicas de optimización,
puede atender a sus sombreadores a su juego y la plataforma de destino, Shaders muy
aerodinámicos y un agradable framerate constante.

Perfilado de tus shaders


Ahora que sabemos cómo podemos reducir la sobrecarga que nuestros shaders podrían tomar,
echemos un vistazo a cómo encontrar shaders problemáticos en una escena en la que podría
tener un montón de shaders o una tonelada de objetos, shaders y secuencias de comandos,
todo en ejecución al mismo tiempo. Encontrar un solo objeto o un shader entre un juego
entero puede ser muy desalentador, pero Unity nos proporciona su Profiler incorporado. Esto
nos permite ver, en base de cuadro por cuadro, lo que está sucediendo en el juego y cada
elemento que está siendo utilizado por la GPU y la CPU. Utilizando el Analizador, podemos
aislar elementos como shaders, geometría y elementos de representación generales utilizando
su interfaz para crear bloques de trabajos de creación de perfiles. Podemos filtrar los
elementos hasta que estamos mirando el rendimiento de un solo objeto. Esto nos permite ver
los efectos en la CPU y la GPU que el objeto tiene mientras está realizando sus funciones en
tiempo de ejecución. Echemos un vistazo a las diferentes secciones del Profiler y aprendamos
a depurar nuestras escenas y, lo más importante, nuestros shaders.

Preparándose
Vamos a utilizar nuestro Profiler por tener algunos activos listos y lanzar la ventana Profiler:
1. Utilicemos la escena de la última receta y lanzamos el Unity Profiler desde Window
Profiler o Ctrl + 7.
2. Vamos a duplicar nuestra esfera un par de veces más para ver cómo afecta a nuestra
representación.
Debería ver algo similar a la siguiente imagen:
Cómo hacerlo…
Para usar el Profiler, echaremos un vistazo a algunos de los elementos de la interfaz de usuario
de esta ventana. Antes de que toquemos el juego, echemos un vistazo a cómo obtener la
información que necesitamos del perfilador:
1. Primero, haga clic en los bloques más grandes de la ventana Profiler denominados Uso
de GPU, Uso de CPU y Rendimiento. Encontrará estos bloques en el lado izquierdo de
la ventana superior:

Usando estos bloques, podemos ver diferentes datos específicos a las funciones principales de
nuestro juego. El uso de la CPU nos está mostrando lo que la mayoría de nuestros scripts están
haciendo así como la física y la representación general. El bloque de uso de la GPU nos
proporciona información detallada sobre los elementos que son específicos de nuestra
iluminación, sombras y colas de procesamiento. Por último, el bloque Rendering nos da
información sobre los drawcalls y la cantidad de geometría que tenemos en nuestra escena en
cualquier marco. Al hacer clic en cada uno de estos bloques, podemos aislar el tipo de datos
que vemos durante nuestra sesión de creación de perfiles.
2. Ahora, haga clic en los diminutos bloques de color en uno de estos bloques de Perfil y
presione la reproducción o Ctrl + P para ejecutar la escena. Esto nos permite
profundizar aún más en nuestra sesión de creación de perfiles para poder filtrar lo que
se está reportando para nosotros. Mientras se está ejecutando la escena, desmarque
todas las casillas, excepto para Opaco en el bloque Uso de la GPU. Observe que ahora
podemos ver cuánto tiempo se está utilizando para procesar los objetos que se
establecen en la cola de procesamiento de Opaque:

3. Otra gran función de la ventana Profiler es la acción de hacer clic y arrastrar en la vista
de gráfico. Esto pondrá pausas automáticamente su juego de modo que usted pueda
analizar más lejos cierto punto en el gráfico para descubrir exactamente qué artículo
está causando el problema del funcionamiento. Haga clic y arrastre alrededor en la
vista de gráfico para pausar el juego y ver el efecto de usar esta funcionalidad:

4. Volviendo nuestra atención ahora hacia la mitad inferior de la ventana del Profiler,
notará que hay un elemento desplegable disponible cuando tenemos el bloque de
GPU seleccionado. Podemos ampliar esto para obtener información aún más detallada
sobre la sesión de creación de perfiles activa actual y, en este caso, obtener más
información sobre lo que la cámara está procesando actualmente y cuánto tiempo
tarda en ocuparse:
Esto nos da una mirada completa al funcionamiento interno de lo que la Unidad está
procesando en este marco particular. En este caso, podemos ver que nuestras tres esferas con
nuestro sombreado optimizado están tomando aproximadamente 0.14 milisegundos para
dibujar a la pantalla, están tomando siete drawcalls, y este proceso está tomando el 3.1 por
ciento del tiempo de la GPU en cada marco. Es este tipo de información que podemos utilizar
para diagnosticar y resolver problemas de rendimiento con respecto a shaders. Hagamos una
prueba para ver los efectos de agregar una textura más a nuestro shader y mezclar dos
texturas difusas juntas usando una función lerp. Verá los efectos en el generador de perfiles
con bastante claridad.
5. Modifique el bloque de propiedades de su sombreador con el código siguiente para
darnos otra textura a utilizar:

6. Entonces vamos a alimentar nuestra textura a CGPROGRAM:

7. Ahora es el momento de actualizar nuestra función surf () de forma que combinemos


nuestras texturas difusas de textura juntos:

Una vez que guardes las modificaciones en tu shader y regresas al editor de Unity,
podemos ejecutar nuestro juego y ver el aumento en milisegundos de nuestro nuevo
shader. Pulse play una vez que haya regresado a Unity y echemos un vistazo a los
resultados en nuestro profiler:
Ahora puedes ver que la cantidad de tiempo para renderizar a nuestros Shaders opacos en
esta escena toma 0.179 milisegundos, arriba de 0.140 milisegundos. Añadiendo otra textura y
utilizando la función lerp (), aumentamos el tiempo de renderización de nuestras esferas. Si
bien es un pequeño cambio, imagine tener 20 shaders todos trabajando de diferentes maneras
en diferentes objetos. Utilizando la información dada aquí, puede identificar las áreas que
están causando disminuciones de rendimiento más rápidamente y resolver estos problemas
utilizando las técnicas de la receta anterior.

Cómo funciona…
Aunque está completamente fuera del alcance de este libro para describir cómo esta
herramienta realmente funciona internamente, podemos suponer que Unity nos ha dado una
manera de ver el rendimiento de la computadora mientras nuestro juego se está ejecutando.
Básicamente, esta ventana está muy unida a la CPU y la GPU para darnos retroalimentación en
tiempo real de cuánto tiempo se está tomando para cada uno de nuestros scripts, objetos y
colas de procesamiento. Usando esta información, hemos visto que podemos rastrear la
eficiencia de nuestra escritura de shader para eliminar áreas problemáticas y código.

Hay más…
También es posible crear perfiles específicos para plataformas móviles. Unity nos proporciona
un par de características adicionales cuando el objetivo de compilación de Android o IOS se
establece en la configuración de compilación. Realmente podemos obtener información en
tiempo real de nuestros dispositivos móviles mientras el juego se está ejecutando. Esto se
convierte en muy útil porque es capaz de perfilar directamente en el propio dispositivo en
lugar de crear perfiles directamente en su editor. Para obtener más información sobre este
proceso, consulte la documentación de Unity en el siguiente enlace:
Http://docs.unity3d.com/Documentation/Manual/MobileProfiling.html

Modificación de nuestros shaders para móviles


Ahora que hemos visto un amplio conjunto de técnicas para crear shaders realmente
optimizados, echemos un vistazo a la escritura de un sombreado de alta calidad y de calidad
para un dispositivo móvil. En realidad, es bastante fácil hacer algunos ajustes a los shaders que
hemos escrito para que funcionen más rápido en un dispositivo móvil. Esto incluye elementos
tales como el uso de las variables de la función de iluminación approxview o halfasview.
También podemos reducir la cantidad de texturas que necesitamos e incluso aplicar una mejor
compresión de las texturas que estamos usando. Al final de esta receta, tendremos un
sombreador especular de mapas normales perfectamente optimizado para usar en nuestros
juegos para móviles.

Preparándose
Antes de empezar, obtengamos una nueva escena y la llenamos con algunos objetos para
aplicar nuestro shader móvil:
1. Cree una nueva escena y llénela con una esfera predeterminada y una sola luz
direccional.
2. Cree un nuevo material y sombreador y asigne el sombreado al material.
3. Finalmente, asignar el material a nuestro objeto de esfera en nuestra escena.
Una vez completado, debe tener una escena similar a la de la siguiente imagen:

Cómo hacerlo…
Para esta receta, escribiremos un shader móvil desde el principio y discutiremos los elementos
que lo hacen más móvil:
1. Primero llene nuestro bloque de Propiedades con las texturas necesarias. En este caso,
vamos a utilizar una textura difusa única con el mapa de brillo en su canal alfa, mapa
normal y deslizador para la intensidad especular:

2. Nuestra próxima tarea es establecer nuestras declaraciones # pragma. Esto


simplemente activará y desactivará ciertas características del Surface Shader, lo que
hará al shader más barato o más caro:

3. Entonces necesitamos hacer la conexión entre nuestro bloque de propiedades y


CGPROGRAM. Esta vez, vamos a utilizar el tipo de variable fija para nuestro deslizador
de intensidad especular para reducir su uso de memoria:
4. Para que podamos mapear nuestras texturas a la superficie de nuestro objeto,
necesitamos obtener algunas UVs. En este caso, vamos a obtener sólo un conjunto de
UVs para mantener la cantidad de datos en nuestro sombreador a un mínimo:

5. El siguiente paso es rellenar nuestra función de iluminación usando algunas nuevas


variables de entrada que están disponibles para nosotros usando las nuevas
declaraciones #pragma:

Finalmente, completaremos el shader creando la función surf () y procesando el color


final de nuestra superficie:

Cuando se complete con la parte de código de esta receta, guarde su sombreador y


regrese al editor de Unity para que el shader compile. Si no se produjeron errores,
debería ver un resultado similar al siguiente:

Cómo funciona…
Cómo funciona…
Entonces, comencemos la descripción de este shader explicando lo que hace y lo que no hace.
Primero, excluye el paso de iluminación diferida. Esto significa que si creó una función de
iluminación conectada al prepass del renderizador diferido, no utilizaría esa función de
iluminación en particular y buscaría la función de iluminación predeterminada como las que
hemos estado creando hasta ahora en este libro. Este sombreador en particular no admite
Lightmapping por el sistema de mapeo de luz interno de Unity. Esto sólo evita que el
sombreador intente encontrar mapas de luz para el objeto al que está conectado el
sombreador, lo que hace que el sombreador resulte más amigable porque no tiene que
realizar la verificación lightmapping. Hemos incluido la declaración noforwardadd para que
procesemos sólo texturas por píxel con una única luz direccional. Todas las demás luces se ven
obligadas a convertirse en luces por vértice y no se incluirán en ninguna operación por pixel
que pueda realizar en la función surf (). Finalmente, estamos utilizando la declaración
halfasview para decirle a Unity que no vamos a usar el parámetro viewDir principal encontrado
en una función de iluminación normal. En su lugar, vamos a utilizar el medio vector como la
dirección de la vista y procesar nuestro especular con esto. Esto se hace mucho más rápido
para que el sombreador procese, ya que se hará sobre una base por vértex. No es
completamente preciso cuando se trata de simular especular en el mundo real, pero
visualmente en un dispositivo móvil, se ve muy bien y el sombreado es más optimizado. Sus
técnicas como éstas que hacen un sombreador más eficiente y más limpio, codewise. Siempre
asegúrese de que está utilizando sólo los datos que necesita mientras pesa esto en contra de
su hardware de destino y la calidad visual que el juego requiere. Al final, se convierte en un
cóctel de estas técnicas que en última instancia, componen sus shaders para sus juegos.

Capítulo 8. Efectos de pantalla con Unity Render Textures


En este capítulo, aprenderá las siguientes recetas:
Configuración del sistema de secuencias de comandos de efectos de pantalla
Uso de brillo, saturación y contraste con efectos de pantalla
Uso de modos básicos de mezcla de Photoshop con efectos de pantalla
Uso del modo Overlay Blend con efectos de pantalla

Introducción
Uno de los aspectos más impresionantes de aprender a escribir shaders es el proceso de crear
sus propios efectos de pantalla, también conocido como efectos de post. Con estos efectos de
pantalla, podemos crear impresionantes imágenes en tiempo real con Bloom, Motion Blur,
efectos HDR, etc. La mayoría de los juegos modernos en el mercado hoy en día hacen un uso
intensivo de estos efectos de pantalla por su profundidad de efectos de campo, efectos de
floración e incluso efectos de corrección de color. A lo largo de este capítulo, aprenderá cómo
crear el sistema de secuencias de comandos que nos proporciona el control para crear estos
efectos de pantalla. Cubriremos Render Textures, lo que es el buffer de profundidad y cómo
crear efectos que le den el control de Photoshop sobre la imagen renderizada final de su juego.
Utilizando efectos de pantalla para tus juegos, no sólo redondeas tu conocimiento de escritura
de sombreado, sino que también tendrás el poder de crear tus propios increíbles
renderizaciones en tiempo real con Unity.

Configuración del sistema de secuencias de comandos de efectos de pantalla


Configuración del sistema de secuencias de comandos de efectos de pantalla
El proceso de crear efectos de pantalla es aquel en el que tomamos una imagen de pantalla
completa (o textura), usamos un sombreado para procesar sus píxeles en la GPU y luego lo
enviamos al renderizador de Unity para aplicarlo a toda la imagen renderizada del juego . Esto
nos permite realizar operaciones por pixel en la imagen renderizada del juego en tiempo real,
dándonos un control artístico más global. Imagínese si tuvo que pasar y ajustar cada material
en cada objeto de su juego para ajustar el contraste de la apariencia final de su juego. Aunque
no es imposible, esto requeriría un poco de trabajo. Utilizando un efecto de pantalla, podemos
ajustar el aspecto final de la pantalla como un todo, lo que nos da un control más similar a
Photoshop sobre la apariencia final de nuestro juego. Con el fin de poner en marcha un
sistema de efectos de pantalla, debemos configurar un solo script para actuar como el
mensajero de la imagen renderizada actual del juego o, lo que llama Unity, la textura de
render. Al utilizar esta secuencia de comandos para pasar la textura de render a un
sombreado, podemos crear un sistema flexible para crear efectos de pantalla. Para nuestro
primer efecto de pantalla, vamos a crear un efecto de escala de grises muy simple, donde
podemos hacer que nuestro juego se vea en blanco y negro. Echemos un vistazo a cómo se
hace esto.

Preparándose
Para que nuestro sistema de efectos de pantalla funcione, necesitamos crear algunos activos
para nuestro proyecto Unity actual. Haciendo esto, nos prepararemos para los pasos de las
siguientes secciones:
1. En el proyecto actual, necesitamos crear un nuevo script C # y llamarlo
TestRenderImage.cs.
2. Cree un nuevo shader y llámelo como ImageEffect.shader.
3. Crear una esfera simple en la escena y asignarle un nuevo material. Este nuevo
material puede ser cualquier cosa, pero para nuestro ejemplo, haremos un simple
material rojo, especular.
4. Finalmente, cree una nueva luz direccional y guarde la escena.
Con todos nuestros activos listos, usted debe tener una configuración de escena simple, que se
parece a la siguiente imagen:

Cómo hacerlo…
Para que nuestro efecto de pantalla en escala de grises funcione, necesitamos un script y un
shader. Por lo tanto, completaremos estos dos nuevos elementos aquí y los rellenaremos con
el código apropiado para producir nuestro primer efecto de pantalla. Nuestra primera tarea es
completar el script C #. Esto hará funcionar todo el sistema. Después de esto, completaremos
el sombreador y veremos los resultados de nuestro efecto de pantalla. Vamos a completar
nuestro script y shader con los siguientes pasos:
1. Abra el script TestRenderImage.cs C # y comencemos escribiendo algunas variables
que necesitaremos para almacenar objetos y datos importantes. Escriba el código
siguiente en la parte superior de la clase TestRenderImage:
2. Para que podamos editar el efecto de pantalla en tiempo real, cuando el editor Unity
no está reproduciendo, necesitamos introducir la siguiente línea de código justo
encima de la declaración de la clase TestRenderImage:

3. Como nuestro efecto de pantalla está usando un sombreado para realizar las
operaciones de píxeles en nuestra imagen de pantalla, tenemos que crear un material
para ejecutar el sombreado. Sin esto, no podemos acceder a las propiedades del
shader. Para ello, crearemos una propiedad C # para comprobar un material y
crearemos uno si no lo encontramos. Ingrese el siguiente código justo después del
Declaración de las variables del paso 1:

4. Ahora queremos configurar algunas comprobaciones en nuestro script para ver si la


plataforma de destino actual en la que estamos construyendo el juego Unity en
realidad es compatible con los efectos de imagen. Si no encuentra nada al principio de
este script, entonces el script se desactivará:

5. Para captar realmente la imagen renderizada de Unity Renderer, necesitamos utilizar


la siguiente función incorporada que Unity nos proporciona, llamada OnRenderImage
(). Ingrese el código siguiente para que podamos tener acceso a la textura de
procesamiento actual:
6. Nuestro efecto Screen tiene una variable llamada grayScaleAmount con la que
podemos controlar cuánta escala de grises queremos para nuestro efecto de pantalla
final. Por lo tanto, en este caso, necesitamos hacer que el valor pase de 0 a 1, donde 0
es ningún efecto de escala de grises y 1 es el efecto de escala de grises completo.
Realizaremos esta operación en la función Update () para que establezca cada trama
que ejecute este script:

7. Finalmente, completamos nuestro script haciendo un poco de limpieza de los objetos


que creamos cuando se inició el guión:

En este punto, ahora podemos aplicar este script a la cámara, si se compila sin errores,
en Unity. Vamos a aplicar el script TestRenderImage.cs a nuestra cámara principal en
nuestra escena. Debería ver el valor grayScaleAmount y un campo para un shader,
pero el script arroja un error a la ventana de la consola. Dice que falta una instancia a
un objeto y por lo tanto no procesará apropiadamente. Si recuerda de paso 4, estamos
haciendo algunas comprobaciones para ver si tenemos un sombreado y la plataforma
actual soporta el sombreado. Como no hemos dado al script Screen Effect un shader
con el que trabajar, la variable curShader es simplemente null, lo que arroja el error.
Continuemos con nuestro sistema de efectos de pantalla completando el sombreado.
8. Para comenzar nuestro sombreado, vamos a poblar nuestras propiedades con algunas
variables para que podamos enviar datos a este sombreado:

9. Nuestro sombreado ahora va a utilizar el código puro del sombreador del CG en vez de
utilizar el código incorporado de la sombra de la superficie de la unidad. Esto hará que
nuestro efecto de pantalla esté más optimizado, ya que solo necesitamos trabajar con
los píxeles de la textura de render. Así, crearemos un nuevo bloque de Paso en nuestro
shader y lo rellenaremos con algunas nuevas sentencias #pragma que no hemos visto
antes:

10. Para acceder a los datos que se envían al sombreado desde el editor Unity,
necesitamos crear las variables correspondientes en nuestro CGPROGRAM:
11. Finalmente, todo lo que necesitamos hacer es configurar nuestra función de píxeles,
en este caso, llamada frag (). Su es donde está la carne del Efecto de la Pantalla. Esta
función procesará cada píxel de la textura de render y devolverá una nueva imagen a
nuestro script TestRenderImage.cs:

Una vez que el shader esté completo, regrese a Unity y deje que compile para ver si se
produjeron errores. Si no es así, asigne el nuevo sombreado al script TestRenderImage.cs y
cambie el valor de la variable de escala de grises. Usted debe ver la vista del juego ir de una
versión coloreada del juego a una versión en escala de grises del juego. La siguiente imagen
muestra este efecto de pantalla:

Con esto completo, ahora tenemos una manera fácil de probar los nuevos shaders de Screen
Effect sin tener que escribir todo nuestro sistema Screen Effect una y otra vez. Vamos a bucear
en un poco más profundo y aprender acerca de lo que está pasando con la textura de
procesamiento y cómo se procesa a lo largo de su existencia.

Cómo funciona…
Para obtener un efecto de pantalla en funcionamiento dentro de Unity, necesitamos crear un
script y shader. El script controla la actualización en tiempo real en el editor y también es
responsable de capturar la Textura de procesamiento de la cámara principal y pasarla al
sombreador. Una vez que la textura de render llega al shader, podemos usar el shader para
realizar operaciones por pixel. Al inicio de la secuencia de comandos, realizamos algunas
comprobaciones para asegurarnos de que la plataforma de compilación actualmente
seleccionada admita realmente los efectos de pantalla y el propio sombreado. Hay casos en los
que una plataforma actual no admite Screen Effects o el shader que estamos utilizando. Así
que las comprobaciones que hacemos en la función Start () nos aseguramos de que no
obtengamos ningún error si la plataforma no soporta el sistema de pantalla. Una vez que el
script pasa estas comprobaciones, iniciamos el sistema Screen Effects llamando a la función
integrada OnRenderImage (). Esta función es responsable de agarrar la renderTexture, dándole
al shader usando la función Graphics.Blit () y devolviendo la imagen procesada al renderizador
Unity. Puede encontrar más información sobre estas dos funciones en las siguientes URL:
 OnRenderImage:http://docs.unity3d.com/Documentation/ScriptReference/MonoBeh
 Graphics.Blit:http://docs.unity3d.com/Documentation/ScriptReference/Graphics.Blit
Una vez que la textura de renderizado actual llega al sombreado, el sombreador lo toma, lo
procesa a través de la función frag () y devuelve el color final de cada píxel. Usted puede ver
cómo es poderoso esto se convierte como nos da Photoshop-como el control sobre la imagen
rendida final de nuestro juego. Estos efectos de pantalla funcionan secuencialmente como las
capas de Photoshop en la cámara. Cuando coloque estos efectos de pantalla uno tras otro, se
procesarán en ese orden. Estos son sólo los pasos básicos para obtener un efecto de pantalla
de trabajo, pero es el núcleo de cómo funciona el sistema de efectos de pantalla.

Hay más…
Ahora que tenemos nuestro sencillo sistema Screen Effect instalado y funcionando, echemos
un vistazo a algunas de las otras informaciones útiles que podemos obtener del renderizador
de Unity:

En realidad, podemos obtener la profundidad de todo en nuestro juego actual activando el


modo Profundidad incorporado de Unity. Una vez que se enciende, podemos utilizar la
información de profundidad para una tonelada de efectos diferentes. Echemos un vistazo a
cómo se hace esto:
1. Cree un nuevo sombreador y llámelo SceneDepth_Effect. A continuación, haga doble
clic en este sombreador para abrirlo en el editor MonoDevelop.
2. Crearemos la propiedad Textura principal y una propiedad para controlar la potencia
del efecto de profundidad de la escena. Ingrese el código siguiente en su shader:

3. Ahora necesitamos crear las variables correspondientes en nuestro CGPROGRAM.


Vamos a añadir una variable más llamada _CameraDepthTexture. Esta es una variable
incorporada que Unity nos ha proporcionado mediante el uso del archivo cginclude de
UnityCG. Nos da la información de profundidad de la cámara:

4. Completaremos nuestro shader de profundidad utilizando un par de funciones


incorporadas que Unity nos proporciona, las funciones UNITY_SAMPLE_DEPTH () y
linear01Depth (). La primera función obtiene realmente la información de profundidad
de nuestra _CameraDepthTexture y produce un solo valor de flotador para cada píxel.
La función Linear01Depth () asegura entonces que los valores están dentro del rango
0-1 tomando este valor de profundidad final a una potencia que podemos controlar,
donde el valor medio en el rango 0-1 se encuentra en la escena ased off of the Posición
de la cámara:

5. Con nuestro shader completo, volvamos nuestra atención a nuestro script Screen
Effects. Necesitamos agregar la variable depthPower al script para que podamos
permitir que los usuarios cambien el valor en el editor:

6. Nuestra función OnRenderImage () necesita ser actualizada para que pase el valor
correcto a nuestro shader:

7. Para completar nuestro efecto de profundidad en la pantalla, necesitamos decirle a


Unity que active la representación de profundidad en la cámara actual. Esto se hace
simplemente ajustando el depthTextureMode de la cámara principal:

Con todo el código establecido, guarde su script y shader y regrese a Unity para que ambos
compilen. Si no se encuentran errores, debería ver un resultado similar al siguiente:
Uso de brillo, saturación y contraste con efectos de pantalla
Ahora que tenemos nuestro sistema de efectos de pantalla en funcionamiento, podemos
explorar cómo crear operaciones de píxeles más implicadas para realizar algunos de los efectos
de pantalla más comunes que se encuentran en los juegos de hoy. Para empezar, utilizar un
efecto de pantalla para ajustar los colores finales generales de tu juego es crucial para dar a los
artistas un control global sobre el aspecto final del juego. Técnicas como los controles de
ajuste de color para ajustar la intensidad de los rojos, azules y verdes del último juego o
técnicas como poner cierto tono de color en toda la pantalla como se ve en algo como un
efecto de película sepia. Para esta receta en particular, vamos a cubrir algunas de las
operaciones de ajuste de color más básicas que podemos realizar en una imagen. Estos son
brillo, saturación y contraste. Aprender a codificar estos ajustes de color nos da una buena
base para aprender el arte de los efectos de pantalla.

Preparándose
Necesitaremos crear un par de nuevos activos. Podemos utilizar la misma escena que nuestra
escena de prueba, pero necesitaremos un nuevo script y shader:
1. Cree un nuevo script y llámelo BSC_ImageEffect.
2. Cree un nuevo shader llamado BSC_Effect.
3. Ahora simplemente necesitamos copiar el código que teníamos del script C # en la
receta anterior a nuestro nuevo script C #. Esto nos permitirá centrarnos sólo en las
matemáticas para el brillo, la saturación y el efecto de contraste.
4. Copie el código del sombreador en la receta anterior a nuestro nuevo sombreador.
5. Cree un par de objetos nuevos en la escena, configure algunos materiales difusos de
diferentes colores y asignelos al azar a los nuevos objetos de la escena. Esto nos dará
una buena gama de colores para probar con nuestro nuevo efecto de pantalla.
Una vez completado, debe tener una escena similar a la siguiente imagen:

Cómo hacerlo…
Ahora que hemos completado nuestra configuración de escena y hemos creado nuestro nuevo
script y shader, podemos comenzar a rellenar el código necesario para lograr el brillo, la
saturación y el efecto de pantalla de contraste. Nos centraremos sólo en la operación de los
píxeles y en la configuración de las variables para nuestro script y shader, ya que obtener un
sistema Screen Effect instalado y ejecutado se describe en la sección Configuración de la receta
del sistema de scripts de efectos de pantalla:
1. Comencemos lanzando nuestro nuevo shader y script en MonoDevelop. Simplemente
haga doble clic en los dos archivos de la vista del proyecto para realizar esta acción.
2. Editar el shader primero tiene más sentido para que sepamos qué tipo de variables
necesitaremos para nuestro script C #. Comencemos esto introduciendo las
propiedades apropiadas para nuestro brillo, saturación y efecto de contraste.
Recuerde, tenemos que mantener la_MainTex propiedad en nuestro sombreado ya
que esta es la propiedad que los objetivos RenderTexture al crear Efectos de pantalla:

3. Como es habitual, para que podamos acceder a los datos procedentes de nuestras
propiedades en nuestro CGPROGRAM, necesitamos crear las variables
correspondientes en el CGPROGRAM:
4. Ahora necesitamos crear las operaciones que realizarán los efectos de brillo,
saturación y contraste. Introduzca la nueva función siguiente en nuestro shader, justo
encima de la función frag (). No se preocupe si todavía no tiene sentido; Todo el código
se explicará en la siguiente receta:

5. Finalmente, solo necesitamos actualizar nuestra función frag () para usar la función
ContrastSaturationBrightness (). Esto procesará todos los píxeles de nuestra textura de
render y lo devolverá a nuestro script:

Con el código introducido en el shader, regrese al editor de Unity para permitir que el nuevo
shader compile. Si no hay errores, podemos volver a MonoDevelop para trabajar en nuestro
script. Empecemos por crear un par de nuevas líneas de código que enviarán los datos
adecuados a nuestro shader:
1. Nuestro primer paso en la modificación de nuestro script es añadir las variables
adecuadas que impulsarán los valores de nuestro efecto de pantalla. En este caso,
necesitaremos un deslizador para el brillo, un control deslizante para la saturación y un
control deslizante para el contraste:

2. Con nuestras variables configuradas, ahora necesitamos decirle al guión que pase sus
datos al shader. Lo hacemos en la función OnRenderImage ():
3. Finalmente, todo lo que necesitamos hacer es sujetar los valores de las variables
dentro de un rango que sea razonable. Estos valores de abrazadera son totalmente
preferenciales, por lo que puede utilizar cualquiera de los valores que vea en forma:

Con el script terminado y el shader terminado, simplemente asignamos nuestro script a


nuestra cámara principal y nuestro shader al script, y deberías ver los efectos de brillo,
saturación y contraste manipulando los valores del deslizador. La siguiente imagen muestra un
resultado que puede lograr con este efecto de pantalla:

La siguiente imagen muestra otro ejemplo de lo que se puede hacer ajustando los colores de la
imagen renderizada:
Cómo funciona…
Dado que ahora sabemos cómo funciona el sistema básico de Screen Effects, vamos a cubrir
las operaciones perpixel que creamos en la función ContrastSaturationBrightness (). La función
comienza tomando algunos argumentos. La primera y más importante es la textura de render
actual. Los otros argumentos simplemente ajustan el efecto general del efecto de pantalla y
están representados por deslizadores en la pestaña Inspector de los efectos de pantalla. Una
vez que la función recibe la textura de render y los valores de ajuste, declara algunos valores
constantes que usamos para modificar y comparar contra la textura de render original. La
variable luminanceCoeff almacena los valores que nos darán el brillo general de la imagen
actual. Estos coeficientes se basan en las funciones de coincidencia de colores CIE y son
bastante estándar en toda la industria. Podemos encontrar el brillo general de la imagen
obteniendo el producto punto de la imagen actual punteada con estos coeficientes de
luminancia. Una vez que tenemos el brillo, simplemente utilizamos un par de funciones de lerp
para mezclar desde la versión en escala de grises de la operación de brillo y la imagen original
multiplicada por el valor de brillo, pasando a la función. Los efectos de pantalla, como este,
son cruciales para lograr gráficos de alta calidad para sus juegos, ya que le permite ajustar la
apariencia final de su juego sin tener que editar cada material en su escena de juego actual.
Uso de modos básicos de mezcla de Photoshop con efectos de pantalla
Los efectos de pantalla no se limitan sólo a ajustar los colores de una imagen renderizada de
nuestro juego. También podemos usarlas para combinar otras imágenes con nuestra textura
de render. Esta técnica no es diferente de crear una nueva capa en Photoshop y elegir un
modo de mezcla para mezclar dos imágenes juntos o, en nuestro caso, una textura con una
textura Render. Esto se convierte en una técnica muy potente, ya que da a los artistas en un
entorno de producción una forma de simular sus modos de mezcla en el juego en lugar de sólo
en Photoshop. Para esta receta en particular, vamos a echar un vistazo a algunos de los modos
de mezcla más comunes, como Multiply, Add y Overlay. Verá lo sencillo que es tener el poder
de los modos de Photoshop Blend en su juego.
Preparándose
Cómo hacerlo…
Cómo funciona…
Hay más…
Uso del modo Overlay Blend con efectos de pantalla
Preparándose
Cómo hacerlo…
Cómo funciona…

Capítulo 9. Efectos de juego y pantalla


Cuando se trata de crear juegos creíbles e inmersivos, el diseño del material no es el único
aspecto que debemos tener en cuenta. La sensación general puede ser alterada usando
efectos de pantalla. Esto es muy común en películas, por ejemplo, cuando los colores se
corrigen en la fase de post-producción. Puede implementar estas técnicas en sus juegos
también, utilizando los conocimientos del capítulo 8, Efectos de pantalla con Unity Render
Texture. Dos efectos interesantes se presentan en este capítulo; Usted puede, sin embargo,
adaptarlos para adaptarse a sus necesidades y crear su propio efecto de pantalla.
En este capítulo, aprenderá las siguientes recetas:
Creación de un efecto de pantalla de película antigua
Creación de un efecto de pantalla de visión nocturna
Introducción
Creación de un efecto de pantalla de película antigua
Preparándose
Cómo hacerlo…
Cómo funciona…
Ver también
Creación de un efecto de pantalla de visión nocturna
Preparándose
Cómo hacerlo…
Cómo funciona…
Hay más…

Capítulo 10. Técnicas avanzadas de sombreado


En este capítulo, aprenderá las siguientes recetas:
 Uso de archivos CgInclude que están incorporados en Unity
 Haciendo su mundo de sombreado modular con CgInclude
 Implementación de un shader de pieles
 Implementación de mapas de calor con arreglos
Introducción
Este capítulo final cubre algunas técnicas de sombreado avanzadas que puede utilizar para
nuestro juego. Debe recordar que muchos de los efectos más llamativos que se pueden ver en
los juegos se realizan probando el límite de lo que pueden hacer los shaders. Este libro le
proporciona la base técnica para modificar y crear shaders, pero se le recomienda
encarecidamente que juegue y experimente con ellos tanto como pueda. Hacer un buen juego
no es una búsqueda de fotorealismo; Usted no debe acercarse shaders con la intención de
replicar la realidad porque esto es poco probable que suceda. En su lugar, debe intentar
utilizar shaders como una herramienta para hacer su juego verdaderamente único. Con el
conocimiento de este capítulo final, usted será capaz de crear los materiales que desee.

Uso de archivos CgInclude que están incorporados en Unity


Nuestro primer paso para escribir nuestros propios archivos de CgInclude es entender lo que
Unity ya nos está proporcionando con shaders. Al escribir Surface Shaders, hay mucho
sucediendo bajo el capó, lo que hace que el proceso de escribir Surface Shaders sea tan
eficiente. Podemos ver este código en los archivos CgInclude incluidos en la carpeta de
instalación de Unity en el Editor | Datos | CGIncludes. Todos los archivos contenidos en esta
carpeta hacen su parte para renderizar nuestros objetos con nuestros shaders a la pantalla.
Algunos de estos archivos cuidan de las sombras y la iluminación, algunos cuidan de las
funciones auxiliares, y algunos controlan las dependencias de la plataforma. Sin ellos, nuestra
experiencia de escritura de sombreros sería mucho más laboriosa. Puede encontrar una lista
de información que Unity nos ha proporcionado en el siguiente enlace:
Http://docs.unity3d.com/Documentation/Components/SL-BuiltinIncludes.html
Vamos a comenzar el proceso de comprensión incorporados en estas CgInclude archivos,
utilizando algunas de las funciones de ayuda incorporadas desde el archivo UnityCG.cginc.

Preparándose
Antes de empezar a bucear en la carne de escribir el sombreado, tenemos que conseguir
algunos elementos establecidos en nuestra escena. Vamos a crear lo siguiente y luego abrir el
sombreado en MonoDevelop:
1. Cree una nueva escena y llénela con un modelo de esfera simple.
2. Cree un nuevo shader y material.
3. Conecte el nuevo sombreado al nuevo material y asigne el material a la esfera.
4. Entonces, vamos a crear una luz direccional y la posición por encima de nuestra esfera.
5. Por último, vamos a querer abrir el archivo UnityCG.cginc desde la carpeta CgInclude
de Unity ubicada en el directorio de instalación de Unity. Esto nos permitirá analizar
algunos de los códigos de la función auxiliar para que podamos entender mejor lo que
está sucediendo cuando los usamos.
6. Debería tener una escena simple configurada para trabajar en el shader. Consulte la
siguiente captura de pantalla como ejemplo:

Cómo hacerlo…
Con la escena preparada, ahora podemos comenzar el proceso de experimentar con algunas
de las funciones auxiliares integradas incluidas con el archivo UnityCG.cginc. Haga doble clic en
el sombreado que se creó para esta escena con el fin de abrirlo en MonoDevelop e insertar el
código dado en los siguientes pasos:
1. Agregue el siguiente código al bloque Propiedades del nuevo archivo de sombreado.
Necesitaremos una sola textura y diapositiva para nuestro ejemplo de sombreado:
Properties{
_MainTex ("Base (RGB)", 2D) = "white" {}
_DesatValue ("Desaturate", Range(0,1)) = 0.5
}
2. A continuación, debemos asegurarnos de crear la conexión de datos entre nuestros
bloques de propiedades y CGPROGRAM, con el siguiente código colocado después de
la declaración CGPROGRAM y las directivas #pragma:
sampler2D _MainTex;
fixed _DesatValue;
3. Finalmente, solo tenemos que actualizar nuestra función surf () para incluir el siguiente
código. Introducimos una nueva función que aún no hemos visto, que está integrada
en el archivo UnityCG.cginc de Unity:
void surf (Input IN, inout SurfaceOutput o){
half4 c = tex2D (_MainTex, IV.uv_MainTex);
c.rgb = lerp(c.rgb, Luminance(r.rgb), _DesatValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
Con el código de shader modificado, debería ver algo similar a la siguiente captura de pantalla.
Simplemente hemos utilizado una función de ayuda, integrada en el archivo CgInclude de
Unity, para darnos un efecto de desaturar la textura principal de nuestro sombreado:

Cómo funciona…
Utilizando la función de ayuda integrada llamada Luminance (), podemos obtener rápidamente
un efecto de desaturación o escala de grises en nuestros shaders. Todo esto es posible porque
el archivo UnityCG.cginc se transfiere automáticamente a nuestro shader cuando estamos
usando un sombreado de superficie. Si busca a través del archivo UnityCG.cginc, abierto en
MonoDevelop, encontrará la implementación de esta función en la línea 276. El fragmento
siguiente se toma del archivo:
inline fixed Luminance (fixed3 c){
return dot(c, fixed3(0.22, 0.707, 0.071));
}
Como esta función se incluye en el archivo y Unity compila automáticamente con este archivo,
podemos utilizar la función en nuestro código, así, reduciendo así la cantidad de código que
tenemos que escribir una y otra vez. Si observa que también hay un archivo Lighting.cginc que
Unity viene con. Este archivo contiene todos los modelos de iluminación que usamos cuando
declaramos algo como #pragma Surface surf Lambert. Examinar a través de este archivo revela
que todos los modelos de iluminación incorporados se definen aquí para su reutilización y
modularidad.
Shader "Custom/ShaderCgInclude" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_DesatValue ("Desaturate", Range(0,1)) = 0.5
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert
//#pragma Surface surf Lambert

sampler2D _MainTex;
fixed _DesatValue;

struct Input {
float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o){


half4 c = tex2D (_MainTex, IN.uv_MainTex);
c.rgb = lerp(c.rgb, Luminance(c.rgb), _DesatValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
}

ENDCG
}
FallBack "Diffuse"
}

Haciendo su mundo de sombreado modular con CgInclude


Preparándose
Cómo hacerlo…
Cómo funciona…
Implementación de un shader de pieles
La apariencia de un material depende de su estructura física. Los shaders intentan simularlos,
pero al hacerlo, simplifican excesivamente la forma en que se comporta la luz. Los materiales
con estructura macroscópica compleja son particularmente difíciles de procesar. Este es el
caso de muchas telas textiles y pieles de animales. Esta receta le mostrará cómo es posible
simular la piel y otros materiales (como la hierba) que son más que una superficie plana. Para
hacer esto, el mismo material se dibuja varias veces una y otra vez, aumentando su tamaño
cada vez. Esto crea la ilusión de la piel. El shader presentado aquí se basa en la obra de
Jonathan Czeck y Aras Pranckevičius:
Preparándose
Para que esta receta funcione, necesitará dos cosas. La primera es la textura de la piel como
aparece desde el exterior. La segunda textura se utilizará para controlar la distribución de la
piel y está profundamente conectada a la original. La siguiente imagen muestra una piel de
leopardo (izquierda) y una posible máscara de control (derecha):

Los píxeles blancos en la máscara de control serán extruidos del material original, simulando
una piel. Es importante que la distribución de estos píxeles blancos sea escasa con el fin de dar
la ilusión de que el material está hecho de muchos cabellos pequeños. Una forma suelta de
crear una textura es la siguiente:
1. Aplique un umbral a su textura original para capturar mejor los parches donde la piel
es menos densa.
2. Aplique un filtro de ruido que pixelice la imagen. Los canales RGB de ruido no deben
depender para producir un resultado en blanco y negro.
3. Para una mirada más realista, sobreponga un filtro de ruido de Perlin que agregue a la
variabilidad de la piel.
4. Finalmente, aplique de nuevo un filtro umbral para separar mejor los píxeles en su
textura.
Como todos los otros shaders antes, tendrá que crear un nuevo sombreado estándar y
material para alojarlo.
Cómo hacerlo…
Para esta receta, podemos comenzar a modificar un sombreado estándar:
1. Agregue las siguientes propiedades:
_FurLength ("Fur Length", Range (.0002, 1)) = .25
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_CutoffEnd ("Alpha cutoff end", Range(0,1)) = 0.5
_EdgeFade ("Edge Fade", Range(0,1)) = 0.4
_Gravity ("Gravity direction", Vector) = (0,0,1,0)
_GravityStrength ("G strenght", Range(0,1)) = 0.25
2. Este sombreado requiere que repita el mismo pase varias veces. Utilizaremos la
técnica introducida en la sección Hacer su mundo de sombreado modular con la
sección CgIncludes para agrupar todo el código necesario desde una sola pasada en un
archivo externo. Comencemos creando un nuevo archivo de CgInclude llamado
FurPass.cginc con el código siguiente:
#pragma target 3.0
fixed4 _Color;
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
uniform float _FurLength;
uniform float _Cutoff;
uniform float _CutoffEnd;
uniform float _EdgeFade;
uniform fixed3 _Gravity;
uniform fixed _GravityStrength;
void vert (inout appdata_full v)
{
fixed3 direction = lerp(v.normal, _Gravity * _GravityStrength +
v.normal * (1-_GravityStrength), FUR_MULTIPLIER);
v.vertex.xyz += direction * _FurLength * FUR_MULTIPLIER *
v.color.a;
}
struct Input {
float2 uv_MainTex;
float3 viewDir;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
//o.Alpha = step(_Cutoff, c.a);
o.Alpha = step(lerp(_Cutoff,_CutoffEnd,FUR_MULTIPLIER), c.a);
float alpha = 1 - (FUR_MULTIPLIER * FUR_MULTIPLIER);
alpha += dot(IN.viewDir, o.Normal) - _EdgeFade;
o.Alpha *= alpha;
}
3. Vuelva a su sombreador original y agregue este pase extra después de la sección
ENDCG:
CGPROGRAM
#pragma surface surf Standard fullforwardshadows alpha:blend
vertex:vert
#define FUR_MULTIPLIER 0.05
#include "FurPass.cginc"
ENDCG
4. Añadir más pases, aumentando progresivamente FUR_MULTIPLIER. Puede obtener
resultados decentes con 20 pases, de 0,05 a 0,95.
Una vez que el sombreador se compila y se adjunta a un material, puede cambiar su apariencia
desde el Inspector. La propiedad Longitud de Piel determina el espacio entre las conchas de
pieles, que estarán alterando la longitud de la piel. Una piel más larga puede requerir más
pases para parecer realistas. Alpha Cutoff y Alpha Cutoff End se utilizan para controlar la
densidad de la piel y cómo se hace progresivamente más delgada. Edge Fade determina la
transparencia final de la piel, resultando en un aspecto más borroso. Los materiales más
suaves deben tener un alto Edge Fade. Finalmente, la dirección de la gravedad y la fuerza de la
gravedad curvan las cáscaras de la piel para simular el efecto de la gravedad.
Cómo funciona…
La técnica presentada en esta receta se conoce como técnica de la cáscara de piel concéntrica
de Lengyel o, simplemente, técnica de la cáscara. Trabaja creando copias progresivamente más
grandes de la geometría que necesita ser rendida. Con la transparencia correcta, da la ilusión
de un hilo continuo de pelo:...

La técnica de shell es extremadamente versátil y relativamente fácil de implementar. La piel


realista y real requiere no sólo la extrusión de la geometría del modelo, sino también la
alteración de sus vértices. Esto es posible con los sombreadores de teselas, que son mucho
más avanzados y no están cubiertos en este libro. Cada paso en este Fur Shader está contenido
en FurPass.cginc. La función vértice crea una versión ligeramente más grande del modelo, que
se basa en el principio de la extrusión normal. Además, el efecto de la gravedad se tiene en
cuenta para que se vuelva más intenso cuanto más lejos estamos del centro:
void vert (inout appdata_full v){
fixed3 direction = lerp(v.normal, _Gravity * _GravityStrength +
v.normal * (1-_GravityStrength), FUR_MULTIPLIER);
v.vertex.xyz += direction * _FurLength * FUR_MULTIPLIER * v.color.a;
}
En este ejemplo, el canal alfa se utiliza para determinar la longitud final de la piel. Esto permite
un control más preciso. Finalmente, la función de superficie lee la máscara de control del canal
alfa. Utiliza el valor de corte para determinar qué píxeles mostrar y cuáles ocultar. Este valor
cambia de la primera a la última piel de la cáscara para coincidir con Alpha Cutoff y Alpha
Cutoff End:
o.Alpha = step(lerp(_Cutoff,_CutoffEnd,FUR_MULTIPLIER), c.a);
float alpha = 1 - (FUR_MULTIPLIER * FUR_MULTIPLIER);
alpha += dot(IN.viewDir, o.Normal) - _EdgeFade;
o.Alpha *= alpha;
El valor alfa final de la piel también depende de su ángulo de la cámara, dándole un
Más suave.

Hay más…
El Fur Shader se ha utilizado para simular pieles. Sin embargo, se puede utilizar para una
variedad de otros materiales. Funciona muy bien para los materiales que se hacen
naturalmente de capas múltiples, tales como marquesinas del bosque, nubes borrosas, pelo
humano, e incluso hierba. Hay muchas otras mejoras que pueden aumentar drásticamente su
realismo. Puede agregar una animación de viento muy simple cambiando la dirección de la
gravedad dependiendo de la hora actual. Si se calibra correctamente, esto puede dar la
impresión de que la piel se está moviendo debido al viento. Además, puedes hacer que tu piel
se mueva cuando el personaje se está moviendo. Todos estos pequeños retoques contribuyen
a la creibilidad de su piel, dando la ilusión de que no es sólo un material estático dibujado en la
superficie. Desafortunadamente, este shader tiene un precio: 20 pases son muy pesados para
computar. El número de pases determina aproximadamente cuán creíble es el material. Usted
debe jugar con la longitud de la piel y pasa a fin de obtener el efecto que funciona mejor para
usted. Dado el impacto de rendimiento de este sombreado, es aconsejable tener varios
materiales con diferentes números de pases; Usted puede utilizarlos en distancias diferentes y
ahorrar mucho de computación....

Implementación de mapas de calor con arreglos


Implementación de mapas de calor con arreglos
Una característica que hace a los shaders difíciles de dominar es la falta de una documentación
adecuada. La mayoría de los desarrolladores aprenden shaders por estropear con el código, sin
tener un profundo conocimiento de lo que está pasando. El problema es amplificado por el
hecho de que Cg / HLSL hace un montón de suposiciones, algunas de las cuales no se anuncian
adecuadamente. Unity3D permite que los scripts C # se comuniquen con shaders usando
métodos tales como SetFloat, SetInt, SetVector, etc. Desafortunadamente, Unity3D no tiene
un método SetArray, lo que llevó a muchos desarrolladores a creer que Cg / HLSL tampoco es
compatible con matrices. Esto no es verdad. Esta publicación le mostrará cómo es posible
pasar matrices a sombreadores. Sólo recuerde que las GPUs están altamente optimizadas para
los cálculos paralelos, y el uso de los bucles en un shader reducirá drásticamente su
rendimiento. Para esta receta, implementaremos un mapa de calor, como se muestra en la
siguiente imagen:

Preparándose
Cómo hacerlo…
Cómo funciona…
Índice

Unidad

También podría gustarte