本教程通过骨骼动画的一个简单Demo,你将学会:
#include 指令复用代码片段#include 指令允许引用外部代码片段,实现代码复用:
#include "Common.glsl"      // 通用函数
#include "Skin.glsl"        // 骨骼动画作用:
Galacean 引擎提供了丰富的内置代码片段:
编辑器默认注册了这些片段,前往 Shader API 了解更多 API
#include "Common.glsl"
 
// 提供常用的数学函数和常量
float value = pow2(0.5);    // 平方函数
vec4 linearColor = sRGBToLinear(color); // sRGB 转线性#include "Skin.glsl"
 
// 提供骨骼动画相关函数
mat4 skinMatrix = getSkinMatrix(attr);骨骼动画需要处理顶点的骨骼变换:
 struct Attributes {
    vec4 POSITION;
    vec2 TEXCOORD_0;
    #ifdef RENDERER_HAS_SKIN
      vec4 JOINTS_0;    // 骨骼索引
      vec4 WEIGHTS_0;   // 骨骼权重
    #endif
};#include "Skin.glsl"
 
Varyings vert(Attributes Attribute) {
  Varyings output;
  
  // 使用引擎内置函数进行骨骼变换
  // getSkinMatrix 函数来自 Skin.glsl
  mat4 skinMatrix = getSkinMatrix(attr);
  
  // 应用骨骼变换
  vec4 skinnedPosition = skinMatrix * attr.POSITION;
  
  // MVP 变换
  gl_Position = renderer_MVPMat * skinnedPosition;
  
  output.uv = attr.TEXCOORD_0;
  return output;
}Shader "Tutorial/05-SkinnedUnlit" {
  SubShader "Default" {
    Pass "Forward" {
      // 引擎内置变量和矩阵
      #include "Transform.glsl"
      
      // 材质属性
      vec4 material_BaseColor;
      sampler2D material_BaseTexture;
      
      struct Attributes {
        vec4 POSITION;
        vec2 TEXCOORD_0;
        #ifdef RENDERER_HAS_SKIN
          vec4 JOINTS_0;    // 骨骼索引
          vec4 WEIGHTS_0;   // 骨骼权重
        #endif
      };
      
      struct Varyings {
        vec2 uv;
      };
      
      // 引用引擎内置的骨骼动画代码片段
      #include "Skin.glsl"
 
      VertexShader = vert;
      FragmentShader = frag;
      
      Varyings vert(Attributes attr) {
        Varyings output;
        
        // 使用引擎内置函数进行骨骼变换
        // getSkinMatrix 函数来自 Skin.glsl
        mat4 skinMatrix = getSkinMatrix(attr);
        
        // 应用骨骼变换
        vec4 skinnedPosition = skinMatrix * attr.POSITION;
        
        // MVP 变换
        gl_Position = renderer_MVPMat * skinnedPosition;
        
        // 传递UV坐标
        output.uv = attr.TEXCOORD_0;
        
        return output;
      }
      
      void frag(Varyings varying) {
        // 简单的 Unlit 着色
        vec4 texColor = texture2D(material_BaseTexture, varying.uv);
        vec4 finalColor = texColor * material_BaseColor;
        
        gl_FragColor = finalColor;
      }
    }
  }
}如果内置代码片段不能满足需求,你也可以创建自己的代码片段:
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);
}引擎提供了丰富的内置变量,直接声明使用即可,比如变换矩阵相关变量:
mat4 renderer_LocalMat;    // 本地变换矩阵
mat4 renderer_ModelMat;    // 模型矩阵
mat4 renderer_MVMat;       // 模型视图矩阵
mat4 renderer_MVPMat;      // MVP矩阵
mat4 renderer_NormalMat;   // 法线变换矩阵前往 Shader 内置变量 了解更多可用变量
// 先实现基础功能
void frag() {
  gl_FragColor = material_BaseColor;
}
 
// 再逐步添加复杂功能
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;
}// 调试UV坐标
gl_FragColor = vec4(uv, 0.0, 1.0);
 
// 调试法线
gl_FragColor = vec4(normal * 0.5 + 0.5, 1.0);shaderlab 编译时会删除没有用到的代码块,因此合理使用宏定义将显著提升性能。
// 不使用宏:每个像素都要判断
void frag() {
  vec4 color = material_BaseColor;
  
  if (useTexture > 0.5) {  // 运行时判断,性能差
    color *= texture2D(material_BaseTexture, uv);
  }
  
  gl_FragColor = color;
}
 
// 使用宏:编译时若没有命中宏条件,会直接删除该代码块,性能好
void frag() {
  vec4 color = material_BaseColor;
  
  #ifdef USE_TEXTURE  // 编译时决定,性能好
    color *= texture2D(material_BaseTexture, uv);
  #endif
  
  gl_FragColor = color;
}这个着色器会:
Skin.glsl 代码片段实现骨骼动画的播放前往 游乐场