UIScript enables intelligent property panel interaction for shaders, while macro definitions allow for conditional compilation of shaders. This tutorial demonstrates how to combine these two features to create flexible shaders. By completing this tutorial, you will learn:
The Editor module defines the property panel for materials in the editor:
Editor {
Properties {
// Basic property definition
material_BaseColor("Base Color", Color) = (1, 1, 1, 1);
material_BaseTexture("Base Texture", Texture2D);
// Grouping properties
Header("Effects") {
material_UseTexture("Use Texture", Boolean) = true;
material_Brightness("Brightness", Range(0, 2, 0.01)) = 1.0;
}
}
}Go to Shader Introduction to learn more about properties
Macro definitions implement conditional compilation for shaders, generating different shader variants based on different conditions:
#ifdef USE_TEXTURE
// This code will only compile when the USE_TEXTURE macro is defined
vec4 texColor = texture2D(material_BaseTexture, input.uv);
color *= texColor;
#endif// Control macros in code
material.shaderData.enableMacro("USE_TEXTURE"); // Enable macro
material.shaderData.disableMacro("USE_TEXTURE"); // Disable macro#ifdef USE_TEXTURE
vec4 texColor = texture2D(material_BaseTexture, uv);
color *= texColor;
#endif#ifndef USE_TEXTURE
// This code will execute when USE_TEXTURE is not defined
color = material_BaseColor;
#endif#if defined(USE_TEXTURE) && defined(USE_NORMAL_MAP)
// This code will execute when both macros are defined
#endif#ifdef USE_TEXTURE
vec4 texColor = texture2D(material_BaseTexture, uv);
color *= texColor;
#else
color = material_BaseColor;
#endifUIScript implements property binding (only works in the editor):
export default class UIScriptDemo extends ShaderUIScript {
constructor() {
super();
// Listen for property changes
this.onPropertyChanged("material_UseTexture", (value) => {
const { material: { shaderData } } = this;
if (value) {
// When texture is enabled
shaderData.enableMacro("USE_TEXTURE");
} else {
// When texture is disabled
shaderData.disableMacro("USE_TEXTURE");
}
});
}
}Shader "Tutorial/03-UIScript" {
// The Editor module defines the material panel properties and interactions
Editor {
Properties {
// Basic properties
material_BaseColor("Base Color", Color) = (1, 1, 1, 1);
material_BaseTexture("Base Texture", Texture2D);
// Grouped properties
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;
// Add vertex animation effect if animation is enabled
#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;
// Sample texture if texture is enabled
#ifdef USE_TEXTURE
vec4 texColor = texture2D(material_BaseTexture, input.uv);
color *= texColor;
#endif
// Apply brightness and contrast adjustments
color.rgb *= material_Brightness;
color.rgb = (color.rgb - 0.5) * material_Contrast + 0.5;
gl_FragColor = color;
}
}
}
}Go to Playground