UIScript 让着色器具备智能的属性面板交互能力,而宏定义则实现着色器的条件编译。本教程展示如何结合两者创建灵活的着色器, 通过本教程,你将学会:
Editor 模块定义材质在编辑器中的属性面板:
Editor {
  Properties {
    // 基础属性定义
    material_BaseColor("Base Color", Color) = (1, 1, 1, 1);
    material_BaseTexture("Base Texture", Texture2D);
    
    // 分组显示
    Header("Effects") {
      material_UseTexture("Use Texture", Boolean) = true;
      material_Brightness("Brightness", Range(0, 2, 0.01)) = 1.0;
    }
  }
}前往 Shader 介绍 了解更多属性
宏定义实现着色器的条件编译,根据不同条件生成不同的着色器变体:
#ifdef USE_TEXTURE
  // 当 USE_TEXTURE 宏被定义时,这段代码才会编译
  vec4 texColor = texture2D(material_BaseTexture, input.uv);
  color *= texColor;
#endif// 在代码中控制宏
material.shaderData.enableMacro("USE_TEXTURE");   // 开启宏
material.shaderData.disableMacro("USE_TEXTURE");  // 关闭宏#ifdef USE_TEXTURE
  vec4 texColor = texture2D(material_BaseTexture, uv);
  color *= texColor;
#endif#ifndef USE_TEXTURE
  // 当没有定义 USE_TEXTURE 时执行
  color = material_BaseColor;
#endif#if defined(USE_TEXTURE) && defined(USE_NORMAL_MAP)
  // 同时定义了两个宏时执行
#endif#ifdef USE_TEXTURE
  vec4 texColor = texture2D(material_BaseTexture, uv);
  color *= texColor;
#else
  color = material_BaseColor;
#endifUIScript 实现属性间的联动(仅在编辑器中生效):
export default class UIScriptDemo extends ShaderUIScript {
  constructor() {
    super();
    
    // 监听属性变化
    this.onPropertyChanged("material_UseTexture", (value) => {
      const { material: { shaderData } } = this;
      
      if (value) {
        // 开启纹理时
        shaderData.enableMacro("USE_TEXTURE");
      } else {
        // 关闭纹理时
        shaderData.disableMacro("USE_TEXTURE");
      }
    });
  }
}Shader "Tutorial/03-UIScript" {
  // Editor 模块定义材质面板的属性和交互
  Editor {
    Properties {
      // 基础属性
      material_BaseColor("Base Color", Color) = (1, 1, 1, 1);
      material_BaseTexture("Base Texture", Texture2D);
      
      // 分组显示
      Header("Effects") {
        material_UseTexture("Use Texture", Boolean) = true;
        material_Brightness("Brightness", Range(0, 2, 0.01)) = 1.0;
        material_Contrast("Contrast", Range(0, 2, 0.01)) = 1.0;
      }
      
      Header("Animation") {
        material_EnableAnimation("Enable Animation", Boolean) = false;
        material_AnimSpeed("Animation Speed", Range(0, 5, 0.1)) = 1.0;
      }
    }
  }
  
  SubShader "Default" {
    Pass "Forward" {
      mat4 renderer_MVPMat;
      float renderer_Time;
      
      vec4 material_BaseColor;
      sampler2D material_BaseTexture;
      float material_Brightness;
      float material_Contrast;
      float material_AnimSpeed;
      
      struct a2v {
        vec4 POSITION;
        vec2 TEXCOORD_0;
      };
      
      struct v2f {
        vec2 uv;
      };
      
      VertexShader = vert;
      FragmentShader = frag;
      
      v2f vert(a2v input) {
        v2f output;
        
        vec4 pos = input.POSITION;
        
        // 如果启用动画,添加顶点动画效果
        #ifdef ENABLE_ANIMATION
          pos.y += sin(pos.x * 3.0 + renderer_Time * material_AnimSpeed) * 0.2;
        #endif
        
        gl_Position = renderer_MVPMat * pos;
        output.uv = input.TEXCOORD_0;
        
        return output;
      }
      
      void frag(v2f input) {
        vec4 color = material_BaseColor;
        
        // 如果启用纹理,进行纹理采样
        #ifdef USE_TEXTURE
          vec4 texColor = texture2D(material_BaseTexture, input.uv);
          color *= texColor;
        #endif
        
        // 应用亮度和对比度调整
        color.rgb *= material_Brightness;
        color.rgb = (color.rgb - 0.5) * material_Contrast + 0.5;
        
        gl_FragColor = color;
      }
    }
  }
}前往 游乐场