UIScript 脚本

UIScript 让着色器具备智能的属性面板交互能力,而宏定义则实现着色器的条件编译。本教程展示如何结合两者创建灵活的着色器, 通过本教程,你将学会:

  • Editor 模块的属性定义语法
  • 各种属性类型的使用方法
  • 宏定义的条件编译语法
  • UIScript 的属性联动逻辑

Editor 模块

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 - 如果定义了宏

#ifdef USE_TEXTURE
  vec4 texColor = texture2D(material_BaseTexture, uv);
  color *= texColor;
#endif

#ifndef - 如果没有定义宏

#ifndef USE_TEXTURE
  // 当没有定义 USE_TEXTURE 时执行
  color = material_BaseColor;
#endif

#if defined - 更复杂的条件

#if defined(USE_TEXTURE) && defined(USE_NORMAL_MAP)
  // 同时定义了两个宏时执行
#endif

#else 和 #endif

#ifdef USE_TEXTURE
  vec4 texColor = texture2D(material_BaseTexture, uv);
  color *= texColor;
#else
  color = material_BaseColor;
#endif

UIScript 交互逻辑

UIScript 实现属性间的联动(仅在编辑器中生效):

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;
      }
    }
  }
}

运行效果

  1. 展示了 Editor Properties 的定义和分组
  2. 演示了宏定义的条件编译(#ifdef USE_TEXTURE, ENABLE_ANIMATION)
  3. 模拟了 UIScript 中的属性联动逻辑
  4. 观察纹理和动画的开关效果,以及亮度对比度的动态变化

前往 游乐场

这篇文档对您有帮助吗?