This tutorial, using a simple skeletal animation demo, will teach you:
#include directive to reuse code snippets#include DirectiveThe #include directive allows you to reference external code snippets, enabling code reuse:
#include "Common.glsl" // Common functions
#include "Skin.glsl" // Skeletal animationBenefits:
The Galacean engine provides a rich set of built-in code snippets:
These snippets are registered by default in the editor. Learn more about the API in the Shader API documentation.
#include "Common.glsl"
// Provides common mathematical functions and constants
float value = pow2(0.5); // Square function
vec4 linearColor = sRGBToLinear(color); // sRGB to linear color conversion#include "Skin.glsl"
// Provides skeletal animation-related functions
mat4 skinMatrix = getSkinMatrix(attr);Skeletal animation requires processing vertex transformations:
struct Attributes {
vec4 POSITION;
vec2 TEXCOORD_0;
#ifdef RENDERER_HAS_SKIN
vec4 JOINTS_0; // Bone index
vec4 WEIGHTS_0; // Bone weight
#endif
};
``` Skeleton Transformation Calculation
```glsl
#include "Skin.glsl"
Varyings vert(Attributes Attribute) {
Varyings output;
// Perform skeleton transformation using engine-provided functions
// The getSkinMatrix function is from Skin.glsl
mat4 skinMatrix = getSkinMatrix(attr);
// Apply skeleton transformation
vec4 skinnedPosition = skinMatrix * attr.POSITION;
// MVP transformation
gl_Position = renderer_MVPMat * skinnedPosition;
output.uv = attr.TEXCOORD_0;
return output;
}Shader "Tutorial/05-SkinnedUnlit" {
SubShader "Default" {
Pass "Forward" {
// Engine-provided variables and matrices
#include "Transform.glsl"
// Material properties
vec4 material_BaseColor;
sampler2D material_BaseTexture;
struct Attributes {
vec4 POSITION;
vec2 TEXCOORD_0;
#ifdef RENDERER_HAS_SKIN
vec4 JOINTS_0; // Bone indices
vec4 WEIGHTS_0; // Bone weights
#endif
};
struct Varyings {
vec2 uv;
};
// Include engine-provided skinned animation code snippet
#include "Skin.glsl"
VertexShader = vert;
FragmentShader = frag;
Varyings vert(Attributes attr) {
Varyings output;
// Perform skeleton transformation using engine-provided functions
// The getSkinMatrix function is from Skin.glsl
mat4 skinMatrix = getSkinMatrix(attr);
// Apply skeleton transformation
vec4 skinnedPosition = skinMatrix * attr.POSITION;
// MVP transformation
gl_Position = renderer_MVPMat * skinnedPosition;
// Pass UV coordinates
output.uv = attr.TEXCOORD_0;
return output;
}
void frag(Varyings varying) {
// Simple Unlit shading
vec4 texColor = texture2D(material_BaseTexture, varying.uv);
vec4 finalColor = texColor * material_BaseColor;
gl_FragColor = finalColor;
}
}
}If the built-in shader fragments don't meet your needs, you can create your own:
import { ShaderFactory } from '@galacean/engine';
const customShaderCode = `
vec3 customLighting(vec3 normal, vec3 lightDir) {
float NdotL = max(dot(normal, lightDir), 0.0);
return vec3(NdotL);
}
`;
ShaderFactory.registerInclude('CustomLighting', customShaderCode);#include "CustomLighting"
void frag() {
vec3 lighting = customLighting(normal, lightDir);
gl_FragColor = vec4(lighting, 1.0);
}The engine provides a rich set of built-in variables that can be used directly, such as transformation matrix variables:
mat4 renderer_LocalMat; // Local transformation matrix
mat4 renderer_ModelMat; // Model matrix
mat4 renderer_MVMat; // Model-view matrix
mat4 renderer_MVPMat; // MVP matrix
mat4 renderer_NormalMat; // Normal transformation matrixGo to Shader Built-in Variables to learn more about available variables.
// First, implement basic functionality
void frag() {
gl_FragColor = material_BaseColor;
}
// Then, gradually add complex features
void frag() {
vec4 color = material_BaseColor;
#ifdef USE_TEXTURE
color *= texture2D(material_BaseTexture, uv);
#endif
#ifdef USE_LIGHTING
color.rgb *= lighting;
#endif
gl_FragColor = color;
}// Debug UV coordinates
gl_FragColor = vec4(uv, 0.0, 1.0);
// Debugging normals
gl_FragColor = vec4(normal * 0.5 + 0.5, 1.0);During compilation, ShaderLab will remove unused code blocks. Therefore, using macros appropriately can significantly improve performance.
// Without macros: Every pixel needs to be checked
void frag() {
vec4 color = material_BaseColor;
if (useTexture > 0.5) { // Runtime check, poor performance
color *= texture2D(material_BaseTexture, uv);
}
gl_FragColor = color;
}
// With macros: If the macro condition is not met, the code block is removed during compilation, resulting in better performance
void frag() {
vec4 color = material_BaseColor;
#ifdef USE_TEXTURE // Determined at compile time, better performance
color *= texture2D(material_BaseTexture, uv);
#endif
gl_FragColor = color;
}This shader will:
Skin.glsl fragment shader to implement skeletal animationGo to Playground