Tabla de equivalencias con GLSL
La traducción completa de tipos, operadores, funciones integradas, constantes y accesores, para convertir cualquier GLSL que ya conozcas.
Tipos y construcción
| GLSL | TSL | Nota |
|---|---|---|
float x = 1.0; |
float( 1 ) |
también const x = float( 1 ).toVar() |
int, uint, bool |
int(), uint(), bool() |
|
vec2, vec3, vec4 |
vec2(), vec3(), vec4() |
|
ivec3, uvec3, bvec3 |
ivec3(), uvec3(), bvec3() |
y sus versiones de 2 y 4 |
mat2, mat3, mat4 |
mat2(), mat3(), mat4() |
|
| sin equivalente | color( 0xff8800 ) |
atajo para colores, admite hexadecimal |
float y = x; (conversión) |
x.toFloat() |
también toVec3(), toMat4(), toInt()… |
El swizzling funciona como propiedades del nodo y admite las tres nomenclaturas de GLSL:
p.x p.xy p.xyz p.wzyxc.r c.rgb c.bgrt.s t.stpY hay dos familias de métodos que GLSL no tiene:
p.setX( 1 ); // devuelve un nodo con la componente sustituidap.setXY( vec2() ); // idem para varias componentesp.flipY(); // invierte el signo de una componentePara acceder a elementos de un array hay índice directo, de 0 a 31, y el método general:
const a = miArray[ 3 ];const b = miArray.element( indiceNodo ); // el indice puede ser un nodoOperadores
| GLSL | TSL funcional | TSL encadenado |
|---|---|---|
a + b |
add( a, b ) |
a.add( b ) |
a - b |
sub( a, b ) |
a.sub( b ) |
a * b |
mul( a, b ) |
a.mul( b ) |
a / b |
div( a, b ) |
a.div( b ) |
a % b |
mod( a, b ) |
a.mod( b ) |
-a |
negate( a ) |
a.negate() |
a += b |
— | a.addAssign( b ) |
a *= b |
— | a.mulAssign( b ) |
a == b |
equal( a, b ) |
a.equal( b ) |
a != b |
notEqual( a, b ) |
a.notEqual( b ) |
a < b |
lessThan( a, b ) |
a.lessThan( b ) |
a > b |
greaterThan( a, b ) |
a.greaterThan( b ) |
a <= b |
lessThanEqual( a, b ) |
a.lessThanEqual( b ) |
a >= b |
greaterThanEqual( a, b ) |
a.greaterThanEqual( b ) |
a && b |
and( a, b ) |
a.and( b ) |
a || b |
or( a, b ) |
a.or( b ) |
!a |
not( a ) |
a.not() |
a & b |
bitAnd( a, b ) |
a.bitAnd( b ) |
a | b |
bitOr( a, b ) |
a.bitOr( b ) |
a ^ b |
bitXor( a, b ) |
a.bitXor( b ) |
~a |
bitNot( a ) |
a.bitNot() |
a << b |
shiftLeft( a, b ) |
a.shiftLeft( b ) |
a >> b |
shiftRight( a, b ) |
a.shiftRight( b ) |
a ? b : c |
— | a.mix( b, c ) |
add, sub, mul, div, and y or aceptan más de dos argumentos.
Funciones integradas
Casi todas conservan el nombre exacto de GLSL. Estas son las que no:
| GLSL | TSL |
|---|---|
inversesqrt |
inverseSqrt |
faceforward |
faceForward |
dFdx, dFdy |
dFdx, dFdy (igual, pero solo en fragmento) |
Los alias en minúscula inversesqrt y faceforward existen como función, pero no están registrados como métodos encadenados.
Y estas existen en TSL sin equivalente directo en GLSL:
| TSL | Equivale a |
|---|---|
oneMinus( x ) |
1.0 - x |
reciprocal( x ) |
1.0 / x |
saturate( x ) |
clamp( x, 0.0, 1.0 ) |
lengthSq( v ) |
dot( v, v ) |
pow2, pow3, pow4 |
x*x, x*x*x, x*x*x*x |
cbrt( x ) |
raíz cúbica con signo |
difference( a, b ) |
abs( a - b ) |
rand( uv ) |
ruido pseudoaleatorio determinista |
mixElement, smoothstepElement, stepElement |
las versiones con el orden invertido que usan los métodos |
El resto se llaman igual: abs, sign, floor, ceil, round, trunc, fract, mod, min, max, clamp, mix, step, smoothstep, length, distance, dot, cross, normalize, reflect, refract, pow, exp, exp2, log, log2, sqrt, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, radians, degrees, all, any, fwidth, transpose, determinant, inverse.
atan acepta uno o dos argumentos; con dos hace de atan2, y el generador emite el nombre correcto según el backend.
Constantes
| GLSL de Three.js | TSL |
|---|---|
PI |
PI |
PI2 |
TWO_PI |
PI_HALF |
HALF_PI |
EPSILON |
EPSILON |
| sin equivalente | INFINITY |
PI2 sigue exportándose en TSL pero está marcada como obsoleta desde r181 en favor de TWO_PI.
El entorno del shader
Variables integradas y accesores
Aquí es donde la traducción deja de ser mecánica, porque los nombres cambian y hay más opciones.
| GLSL en un shader de Three.js | TSL |
|---|---|
position |
positionGeometry o positionLocal |
normal |
normalGeometry o normalLocal |
uv |
uv() |
uv1, uv2 |
uv( 1 ), uv( 2 ) |
modelMatrix |
modelWorldMatrix |
modelViewMatrix |
modelViewMatrix |
normalMatrix |
modelNormalMatrix |
projectionMatrix |
cameraProjectionMatrix |
viewMatrix |
cameraViewMatrix |
cameraPosition |
cameraPosition |
gl_FragCoord.xy |
screenCoordinate |
gl_VertexID |
vertexIndex |
gl_InstanceID |
instanceIndex |
gl_FrontFacing |
frontFacing |
| resolución del viewport | screenSize |
| tiempo (uniforme propio) | time |
Y hay accesores de posición y normal en varios espacios que en GLSL habría que calcular a mano:
| TSL | Qué es |
|---|---|
positionLocal |
posición en espacio de objeto, modificable |
positionWorld |
posición en espacio de mundo |
positionView |
posición en espacio de vista |
positionViewDirection |
dirección desde el fragmento a la cámara |
normalLocal |
normal en espacio de objeto |
normalView |
normal en espacio de vista |
normalWorld |
normal en espacio de mundo |
normalFlat |
normal reconstruida por derivadas |
positionLocal está definido como positionGeometry convertido en varying, y es el que hay que modificar cuando quieres desplazar vértices. Y normalWorld está definido como normalView.transformDirection( cameraViewMatrix ), que es exactamente el cálculo que harías a mano.
Estructura del shader
| GLSL | TSL |
|---|---|
uniform float x; |
const x = uniform( 0 ) |
attribute vec3 a; |
const a = attribute( 'a', 'vec3' ) |
varying float v; |
const v = expr.toVarying( 'v' ) |
float f( vec3 p ) { ... } |
const f = Fn( ( [ p ] ) => { ... } ) |
if / else if / else |
If( c, fn ).ElseIf( c, fn ).Else( fn ) |
switch / case / default |
Switch( v ).Case( n, fn ).Default( fn ) |
for ( int i = 0; i < n; i++ ) |
Loop( n, ( { i } ) => { ... } ) |
break; / continue; |
Break(); / Continue(); |
float t = expr; (temporal) |
const t = expr.toVar() |
const float k = 2.0; |
const k = float( 2 ).toConst() |
gl_Position = ... |
material.positionNode = ... |
gl_FragColor = ... |
material.colorNode = ... u outputNode |
discard; |
.discard() sobre una condición |
#define, #ifdef |
un if normal de JavaScript al construir el grafo |
Esa última fila merece una nota: no hay preprocesador y no hace falta. Una variante que en GLSL se resolvería con un #ifdef en TSL se resuelve eligiendo qué nodos montar, con JavaScript corriente, y el shader generado no contiene ninguna rama.
let c = materialColor;if ( config.tinte ) c = mix( c, color( config.tinte ), 0.3 );if ( config.franjas ) c = c.mul( uv().y.mul( 80 ).sin().mul( 0.1 ).add( 0.9 ) );material.colorNode = c;