https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language Skip to content Navigation Menu Toggle navigation Sign in * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code Explore + All features + Documentation + GitHub Skills + Blog * Solutions For + Enterprise + Teams + Startups + Education By Solution + CI/CD & Automation + DevOps + DevSecOps Resources + Learning Pathways + White papers, Ebooks, Webinars + Customer Stories + Partners * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles Repositories + Topics + Trending + Collections * Pricing Search or jump to... Search code, repositories, users, issues, pull requests... Search [ ] Clear Search syntax tips Provide feedback We read every piece of feedback, and take your input very seriously. [ ] [ ] Include my email address so I can be contacted Cancel Submit feedback Saved searches Use saved searches to filter your results more quickly Name [ ] Query [ ] To see all available qualifiers, see our documentation. Cancel Create saved search Sign in Sign up You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert {{ message }} mrdoob / three.js Public * * Notifications You must be signed in to change notification settings * Fork 35.2k * Star 99.5k * Code * Issues 373 * Pull requests 128 * Actions * Wiki * Security * Insights Additional navigation options * Code * Issues * Pull requests * Actions * Wiki * Security * Insights Three.js Shading Language Jump to bottom sunag edited this page May 29, 2024 * 22 revisions Introduction Why TSL? Creating shaders has always been an advanced step for most developers, many game creators have never created GLSL code from scratch. The shader graph solution adopted today by the industry has allowed developers more focused on dynamics to create the necessary graphic effects to meet the demands of their projects. The aim of the project is to create an easy-to-use, even if for this we need to create complexity behind, this happened initially with Renderer and now with the TSL. Other benefits that TSL brings besides simplifying shading creation is keeping the renderer agnostic, while all the complexity of a material can be imported into different modules and use tree shaking without breaking during the process. Example A detail map makes things look more real in games. It adds tiny details like cracks or bumps to surfaces, like walls. In this example we will scale uv to improve details when seen up close and multiply with a base texture. Old This is how we would achieve that using .onBeforeCompile(): const material = new THREE.MeshStandardMaterial(); material.map = colorMap; material.onBeforeCompile = ( shader ) => { shader.uniforms.detailMap = { value: detailMap }; let token = '#define STANDARD'; let insert = /* glsl */` uniform sampler2D detailMap; `; shader.fragmentShader = shader.fragmentShader.replace( token, token + insert ); token = '#include '; insert = /* glsl */` diffuseColor *= texture2D( detailMap, vMapUv * 10.0 ); `; shader.fragmentShader = shader.fragmentShader.replace( token, token + insert ); }; Any simple change from this makes the code increasingly complicated using .onBeforeCompile, the result we have today in the community is that we have countless types of parametric materials that do not communicate with each other, and that need to be updated periodically to be operating, limiting the creativity to create unique materials reusing modules in a simple way. New With TSL the code would look like this: import { texture, uv } from 'three/tsl'; const detail = texture( detailMap, uv().mul( 10 ) ); const material = new MeshStandardNodeMaterial(); material.colorNode = texture( colorMap ).mul( detail ); TSL is also capable of encoding code into different outputs such as WGSL/GLSL - WebGPU/WebGL, in addition to optimizing the shader graph automatically and through codes that can be inserted within each Node. This allows the developer to focus on productivity and leave the graphical management part to the Node System. Another important feature of a graph shader is that we will no longer need to care about the sequence in which components are created, because the Node System will only declare and include it once. Let's say that you import positionWorld into your code, even if another component uses it, the calculations performed to obtain position world will only be performed once, as is the case with any other renderer component such as: normalWorld, modelPosition, etc. Architeture All TSL component is created from a Node. The Node allows it to communicate with any other, value conversions can be automatic or manual, a Node can receive the output value expected by the parent Node and modify its own output snippet. Since they are all components are extended from the Node class, it is possible to modulate them using tree shaking. In the shader construction process, the Node will have important information such as geometry, material, renderer as well as the backend, which can influence the type and value of output. The build process is based on three pillars: setup, analyze and generate. Use TSL to create a completely customized code for the Node setup output. The Node can use many others within itself, have countless inputs, but there will always be a single output. This proccess will check the nodes that were created in analyze order to create useful information for generate the snippet, such as the need to create or not a cache/variable for optimizing a node. An output of string will be sent to each node independently, generate the node will also be able to create code in the flow, supporting multiple lines. Node also have a native update process invoked by the update() function, these events be called by frame, render call and object draw. It is also possible to serialize or deserialize a Node using serialize() and deserialize() functions. Constants and explicit conversions Input functions can be used to create contants and do explicit conversions. Conversions are also performed automatically if the output and input are of different types. Name Returns a constant or convertion of type: float( node | number ) float int( node | number ) int uint( node | number ) uint bool( node | value ) boolean color( node | hex | r,g,b ) color vec2( node | Vector2 | x,y ) vec2 vec3( node | Vector3 | x,y,z ) vec3 vec4( node | Vector4 | x,y,z,w ) vec4 mat2( node | Matrix2 | a,b,c,d ) mat2 mat3( node | Matrix3 | a,b,c,d,e,f,g,h,i mat3 ) mat4( node | Matrix4 | mat4 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p ) Advanced ivec2( node | x,y ) ivec2 ivec3( node | x,y,z ) ivec3 ivec4( node | x,y,z,w ) ivec4 uvec2( node | x,y ) uvec2 uvec3( node | x,y,z ) uvec3 uvec4( node | x,y,z,w ) uvec4 bvec2( node | x,y ) bvec2 bvec3( node | x,y,z ) bvec3 bvec4( node | x,y,z,w ) bvec4 imat2( node | Matrix2 | a,b,c,d ) imat2 imat3( node | Matrix3 | imat3 a,b,c,d,e,f,g,h,i) imat4( node | Matrix4 | imat4 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p ) umat2( node | Matrix2 | a,b,c,d ) umat2 umat3( node | Matrix3 | umat3 a,b,c,d,e,f,g,h,i ) umat4( node | Matrix4 | umat4 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p ) bmat2( node | Matrix2 | a,b,c,d ) bmat2 bmat3( node | Matrix3 | bmat3 a,b,c,d,e,f,g,h,i ) bmat4( node | Matrix4 | bmat4 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p ) Example: import { color, vec2, positionWorld } from 'three/tsl'; // constant material.colorNode = color( 0x0066ff ); // conversion material.colorNode = vec2( positionWorld ); // result positionWorld.xy Conversion It is also possible to perform conversions using the method chain: Name Returns a constant or conversion of type: .toFloat() float .toInt() int .toUint() uint .toBool() boolean .toColor() color .toVec2() vec2 .toVec3() vec3 .toVec4() vec4 .toMat2() mat2 .toMat3() mat3 .toMat4() mat4 Advanced .toIvec2() ivec2 .toIvec3() ivec3 .toIvec4() ivec4 .toUvec2() uvec2 .toUvec3() uvec3 .toUvec4() uvec4 .toBvec2() bvec2 .toBvec3() bvec3 .toBvec4() bvec4 .toImat2() imat2 .toImat3() imat3 .toImat4() imat4 .toUmat2() umat2 .toUmat3() umat3 .toUmat4() umat4 .toBmat2() bmat2 .toBmat3() bmat3 .toBmat4() bmat4 Example: import { positionWorld } from 'three/tsl'; // conversion material.colorNode = positionWorld.toVec2(); // result positionWorld.xy Uniform Uniforms are useful to update values of variables like colors, lighting, or transformations without having to recreate the shader program. They are the true variables from a CPU's point of view. Name Description uniform( boolean | number | Color | Vector2 | Vector3 | Dynamic Vector4 | Matrix3 | Matrix4, type = null ) values. Example: const posY = uniform( mesh.position.y ); // it's possible use posY.value to update manualy the value posY.value = mesh.position.y; material.colorNode = posY; uniform.on*Update() It is also possible to create update events on uniforms, which can be defined by the user: Name Description .onObjectUpdate It will be updated every time an object like Mesh is ( function ) rendered with this node in Material. .onRenderUpdate It will be updated once per render, common and shared ( function ) materials, fog, tone mapping, etc. It will be updated only once per frame, recommended .onFrameUpdate( for values that will be updated only once per function ) frame, regardless of when render pass the frame has, cases like timer for example. Example: const posY = uniform( 0 ); // it's possible use uniform( 'number' ) // or using event to be done automatically // { object } will be the current rendering object posY.onObjectUpdate( ( { object } ) => object.position.y ); material.colorNode = posY; Method chaining Method chaining will only be including operators, converters, math and some core functions. These functions, however, can be used on any node. Example: // it will invert the texture color material.colorNode = texture( map ).rgb.oneMinus(); Swizzle Swizzling is the technique that allows you to access, reorder, or duplicate the components of a vector using a specific notation within TSL. This is done by combining the identifiers: const original = vec3( 1.0, 2.0, 3.0 ); // (x, y, z) const swizzled = original.zyx; // swizzled = (3.0, 2.0, 1.0) It's possible use xyzw, rgba or stpq. Operators Name Description .add( node | value, ... Return the addition of two or more value. ) .sub( node | value ) Return the subraction of two or more value. .mul( node | value ) Return the multiplication of two or more value. .div( node | value ) Return the division of two or more value. .assign( node | value ) Assign one or more value to a and return the same. .remainder( node | Computes the remainder of dividing the first value ) node by the second. .equal( node | value ) Checks if two nodes are equal. .notEqual( node | value Checks if two nodes are not equal. ) .lessThan( node | value Checks if the first node is less than the ) second. .greaterThan( node | Checks if the first node is greater than the value ) second. .lessThanEqual( node | Checks if the first node is less than or value ) equal to the second. .greaterThanEqual( node Checks if the first node is greater than or | value ) equal to the second. .and( node | value ) Performs logical AND on two nodes. .or( node | value ) Performs logical OR on two nodes. .not( node | value ) Performs logical NOT on a node. .xor( node | value ) Performs logical XOR on two nodes. .bitAnd( node | value ) Performs bitwise AND on two nodes. .bitNot( node | value ) Performs bitwise NOT on a node. .bitOr( node | value ) Performs bitwise OR on two nodes. .bitXor( node | value ) Performs bitwise XOR on two nodes. .shiftLeft( node | Shifts a node to the left. value ) .shiftRight( node | Shifts a node to the right. value ) const a = float( 1 ); const b = float( 2 ); const result = a.add( b ); // output: 3 Function tslFn( function ) It is possible to use classic JS functions or a tslFn() interface. The main difference is that tslFn() creates a controllable environment, allowing the use of stack where you can use assign and conditional, while the classic function only allows inline approaches. Example: // tsl function const oscSine = tslFn( ( [ timer = timerGlobal ] ) => { return timer.add( 0.75 ).mul( Math.PI * 2 ).sin().mul( 0.5 ).add( 0.5 ); } ); // inline function export const oscSine = ( timer = timerGlobal ) => timer.add( 0.75 ).mul( Math.PI * 2 ).sin().mul( 0.5 ).add( 0.5 ); Both above can be called with oscSin( value ). TSL allows the entry of parameters as objects, this is useful in functions that have many optional arguments. Example: const oscSine = tslFn( ( { timer = timerGlobal } ) => { return timer.add( 0.75 ).mul( Math.PI * 2 ).sin().mul( 0.5 ).add( 0.5 ); } ); const value = oscSine( { timer: value } ); If you want to use an export function compatible with tree shaking, remember to use /*@__PURE__*/ export const oscSawtooth = /*@__PURE__*/ tslFn( ( [ timer = timerGlobal ] ) => timer.fract() ); Math Name Description PI The value of p (pi). PI2 The value of 2p (two pi). EPSION A small value used to handle floating-point precision errors. INFINITY Represent infinity. abs( x ) Return the absolute value of the parameter. acos( x ) Return the arccosine of the parameter. all( x ) Return true if all components of x are true. any( x ) Return true if any component of x is true. asin( x ) Return the arcsine of the parameter. atan( x ) Return the arc-tangent of the parameters. atan2( y, x ) Return the arc-tangent of the quotient of its arguments. bitcast( x, y ) Reinterpret the bits of a value as a different type. cbrt( x ) Return the cube root of the parameter. ceil( x ) Find the nearest integer that is greater than or equal to the parameter. clamp( x, min, max ) Constrain a value to lie between two further values. cos( x ) Return the cosine of the parameter. cross( x, y ) Calculate the cross product of two vectors. dFdx( p ) Return the partial derivative of an argument with respect to x. dFdy( p ) Return the partial derivative of an argument with respect to y. degrees( radians ) Convert a quantity in radians to degrees. difference( x, y ) Calculate the absolute difference between two values. distance( x, y ) Calculate the distance between two points. dot( x, y ) Calculate the dot product of two vectors. equals( x, y ) Return true if x equals y. exp( x ) Return the natural exponentiation of the parameter. exp2( x ) Return 2 raised to the power of the parameter. faceforward( N, I, Return a vector pointing in the same direction Nref ) as another. floor( x ) Find the nearest integer less than or equal to the parameter. fract( x ) Compute the fractional part of the argument. fwidth( x ) Return the sum of the absolute derivatives in x and y. inverseSqrt( x ) Return the inverse of the square root of the parameter. invert( x ) Invert an alpha parameter ( 1. - x ). length( x ) Calculate the length of a vector. lengthSq( x ) Calculate the squared length of a vector. log( x ) Return the natural logarithm of the parameter. log2( x ) Return the base 2 logarithm of the parameter. max( x, y ) Return the greater of two values. min( x, y ) Return the lesser of two values. mix( x, y, a ) Linearly interpolate between two values. negate( x ) Negate the value of the parameter ( -x ). normalize( x ) Calculate the unit vector in the same direction as the original vector. oneMinus( x ) Return 1 minus the parameter. pow( x, y ) Return the value of the first parameter raised to the power of the second. pow2( x ) Return the square of the parameter. pow3( x ) Return the cube of the parameter. pow4( x ) Return the fourth power of the parameter. radians( degrees ) Convert a quantity in degrees to radians. reciprocal( x ) Return the reciprocal of the parameter (1/x). reflect( I, N ) Calculate the reflection direction for an incident vector. refract( I, N, eta ) Calculate the refraction direction for an incident vector. round( x ) Round the parameter to the nearest integer. saturate( x ) Constrain a value between 0 and 1. sign( x ) Extract the sign of the parameter. sin( x ) Return the sine of the parameter. smoothstep( e0, e1, Perform Hermite interpolation between two x ) values. sqrt( x ) Return the square root of the parameter. step( edge, x ) Generate a step function by comparing two values. tan( x ) Return the tangent of the parameter. transformDirection( Transform the direction of a vector by a matrix dir, matrix ) and then normalize the result. trunc( x ) Truncate the parameter, removing the fractional part. const value = float( -1 ); // It's possible use `value.abs()` too. const positiveValue = abs( value ); // output: 1 Texture Name Description Type texture( texture, uv = uv(), level = Retrieves texels from a vec4 null ) texture. cubeTexture( texture, uvw = Retrieves texels from a vec4 reflectVector, level = null ) cube texture. triplanarTexture( textureX, textureY = Computes texture using null, textureZ = null, scale = float( 1 triplanar mapping based vec4 ), position = positionLocal, normal = on provided parameters. normalLocal ) Attributes Name Description Type attribute( name, type = null, Getting geometry attribute any default = null ) using name and type. uv( index = 0 ) UV attribute named uv + index. vec2 vertexColor( index = 0 ) Vertex color node for the color specified index. Position Name Description Type positionGeometry Position attribute of geometry. vec3 positionLocal Local variable for position. vec3 positionWorld World position. vec3 positionWorldDirection Normalized world direction. vec3 positionView View position. vec3 positionViewDirection Normalized view direction. vec3 positionLocal represents the position after modifications made by skinning, morpher, etc. Normal Name Description Type normalGeometry Normal attribute of geometry. vec3 normalLocal Local variable for normal. vec3 normalView Normalized view normal. vec3 normalWorld Normalized world normal. vec3 transformedNormalView Transformed normal in view space. vec3 transformedNormalWorld Normalized transformed normal in vec3 world space. transformedClearcoatNormalView Transformed clearcoat normal in vec3 view space. transformed* represents the normal after modifications made by skinning, morpher, etc. Tangent Name Description Type tangentGeometry Tangent attribute of geometry. vec4 tangentLocal Local variable for tangent. vec3 tangentView Normalized view tangent. vec3 tangentWorld Normalized world tangent. vec3 transformedTangentView Transformed tangent in view space. vec3 transformedTangentWorld Normalized transformed tangent in world vec3 space. Bitangent Name Description Type bitangentGeometry Normalized bitangent in geometry vec3 space. bitangentLocal Normalized bitangent in local space. vec3 bitangentView Normalized bitangent in view space. vec3 bitangentWorld Normalized bitangent in world space. vec3 transformedBitangentView Normalized transformed bitangent in vec3 view space. transformedBitangentWorld Normalized transformed bitangent in vec3 world space. Camera Name Description Type cameraNear Near plane distance of the float camera. cameraFar Far plane distance of the camera. float cameraLogDepth Logarithmic depth value for the float camera. cameraProjectionMatrix Projection matrix of the camera. mat4 cameraProjectionMatrixInverse Inverse projection matrix of the mat4 camera. cameraViewMatrix View matrix of the camera. mat4 cameraWorldMatrix World matrix of the camera. mat4 cameraNormalMatrix Normal matrix of the camera. mat3 cameraPosition World position of the camera. vec3 Model Name Description Type modelDirection Direction of the model. vec3 modelViewMatrix View matrix of the model. mat4 modelNormalMatrix Normal matrix of the model. mat4 modelWorldMatrix World matrix of the model. mat4 modelPosition Position of the model. vec3 modelScale Scale of the model. vec3 modelViewPosition View position of the model. vec3 modelWorldMatrixInverse Inverse world matrix of the model. mat4 Reflect Name Description Type reflectView Computes reflection direction in view space. vec3 reflectVector Transforms the reflection direction to world vec3 space. UV Utils Name Description Type matcapUV UV coordinates for matcap material vec2 computation. rotateUV( uv, Rotates UV coordinates around a center rotation, centerNode = point. vec2 vec2( 0.5 ) ) spritesheetUV( count, Computes UV coordinates for a sprite uv = uv(), frame = sheet based on the number of frames, UV vec2 float( 0 ) ) coordinates, and frame index. equirectUV( direction Computes UV coordinates for = equirectangular mapping based on the vec2 positionWorldDirection direction vector. ) Interpolation Variable Description Type remap( node, inLow, inHigh, outLow = Remaps a value from one any float( 0 ), outHigh = float( 1 ) ) range to another. remapClamp( node, inLow, inHigh, Remaps a value from one outLow = float( 0 ), outHigh = float range to another, with any ( 1 ) ) clamping. Random Variable Description Type hash( Generates a hash value in the range [ 0, 1 ] from the float seed ) given seed. range( Generates a range attribute of values between min and min, max max. Attribute randomization is useful when you want any ) to randomize values between instances and not between pixels. Oscillator Variable Description Type oscSine( timer = Generates a sine wave oscillation float timerGlobal ) based on a timer. oscSquare( timer = Generates a square wave oscillation float timerGlobal ) based on a timer. oscTriangle( timer = Generates a triangle wave oscillation float timerGlobal ) based on a timer. oscSawtooth( timer = Generates a sawtooth wave oscillation float timerGlobal ) based on a timer. Packing Variable Description Type directionToColor( value ) Converts direction vector to color. color colorToDirection( value ) Converts color to direction vector. vec3 Functions .toVar( name = null ) To create a variable from a node use .toVar(). The first parameter is used to add a name to it, otherwise the node system will name it automatically, it can be useful in debugging or access using wgslFn. const uvScaled = uv().mul( 10 ).toVar(); material.colorNode = texture( map, uvScaled ); --------------------------------------------------------------------- varying( node, name = null ) Let's suppose you want to optimize some calculation in the vertex stage but are using it in a slot like material.colorNode. For example: // multiplication will be executed in vertex stage const normalView = varying( modelNormalMatrix.mul( normalLocal ) ); // normalize will be executed in fragment stage // because .colorNode is fragment stage slot as default material.colorNode = normalView.normalize(); The first parameter of varying modelNormalMatrix.mul( normalLocal ) will be executed in vertex stage, and the return from varying() will be a varying as we are used in WGSL/GLSL, this can optimize extra calculations in the fragment stage. The second parameter allows you to add a custom name to varying. If varying() is added only to .positionNode, it will only return a simple variable and varying will not be created. Transitioning common GLSL properties to TSL GLSL TSL Type position positionGeometry vec3 transformed positionLocal vec3 transformedNormal normalLocal vec3 vWorldPosition positionWorld vec3 vColor vertexColor() vec3 vUv | uv uv() vec2 vNormal normalView vec3 viewMatrix cameraViewMatrix mat4 modelMatrix modelWorldMatrix mat4 modelViewMatrix modelViewMatrix mat4 projectionMatrix cameraProjectionMatrix mat4 diffuseColor material.colorNode vec4 gl_FragColor material.fragmentNode vec4 Toggle table of contents Pages 17 [ ] * Home * An introduction to using Git * Build instructions * Demoscene * Dev Branch Examples * Editor Manual * GUI Tools with Three.js * JSON Geometry format 4 * JSON Material format 4 * JSON Object Scene format 4 * JSON Texture format 4 * Migration * Migration Guide * Mr.doob's Code Style(tm) * Owners * Three.js Shading Language + Introduction + Why TSL? + Example + Old + New + Architeture + Constants and explicit conversions + Conversion + Uniform + uniform.on*Update() + Method chaining + Swizzle + Operators + Function + tslFn( function ) + Math + Texture + Attributes + Position + Normal + Tangent + Bitangent + Camera + Model + Reflect + UV Utils + Interpolation + Random + Oscillator + Packing + Functions + .toVar( name = null ) + varying( node, name = null ) + Transitioning common GLSL properties to TSL * Using SketchUp Models * Show 2 more pages... * Home * Build instructions * Migration Guide * Owners * An introduction to using Git * Dev Branch Examples * JSON Geometry format 4 * JSON Texture format 4 * JSON Material format 4 * JSON Object Scene format 4 * Three.js Shading Language * Using SketchUp Models Clone this wiki locally [https://github.com/m] Footer (c) 2024 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact * Manage cookies * Do not share my personal information You can't perform that action at this time.