
How to Debug Rendering Artifacts with Code and Screenshots
Your object appears in the wrong place, but the shader compiles. A screenshot shows the symptom; the code shows several plausible causes. Asking AI to “fix the image” without the camera, matrices, or expected result encourages a convincing guess.
Prepare a rendering evidence pack
Include the renderer/API, relevant shader stages, uniform values, geometry, viewport, camera, expected result, actual frame, and reproduction action. Record graphics errors and compile/link diagnostics as observations, not a summary saying “everything is fine.”
| Evidence | Useful content | What a screenshot alone cannot establish |
|---|---|---|
| Expected and actual frame | Same camera and resolution, marked affected region | Which transform or state caused the difference |
| Shader and uniforms | Exact revision, matrix convention, values | Whether the host uploaded the intended values |
| Draw setup | Geometry, attribute layout, depth/cull/blend state | Which objects were actually drawn |
| Reproduction | Minimal scene and steps | Whether a suggested patch fixes another scene |
A missing texture and incorrect lighting may look similar at thumbnail size. Supply an uncompressed crop where practical and avoid changing the camera between comparison frames.
A transform-order example with a checkable expectation
projection * view * model * position, with the rightmost transform applied first.Suppose the shader contains:
gl_Position = uProjection * uModel * uView * vec4(position, 1.0);(1,0,0,1), the intended clip result is (0,0.6,0,1):Model: (1,0) -> (3,0)
View: (3,0) -> (0,3)
Projection: (0,3) -> (0,0.6)
Wrong order: View (1,0)->(0,1), Model ->(2,1), Projection ->(0.4,0.2)Ask for a hypothesis, reproduction, and minimal patch
Investigate the supplied rendering mismatch.
Use the shader, host state, expected behavior, actual frame, and revision.
Treat images as observations, not proof of a particular root cause.
Return:
1. observed symptom and the evidence supporting it;
2. ranked hypotheses, with a discriminating test for each;
3. the smallest test for the leading hypothesis;
4. a patch only if the test supports the claim;
5. missing uniforms, state, or context that prevent a conclusion.
Use the stated matrix/vector convention.
Do not rewrite the renderer, change the camera to hide the symptom,
or claim tests were run if you only proposed them.
Inputs: evidence pack and required transform contract.For this example, the candidate patch is:
gl_Position = uProjection * uView * uModel * vec4(position, 1.0);For the first code request, provide the stated transform convention and failing shader. Ask for one hypothesis and the minimal patch, then use the numeric fixture to distinguish (0,0.6) from (0.4,0.2) before testing the real frame.
Run a small numeric check before judging the frame
The following JavaScript mirrors the two-dimensional part of this example's transforms:
const model = ([x, y]) => [x + 2, y];
const view = ([x, y]) => [-y, x];
const projection = ([x, y]) => [x * 0.2, y * 0.2];
const correct = projection(view(model([1, 0])));
const wrong = projection(model(view([1, 0])));
if (Math.abs(correct[0]) > 1e-9 || Math.abs(correct[1] - 0.6) > 1e-9) {
throw new Error("Unexpected reference transform");
}
if (Math.abs(wrong[0] - correct[0]) < 1e-9 &&
Math.abs(wrong[1] - correct[1]) < 1e-9) {
throw new Error("Fixture does not distinguish transform order");
}When the same screenshot could mean another bug
| Symptom | Discriminating check |
|---|---|
| Position changes with camera motion | Verify transforms and uploaded values against a known point |
| Object disappears from one side | Inspect winding/culling and view orientation |
| Correct position but wrong occlusion | Inspect depth attachment, clear, and comparison state |
| A texture looks stretched or reversed | Inspect UVs, image orientation, and sampler data |
| Only high-density displays differ | Compare drawing-buffer size with viewport and CSS size |
Change one condition at a time. Turning off culling may reveal geometry but does not establish that disabling it is the correct final fix. Save the experiment and its interpretation separately.
Keep a regression scene with the fix
Retain a case where translation and rotation do not commute; identity matrices would hide the bug in this example. Add normal camera motion and representative geometry after the minimal case passes. Compare the same scene revision and note any remaining artifact as a separate issue.
FAQ
Can a screenshot identify the exact root cause?
Usually it supplies a symptom and a location. The code, state, and reproduction distinguish competing causes.
Does a compiled shader prove the math is correct?
No. Valid expressions can apply transformations in the wrong order.
Should I try every suggested patch at once?
Use one hypothesis and test at a time so the result has an interpretable cause.
Does the JavaScript check verify the GPU output?
It checks the stated arithmetic. Rendering and host-state validation are separate steps.
Should the model receive the entire repository?
Start with the scene and relevant state needed to reproduce the mismatch. Add dependencies when a hypothesis requires them.
What belongs in the final bug record?
Revision, inputs, expected and observed behavior, experiment, accepted patch, regression checks, and remaining uncertainty.
Sources
- Khronos: vertex transformation: coordinate stages.
- MDN: matrix uniforms: WebGL upload requirements.


