Tutorial de Efectos y Shaders de 3DMigoto GIMI
Escrito por: SilentNightSound
Este tutorial cubrirá el proceso de cambiar los efectos de Genshin, como los efectos de habilidades, efectos de iluminación y cualquier objeto en el juego que no esté controlado a través de texturas o buffers. Aprender cómo funcionan los shaders aumentará significativamente el rango de lo que puedes modificar.
Este tutorial de modificación de efectos es más difícil que mis anteriores sobre edición/importación básica de mallas y edición de texturas, pero leer esos no es un requisito previo para entender este tutorial.
He organizado este tutorial aproximadamente en orden de dificultad creciente, por lo que incluso leer la primera sección debería ser suficiente para hacer ediciones simples. Las secciones posteriores requerirán conocimientos básicos de programación.
Voy a recorrer tres ejemplos de complejidad creciente:
- Cambiar el color del ataque/habilidad de un personaje (ver https://gamebanana.com/mods/409181 para un ejemplo de recolorear los ataques de hielo de Ganyu)
- Crear un efecto que cambia entre múltiples colores con el tiempo (ver https://gamebanana.com/mods/418434 para un ejemplo de un árbol de Navidad que cambia el color de las luces)
- Demostrar cómo crear animaciones de efectos básicas (ver https://gamebanana.com/mods/420434 para un ejemplo de líneas animadas en el traje cibernético de Raiden)
Requisitos Previos
Tener instalada la versión de desarrollo de 3dmigoto GIMI (el texto verde debe ser visible).
Nota Importante
Por defecto, he deshabilitado la capacidad de GIMI para volcar shaders ya que pueden interferir con los mods. Puedes reactivarlos asegurándote de que la línea marking_actions
en d3dx.ini contenga hlsl
y asm
en la lista.
Además, si tienes problemas con los mods después de probar cosas en este tutorial, intenta vaciar la carpeta ShaderFixes; a veces, un shader volcado puede causar que ciertos mods funcionen mal.
¡Comencemos!
Cambiar los Colores de Ataque de un Personaje (Llamas de Diluc)
Para la primera sección, demostraré cómo recolorear las llamas de Diluc. Esta sección es de dificultad básica/intermedia y no requiere conocimientos previos de codificación/shaders.
Primero, recomiendo viajar a un lugar donde haya la menor cantidad de objetos en pantalla posible, pero el efecto que estás buscando aún se mostrará. Verás por qué en breve, pero cuantos más objetos haya en pantalla, más tiempo llevará buscar los shaders que estamos buscando. El área de la playa inicial siempre es una buena opción.
Una vez que tengas una buena ubicación, activa el efecto que estás buscando y entra en el menú de pausa. En este caso, estamos interesados en el efecto de llama de la habilidad de Diluc, así que presionamos e y luego pausamos (nota: para efectos que solo aparecen cuando el juego no está en pausa, aún es posible obtenerlos, solo un poco más difícil; explicaré cómo más adelante).

Ahora, vamos a presionar
1
y2
en el teclado numérico para recorrer los Pixel Shaders (PS
). Hay dos tipos de shaders: Vertex Shaders (VS
) que controlan dónde se dibujan las cosas en pantalla y Pixel Shaders (PS
) que deciden cómo se ven y dibujan texturas/colores. Como estamos interesados en el color, queremos el PS.Cuando encuentres el
PS
correcto, el efecto desaparecerá en el juego. Por ejemplo, aquí está el shader que controla la llama central del golpe:

Mientras que este controla las nubes de llamas circundantes:

Comenzaremos con estos dos.
Presionar
3
en el teclado numérico copiará el hash del PS en tu portapapeles y guardará el shader en la carpeta ShaderFixes. Los hashes de los dos shaders anteriores sone75b3ffb93a1d268
ydd0757868249aaa5
(Nota: puedes presionar+
en el teclado numérico para restablecer los buffers a 0 si necesitas volver rápidamente al punto de partida). Ten en cuenta que los hashes de los shaders pueden cambiar entre versiones, por lo que tus hashes pueden no ser los mismos valores.Los shaders ahora deberían aparecer en ShaderFixes con un nombre como
hash-ps_replace.txt
.

Si no aparecen después de presionar 3
en el teclado numérico, asegúrate de haber puesto hlsl
y asm
en marking_actions
como se mencionó en la Nota Importante al principio y haber actualizado con F10
.
También ten en cuenta que un pequeño número de shaders no se descompilarán correctamente en hlsl
(lenguaje de alto nivel de shaders) y, en su lugar, volverán a asm
(ensamblador). Estos shaders seguirán funcionando, pero serán más difíciles de editar. No cubriré asm en este tutorial, pero los conceptos son los mismos: la sintaxis de los shaders es simplemente más difícil de leer.
- Ábrelos con tu editor de texto preferido (Notepad/Notepad++/Sublime Text/lo que sea). El archivo parecerá intimidante al principio, pero no te preocupes: no necesitas entender los detalles para hacer cambios básicos (entraré más en detalles sobre cómo funciona este archivo en las secciones posteriores).

- Por ahora, estamos más interesados en jugar con las entradas y salidas. Estas se enumeran justo debajo de main: este archivo toma 9 entradas (numeradas
v0
,v1
,v2
,…v8
) y tiene una salida (o0
).

- Por lo general, es más simple comenzar con la salida. Tiene un tipo de
float4
, lo que significa que tiene un componentex
,y
,z
yw
y toma un número de punto flotante (es decir, decimal) como entrada. Podemos experimentar para ver qué hace poniendo una línea al final del código para establecer el valor en una constante:

(//
y /* */
indican comentarios en el código y son ignorados por el programa. 3dmigoto también exporta el código asm
debajo del código hlsl
: cuando digo el “final”, me refiero justo antes del return
, no después. Todo lo que esté después de ese punto está comentado y no se ejecutará por defecto. Si ves cosas como div
, mul
y mov
, has ido demasiado lejos).
Básicamente, lo que estamos haciendo es sobrescribir lo que el juego está calculando para el valor y sustituyéndolo por el nuestro.
- Guarda el archivo y luego presiona
F10
en el juego para recargar (¡asegúrate de también presionar+
para restablecer los buffers!). 3dmigoto cargará automáticamente el shader desde la carpeta ShaderFixes. Esto es lo que sucede:

La línea central se ha vuelto negra, mientras que las chispas se han vuelto verdes. Si estás familiarizado con cómo se almacenan los colores, podrías tener una idea de lo que representa o0.x
, pero podemos seguir comprobando para estar seguros:
Estableciendo los componentes x
y z
en 0 y y
en 1:

Establece todo en verde:

Mientras que establecer x
y y
en 0 y z
en 1

Establece el color en azul:

O en otras palabras, o0.xyz
corresponde a los colores RGB del efecto. No siempre es el caso que o0
sea el color: algunos shaders tienen múltiples salidas, por lo que el color podría estar en o1
o o2
, etc.; afortunadamente, este shader es bastante simple y solo tiene una salida o0
.
(Si te preguntas qué representa w
, parece estar relacionado con la amplitud/emisión del efecto:)

- Ahora que sabemos a qué corresponden los valores, podemos hacer cambios básicos en los colores. Por ejemplo, establecer los tres
o0.xyz
en 0 hace que las llamas de Diluc se vuelvan negras:


O podemos convertirlas en púrpura estableciendo r
y b
en 1 y dejando g
en 0:

Y ten en cuenta que no estamos limitados a solo establecer constantes: también podemos cambiar el tono del color. Esto reduce la cantidad de rojo en los ataques mientras da más verde y mucho más azul para crear un color rosa salmón:


Ten en cuenta que, a diferencia de los casos en los que establecemos un valor constante, la textura de la llama aún es visible aquí.
Incluso podemos hacer cosas más elegantes como establecer los valores en expresiones matemáticas, pero lo cubriré en la sección final.
En lugar de cambiar la salida, también es posible cambiar los efectos cambiando la entrada usando un método similar (poniendo las líneas justo después de cuando se cargan normalmente y sobrescribiendo los valores del juego), aunque necesitarás experimentar para deducir qué variable cambia qué.
- Este es el proceso básico para cambiar los colores de los efectos: encontrar el hash, volcarlo y luego modificar la entrada o la salida. Sin embargo, si has estado siguiendo, es posible que hayas notado que no todas las texturas de llamas han sido reemplazadas: aún hay más que necesitamos volcar:
0fa220b5adced192
son las chispas:

bf7eb60b256538c7
son las llamas a lo largo de la espada:

439c03865c4ce77e
es el pájaro:

7690cf4aa6647c6c
es el resplandor de la espada durante el ult:

Recopilar todos los diferentes shaders es lo que lleva la mayor parte del tiempo al editar efectos.
- Incluso volviendo todas las anteriores negras, es posible que hayas notado que aún hay efectos de llamas que aparecen durante el ult donde no podemos pausar el juego:

Obtener estos shaders es más molesto, pero no imposible. El primer método es habilitar algo como energía infinita de ráfaga en grasscutter y lanzar el ult una y otra vez mientras recorres. Esto tomará algo de tiempo, pero debería funcionar para cualquier cosa que sea repetible.
(ACTUALIZACIÓN: He recibido recomendaciones de dos formas más de obtener información de shaders de ráfagas: una es pararse en agua poco profunda o con la espalda contra una pared para desactivar la cámara de ráfaga. Esto te permitirá pausar normalmente durante las ráfagas y darte tiempo para recorrer los hashes:

La segunda es usar un software de trampas como Akebi para reducir la velocidad del juego a menos de 1, lo que te permite ver los efectos en cámara lenta. Ten en cuenta que usar software de trampas puede resultar en prohibiciones si se usa en servidores oficiales, por lo que recomiendo usar solo servidores privados si decides usar este método.
¡Muchas gracias a ComplexSignal31#5778 y NK#1321 por las recomendaciones!)
Para efectos que solo aparecen en escenas de corte o son difíciles de reproducir, sin embargo, el método más rápido es hacer un volcado de fotogramas. Consulta el tutorial de modificación de texturas para obtener más detalles sobre cómo realizar volcados de fotogramas, pero esencialmente presionas F8
mientras el efecto está en pantalla para realizar el volcado al mismo tiempo que el efecto es visible.
Desafortunadamente, como no conocemos el hash del shader, tendrá que ser un volcado completo, así que asegúrate de tener 5-10GB de espacio libre y la menor cantidad de objetos en pantalla posible.

Cuando tengas la carpeta de análisis de fotogramas que se crea después de presionar F8
, puedes revisarla para ver cuándo se dibuja el efecto. Los archivos o0
y o1
muestran lo que se está dibujando en cada ID y son muy útiles para aislar el ID exacto en el que se dibuja un efecto en pantalla.
Ejemplo: 000351-o0=3315d2b5-vs=eb65cb4eba57132b-ps=7690cf4aa6647c6c.dds
se ve así en mi volcado de fotogramas:

Mientras que 000352-o0=3315d2b5-vs=f6a1f24f9c9b28c2-ps=a69e25f25a6c8e04.dds
se ve así:

Así que sabemos que en este fotograma, la llamada de dibujo 000352
es responsable del efecto de resplandor en el suelo. También podemos obtener el hash del nombre del archivo, ps=a69e25f25a6c8e04
.
Usando este método, podemos encontrar los hashes restantes:
000353-o0=3315d2b5-vs=f50ce30bb0caf55c-ps=4d4da8a4cbe1149a.dds

Y 000365-o0=3315d2b5-vs=72ce1e39ede0982f-ps=622a52d3edcf0363.dds

- Ahora que tenemos los hashes restantes, necesitamos volcarlos. Presiona
+
en el teclado numérico para restablecer los buffers, lanza el ult y luego comienza a recorrer con1
/2
en el teclado numérico mientras el efecto está en pantalla. Aunque el efecto haya desaparecido de la pantalla para cuando lleguemos al hash, siempre que hayamos comenzado a recorrer cuando el efecto estaba en pantalla, aparecerá en la lista y estará disponible para volcar:
Ejemplo de PS 4d4da8a4cbe1149a
apareciendo aunque el ult no esté activo:

Usando esta técnica, podemos volcar los shaders restantes a69e25f25a6c8e04
, 4d4da8a4cbe1149a
y 622a52d3edcf0363
:

- ¡Modificación completa! O…tal vez no. Si cambias a otro personaje pyro como Hu Tao, es posible que notes un problema:

Hemos establecido TODAS las llamas en negro, no solo las de Diluc. Además, si alguien más creó un mod que cambió las llamas de otro personaje como Hu Tao o Klee, también se superpondría con el de Diluc.
- Queremos una forma de limitar el efecto para que solo aparezca cuando Diluc esté en el campo. Hay un par de formas de hacer esto, pero todas siguen el mismo principio básico: identificamos alguna condición que se produce siempre que Diluc esté en el campo y solo aplicamos los efectos si esa condición es verdadera.
Este es un tema algo más avanzado que tendrá más sentido después de que juegues con los shaders y leas las secciones posteriores; si tienes problemas para entender esto, intenta leer las siguientes secciones y volver más tarde.
Primero, necesitamos identificar un hash que sea único para Diluc. Para simplificar, voy a usar el hash VB
de Diluc 56159d74
(VB
se puede recorrer con /
y *
en el teclado numérico y copiar con -
en el teclado numérico):

- A continuación, construimos un
.ini
que usaremos para aplicar selectivamente los efectos. Definimos una variable llamada$ActiveCharacter
y la establecemos en 0 al comienzo de cada fotograma ([Present]
se ejecuta una vez por fotograma al inicio). Solo establecemos el valor en 1 siempre que Diluc esté en el campo, indicado por el hashVB
coincidente:
[Constants]
global $ActiveCharacter
[Present]
post $ActiveCharacter = 0
[TextureOverrideDilucVB]
hash = 56159d74
match_priority = 1
$ActiveCharacter = 1
La match_priority
aquí es solo para asegurar que este efecto no interfiera con ningún mod de Diluc cargado; si estás agregando este efecto como parte de un mod y no por separado, no necesitarás incluirlo.
- Ahora, hay dos formas de aislar el shader. La más fácil de las dos es simplemente definir un shader personalizado y realizar el reemplazo, luego crear un
shaderoverride
y ejecutar el shader personalizado solo cuando Diluc sea el personaje activo:
[ShaderOverrideDilucFlame]
hash = 4d4da8a4cbe1149a
if $ActiveCharacter == 1
run = CustomShaderDilucFlame
endif
[CustomShaderDilucFlame]
ps = 4d4da8a4cbe1149a-ps_replace.txt
handling = skip
drawindexed = auto
Esto generalmente funcionará, pero 3dmigoto a veces no compila correctamente el hlsl
si se hace de esta manera, lo que lleva a errores. Además, no funcionará con asm
. Pero las ventajas son que el shader se puede agrupar junto con el resto del mod en la carpeta del mod y no interferirá si otro mod intenta modificar el mismo shader.
El otro método es pasar una variable personalizada al shader y realizar el efecto solo si la variable coincide. La siguiente sección cubrirá esto con más detalle, pero esencialmente quieres una sección como esta para cada shader:
[ShaderOverrideDilucFlame]
hash = 0fa220b5adced192
x160 = $ActiveCharacter
Luego, definir una nueva constante en el shader:

Y realizar el efecto solo si esa constante es igual a 1 (por ejemplo, el personaje está en pantalla):

Con esto, Diluc mantiene el efecto:

Mientras que los de Hu Tao son normales:

En movimiento (dejé algunos de los efectos en rojo para contrastar):
Pasar Valores Personalizados a Shaders (Cambiando Colores)
En esta sección, demostraré cómo cargar valores personalizados desde los archivos .ini
a los shaders y cómo puedes usar esto para hacer efectos que cambian entre múltiples colores. También demostraré cómo encontrar las partes del shader que controlan la emisión, algo que es más desafiante que solo los colores de los efectos.
Esta sección es de dificultad media-avanzada: asumiré que has leído la mayor parte de la sección anterior y tienes al menos una familiaridad básica con los archivos .ini
y shaders (por ejemplo, saber cómo abrirlos y entender al menos vagamente las diferentes partes). El conocimiento básico de programación será útil.
- Como antes, comenzamos recopilando los hashes de los shaders, esta vez para el pilar de Zhongli:

A diferencia de Diluc, este hash no hará que todo el pilar desaparezca, solo las texturas. Esto se debe a que se dibuja usando múltiples shaders, por lo que incluso si omitimos uno, partes del objeto aún se dibujarán (en este caso, el contorno del pilar permanece).
El hash en este caso es 4c99fec14dca7797
: presiona 3
para volcar el shader a ShaderFixes.
Nuestro objetivo final aquí es cambiar el color del efecto geo amarillo mientras dejamos las otras partes iguales.

- Al abrir el shader, podemos ver que es más complicado que el anterior, con 9 entradas y 6 salidas. Esto se debe a que el shader es responsable de hacer múltiples cosas como dibujar la textura, manejar la emisión, calcular el sombreado, etc.

Comenzamos intentando el mismo método que antes: establecer cada una de las salidas en constantes para aprender sobre lo que controlan.
o0
parece tener algo que ver con los contornos, haciéndolos más gruesos y delgados (un poco difícil de ver, pero se puede usarF9
para alternar entre modificado y no modificado):

o1.xyz
parecen corresponder a los colores RGB como antes, y w
parece controlar el brillo.


o2
también parece controlar el color:

o3
-o5
no están claros, pero parecen afectar el grosor de la línea.
Sin embargo, es posible que hayas notado un problema: todas estas opciones cambian el color de todo el pilar, no solo la línea geo amarilla. Tenemos que profundizar un poco más para encontrar dónde se maneja eso.
- Antes de continuar, permíteme explicar los símbolos más importantes en el shader con más detalle:
v0
,v1
,v2
... etc. son los datos de entrada en los archivos vb que se cargan, es decir, datos relacionados con cosas como la posición del vértice, colores del vértice (diferentes de los colores de la textura), mapas UV, pesos de mezcla, etc.o0
,o1
,o2
... son los objetivos de salida y son lo que realmente se dibuja en la pantalla (o en el caso de los shaders de vérticesVS
, lo que se pasa al shader de píxelesPS
).t0
,t1
,t2
... son las texturas, cosas como texturas dds típicamente, aunque también pueden ser buffers en algunos casos. Siempre que veasps-t0
,vs-t0
,ps-t1
,vs-t1
, etc. en archivos .ini, esto es a lo que corresponden.r0
,r1
,r2
... son los registros, estas son variables temporales que el shader usa para almacenar los resultados de los cálculos.cb0
,cb1
,cb2
... son los buffers constantes, estos son valores pasados por el juego al shader que representan valores del estado actual del juego, como la ubicación global de los objetos o el tiempo transcurrido desde que comenzó el juego.
Con esto en mente, podemos centrarnos en la parte del código que nos interesa en lugar de tratar de entender todas las 200+ líneas.
Estamos interesados en el resplandor del pilar de Zhongli. Al mirar las texturas del pilar, podemos ver que la textura difusa contiene la parte resplandeciente sobre la capa alfa y se carga en la ranura 0 (es el primer hash del hash.json del pilar, o mirando en un mod creado y viendo que la difusa se carga como ps-t0
):


Por lo tanto, estamos interesados en cualquier parte del código que involucre la variable t0
, que corresponde a la textura difusa. Específicamente, estamos más interesados en cualquier cosa que involucre el componente w, ya que eso es lo que representa la parte resplandeciente.
t0
se carga dos veces en el shader: una vez alrededor de la línea 100 en la variabler2
:

Y una vez alrededor de la línea 235 en la variable r0
:

Hay formas de saber cuál es correcto leyendo el código, pero experimentar con cada uno también funcionará:
Establecer r2.x
como una constante en el primer bloque:

Vuelve el pilar verde, pero deja el efecto geo normal:

Así que probablemente deberíamos mirar cerca del segundo bloque en su lugar. El color es más probable que esté representado por una variable con 3 componentes (uno para cada canal de color), y la más cercana a ese bloque es el r1
que aparece 3 líneas abajo:

Si establecemos r1.x
en 1 aquí:

Obtenemos:

¡Éxito! Este valor r1
es lo que controla el RGB del resplandor del pilar (establecimos el componente rojo en 0).
(Nota: esto no significa que r1
siempre sea responsable del color del resplandor del pilar en todas partes del código, solo que contiene el resplandor del pilar en este punto específico en el tiempo. Los valores de los registros son reutilizados por el shader al realizar cálculos, por lo que el “significado” de lo que representa cada uno puede cambiar de línea a línea, a diferencia de las entradas y salidas).
Este mismo principio básico se puede usar en otras situaciones para encontrar qué parte del shader controla qué salida: comienza con algún componente que sabes que está relacionado con lo que estás buscando (como una textura o un valor vb específico), luego busca en el código del shader circundante y experimenta para encontrarlo.
- Un color es aburrido, sin embargo: ¿qué tal si pudiéramos establecer el color que quisiéramos? En realidad, es posible pasar valores personalizados desde los archivos
.ini
al shader.
Primero, define las variables que quieres usar cerca de la parte superior del archivo bajo las declaraciones de 3dmigoto (180 se eligió arbitrariamente, aunque idealmente deberías elegir números superiores a 100 para que no interfieran accidentalmente con los que usa el juego).

A continuación, establecemos el R, G y B debajo de la línea t0 que encontramos en la parte anterior.

(NOTA: el r1
tiene un componente x
, z
y w
, no un componente x
, y
y z
. Sin embargo, aún corresponden a RGB, solo que las letras son diferentes).
Finalmente, en el .ini vamos a establecer los tres valores siempre que veamos el IB
para el pilar:

(Puedes encontrar el IB
del pilar usando 7
/8
en el teclado numérico para recorrer hasta encontrar el que hace que el pilar desaparezca, o mirando en hash.json):

¡Éxito! Hemos establecido las líneas en rojo:

Y podemos establecerlas en otros colores simplemente cambiando los valores del .ini; esto las establecerá en púrpura:


Sin embargo, ten en cuenta que esto no es perfecto: hemos perdido algunos de los efectos animados a cambio de colores personalizados. Cubriré formas de implementar animaciones en la sección final de este tutorial.
- Aún podemos hacer más con esto. Un color es genial, pero ¿qué tal si pudiéramos hacer que cambie automáticamente entre ellos? 3dmigoto tiene una variable especial llamada
time
que representa el número de segundos que han pasado desde que comenzó el juego. Podemos usar esto para cambiar automáticamente entre colores con el tiempo:
[TextureOverridePillarIB]
hash = 34e18b4f
if time % 3 <= 1
x180 = 1
y180 = 0
z180 = 0
else if time % 3 <= 2
x180 = 0
y180 = 1
z180 = 0
else
x180 = 0
y180 = 0
z180 = 1
endif
Lo que esto hace es tomar el tiempo actual y ponerlo en 1 de 3 cubos, luego establecer el pilar en rojo, verde o azul dependiendo del tiempo actual (cambiando cada 3 segundos). Al cambiar los números, puedes hacer que cambie más rápido o más lento, o agregar/eliminar colores, etc.
- Finalmente, similar a antes, podemos cargar el shader en el
.ini
en lugar de ponerlo en ShaderFixes:
[TextureOverridePillarIB]
hash = 34e18b4f
run = CustomShaderPillarColor
[CustomShaderPillarColor]
if time % 3 <= 1
x180 = 1
y180 = 0
z180 = 0
else if time % 3 <= 2
x180 = 0
y180 = 1
z180 = 0
else
x180 = 0
y180 = 0
z180 = 1
endif
ps = 4c99fec14dca7797-ps_replace.txt
handling = skip
drawindexed = auto
Esto funcionará en su mayoría, pero hay un error en la compilación aquí que hará que el pilar deje un residuo durante ~1 segundo después de desaparecer:

También es posible restringir este shader específicamente a cuando Zhongli esté en el campo, aunque en este caso no conozco ningún otro objeto que comparta este shader, por lo que no es tan importante como lo fue para las llamas de Diluc.
Efectos Animados
En esta sección final, demostraré cómo podemos usar los principios de las dos secciones anteriores para crear efectos animados simples: recorreré el proceso de crear las líneas animadas en el traje cibernético de Raiden (https://gamebanana.com/mods/420434). Esta sección es avanzada: asumiré que entiendes las dos secciones anteriores, sabes cómo hacer mods y tienes algunos conocimientos básicos de programación.
- Para comenzar, encontramos el shader que controla el dibujo de las texturas en Raiden Shogun:

Raiden en realidad usa al menos dos: uno para el objeto del cuerpo y otro para el objeto del vestido, pero estamos interesados en el objeto del cuerpo, ya que es el que tiene el efecto de emisión que necesitamos (encontrado a través de prueba y error previamente).
El hash es 7d2763cf91813333
y lo volcamos a ShaderFixes.
- Ahora, buscamos la parte del shader responsable de la emisión. La emisión está sobre la capa alfa en la textura difusa, que está en la ranura 0, por lo que estamos buscando cosas relacionadas con
t0.w
. Solo hay una línea relevante en el shader:

Y al probarlo, encontramos que es responsable del resplandor:


Y podemos modificar las partes resplandecientes de su textura solo agregando una condicional que solo se activa en píxeles que tienen un valor alfa mayor que algún número arbitrario:


- Ahora, voy a demostrar el proceso de agregar líneas a mi mod de traje cibernético de Raiden:

Primero, dibujo las líneas:

Hice esto a través de la pestaña de pintura de texturas de Blender, pero también podrías pintar directamente en la textura usando paint.net/photoshop. Ten en cuenta que la salida final debe ser BC7 SRGB
dds
para la textura difusa. Además, no seas como yo: pinta estas en una capa separada para que puedas separarlas fácilmente más tarde ;-;.
- La textura final se ve así después de mover las líneas sobre la capa alfa (nota: es ancha porque fusioné algunos modelos y puse sus texturas una al lado de la otra):

Lo que nos da líneas resplandecientes en el juego.

- Ahora, es hora de implementar algunas animaciones básicas. Separo las líneas de la textura difusa en otra textura vacía que voy a llamar la textura de “control”:

Esta textura será esencialmente lo que vamos a usar para decirle al shader qué partes de la textura tendrán efectos animados (ya que todos los cuatro canales de la difusa/mapa de luz ya están en uso). El tipo para esta textura debe ser BC7 Linear
, ya que queremos que los valores de color estén uniformemente espaciados.
También la he recoloreado a negro por simplicidad: no usaremos colores específicos en este ejemplo para mantener las cosas más simples, por lo que establecemos todos los canales de color iguales; si quisieras, podrías usar cada canal de color para controlar diferentes cosas. Asegúrate de que el color sea mayor que 0, ya que queremos poder diferenciarlo del fondo sin depender del canal alfa.
Ten en cuenta que he eliminado las líneas de la textura difusa original ahora, por lo que volvemos al traje de cibernético de Raiden original:

- A continuación, agregamos una sección en el
BodyOverride
en el.ini
del mod para pasar la nueva textura al shader:
[TextureOverrideRaidenShogunBody]
hash = 428c56cd
match_first_index = 17769
ib = ResourceRaidenShogunBodyIBZipped
ps-t0 = ResourceRaidenShogunBodyDiffuseRed
ps-t1 = ResourceRaidenShogunBodyLightMap
ps-t26 = ResourceRaidenShogunBodyControl
[ResourceRaidenShogunBodyControl]
filename = RaidenShogunBodyControl.dds
Elegí cargarlo en la ranura 26 arbitrariamente: no recomiendo ir más bajo que 20, ya que he visto algunos casos donde llegan tan alto (la gran mayoría de las cosas usan <10 y es raro que algo por encima de 5 sea importante).
- También necesitamos agregar la variable en el shader cerca de la parte superior:

Ahora podemos cargar esta textura de manera similar a cómo cargamos las otras texturas:
r2.xyzw = t26.SampleBias(s0_s, v2.xy, r0.x).xyzw;
(Si te preguntas cómo obtuvimos esta línea, fue al mirar cómo se cargan las texturas t0
y t1
y mimetizar el formato. Elegí r2
ya que sé que será reemplazado por lo que carguemos desde la difusa, por lo que no romperá ninguna otra línea de código: otra opción sería crear una variable de registro adicional).
- Ahora, podemos agregar una condicional que solo se activa en píxeles de la textura de control que tienen un valor de canal rojo mayor que 0. Cuando veamos eso, establecemos el color del píxel en verde; de lo contrario, simplemente cargamos el valor del píxel desde la textura difusa original:
r2.xyzw = t26.SampleBias(s0_s, v2.xy, r0.x).xyzw;
if (r2.x > 0){
r2.xyz = float3(0,1,0);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
(Nota: estoy siendo un poco perezoso al establecer los valores aquí, ya que deberían estar normalizados, pero no hará mucha diferencia).
Lo que nos lleva de vuelta a nuestro punto de partida original:

Sin embargo, ahora hay una diferencia clave: los colores y ubicaciones de las líneas están siendo controlados completamente a través de la textura de control y los cálculos del shader, y no se están leyendo desde la textura original.
Esto nos permite cambiar fácilmente el color simplemente cambiando el valor de r2.xyz = float3(R,G,B)
:


O incluso establecerlos en el .ini
como lo hicimos en la sección anterior. ¡Incluso podemos hacer que cambien entre colores usando esto también!
- Ahora que las líneas están siendo controladas a través del shader y la textura de control, tenemos mucha más flexibilidad en lo que podemos hacer. Comencemos animándolas. En lugar de usar un negro constante en todas las líneas de la textura de control, voy a usar un degradado de negro a blanco:

Ahora, el valor de r2.x
aumentará linealmente de 0 a 1 a medida que viajes por las líneas (por eso guardamos como BC7 linear
: de lo contrario, los valores estarían sesgados, lo que llevaría a problemas). Luego podemos pasar la variable de tiempo desde el .ini
al shader:
[TextureOverrideRaidenShogunBody]
hash = 428c56cd
match_first_index = 17769
ib = ResourceRaidenShogunBodyIBZipped
ps-t0 = ResourceRaidenShogunBodyDiffuseRed
ps-t1 = ResourceRaidenShogunBodyLightMap
ps-t26 = ResourceRaidenShogunBodyControl
x180 = time
Define una nueva variable en el shader:
#define TIME IniParams[180].x
Ahora, podemos comparar el valor de r2.x
con TIME
para averiguar qué parte del modelo queremos dibujar. r2.x
está en el rango de 0 a 1, por lo que necesitamos cambiar el TIME
a este rango también: podemos dividir TIME
en cubos repetitivos usando el operador módulo y luego dividir por el valor máximo para poner en el rango de 0 a 1. Así que la ecuación sería TIME%2/2
para que cambie entre 0 y 1 cada dos segundos.
if (r2.x > TIME%2/2){
r2.xyz = float3(0,1,0);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
Resultado:
Alternativamente, para cambiar la dirección, podemos usar 1- TIME%2/2
en su lugar:
if (r2.x > 1-TIME%2/2){
r2.xyz = float3(0,1,0);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
- El resultado de esto no está mal, pero no es exactamente lo que estaba buscando: no me gusta cómo las líneas aparecen/desaparecen gradualmente y esperaba un efecto más “tipo matrix” donde la línea viaja a lo largo del cuerpo.
En lugar de usar una sola condición, podemos definir un rango donde las líneas aparecerán. Esto solo permitirá valores que estén a lo sumo a 0.2 de TIME%2/2
:
r2.xyzw = t26.SampleBias(s0_s, v2.xy, r0.x).xyzw;
if (r2.x > TIME%2/2 && r2.x < TIME%2/2+0.2){
r2.xyz = float3(0,1,0);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
Mucho mejor, pero se mueve un poco rápido. Además, las líneas aún aparecen todas a la vez al comienzo del ciclo, haciendo que los puntos de inicio y parada sean obvios. La ecuación final que decidí fue:
if (r2.x > 0 && (TIME % 3)/2.5 > r2.x && (TIME % 3)/2.5-0.2 < r2.x){
r2.xyz = float3(0,1,0);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
Esto se repite cada 3 segundos y en realidad ponemos el tiempo en el rango de 0 a 1.2 en lugar de 0 a 1 dividiendo por 2.5 en lugar de 3: el extra 0.2 permite que las líneas aparezcan y desaparezcan gradualmente al final del ciclo.
- Ahora, agreguemos algunos efectos más geniales. Solo estamos usando un color verde constante, pero no tenemos que hacerlo: podemos usar matemáticas para hacer que los colores cambien. Explicar cómo funciona esto está más allá del alcance de este tutorial, pero básicamente estamos usando ondas sinusoidales desincronizadas para viajar alrededor de la rueda de colores. Para más detalles, mira aquí: https://krazydad.com/tutorials/makecolors.php
if (r2.x > 0 && (TIME % 3)/2.5 > r2.x && (TIME % 3)/2.5-0.2 < r2.x){
r2.xyz = float3((sin(TIME)+1)/2, (sin(TIME+2)+1)/2, (sin(TIME+4)+1)/2);
r2.w = 0.6;
}
else{
r2.xyzw = t0.SampleBias(s0_s, v2.xy, r0.x).xyzw;
}
- En este punto, he terminado en su mayoría de explicar cómo crear el efecto. El mod de Raiden cibernético real también tiene algunos interruptores adicionales para activar y desactivar los efectos, para limitarlos a cuando Raiden esté en pantalla y para permitir que el usuario elija colores personalizados, pero todos esos ya se han cubierto en secciones anteriores.
Lo único adicional que señalaría es que no estás limitado solo a usar colores con esta técnica: en lugar de establecer r2
como un color constante, podrías usar esto para elegir entre diferentes texturas. También puedes usar los canales separados en la textura de control para diferentes efectos o usar diferentes variables para interruptores: ¡las posibilidades son infinitas! (no realmente, pero aún puedes hacer mucho).
- Aunque hemos terminado en su mayoría, señalaré algunos problemas:
- Las líneas no aparecen durante ~1-2 segundos después del cambio de personaje. Esto se debe a que los personajes en realidad usan un shader diferente cuando se están cargando en el juego durante unos segundos: puedes buscar este shader y reemplazarlo también para eliminar este problema.
- Las reflexiones no tienen las líneas. Esto también se debe a que las reflexiones usan un shader diferente y se puede solucionar buscando y reemplazando el shader.

- El filtro de transparencia se aplica. Aunque esto no es un error, significa que alguien que use el mod de eliminar el filtro de transparencia tendrá ese sobrescrito para Raiden. Si quieres solucionar esto, haz un dif en el archivo del shader con el del filtro de transparencia para ver cuáles son las diferencias y aplicarlas a tu archivo.

- Algunos personajes se rompen mientras Raiden está en pantalla por un momento al usar el menú de la fiesta. No sé por qué sucede esto: la parte que se rompe ni siquiera usa el mismo shader y no pude aislar el problema. Si alguien lo sabe, por favor envíame un mensaje.

El efecto de color arcoíris es genial, pero no es 100% matemáticamente correcto: la textura difusa usa un espacio de color
SRGB
, no unolineal
, lo que significa que necesitarías un paso adicional para convertir los colores (puedes ver que las líneas nunca se vuelven completamente rojas/verdes/azules como esperarías). Consulta algo como https://lettier.github.io/3d-game-shaders-for-beginners/gamma-correction.html para obtener detalles.Los shaders pueden cambiar de hash entre versiones y tienden a hacerlo más comúnmente que los hashes de personajes, por lo que es posible que necesites actualizar los mods de efectos más a menudo que los de personajes.
Si has llegado a este punto, ¡felicidades! Conoces la mayoría de los conceptos básicos de cómo se pueden usar los shaders para cambiar efectos o incluso crear efectos personalizados. ¡Gracias por leer y espero ver lo que creas!