A ray-mmd compatible material effect.
Blend up to 8 different materials on a single subset (material) using bitmap masks (with RGBA packing).
Each layer can have its own external texture maps (normal, smoothness, emissive, etc.).
- Overview
- File Structure
- Quick Start
- Layer Structure
- Mask Image Spec
- Parameter Reference
- Texture Map Reference
- Migration from Existing Materials
- Conversion Tool (convert_material.py)
- Shading Models
- ray-mmd Material Pattern Reference
- Mask Packing
- Sampler Budget
- Combining with Main Pass
- Limitations
- Troubleshooting
In ray-mmd's standard material system, one subset = one material.
To assign different materials to different parts, you had to split materials in PMX Editor.
MaskedMaterial uses grayscale mask images for per-pixel material compositing within a single subset.
- Up to 6 layers (individual masks) / up to 8 layers (RGBA packing)
- Per-layer external texture maps (normal, smoothness, emissive, etc.)
- Grayscale masks support soft boundaries (blending)
- RGBA packing saves up to 75% VRAM and 5 sampler slots
- Unused layers and textures consume no samplers
- Conversion tool (
convert_material.py) for automatic migration from material_2.0.fx
MaskedMaterial/
├── masked_material.fx <- User settings template
├── masked_material_common.fxsub <- Core shader (do not edit)
├── convert_material.py <- material_2.0.fx -> layer conversion tool
├── masks/ <- Place mask images here
│ ├── mask_1.png (individual mode)
│ ├── mask_packed_a.tga (packing mode: RGBA)
│ └── mask_packed_b.tga (packing mode: RGB)
├── textures/ <- Place per-layer textures here
│ ├── L0_normal.png
│ ├── L1_smoothness.png
│ └── ...
└── README.md
Place 8-bit grayscale PNGs matching the model's UV layout in the masks/ folder.
#define LAYER_COUNT 3
// Mask files
#define MASK_1_FILE "masks/skin.png"
#define MASK_2_FILE "masks/metal.png"
// Layer 0: Base (fills remaining area)
#define L0_ALBEDO float3(1, 1, 1)
#define L0_SMOOTHNESS 0.0
// Layer 1: Skin (with normal map)
#define L1_ALBEDO float3(1.0, 0.95, 0.9)
#define L1_SMOOTHNESS 0.3
#define L1_SHADING_MODEL 1
#define L1_NORMAL_MAP_FROM 1
#define L1_NORMAL_MAP_FILE "textures/skin_normal.png"
#define L1_NORMAL_SUB_MAP_FROM 1
#define L1_NORMAL_SUB_MAP_FILE "textures/skin_detail.png"
// Layer 2: Metal (with dedicated albedo texture)
#define L2_ALBEDO_MAP_FROM 1
#define L2_ALBEDO_MAP_FILE "textures/metal_albedo.png"
#define L2_ALBEDO float3(1, 1, 1)
#define L2_ALBEDO_APPLY_DIFFUSE 0
#define L2_SMOOTHNESS 0.85
#define L2_METALNESS 1.0
#include "masked_material_common.fxsub"MaskedMaterial must be assigned to the MaterialMap pass in ray-mmd.
- MMD menu bar -> "MMEffect" -> "Effect Mapping"
- Select the "MaterialMap" tab (NOT the "Main" tab)
- Expand the target model tree and select the subset (material) to apply
- Click "Select File" and choose
masked_material.fx - Optionally assign
main.fx(or an appropriate variant) to the "Main" tab
Effect Mapping Dialog:
+--------+---------------+--------+----------+
| Main | MaterialMap | Edge | Shadow |
| | <- HERE | | |
+--------+---------------+--------+----------+
| Model Name |
| Subset 0: (none) |
| Subset 1: masked_material.fx <- assign |
| Subset 2: (none) |
+---------------------------------------------+
Important: Always assign
masked_material.fxto the MaterialMap tab.
Assigning it to the Main tab will not write to the G-Buffer, and materials will not be applied.
| Layer | Mask (Individual) | Mask (RGBA Packing) | Description |
|---|---|---|---|
| Layer 0 | Not needed | Not needed | Base. Automatically fills areas not covered by other layers |
| Layer 1 | mask_1.png | Tex A : R | Overlay |
| Layer 2 | mask_2.png | Tex A : G | Overlay |
| Layer 3 | mask_3.png | Tex A : B | Overlay |
| Layer 4 | mask_4.png | Tex A : A | Overlay |
| Layer 5 | mask_5.png | Tex B : R | Overlay |
| Layer 6 | (PACKING=1 only) | Tex B : G | Overlay |
| Layer 7 | (PACKING=1 only) | Tex B : B | Overlay |
- Layer 0 weight is auto-calculated as
w0 = max(0, 1 - sum(masks)). - MASK_PACKING=0: up to Layer 5 (6 layers).
- MASK_PACKING=1: up to Layer 7 (8 layers).
| Property | Specification |
|---|---|
| Format | 8-bit grayscale PNG |
| Resolution | Same as model texture (recommended) |
| UV coordinates | Must match model's UV layout |
| White (255) | Fully show this layer |
| Black (0) | Hide this layer |
| Mid values | Blend ratio (for soft boundaries) |
Prefixed with L{N}_. N = 0-5 (or 0-7 when MASK_PACKING=1).
| Parameter | Type | Default | Description |
|---|---|---|---|
ALBEDO |
float3 | (1,1,1) | Albedo color / tint |
ALBEDO_MAP_FROM |
int | 0 | 0=constant, 1=external file |
ALBEDO_MAP_FILE |
string | - | Albedo texture path |
ALBEDO_APPLY_DIFFUSE |
int | 1 | 1=multiply by model texture (MaterialDiffuse) |
SMOOTHNESS |
float | 0.0 | Smoothness value (when no texture) |
METALNESS |
float | 0.0 | Metalness value |
SPECULAR |
float | 0.5 | Specular intensity (x0.08 -> F0) |
SHADING_MODEL |
int | 0 | Shading model ID |
EMISSIVE |
float3 | (0,0,0) | Emissive color |
EMISSIVE_INTENSITY |
float | 0.0 | Emissive intensity |
CUSTOM_A |
float | 0.0 | Custom data A |
CUSTOM_B |
float3 | (0,0,0) | Custom data B |
Each layer can have individual texture maps.
_MAP_FROM = 0 (default) disables, = 1 loads an external file.
| Parameter | Default | Description |
|---|---|---|
L{N}_ALBEDO_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_ALBEDO_MAP_FILE |
- | Texture file path |
L{N}_ALBEDO_APPLY_DIFFUSE |
1 | When FROM=0: 1=multiply by model texture |
When FROM=1, used as texture x L{N}_ALBEDO (tint).
| Parameter | Default | Description |
|---|---|---|
L{N}_ALBEDO_SUB_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_ALBEDO_SUB_MAP_FILE |
- | Texture file path |
L{N}_ALBEDO_SUB_ENABLE |
1 | Blend mode: 1=multiply, 2=power, 3=add, 5=alpha blend |
Composites additional texture detail (melanin, dirt, etc.) onto the albedo.
| Parameter | Default | Description |
|---|---|---|
L{N}_NORMAL_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_NORMAL_MAP_FILE |
- | Texture file path |
L{N}_NORMAL_MAP_TYPE |
0 | 0=RGB normal, 1=RG compressed |
L{N}_NORMAL_MAP_SCALE |
1.0 | Normal intensity |
L{N}_NORMAL_MAP_LOOP |
1.0 | UV repeat count |
| Parameter | Default | Description |
|---|---|---|
L{N}_NORMAL_SUB_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_NORMAL_SUB_MAP_FILE |
- | Texture file path |
L{N}_NORMAL_SUB_MAP_TYPE |
0 | 0=RGB normal, 1=RG compressed |
L{N}_NORMAL_SUB_MAP_SCALE |
1.0 | Normal intensity |
L{N}_NORMAL_SUB_MAP_LOOP |
1.0 | UV repeat count |
When both NORMAL_MAP and NORMAL_SUB_MAP are enabled, they are composited using RNM (Reoriented Normal Mapping).
Normals between layers are blended via weighted linear interpolation followed by normalize().
| Parameter | Default | Description |
|---|---|---|
L{N}_SMOOTHNESS_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_SMOOTHNESS_MAP_FILE |
- | Texture file path |
L{N}_SMOOTHNESS_MAP_SWIZZLE |
0 | 0=R, 1=G, 2=B, 3=A |
L{N}_SMOOTHNESS_MAP_TYPE |
0 | 0=Smoothness, 1=Roughness (inverted) |
L{N}_SMOOTHNESS_MAP_LOOP |
1.0 | UV repeat count |
When FROM=1, the texture value is used directly (L{N}_SMOOTHNESS constant is ignored).
| Parameter | Default | Description |
|---|---|---|
L{N}_{MAP}_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_{MAP}_MAP_FILE |
- | Texture file path |
L{N}_{MAP}_MAP_SWIZZLE |
0 | 0=R, 1=G, 2=B, 3=A |
L{N}_{MAP}_MAP_LOOP |
1.0 | UV repeat count |
| Parameter | Default | Description |
|---|---|---|
L{N}_OCCLUSION_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_OCCLUSION_MAP_FILE |
- | Texture file path |
L{N}_OCCLUSION_MAP_SWIZZLE |
0 | 0=R, 1=G, 2=B, 3=A |
L{N}_OCCLUSION_MAP_LOOP |
1.0 | UV repeat count |
Maps to ray-mmd's material.visibility. When FROM=0, occlusion = 1.0 (no occlusion).
| Parameter | Default | Description |
|---|---|---|
L{N}_EMISSIVE_MAP_FROM |
0 | 0=disabled, 1=external file |
L{N}_EMISSIVE_MAP_FILE |
- | Texture file path |
L{N}_EMISSIVE_MAP_LOOP |
1.0 | UV repeat count |
When FROM=1, used as texture x L{N}_EMISSIVE (tint).
| Old Parameter | New Parameter |
|---|---|
const float3 albedo = ... |
#define L{N}_ALBEDO float3(...) |
#define ALBEDO_MAP_FROM 1 |
#define L{N}_ALBEDO_MAP_FROM 1 |
#define ALBEDO_MAP_FILE "..." |
#define L{N}_ALBEDO_MAP_FILE "..." |
const float smoothness = ... |
#define L{N}_SMOOTHNESS ... |
#define SMOOTHNESS_MAP_FROM 1 |
#define L{N}_SMOOTHNESS_MAP_FROM 1 |
const float metalness = ... |
#define L{N}_METALNESS ... |
#define NORMAL_MAP_FROM 1 |
#define L{N}_NORMAL_MAP_FROM 1 |
#define NORMAL_MAP_FILE "..." |
#define L{N}_NORMAL_MAP_FILE "..." |
const float normalMapScale = ... |
#define L{N}_NORMAL_MAP_SCALE ... |
#define NORMAL_SUB_MAP_FROM 1 |
#define L{N}_NORMAL_SUB_MAP_FROM 1 |
#define OCCLUSION_MAP_FROM 1 |
#define L{N}_OCCLUSION_MAP_FROM 1 |
Old (material_skin.fx):
const float3 albedo = 1.0;
const float smoothness = 0.5;
#define NORMAL_SUB_MAP_FROM 1
#define NORMAL_SUB_MAP_FILE "skin_normal.png"
const float normalSubMapScale = 1.0;
#define CUSTOM_ENABLE 1
const float customA = 0.5;
const float3 customB = float3(0.9, 0.5, 0.4);New (as Layer 1):
#define L1_ALBEDO float3(1.0, 1.0, 1.0)
#define L1_SMOOTHNESS 0.5
#define L1_SHADING_MODEL 1
#define L1_NORMAL_SUB_MAP_FROM 1
#define L1_NORMAL_SUB_MAP_FILE "textures/skin_normal.png"
#define L1_NORMAL_SUB_MAP_SCALE 1.0
#define L1_CUSTOM_A 0.5
#define L1_CUSTOM_B float3(0.9, 0.5, 0.4)| Old Parameter | Conversion | Notes |
|---|---|---|
ALBEDO_MAP_FROM 3 |
L{N}_ALBEDO_MAP_FROM 0 + L{N}_ALBEDO_APPLY_DIFFUSE 1 |
FROM=3 (model texture) not supported; use APPLY_DIFFUSE instead |
const float3 albedo = 1.0 |
#define L{N}_ALBEDO float3(1.0, 1.0, 1.0) |
const -> #define, scalar -> float3() |
SMOOTHNESS_MAP_FROM 9 |
Disabled (FROM=0) |
FROM values 2-9 not supported in MaskedMaterial |
CUSTOM_ENABLE 1 |
L{N}_SHADING_MODEL 1 + L{N}_CUSTOM_A/B |
In ray-mmd, CUSTOM_ENABLE value maps directly to shading model ID |
SSS_SKIN_TRANSMITTANCE(x) |
Pre-computed float3(...) |
This macro is not available in masked_material_common.fxsub |
EMISSIVE_ENABLE 0 |
L{N}_EMISSIVE float3(0,0,0) |
Non-zero automatically triggers Emissive shading model |
A Python script that automatically converts existing ray-mmd material_2.0.fx files to MaskedMaterial layer definitions.
# Basic: convert material_body.fx as L1 (auto-detect shading model)
python convert_material.py material_body.fx --layer 1
# Explicitly specify shading model
python convert_material.py material_cloth.fx -l 2 -s 5
# Output to file
python convert_material.py material_skin.fx -l 1 -o layer1_output.txt| Option | Short | Default | Description |
|---|---|---|---|
--layer |
-l |
0 | Output layer number (0-7) |
--shading-model |
-s |
Auto-detect | Shading model ID |
--output |
-o |
stdout | Output file path |
- Shading model: Detected from
CUSTOM_ENABLEvalue (1=Skin, 3=Anisotropy, 5=Cloth, 7=Subsurface).EMISSIVE_ENABLE=1-> Emissive (2) - SSS_SKIN_TRANSMITTANCE macro: Pre-computes
exp((1-saturate(x)) * float3(-8,-40,-64))tofloat3(...) - MAP_FROM values 2-9: Automatically converted to 0 (disabled) with a warning
- Scalar to vector:
albedo = 1.0->float3(1.0, 1.0, 1.0)
Input (material_body.fx):
#define ALBEDO_MAP_FROM 3
#define ALBEDO_MAP_APPLY_DIFFUSE 1
const float3 albedo = 1.0;
const float smoothness = 0.55;
#define CUSTOM_ENABLE 1
const float customA = 0.6;
#define SSS_SKIN_TRANSMITTANCE(x) exp((1 - saturate(x)) * float3(-8, -40, -64))
const float3 customB = SSS_SKIN_TRANSMITTANCE(0.75);Output (python convert_material.py material_body.fx -l 1):
#define L1_ALBEDO float3(1.0, 1.0, 1.0)
#define L1_ALBEDO_MAP_FROM 0
#define L1_ALBEDO_APPLY_DIFFUSE 1
#define L1_SMOOTHNESS 0.55
#define L1_METALNESS 0.0
#define L1_SPECULAR 0.35
#define L1_SHADING_MODEL 1 // Skin
#define L1_CUSTOM_A 0.6
#define L1_CUSTOM_B float3(0.135335, 4.53999e-05, 1.12535e-07)| ID | Name | Use | CUSTOM_A | CUSTOM_B |
|---|---|---|---|---|
| 0 | Default | Standard PBR | - | - |
| 1 | Skin | Skin SSS | Curvature | Subsurface color |
| 2 | Emissive | Light emission | - | (auto: emissive color) |
| 3 | Anisotropy | Anisotropic | Angle shift | - |
| 4 | Glass | Glass | - | - |
| 5 | Cloth | Fabric | Sheen | Sheen color |
| 6 | ClearCoat | Clear coat | Smoothness | - |
| 7 | Subsurface | Subsurface scattering | Curvature | Subsurface color |
When the EMISSIVE constant is non-zero, the shading model is automatically set to Emissive (2).
In ray-mmd, CUSTOM_ENABLE values map directly to shading model IDs:
CUSTOM_ENABLE = 0-> Default (0)CUSTOM_ENABLE = 1-> Skin (1)CUSTOM_ENABLE = 3-> Anisotropy (3)CUSTOM_ENABLE = 5-> Cloth (5)CUSTOM_ENABLE = 7-> Subsurface (7)
Material types found in ray-mmd's Materials/ directory and their conversion compatibility.
| Type | CUSTOM_ENABLE | SHADING_MODEL | Characteristics | Convertible |
|---|---|---|---|---|
| Standard | 0 | 0 | Basic PBR, no special settings | OK |
| Skin | 1 | 1 | SSS_SKIN_TRANSMITTANCE, ALBEDO_SUB_ENABLE=4, skin.png | OK |
| Hair (Anisotropy) | 3 | 3 | CUSTOM_B_MAP_FROM=1 (shift2.png) | Partial* |
| Hair (SSS) | 7 | 7 | CUSTOM_B_MAP_FROM=3 | Partial* |
| Cloth | 5 | 5 | Fabric normal map, sheen color | OK |
| ClearCoat | 0 | 0 | metalness=1.0, high smoothness | OK |
| Metal | 0 | 0 | ALBEDO_MAP_FROM=0, fixed albedo color | OK |
| Glass | 0 | 4 | ALPHA_MAP_FROM=3, transparency | OK |
| Subsurface | 0 | 7 | Jade/marble/lampshade translucency | OK |
| Emissive | 0 | 2 | EMISSIVE_ENABLE=1, blink variants | OK |
| Editor | Dynamic | Dynamic | static const + PMX controller | NG (dynamic params) |
| Programmable | N/A | N/A | Water/Wetness, custom fxsub | NG (custom shader) |
convert_material.pycan automatically convert all types marked OK.
*Partial: Hair materials useCUSTOM_B_MAP_FROM(external texture for shift/SSS data).
MaskedMaterial currently only supports constantCUSTOM_Bvalues, not texture-mapped CUSTOM_B.
The converter will output the constantCUSTOM_Bvalue, warn about the unsupported texture map, and drop the reference.
Editor types (PMX controller linked) and Programmable types (Water/Wetness) are not convertible.
MASK_PACKING switches the mask storage method.
| Value | Method | Description |
|---|---|---|
| 0 | Individual grayscale (default) | One 8-bit grayscale PNG per layer |
| 1 | RGBA packing | Channel-packed into 2 RGBA textures |
| Texture | R | G | B | A |
|---|---|---|---|---|
MASK_PACKED_A_FILE |
mask1 (Layer 1) | mask2 (Layer 2) | mask3 (Layer 3) | mask4 (Layer 4) |
MASK_PACKED_B_FILE |
mask5 (Layer 5) | mask6 (Layer 6) | mask7 (Layer 7) | (unused) |
- LAYER_COUNT 2-5: Tex A only (1 sampler)
- LAYER_COUNT 6-8: Tex A + Tex B (2 samplers)
- MASK_PACKING=1 enables up to 8 layers
#define LAYER_COUNT 8
#define MASK_PACKING 1
#define MASK_PACKED_A_FILE "masks/mask_packed_a.tga" // RGBA: L1,L2,L3,L4
#define MASK_PACKED_B_FILE "masks/mask_packed_b.tga" // RGB: L5,L6,L7 (6+ layers)How to create RGBA packed images from individual grayscale mask images.
Recommended output format: Targa (TGA) 32-bit
PNG is lossless but tool-dependent sRGB gamma correction and color space auto-conversion
can introduce unintended errors in mask values.
TGA is uncompressed and preserves values exactly, making it the safest choice for packed textures.
Preparation: Menu bar -> "Window" -> "Layers" / "Channels" to show panels.
Storing to RGB channels:
- Place each grayscale mask image on a separate layer
- Double-click the layer -> open "Layer Style"
- Under "Channels", check only the target channel:
- R only -> stores to R channel (mask1)
- G only -> stores to G channel (mask2)
- B only -> stores to B channel (mask3)
Storing to Alpha channel:
- Hide the RGB-assigned layers temporarily
- Select the Alpha layer,
Ctrl+A(Select All) ->Ctrl+C(Copy) - Click the "+" button in the Channels panel to add "Alpha Channel 1"
- Select "Alpha Channel 1" and
Ctrl+Shift+V(Paste in Place)
Export:
- Hide the Alpha channel and Alpha layer
- "File -> Save a Copy" -> Targa (TGA) -> 32 bits/pixel
Reference: Storing Separate Textures in RGBA Channels (taka)
Tex A (RGBA: L1, L2, L3, L4):
magick mask_1.png mask_2.png mask_3.png mask_4.png \
-channel RGBA -combine mask_packed_a.tgaTex B (RGB: L5, L6, L7):
magick mask_5.png mask_6.png mask_7.png \
-channel RGB -combine mask_packed_b.tga3 layers only (fill A channel with black):
magick mask_1.png mask_2.png mask_3.png \
( -clone 0 -evaluate set 0 ) \
-channel RGBA -combine mask_packed_a.tga
-combineassigns grayscale images to R, G, B, A in input order.
Verify:magick identify -verbose mask_packed_a.tgato check channel depth.
from PIL import Image
r = Image.open("mask_1.png").convert("L")
g = Image.open("mask_2.png").convert("L")
b = Image.open("mask_3.png").convert("L")
a = Image.open("mask_4.png").convert("L")
Image.merge("RGBA", (r, g, b, a)).save("mask_packed_a.tga")- Use the RGBA Merge node
- Connect grayscale images to each channel input
- Export in Targa format
- VRAM reduction: Individual grayscale textures are internally converted to 32-bit/pixel in DX9, so packing saves up to 75%
- Sampler savings: Mask samplers reduced from up to 7 to a maximum of 2 (+5 slots freed)
- Speed improvement: tex2D calls reduced from up to 7 to a maximum of 2
- Layer expansion: Individual mode max 6 -> packing mode max 8 layers
SM3.0 limit: 16 samplers
| Usage | MASK_PACKING=0 | MASK_PACKING=1 |
|---|---|---|
| DiffuseMapSamp | 1 | 1 |
| Mask textures | LAYER_COUNT - 1 | 1-2 |
| Fixed total | LAYER_COUNT | 2-3 |
| LAYER_COUNT | Individual (PACKING=0) | Packed (PACKING=1) |
|---|---|---|
| 2 | 14 | 14 |
| 3 | 13 | 14 |
| 4 | 12 | 14 |
| 5 | 11 | 14 |
| 6 | 10 | 13 |
| 7 | N/A | 13 |
| 8 | N/A | 13 |
Only maps with L{N}_*_MAP_FROM = 1 consume a sampler.
_MAP_FROM = 0-> no sampler consumed (constant value used)_MAP_FROM = 1-> 1 sampler consumed
| Layer | ALBEDO | NORMAL | N_SUB | SMOOTH | Subtotal |
|---|---|---|---|---|---|
| L0 Base | FROM=0 |
1 | - | - | 1 |
| L1 Skin | FROM=0 |
1 | 1 | 1 | 3 |
| L2 Metal | 1 | 1 | - | 1 | 3 |
| Total | 7/13 |
In ray-mmd, the MME effect mapping assigns separate effects to the Main tab and MaterialMap tab.
MaskedMaterial is assigned to the MaterialMap tab.
MME Effect Mapping:
+--------------+------------------------------------------+
| Main tab | main.fx (forward rendering) |
| MaterialMap | masked_material.fx (G-Buffer write) |
+--------------+------------------------------------------+
| File | alphaThreshold | Use |
|---|---|---|
main.fx |
0.999 | Standard opaque objects (default) |
main_ex_alpha.fx |
1.1 | Completely ignore semi-transparency (exclude from G-Buffer) |
main_ex_noalpha.fx |
0.01 | Completely disable alpha (always opaque) |
main_ex_mask.fx |
0.01 | Alpha mask rendering (sharp cutout) |
main_ex_with_sphmap.fx |
0.999 | main.fx + sphere map support |
| Material Type | Main Tab | MaterialMap Tab |
|---|---|---|
| Standard (opaque) | main.fx |
masked_material.fx |
| Sharp cutout (leaves, lace) | main_ex_mask.fx |
masked_material.fx |
| Broken alpha model | main_ex_noalpha.fx |
masked_material.fx |
| With sphere map | main_ex_with_sphmap.fx |
masked_material.fx |
Main and MaterialMap are independent rendering passes.
Main handles "forward rendering", MaterialMap handles "PBR attribute G-Buffer writing" -- they do not interfere with each other.
Important: KeepALPHA_MAP_FROMsettings consistent between Main and MaterialMap.
-
ALPHA_MAP / PARALLAX_MAP cannot be set per layer
- ALPHA: Controls geometry-wide visibility, so per-layer separation is meaningless
- PARALLAX: UV displacement affects all texture sampling, fundamentally incompatible with mask-based compositing
-
_MAP_FROMonly supports 0 (disabled) and 1 (external file)- ray-mmd's
3(model texture),4(sphere),5(toon) are not supported in the mask layer system - To use model texture for albedo, use
ALBEDO_APPLY_DIFFUSE = 1
- ray-mmd's
-
Non-blendable attribute boundaries
- SHADING_MODEL, CUSTOM_A, CUSTOM_B are not blended; the dominant (max weight) layer's values are used
- Abrupt transitions may occur at mask boundaries
-
CONTROLOBJECT not supported
- Runtime value changes via bones/morphs are not supported
-
Normal map TYPE limitation
- Per-layer normal maps support only TYPE 0 (RGB) and TYPE 1 (RG compressed)
- TYPE 2 (grayscale low quality) and TYPE 3 (grayscale high quality) are not supported
-
CUSTOM_A / CUSTOM_B are constants only (no texture map support)
CUSTOM_A_MAP_FROMandCUSTOM_B_MAP_FROMfrom ray-mmd are not supported- Hair (Anisotropy) materials using
CUSTOM_B_MAP_FROM=1(shift texture) will lose the texture reference - Hair (SSS) materials using
CUSTOM_B_MAP_FROM=3will also lose the texture reference - Only constant
CUSTOM_A/CUSTOM_Bvalues are supported per layer
- Verify mask image is grayscale (RGB images will only use the R channel)
- Check file path case sensitivity
- Ensure
LAYER_COUNTis >= number of masks + 1
- Check that
L{N}_ALBEDOis notfloat3(0,0,0) - Verify albedo texture path is correct
- When
ALBEDO_APPLY_DIFFUSE = 0with constant color only, setALBEDOto an appropriate color
- Verify
_MAP_FROM = 1is set - Check file path is correct (relative to .fx file)
- Ensure SM3.0 sampler limit (16) is not exceeded
- Verify
LAYER_COUNTis in range 2-6 (MASK_PACKING=0) or 2-8 (MASK_PACKING=1) - Check for semicolons at the end of
#definelines - Verify
float3(...)parentheses are correct - Check that file path double quotes
"are properly closed (error X1005: string continues past end of line)
- Add blur (gradient) to mask image boundaries
- Normals are blended via weighted linear interpolation in tangent space, so abrupt mask changes can cause visible seams