Jump to content
UnitySpain

Aceptamos donaciones vía Paypal.

UnitySpain.com es un servicio gratuito, pero mantener la Comunidad conlleva una serie de gastos.

Fondo Anual Unityspain: Donados 15,00€ de 150,00€

  • Servidor: Dominio.com y Hosting Web
  • Mantenimiento de los Foros
  • Contenido y Servicios Extras
  • Mantenimiento para Redes Sociales

Leaderboard


Popular Content

Showing most liked content since 03/23/2020 in all areas

  1. 2 likes
    Desarrollo en Unity 2019.3. Una primera aproximación a un FPS sin prácticamente optimización más allá de los lightmaps del entorno. Es posible utilizar diferentes FX dependiendo del material de contacto. He utilizado algunos assets de pago y otros gratuitos en los apartados de diseño solamente. El código y la integración es mío desde cero.
  2. 2 likes
    Pues una manera de solventarlo muy elegante. Usando una extensión en MonoBehaviour y cambiándolo por una coroutine y delegate: https://wp.flyingshapes.com/dont-use-monobehaviour-invoke-or-how-to-properly-invoke-a-method/
  3. 2 likes
    En esta entrada vamos a aprender a crear nuestro primer shader Vertex/Fragment en Unity. Iremos paso a paso hasta terminar creando nuestra propia bandera con ondulación: Requisitos Para crear este proyecto podemos usar cualquier versión de Unity 2018/2019/2020 siempre y cuando a la hora de crear el proyecto usemos la plantilla de 3D (Built-in Render Pipeline) Antes de pasar al código, creo que es necesario que definamos qué es un shader y las partes que lo componen. ¿Que es un Shader? Un shader es un contenedor de instrucciones que se envían a la GPU para mostrar los elementos que tenemos en pantalla. Al igual que el script de un personaje contiene las instrucciones para que este se mueva o gire, el shader contiene información sobre el color o los vértices de un objeto. Al igual que para escribir comportamientos Unity usa C#, para escribir shaders Unity usa su lenguaje llamado Shaderlab. A su vez Shaderlab permite escribir fragmentos de código en CG (C for Graphics) que es donde realmente estará el código del shader en sí. El lenguaje CG fue desarrollado por Nvidia y Microsoft y es compatible tanto con OpenGL como con DirectX. Veremos más adelante en profundidad las partes que componen Shaderlab y CG. Tipos de Shaders En Unity tenemos dos opciones a la hora de crear un shader. Surface shaders: podemos definirlo como una "plantilla" creada por Unity para facilitarnos gran parte del trabajo aunque dando menos control sobre el resultado final. Vertex/Fragment shaders: el código que existe bajo la capa de Surface anterior. Tiene mayor complejidad pero ofrece mayor control sobre el resultado. En esta entrada nos centraremos en estos últimos, ya que el paso a Surface (o a ShaderGraph) es mucho más productiva si conocemos los conceptos del Vertex/Fragment. Fases del Pipeline Para conseguir mostrar un objeto de juego (3D) por pantalla (2D) se requiere de un proceso con varias fases. Ese proceso es llamado Rendering Pipeline. Para que sea más sencillo de entender, voy a resumir los pasos para centrarnos en los que nos interesa ahora. Básicamente tenemos dos funciones que podemos modificar: Vertex: En esta función podemos modificar datos relativos al mesh, principalmente la posición de los vértices, de ahí su nombre. Consta de una entrada de datos (como la posición de los vértices mencionada o las uvs) y produce una salida que será el input de la función fragment. Fragment: En esta función obtenemos los datos recibidos después de convertir las coordenadas del objeto en coordenadas de pantalla. Por ello todas las modificaciones aquí son a nivel de coordenadas de pantalla. Por ejemplo el color, su transparencia etc. Esta explicación es muy superficial y muy rápida, puedes encontrar más información aquí: https://en.wikipedia.org/wiki/Graphics_pipeline Una vez explicado esto, vamos a pasar a la parte práctica. Abrimos por fin Unity y creamos el proyecto con la plantilla de 3D que comentamos (Built-in Render Pipeline). Partes de un Shader en Unity Para empezar vamos a crear una nueva escena. La abrimos y creamos un nuevo Shader Unlit: Le ponemos de nombre FlagEffect por ejemplo y creamos un Material que lo use. Ahora crearemos un cubo en la escena y le colocamos las siguiente posición, rotación y escala: Creamos ahora un Plano, le arrastramos nuestro material y lo colocamos en: Si todo ha ido bien deberás tener algo así. Puedes ajustarlo tú a tu gusto (aunque ten cuidado al girarlo ya que podrías no tener el resultado esperado al final) Una vez creada nuestra bandera, vamos a abrir el shader FlagEffect. Vamos a borrar todo el contenido y vamos a ir conociendo sus partes poco a poco. Esta sería la estructura del shader con la que deberías empezar: Shader: La carpeta donde se registrará nuestro shader. Properties: Son las variables que podemos crear y ver a través del inspector del material. Cuando en un inspector ves una cajita para la textura, un botón para el color o un campo con un valor numérico aquí es donde se deben crear. La sintaxis es la siguiente: NombreVar ("TextoInspector",TipoVariable) = ValorPorDefecto Aquí podemos ver las diferentes propiedades que podemos crear en Unity: https://docs.unity3d.com/Manual/SL-Properties.html SubShader: El contenido del shader estará aquí dentro. Pass: Cada llamada de pintado que haremos. Cada una tendrá sus funciones Vertex y Fragment. Por defecto solo tendremos y necesitaremos una. Aquí es donde estará el código, acotado entre la pauta de inicio y de fin de CG que vemos en la imagen. Fallback: no es obligatorio aunque es recomendable. Si nuestro shader no es compatible con la plataforma en la que corra, se llamará a otro shader para evitar que se vea rosa. Pero para nuestras pruebas recomiendo borrar la línea. De esta forma si tenemos algún problema con nuestro shader podremos verlo directamente (de otra forma puede que llame al Fallback y no lo veamos rosa). Una vez tenemos la plantilla básica, vamos a añadir las funciones de Vertex y Fragment y los input/output necesarios. Vamos a empezar creando estos últimos. Como vimos anteriormente vamos a necesitar una entrada a la función Vertex y una salida de ella. También necesitamos una salida de la función fragment. Para esto usaremos struct de C (que se declaran igual que en C#). Ahora vamos a necesitar las funciones Vertex y Fragment. La primera tendrá como parámetro su input y devolverá el output. La Fragment tendrá como parámetro el output del vertex. Hasta aquí simplemente tenemos las funciones creadas con sus correspondientes inputs/ouputs configurados (aunque vacíos). Si vamos a Unity veremos nuestro material rosa y eso es debido a que el shader cree que nuestras funciones son diferentes a las Vertex/Fragment. Tenemos que decirle mediante un #pragma que la función de vertex se llama vert en nuestro código y que la función fragment se llama frag. Puedes ponerle otro nombre a las funciones sin problema, siempre que en esta conexión lo pongas bien: De esta forma ya no tendremos problemas con nuestro shader, salvo por el pequeño problema de que no se ve nada. Inputs y Outputs En este apartado vamos a rellenar nuestros structs de datos con la información que necesitamos en nuestras funciones de vert y frag. Dependiendo del shader que estemos creando vamos a necesitar más o menos datos dentro de los inputs. Por ejemplo si solamente queremos cambiar el color del objeto a rojo, no necesitamos datos de UV. Podemos ver todos los datos que podemos usar en cada función aquí: https://docs.unity3d.com/Manual/SL-VertexProgramInputs.html En nuestro caso vamos a necesitar para las ondulaciones de la bandera, la posición de los vértices y las coordenadas de uv del modelo. Para ello vamos a crear estas dos variables dentro de vertexInput y de vertexOutput. Lo que aparece después de los dos puntos es la semántica y es necesario para conectar los datos. En el artículo anterior de Unity explica esto más detenidamente* * Te darás cuenta de que Unity en el output usa SV_Position, que simplemente es un valor de sistema que lo hace más compatible con los sistemas actuales, para facilitar el entendimiento lo he obviado y he usado el POSITION Llegados a este punto voy a explicar rápidamente los tipos de variables que podemos usar dentro de CG. Como ves he usado fixed2 y fixed4. El número al final hace referencia a vectores de 2 y 4 datos. Sería algo similar a un Vector2 o un Vector4. El tipo fixed es el tipo de menos coste y de menor precisión. Estarás acostumbrado a ver float o int. Estos también pueden ser usados de la misma forma que fixed. Aquí puedes ver los tipos de datos: https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html También puedes crear matrices siguiendo la misma lógica, la sintaxis sería “float4x4”,siendo los valores del final las filas y las columnas. Una vez tenemos el vertex con los datos, vamos a pasar al fragment. Este simplemente necesitará un valor, el color final del pixel. En este caso usaremos SV_Target que es el que Unity recomienda por defecto. Como he dicho tienes más información acerca de la semántica en los enlaces anteriores. Como normalmente el fragment solo tiene un valor de devolución, puedes optimizar y borrar el struct de output cambiando el frag de la siguiente manera. El resultado es el mismo que el anterior pero en menos pasos. Ahora vamos a rellenar nuestra función de vertex. Pero antes vamos a necesitar incluir funciones que Unity nos ofrece para facilitarnos el trabajo. Como hemos dicho antes, vamos a convertir la posicion 3D del objeto en posición de pantalla. Para esto vamos a usar una función de Unity llamada UnityObjectToClipPos. Esta función recibe la posición de los vértices y devuelve las posiciones en espacio de pantalla para después usarlas en la función fragment. Para poder usarla vamos a incluir esta línea debajo de los #pragma. El #include sigue la misma lógica que los using en C#, de esta forma podemos hacer uso de funciones que Unity tiene preparadas (como la que hemos comentado antes). Ahora en la función vertex vamos a igualar el output que devolveremos a lo que la función UnityObjectToClipPos nos devuelva. De esta forma convertiremos los vértices para el fragment. También vamos a igualar de momento las uv del input y del output, aunque las usaremos justo ahora. Procedemos ahora a mover por fin los vértices. Para esto vamos a necesitar conocer por un lado la variable interna _Time que posee Unity, similar a Time.deltaTime. Esta es un Vector4 con diferentes tipos de tiempo en cada una. Aquí podemos ver más información de esa y de otras variables internas de Unity: Por otro lado vamos a hacer uso de la función seno de CG para las ondulaciones. Creamos una nueva función en nuestro shader, de nombre flag que devuelva un fixed4. Además tendrá como parámetros la vertexPosition y las uv. Tened en cuenta que el shader compila de arriba hacia abajo, asique si escribís vuestra función al final y la usáis antes, dará error. Siempre tenéis que crear vuestras funciones antes de usarlas (a diferencia de C# por ejemplo). ahora dentro vamos a ir sumando a la vertexPosition el seno de _Time.y (el tiempo por defecto).Lo igualamos en la función vert al o.vertex quedando así: Estamos simplemente moviendo los vértices del input y asignándoles al output. Posteriormente convertimos con el UnityObjectToClipPos y seteamos las uv del input al output. Si probamos el efecto, vemos el movimiento pero no es lo que queremos. Esto es debido a que el seno en Time es constante por lo que siempre se mueve de forma uniforme. Vamos a añadir el valor de uv.x (valor de 0 a 1) y restarle el Time para conseguir el efecto que queremos. De esta forma funciona, pero vamos a necesitar ajustarlo. Para esto vamos a crear tres propiedades de tipo Range. Esto es similar al atributo Range de Unity para los valores numéricos en C#. Te permite mostrar un float en el inspector del material y moverlo con un slider entre dos valores. Ahora podrás ver las tres variables en el editor. Pero para poder usarlas tenemos que declararlas en CG con el mismo nombre que tengan en la propiedad (_Speed,_Frecuency, _Amplitude). Usaremos el tipo fixed. Estas variables las vamos a usar de la siguiente forma: Speed: va a multiplicar al _Time.y para acelerar y reducir la velocidad. Frecuency: va a multiplicar a la resta entre uv.x y la multiplicación del Time y la speed. Justo antes de hacer el seno. Amplitude: multiplica al valor una vez el seno se ha calculado. Estos son los tres cálculos básicos a la hora de modificar la ondulación. Grábalos en tu mente (o apuntalo mejor) porque los usarás muchas más veces en tu vida. Si todo ha ido bien, podrás ajustar los valores como quieras para mejorar la ondulación. Aquí puedes ver mi configuración: Solo nos quedan dos pasos para terminar. El primero es hacer que la bandera se pegue al poste y no ondule. Esto es muy fácil de conseguir teniendo las referencias de uv.x que, como dijimos van de 0 a 1. Solo tenemos que multiplicar el seno que tenemos por la uv.x. De esta forma anclamos la bandera, consiguiendo que la ondulación vaya de mayor a menor fuerza desde el poste (gracias al valor entre 0 y 1 de uv.x) Lo otro que nos queda es añadirle color. Aquí voy a poner dos opciones, la primera es la básica, añadir un color sin más. Si estás cansado y no quieres continuar es la opción que te recomiendo. Podrás volver otro día para ver la segunda opción. En la segunda opción usaré un lerp para darle a la bandera 3 colores diferentes en función de la Y de su UV. Añadir color básico Simplemente tendrás que añadir una propiedad _Color y una variable de CG para conectarla (al igual que la _Speed y los demás valores anteriores). En este caso el color tiene cuatro valores, así que usaremos fixed4. Y en la función frag devolvemos el color a pelo. Añadir color avanzado Si venimos del básico vamos a borrar la propiedad de color (o cambiarle el nombre) y crear otras tres diferentes. Estas se llamarán ColorTop, ColorMiddle, ColorBotton (o el nombre que tu quieras). Estos tres colores serán las tres franjas que tendrá la bandera. Ahora creamos una función nueva encima de frag con el nombre getPixelColor que devolverá un fixed4. Esta función analizará la posición de la uv en Y (la necesita como parámetro) y haciendo uso de la función lerp y step nos devuelve un color u otro. ¿Qué hacen realmente cada una de estas funciones? Puede ser complicado de leer tal y como está, pero solo necesitas saber lo que hace para entenderlo. La función lerp interpola entre dos valores con un valor t. Cuando t es 0 devuelve el primer valor y cuando es 1 devuelve el segundo valor, devolviendo valores intermedios. En nuestro caso no queremos valores intermedios, queremos una interpolación directa dado un valor de referencia. El valor de referencia será ⅓ ya que tenemos 3 franjas. La función step tiene dos parámetros, si el primero es mayor que el segundo, devuelve 1, si es menor devuelve 0. Esto unido al lerp hace que en valores superiores a 0.33 pero inferiores a 0.66 se coloree del color del centro. Y de igual forma en valores superiores e inferiores. Con esto hemos llegado al final de la entrada. Si tienes cualquier duda con esto déjamela en los comentarios. PD: Si queréis que la bandera se muestre en ambas caras, tendréis que cambiar un valor del Cull. Esto es fácil de hacer pero da para otra entrada junto a otros añadidos como el Blend el ZTest o el ZWrite. De momento para que te quede perfecto, solamente tendrás que añadir junto antes del Pass (o dentro del Pass antes de CGPROGRAM) la siguiente linea: Con Cull en Off no ocultará ninguna cara. Nos vemos en la próxima entrada.
  4. 2 likes
    El componente button contiene un booleano que se llama Interactable justamente para esta situación.
  5. 1 like
    Se ve bien, aunque el efecto de ráfaga del arma es muy grande (escala), pienso que estaría mejor si se reduce el tamaño solo un poco. Lo de los Zombies es algo muy sobreexplotado pero se puede sacar cosas originales, algo como especies de"Hunter" (Left 4 dead), que tengan la habilidad de moverse rápido cada cierto tiempo, y se puede hasta sacar una historia; "Eres un guardia de seguridad que trabajaba para una corporación que quería inventar la cura para algo o para hacer el gran paso a humanos mejorados, pero sucedió algo, y se salió de control, entonces tu misión es salir de esa corporación que su laboratorio es muy grande (algo como todo una residencia *como lo extenso que era Umbrella xD* ) y pasas por diferentes sectores en el que cada uno estaban creando diferentes tipos de vacunas y por eso tienen infectados especiales, quizá vallas encontrando más aliados inmunes y van creando una resistencia que quieren lograr salir de ahí. No sé, con una buena historia por detrás estaría bueno xD
  6. 1 like
    @AngelFG Te aconsejo normalizar la diferencia entre mouse y player
  7. 1 like
    Me gusta mucho el efecto visual del disparo, la forma en que altera la imagen. ¡Ánimo! ¿tienes ya una idea de lo que quieres hacer o sólo estás experimentando con el FPS?
  8. 1 like
    Por un lado tenés Transform (pos,rot y escala), que se utiliza principalmente para el renderizado, es decir antes de realizar el render del frame se leen los datos de este transform, se procesan en el shader (basicamente creando una gran matriz) representando en 2D (tu pantalla) un objeto ficticio en 3D proyectado (ortonormal o perspectiva) sobre una cámara dada. Como regla, asocia al Transform al render. Por otra parte están las físicas, que trabajan con el concepto de collider + rigidbody. Siempre al final de cada update físico se pasan los datos del rigidbody (pos y rot) al transform. Las físicas hacen lo suyo y a lo último actualizan el transform, así vos podés ver en pantalla lo que pasa, de lo contrario estaría todo congelado. Las colisiones y demás pasarían internamente pero no serías capaz de verlas en pantalla. Este es un resumen usando un caso en concreto, modifcando rigidbodies en fixedUpdate y modificando transforms en update: Fijate como una vez que te metés con rigidbodies, al transform solamente se le pasan los datos del rigidbody (sincronización RB -> T ) para que este renderize. PERO, el mundo del rigidbody no está enterado si luego vos modificas el transform. Siguiendo con el video de ejemplo de arriba, este es el porqué se ven esos tembleques en pantalla, transform dice "nos vamos para adelante" y el rigidbody dice "no, hay un muro adelante!" y como Update suele correr más veces que la simulación física, parece como que Update gana hasta que la simulación entra y lo tira para atrás. Por esta razón usar las físicas (es decir, usar rigidbodies) y modificar los datos del transform es contradecirte a vos mismo. El video de arriba está casi completamente mal dado, así que cuidado. En los dos primeros casos está moviendo un rigidbody dinámico usando Transform, lo cual está horriblemente mal. Entiendo si lo hizo para mostrar lo mal que está, pero lo preocupante es que en un momento dice "Este método es el que más uso", cosa que me incomoda. En el caso de la fuerza está aplicandola en Update ... se hace en FixedUpdate. En MovePosition dice que funciona como transform.position , cosa que está tremendamente mal. Funciona como rigidbody.position, que no es lo mismo (ver el diagrama de arriba). Normalmente no funciona así, esto pasa solo con rigidbodies dinámicos. MovePosition se suele usar para rigidbodies kinemáticos. También usa Update. En el de velocity otra vez usa Update. TL;DR: Si tenés un rigidbody en tu código, andá mirando a los transform con cuidado, no solo a la hora de escribirlos (que claro que está mal), sino también a la hora de leerlos. Si dentro de tu loop moviste al rigidbody (rigidbody.position) el transform no lo va a reflejar para nada, son dos conjuntos de datos separados uno de otro. Saludos.
  9. 1 like
    @Jhonatan00_00, Claramente te entendi de ante manos, por eso te comentaba que para el efecto en 3D deberias utilizar Post Procesado, digamos que un objeto 3D es eso y nada más, no se puede comportar como 2D por que si, si bien se puede desarrollar shaders para que las caras de atrás no se rendericen y encontrar una forma de renderizar todo lo no afectado por el alpha entonces podrias crear algo similar. Si queres lograr este efecto de modificar el Culling y Z-depth para obtener un alpha por shaders deberias aprender a utilizar shaders, la guia oficial esta bastante bien para los conceptos basicos. https://docs.unity3d.com/Manual/ShadersOverview.html Contenido de interés para realizar shaders. http://wiki.unity3d.com/index.php?title=AlphaVertexLitZ https://github.com/andydbc/HologramShader https://forum.unity.com/threads/transparent-depth-shader-good-for-ghosts.149511/ https://forum.unity.com/threads/question-about-zwrite-and-transparency.529054/
  10. 1 like
    @pioj están todavía los artículos que escribí sobre ésto? Deben tener como 3 ó 4 años. Sino, tiro de hemeroteca de código hecho. Hice ésto:
  11. 1 like
    Podrias tomar el valor de la frecuencia actual, y definir en que momento es el pulso Podrias buscar el algoritmo que se utiliza para buscar el BEAT de una pista. Podrias asignar manualmente el valor de BEAT de la pista Podrias asignar manualmente el valor del BEAT en función del TIEMPO de la PISTA. Con eso tenes para buscar alguna solución.
  12. 1 like
    Para este tipo de cosas necesitas POST-EFFECTS Te recomiendo sigas esto como para tener una idea
  13. 1 like
    Introducción En esta ocasión vamos a crear una Carta con un efecto 3D similar a la que vemos en el gif anterior. Para ello vamos a partir de un proyecto donde tenemos preparados los assets necesarios. Este proyecto podemos descargarlo desde aquí: URL: https://drive.google.com/open?id=19J6lgCA-DH28lMu9dmntk1K5l3lPE66f Una vez descargado vamos a abrirlo desde el Unity Hub. Este proyecto está creado con la versión 2019.2.9f1 asique podría ser incompatible con versiones anteriores. Una vez abierto vamos a tener una escena llamada “Sample Scene” y una serie de carpetas con los modelos,sprites,prefabs… necesarios. Además contamos con los paquetes TextMeshPro y PostProcessing que usaremos a lo largo del tutorial. Puedes acceder en todo momento a los paquetes usados a través del Package Manager. Preparando el proyecto En primer lugar vamos a abrir la escena “Sample Scene” y vamos a arrastrar en ella el prefab “Hornet” dentro a la ventana de Hierarchy. Verás que aparece en rosa, eso es debido a que no tenemos aún ajustado el Render Pipeline. Vamos a empezar por ahí. Vamos a crear una carpeta Settings donde colocaremos aquellos ajustes globales del proyecto. Siéntete libre de elegir otro nombre, yo siempre soy malisimo poniéndolos. Una vez creada vamos crear una configuración pulsando click derecho y Create/Rendering/LightweightRenderPipeline/PipelineAsets. Elige el nombre que quieras y seleccionalo para verlo en la ventana de Inspector. Aquí tenemos los ajustes del Render Pipeline. La configuración es muy intuitiva y prácticamente todo hace lo que indica. En nuestro caso solamente vamos a activar el HDR y el antialiasing. Lo demás no lo tocaremos, aunque esto va de experimentar, así que “rómpelo” a tu gusto. Una vez creado vamos a añadirlo a nuestro proyecto, para ello vamos a irnos a ProjectSettings/Graphics y lo colocamos en el hueco de Scriptable Render Pipeline Settings. Hecho esto si volvemos a la escena podemos ver que el color rosa ha desaparecido. Aunque ahora se verán blanco. No te preocupes que ese será nuestro siguiente paso. También verás que hay una luz añadida dentro del prefab de Hornet. Está configurada por mi pero llegado el punto puedes quitarla o cambiarla como quieras. Creación de Shaders Vamos a darle color a nuestros objetos ahora. Como has podido ver todos los objetos están en blanco. Eso es porque nuestros shaders y materiales están por defecto. Para nuestro objetivo vamos a necesitar crear un shader con la herramienta Shader Graph. Esta ya viene por defecto en todos los proyectos que usen el Scriptable Render Pipeline nuevo. Para ello vamos a crear una nueva carpeta llamada Shaders y vamos a crear un nuevo Shader PBR (Create/Shader/PBR Graph). Puedes ponerle el nombre que quieras, en mi caso lo he llamado Standard_SG (ya he dicho que soy muy malo poniendo nombres). Vamos a seleccionarlo y abrirlo. No es el objetivo de este taller aprender a usar ShaderGraph, asique no voy a pararme en explicar el contenido, aunque como veréis es muy fácil e intuitivo lo que haremos. Crearemos las siguientes variables: Conectaremos al Albedo un Sampler de una textura y la multiplicaremos por un color. Cómo veis el shader es lo más simple posible. Haremos lo mismo en el canal de Emission para poder ajustar la emisión de algunos objetos posteriormente. Pulsamos en Save arriba a la izquierda y cerramos. Ya tenemos nuestro shader creado pero tenemos un problema que veremos a continuación. Stencil Buffers Para obtener el efecto que buscamos tenemos que conocer el concepto de Stencil Buffer. Con ellos podemos principalmente renderizar unas partes de un objeto y descartar otras. Aquí podemos obtener más información: https://docs.unity3d.com/Manual/SL-Stencil.html https://www.ronja-tutorials.com/2018/08/18/stencil-buffers.html El objetivo es mantener “invisible” un objeto y que cuando este se encuentre en la “visión” de otro objeto, se muestre. Vamos a necesitar por dos shaders: Uno que leerá del stencil buffer y se dibujará solo donde el búfer tenga un valor específico, en cualquier otro lugar se descartará. Por otro, un shader que permanerá siempre invisible y que solamente mostrará aquellos valores específicos que coincidan con el stencil buffer. Para el primer solo necesitamos añadir unas líneas, pero actualmente no es posible hacerlo a través del ShaderGraph. Por lo que vamos a realizar una transcripción del Shader Graph al código. Para esto vamos crear un nuevo Shader Unlit pero esta vez no será de ShaderGraph. Le ponemos de nombre Standard_StencilRead por ejemplo y lo abrimos. Mientras se abre vamos a volver al Shader Graph y vamos a realizar la copia del código del shader. Para ello nos vamos al PBR Master Node y con el botón derecho seleccionamos Copy Shader. Una vez copiado vamos a pegarlo en nuestro reciente nuevo shader unlit creado, sobreescribiendo todo lo que este tuviese. También deberás cambiar la primera línea y ponerle tu la dirección y el nombre que elegimos antes. En mi caso lo he guardado en MyShaders con el nombre Standard_StencilRead. Ahora tenemos el mismo shader de Shader Graph pero en código para poder modificarlo. Vamos a necesitar añadir una nueva propiedad que será el valor específico del stencil. Simplemente dentro de properties al final, debajo del Color añadimos lo siguiente: [IntRange] _Stencil ("Stencil Value", Range(0,255))= 0 Quedando de la siguiente manera: Por último vamos a necesitar añadir los ajustes del Stencil debajo de los Tags antes de los diferentes pases. Esto hará que sólo cuando tengamos el valor de referencia, el objeto se muestre. Podemos obtener más información sobre la sintaxis y el contenido en los enlaces que anteriores. Ya podemos guardar el shader y volver a Unity. Tenemos preparado nuestro primer Shader. Para el segundo simplemente creamos otro shader unlit de nombre StencilWrite por ejemplo. Este estará siempre invisible y servirá para mostrar los objetos que compartan valor de referencia. Abrimos el shader y lo modificamos de la siguiente forma: En properties añadimos el valor de Stencil como con el anterior y eliminamos cualquier otra propiedad (normalmente textura) ya que este shader queremos que se muestre totalmente invisible. Añadimos el Stencil y le decimos que esté siempre comparando y si en algún momento el valor de referencia es el mismo, lo reemplace. Ahora le decimos al buffer de profundidad que no se escriba (por ser un objeto que será invisible) y cambiamos el modo de Blend. Podemos obtener más y mejor información de esto aquí: https://docs.unity3d.com/Manual/SL-Blend.html También eliminamos todos los añadidos de Fogs por defecto de Unity y aquellas referencias a texturas que no nos interesan. Por último hacemos que la función frag devuelva siempre 0, ya que como hemos comentado esto se muestra siempre como invisible. Con esto tenemos todo listo para pasar al montaje de la carta. PD: Asegurate también de que lo tienes en la misma ruta que el anterior. Esto no es necesario, pero te facilitará encontrarlo más adelante. En mi caso al igual que antes, esta en la ruta MyShaders. Montaje de la carta Para empezar vamos a cambiar por fin el blanco de nuestra escena, para ello vamos a irnos a Materials/Hornet y seleccionando todo vamos a elegir nuestro Shader (en este caso el Standard_StencilRead) Ahora tendremos que arrastrar las texturas requeridas (Main Texture y Emission Texture) si procede y establecer el MainColor en blanco (tened cuidado que el alfa no este a 0) y el EmissionColor en negro. En el caso que no tengamos texturas (como en el caso del personaje Hornet) simplemente cambiale el MainColor a tu gusto. Puedes hacerlo similar al original o ponerle tu propia versión. Así quedaría el del grass_material por ejemplo: O el dress_material, que no tendría textura: Sigue estas pautas en los demás materiales. El stencil value lo cambiaremos más adelante, de momento necesitamos ver en pantalla todo y no solo cuando este a través de nuestro “marco”.. Una vez terminado debería de quedarte algo similar a esto: Ahora vamos a crear un nuevo objeto en la escena llamado Card y lo ponemos en la posición 0,0,0. Ahora buscamos el Sprite “cover” dentro de Sprites y lo arrastramos dentro de Card. Le damos el tamaño a nuestro gusto. Este será el envoltorio de la carta. Ahora vamos a crear un Quad dentro de Card. Le ponemos de nombre StencilMask y lo colocamos en la posición 0,0,0 también. Este será nuestro marco a través del que se verá nuestra Hornet. Para poder ajustarlo vamos a tener que configurar ya el valor de referencia del stencil así como crear un nuevo material StencilWrite para nuestro quad recién creado. Vamos a empezar por esto último, crearemos un nuevo material con el shader StencilWrite. Lo colocamos dentro de Materials para tenerlo ordenado. A este material le vamos a poner en el valor de Stencil el valor 2 por ejemplo. Es simplemente un valor referencia, mientras tanto el receptor como el emisor tengan el mismo, funcionará. Una vez configurado vamos a arrastrarlo al StencilMask (o cambiamos el material dentro de MeshRenderer) para colocarselo. Veréis que se ha vuelto invisible, eso es que funciona como debería. Por último vamos a irnos a los materiales StencilRead que configuramos anteriormente y vamos a ponerles el mismo valor de referencia al Stencil. Seleccionamos todos o uno a uno y en Stencil Value le colocamos 2. Ahora debería de volverse todo invisible en la escena quedando solo la carta en negro. Vamos a desactivar de momento la carta llamada “cover” (o el nombre que tu le hayas puesto) de la ventana de Hierarchy. No se ve nada pero no es un error. Ahora mismo todos los shaders tienen el mismo Render Queue por lo que nuestro efecto no está funcionando. Más información de esto aquí: https://docs.unity3d.com/Manual/SL-SubShaderTags.html Sabiendo esto solamente tendremos que asegurarnos que nuestro StencilWrite se renderice antes. Vamos a nuestro material y le restamos 1 al RenderQueue. Con esto ya deberiamos tener visible nuestro modelo únicamente cuando estemos viendolo a través de nuestro quad. Ahora volvemos a activar el cover para ajustar la carta y el hueco como queramos. Al activarlo verás que tapa la visión a través del quad. No te preocupes, simplemente ve a los ajustes del SpriteRenderer del cover y cambia el MaskInteraction a Visible Outside Mask. Ahora ajustamos el Quad hasta tener el tamaño que deseemos. Una vez confirmado podemos probar a girar en la escena alrededor de la carta para comprobar el efecto. Para organizar mejor la escena vamos a colocar el objeto Hornet dentro de Card y vamos a crear un prefab para tenerlo guardado. Gracias a los Nested Prefab ya podemos hacer prefabs dentro de prefabs, asique nos viene genial. Ahora solo nos falta añadirle algo de texto. Vamos a usar TextMeshPro para esto. Pulsamos botón derecho y creamos un nuevo texto en 3D. Vamos a empezar por el nombre de la carta, por lo que le ponemos de nombre al text “Name” por ejemplo. Vamos a escribir el texto del nombre en el apartado Text y vamos mover y escalar el objeto colocándolo donde más nos guste. En mi caso he decidido ponerlo arriba. Vereis que no se ve cuando lo colocamos en la zona de la carta. Eso es por el orden de renderizado que tienen. Vamos a abrir los ajustes extra del texto (extra settings) y vamos a subirle el orden in layer a 1. Con esto conseguimos verlo, pero existe un problema, que el texto aparece también por detrás. Esto lo solucionaremos más adelante ya que tendremos que añadir un script simple para que active el culling. Llegados a este punto me he dado cuenta de que la carta está colocada del revés y el nombre por la parte trasera se ve invertido. En nuestro caso podemos solucionarlo rápido ya por delante la carta no se ve afectada. Simplemente nos vamos al objeto “cover” y marcamos Flip X. De esta forma la parte de atrás estará perfecta. En el caso de que tuvieras una imagen diferente deberías tener cuidado ya que la carta es la misma tanto en la partal como en la dorsal. Todo se dará la vuelta. Si quieres tener un control más exacto, deberás tener una carta delantera y otra trasera, cada una con su ajustes (y quizás optimizada para no pintar dos cada cara). Vamos a guardar el avance, aplicamos el prefab y guardamos escena. Deberíamos tener algo similar en estos momentos: Vamos a crear ahora otro texto para colocarlo en la parte de debajo. Duplicamos el objeto Name con Control+D y cambiamos el nombre a Description. Movemos el objeto a nuestro gusto, en mi caso lo coloque en la parte de abajo, centrado. Puedes tocar los ajustes del TextMesh que son maravillosos, sobre todo si has trabajado con el deprecado Text. Si queremos podemos cambiar la fuente. Para ello simplemente tendrás que descargarla de internet (o obtenerla de otra forma) y meterla dentro de Unity. Una vez dentro no es simplemente arrastrarla. Para poder usarla en TextMesh necesitarás crear un atlas de la fuente. Pero es fácil de hacer, simplemente abre la ventana Font Asset Creator desde WIndows/TextMeshPro. Selecciona la fuente y pulsa Generate Font Atlas (si quieres cambiar algún otro ajuste puedes hacerlo, pero ten cuidado. Más información sobre esto aquí: http://digitalnativestudios.com/textmeshpro/docs/font/ ) Una vez generada simplemente guárdala donde quieras y luego colócala en el texto dentro de Main Settings: Una vez tengamos la descripción vamos a guardar el prefab y a colocar los Post Procesos. Para ello Vamos a ir a la cámara y vamos a asegurarnos que tiene añadido un Post Process Layer. Esto es imprescindible para poder continuar. También vamos a activar a antialiasing FXAA y vamos a crear un nuevo layer PostProcessing para posteriormente usarlo en el Volume. Ahora vamos a crear un nuevo objeto en la escena. Le vamos a añadir el componente Post Process Volume. Vamos a marcar IsGlobal porque solo tendremos este para toda la escena y vamos a seleccionar un profile. En nuestro caso no tenemos ninguno creado, asique le daremos a New. Ahora simplemente asegurate de que este objeto está en el Layer PostProcessing De otra forma no funcionará y no podremos ver ningún cambio. Una vez configurado podremos ir añadiendo efectos ya creados por Unity o crear los nuestros propios. En este caso vamos a usar dos creados, el Bloom y el Color Grading. Pulsamos en Add Effect y seleccionamos uno y después el otro. Aquí puedes sacar tu lado artístico y elegir la configuración que más te guste. En mi caso después de una serie de pruebas quedó así: En este punto deberías tener algo similar a esto. Si no es así asegúrate de que el material del lazo tiene un emission color bien configurado. Por último nos queda solucionar el error del texto por ambas caras. Para eso simplemente vamos a crear un script con el nombre CullingTextMeshPro o el que querais y vamos a dejar solo la función Awake con el siguiente código: De esta forma al darle al Play el texto automáticamente hará culling y no se renderiza por detrás. Estuve informándome sobre otras formas de hacer esto, pero las respuesta de Unity fueron que esta era la forma. Pero si conocéis otra forma contactad conmigo y lo actualizo. Espero que os haya gustado y hayáis aprendido cosas nuevas que al final es el objetivo esto. Cualquier duda contacta conmigo via twitter: https://twitter.com/_davidlopez29 o por aquí Hasta pronto!
×
×
  • Create New...