Common Pitfalls and Troubleshooting
Indexed by symptom: find your situation in the table, jump to the section; each entry gives the likely cause, the fix, and where to read more.
| Symptom | Jump |
|---|---|
| Black screen / nothing renders at all | #black-screen |
| Surfaces flicker, overlapping faces can't be ordered (Z-Fighting) | #z-fighting |
| Distant or animated models wrongly culled, vanishing into thin air | #culling |
| macOS viewport stays blank | #macos |
| Black screen on iOS once blending is on (float render targets) | #ios-blend |
| Something vanishes silently in the browser but works on desktop | #browser-drops |
| Browser crashes on the first frame / aborts at startup | #browser-crash |
| First frame very slow on iOS / browser | #slow-first-frame |
| Writing bone matrices directly has no effect | #bone-matrix |
| Particles completely invisible | #particles |
| Per-instance attributes (color etc.) do nothing or render wrong | #instance-location |
| Custom pass draws nothing on frame 1, fine from frame 2 | #shader-order |
| Uniforms never take effect | #uniform-silent |
| GLSL compiles nowhere although it looks correct | #glsl-chars |
| PipelineSettings changes have no effect | #settings-timing |
Compile error: Color is an ambiguous reference between System.Drawing.Color and Avalonia.Media.Color | #color-ambiguity |
| The 5th light of a type does nothing / ambient parameter ignored | #limits-ambient |
| After backgrounding or detach/reattach, must the scene be rebuilt? | #context-restore |
Nothing renders
Black screen / nothing renders at all
Check the four causes in order of hit rate:
- No light added — the default BlinnPhong pipeline renders pure black without lights. Add a
DirectionalLightand set itsRotationDegrees(a directional light's direction comes from its rotation; rotating it away from your model is just as dark). - Camera not aimed — the scene needs a camera;
Aura3DView.MainCamerais the default one. Make sure it isn't behind the objects or inside them; usecamera.LookAt(...)orFitToBoundingBox(model.BoundingBox, padding: 0.5f). CreateRenderPipelineassigned too late — it must be set before the control's GL initialization (i.e. before the control loads). Assigning it afterSceneInitializedis too late and the chosen pipeline never applies.BlendMode.Maskedclipping translucent pixels away — under Masked, pixels with alpha ≤AlphaCutoff(default 0.5) arediscarded, so translucent textures show big holes or disappear entirely. UseBlendMode.Translucentfor actual transparency.
Light and camera details: Lighting and Shadows, Cameras and View Control; pipeline assignment timing: Choosing and Configuring Pipelines.
Surfaces flicker, overlapping faces can't be ordered (Z-Fighting)
- Likely cause: the depth buffer lacks precision, so two nearly-coincident faces can't be ordered.
- What to do: set
PipelineSettings.DepthFormattoDepthComponent32f(it is the default; if you dropped it to 16/24 to save memory, raise it back). It cannot be changed after the pipeline is created. - Read more: Choosing and Configuring Pipelines.
Distant or animated models wrongly culled, vanishing into thin air
- Likely cause: frustum culling uses the T-Pose bounding box computed from static vertex data; when skinning animation (walking, jumping…) pushes the model visibly outside it, meshes still on screen get culled.
- What to do: widen the box with
model.BoundingBoxPadding = new Vector3(2f), or fully cover the animation range withmodel.CustomBoundingBox = new BoundingBox(min, max). To verify, temporarily setSettings.EnableFrustumCulling = false, or look at the boxes directly viaSettings.Debug.ShowBoundingBox = true. Static models need no adjustment. - Read more: Animation System.
macOS viewport stays blank
- Likely cause: the host is running a native rendering mode other than OpenGL.
- What to do: pin
AvaloniaNativeRenderingMode.OpenGlexplicitly inAppBuilder. iOS needs no such special-casing (it has its own ANGLE path). - Read more: Platforms and Render Backends.
Platform-specific silent failures
Black screen on iOS once blending is on (float render targets)
- Likely cause: ANGLE's Metal backend on iOS does not provide
GL_EXT_float_blend; drawing with blending enabled into a 32-bit float color attachment (e.g.Rgba32f) is treated asGL_INVALID_OPERATIONand the entire draw is dropped silently — a black image, not an error. - What to do: use
Rgba16ffor HDR render targets (all built-in pipelines do); never allocate anRgba32fcolor attachment and then enable blending in a custom pass. - Read more: Platforms and Render Backends → the GLES 3.0 subset.
Something vanishes silently in the browser but works on desktop
- Likely cause: WebGL2 validates strictly and drops the entire offending draw silently. Four triggers: (1) an enabled draw buffer with no fragment output actually written (a depth-only FBO without
glDrawBuffers([GL_NONE]); a fragment shader declaringoutthat no#ifdefbranch ever writes); (2)UseShader/UseShader_Internalwritten in the wrong order (see #shader-order); (3) a color attachment using an RGB-family internal format (RGB8/RGB16F/RGB32Fare not color-renderable — use the RGBA family); (4) calling an entry point WebGL2 does not have (e.g.glGetTexLevelParameteriv). - What to do: check a custom pass against these four GLES 3.0-subset rules; built-in pipelines are unaffected.
- Read more: Platforms and Render Backends → the GLES 3.0 subset.
Browser crashes on the first frame / aborts at startup
- Likely cause: .NET 10 Release trimming removed WASM interpreter-to-native trampolines (the log reaches
[aura3d-webgl] output ..., thenaot-runtime-wasm.c:188 <disabled>andexit(1)), or the linked-icall table disagrees with the loadedSystem.Private.CoreLib(Your mono runtime and class libraries are out of sync/function signature mismatch). ADllNotFoundException: libSkiaSharpright at startup means the wasm module was never natively linked. - What to do: in the app
.csprojunder Release set all three together:PublishTrimmed=false+RunAOTCompilation=false+WasmLinkIcalls=false(one or two of the three still fails); after changing them, publish to freshbin/obj/publish directories, replace the remote static site wholesale, and refresh thedotnet.jscaches. - Read more: Platforms and Render Backends → .NET 10 Release required settings.
First frame very slow on iOS / browser — expected
- Likely cause: on iOS, ANGLE translates GLSL to MSL and compiles every variant at runtime with no on-disk cache; in the browser the Mono interpreter executes host code slowly. Measured: roughly 5.6–5.9 s for a PBR scene with cascaded shadows and 0.4–0.7 s for simple scenes on iOS; heavy browser scenes can take minutes to reach frame 1.
- What to do: wait for frame 1 — it is normal. Do not enable
RunAOTCompilationalone as a performance fix (it regresses to the #browser-crash crash). - Read more: Platforms and Render Backends.
Animation, instancing and particles
Writing bone matrices directly has no effect
- Likely cause: bone matrices are recomputed every frame during the animation sampling stage by
IAnimationSampler.Update(); values you write inSceneUpdatedget overwritten. - What to do: for procedural bone control, implement a custom
IAnimationSampler, or make sure the override happens after animation sampling. - Read more: Animation System → manual bone manipulation.
Per-instance attributes (color etc.) do nothing or render wrong
- Likely cause: the
layout(location = N)in the vertex shader doesn't match the numeric value of theBuildInVertexAttributeenum (the location is the enum value), or the#ifdef INSTANCED_MESHbranch forgot to multiply the per-instancemodelMatrixitself — overriding the vertex stage means taking over the instance transform. - What to do: use the location convention table in Built-in Passes and Shader Macros;
InstancedMesh.SetAttributeEnabled("InstanceNormalTransform", false)drops the unneeded instance normal matrix to save bandwidth. - Read more: Instanced Rendering.
Particles completely invisible
- Likely cause:
Play()was never called; or auto-rendering is off (AutoRequestNextFrameRendering = false) without per-frameRequestNextFrameRendering(). - What to do: call
ps.Play()after configuration; for "particles stuck at origin / not moving / mesh mode all black / flipbook not animating" etc., walk the per-parameter checklist. - Read more: Particle System → troubleshooting table.
Shaders and custom passes
Custom pass draws nothing on frame 1, fine from frame 2
- Likely cause:
UseShader_Internal()was called beforeUseShader(...)/AddDefines(...)— the frame bound the previous frame's variant (on frame 1, the empty-macro one). - What to do: always write
UseShader→UseShader_Internal→ set uniforms → draw; note also that macro order affects the cache key (joined with;), so declare all macros in oneUseShadercall. - Read more: Custom Render Pipelines.
Uniforms never take effect
- Likely cause: the
Uniform*methods silently skip — no error — when no shader is active (CurrentShaderis null) or the uniform name doesn't exist in the program. A pass that doesn't iterate meshes (pureRenderQuad/RenderCube) must callUseShader_Internal()manually. - What to do: first check the activation order (#shader-order), then verify the exact uniform name in the GLSL; the engine always provides
modelMatrix/viewMatrix/projectionMatrix/cameraPosition, and material parameters bind to same-named uniforms. - Read more: Custom Render Pipelines, Custom Materials and Shaders.
GLSL compiles nowhere although it looks correct
- Likely cause: the shader source contains Chinese comments or other full-width characters.
- What to do: keep GLSL comments ASCII-only; write the explanations on the C# side.
- Read more: Custom Materials and Shaders.
Configuration and timing
PipelineSettings changes have no effect
- Likely cause: some settings are only read before the pipeline is created and are ignored afterwards:
DepthFormat,DirectionalLightLimit/PointLightLimit/SpotLightLimit,CsmCascadeCount,CsmShadowMapResolution. The rest (CsmSplitLambda,ToneMappingExposure,BrightnessClamp,AmbientIntensity,EnableFxaa,EnableFrustumCulling,Debug.*) can change any time and take effect on the next frame. - What to do: set pre-creation values in XAML
PipelineSettingsor at control construction; for live tuning useview.Scene.RenderPipeline.Settings.X = .... - Read more: Choosing and Configuring Pipelines.
Compile error: Color is an ambiguous reference
- Likely cause: Aura3D color properties (
Material.BaseColor,Light.LightColor,Texture.CreateFromColor(...)etc.) takeSystem.Drawing.Color, while an Avalonia project normally usesAvalonia.Media.Color; with both namespaces imported, a bareColorcollides. - What to do: write the fully qualified
System.Drawing.Color.White, or alias it:using DrawingColor = System.Drawing.Color;. To convert an Avalonia color:System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B). - Read more: Quick Start.
The 5th light of a type does nothing / ambient parameter ignored
- Likely cause: each light type defaults to a limit of 4; lights beyond the limit produce neither illumination nor shadows. Limits range 1..10 and must be raised before pipeline creation. Also,
AmbientIntensityapplies to BlinnPhong/CelShading only — the PBR pipeline uses IBL ambient light and ignores this parameter. - What to do: raise the matching
*LightLimitbefore the pipeline is created; to brighten a PBR scene's ambience, set up an HDR environment. - Read more: Lighting and Shadows, Choosing and Configuring Pipelines.
Context and lifecycle
After backgrounding or detach/reattach, must the scene be rebuilt?
- No. When the context is lost,
ContextLostfires; when a new context is ready,ContextRestoredfires and every GPU resource is rebuilt on demand — the picture comes back by itself. The scene, nodes and materials are preserved, so there is nothing to rebuild, andSceneInitializeddoes not fire a second time — the scene is built once, on first initialization. - Note: don't "guard against loss" by rebuilding the scene from
SceneInitialized; to return VRAM explicitly useAura3DView.ReleaseGpuResources(), to end the current scene useDestroyScene(). - Read more: GPU Resource Lifecycle.