본문으로 건너뛰기
cccskills
무료GitHub에 공개

shader-debugging

Reading a GLSL compiler's log, mapping its line numbers to your source, telling "compiles" from "draws", and the triage order for a black canvas. Read this when a shader fails in a way that does not make sense, or when it compiles and still shows nothing.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md6.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Debugging a shader

Shader debugging is unusual: there is no breakpoint, no stack, and the compiler's message is often three lines away from the cause. The order below is the fastest path from "it does not work" to a specific fault.

Step 1: read the rung, not the mood

Every answer from this row names how far it got:

shader my-shader — FAILED at v1-static (es100)
v1-static: only the static rules ran - no compiler was asked

v1-static on a machine that has a browser means the source was refused before a compiler saw it (a #version in a wrapper dialect, or desktop GLSL). v2-compiled means a real compiler accepted it - so a broken picture is not a syntax problem. Never say "it does not compile" from a v1 answer.

Step 2: trust the line numbers, and know why

A compiler reports a line in the string it was HANDED. For an es100 source that is your file, so line numbers match. For a wrapper dialect (Shadertoy, modules) the row prepends a header and appends main(), so the compiler's line 40 might be your line 27. The mapping is done for you: a diagnostic says

line 27 — 'dot2' : no matching overloaded function found

and that is your line 27. If a diagnostic says [in noise], the error is inside an included module, not your file - fix the include or the call.

Step 3: the four messages that cover most failures

ERROR: 0:12: '=' : dimension mismatch

A vector assigned to a scalar, or the reverse. Read the second line - ANGLE adds cannot convert from 'const 3-component vector of float' to 'highp float' and names both types.

ERROR: 0:8: '}' : syntax error

A missing semicolon or an unbalanced brace, reported at the next }. Look at the line ABOVE the reported one.

ERROR: 0:5: 'x' : undeclared identifier

A typo, a variable declared in another scope, or a uniform you forgot to declare. For iTime / iResolution this also means the dialect is wrong: those only exist in a wrapper dialect, not in es100.

ERROR: 0:14: 'min' : no matching overloaded function found

The function exists but not for those types: min(1.0, 2) has no min(float, int). Also check the argument COUNT - this is what a two-argument call to a one-argument function looks like.

Step 4: undefined uniforms are silent

A uniform that is declared but never used is deleted by the linker. Setting it is a no-op, and getUniformLocation returns null - which most code ignores. So a shader can read a uniform, "get" nothing, and render a constant.

shader_verify reports the active uniforms after link: that is the linker's own list of what survived. If your uniform is not in it, nothing you pass reaches the shader.

active uniforms after link: u_time (FLOAT), u_speed (FLOAT)

Two causes: the uniform is genuinely unused (dead code from an edit), or it is used only inside a branch the compiler eliminated (a if (false) left in).

Step 5: it compiles, links, draws - and is black

shader_render answers three questions at once:

reportmeans
black: trueevery pixel is 0. The shader ran and wrote nothing visible.
flat: trueone colour everywhere. uv collapsed, or everything is clamped.
distinctColors: 3it varies, but barely - usually one term dominating

Triage order for a black frame, cheapest first:

  1. Is anything written? Replace the body with gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);. If that is still black, the problem is not the shader - it is the program, the viewport, or the draw call.
  2. Is the colour in range? A value like 1e6 becomes inf, and inf clamps to white or NaN to black unpredictably. Print by hard-coding: vec4(vec3(x), 1.0).
  3. NaN. sqrt of a negative, log(0), 0.0/0.0, pow(negative, fractional) all produce NaN, and one NaN pixel is black. Bisect by returning each term alone.
  4. The camera. A raymarcher whose ray points away from the object draws the background, which is black by default. Draw the ray direction as colour (gl_FragColor = vec4(rd * 0.5 + 0.5, 1.0)) - if that is a flat colour, the ray is degenerate.
  5. Uniforms. See step 4.
  6. The clock. If it is black only at time = 0, a term like sin(t) * something is zero at the start. Render at time: 2.

Step 6: it draws here, not there

The compile this row runs uses ANGLE with a software rasterizer (SwiftShader). It is a real GLSL compiler, and it is not the user's driver:

  • mediump precision differences: desktop GPUs honour highp everywhere, mobile ones may not. A shader that relies on mediump being 32-bit fails on a phone.
  • OES_standard_derivatives: needed in ES 1.00, and a page must request it. The row requests it for its own compile, so fwidth passes here and fails on a page that did not.
  • Non-constant loop bounds: allowed by ANGLE, rejected by some mobile drivers.
  • Large integer hashes: uint maths is ES 3.00; in ES 1.00 the same code does not compile at all.

If a shader fails only on a page, get that page's getShaderInfoLog - the browser in front of you is the authority for that browser.

Step 7: the picture is right but wrong

  • Upside down: gl_FragCoord.y runs from the bottom. Flip with 1.0 - uv.y, or accept it.
  • Stretched: uv divided by u_resolution without the aspect fix. Divide by min(u_resolution.x, u_resolution.y) and centre it.
  • Banded: 8-bit output, no dither. Add (hash(gl_FragCoord.xy) - 0.5) / 255.0.
  • Crawling noise: white-noise dither animates. Use an ordered dither for still images.
  • Different on every machine: uninitialised behaviour - smoothstep with edge0 >= edge1, reading a varying that was never written, mix past its range.

Using the tools in the right order

  1. shader_verify - did a compiler accept it, and on which line did it not?
  2. shader_render - did a frame draw, is it varied, does it move?
  3. Look at the PNG. Compilation is not a picture.
  4. shader_target target=web, open it, and read the on-page log for the driver that will actually run it.

The one habit that matters: do not move to the next step until the current one is green. A "probably fine" compile that was never read is the most expensive kind of wrong.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Author an editorial HTML+SVG diagram with the Diagram Design kit - choose the visual type, read its reference, start from an example, and satisfy the safety and accessibility contract that diagram_write({kind:"html"}) enforces.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

Control this machine's Docker or Podman engine through the docker_* tools - read the engine's state first, list containers and images, inspect one, tail its logs, pull an image and start it, then stop, restart or remove what you started. Covers the engine that is asleep behind Docker Desktop and the exact call for each verb, the confirm rule removals obey, and the flags the tools deliberately do not have.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

Drive ffmpeg through the media_run tool as an argv array - remux instead of re-encode, map streams explicitly, trim at keyframes, scale and crop, extract audio and frames, and build GIFs, contact sheets and concatenations. Covers the flags this pack injects, reading a failure from its exit code and last log lines, and the version-dependent spellings to hedge.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

Read a media file's real structure with ffprobe through media_run - the JSON invocation and how to read it, what every family of field means, how to count frames, sample without decoding everything, detect HDR and interlacing, and diagnose a missing duration, a stream with no frames or a container that lies. Covers when media_probe's own report is already enough.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

Writing a fragment shader that looks like something - the full-screen triangle, uv space, signed distance fields, noise and fbm, domain warping, raymarching budgets, tone mapping and banding. Every example compiles. Read this when the task is "make it look good", not "make it compile".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

Use when the user wants to design, redesign, shape, critique, audit, polish, simplify, harden, animate, or otherwise improve a frontend interface: websites, landing pages, dashboards, product UI, components, forms, settings, onboarding, empty states, and native iOS or Android screens. Handles UX review, hierarchy, layout, typography, color, motion, UX copy, accessibility, responsive behavior, edge cases, and capturing or documenting design systems (DESIGN.md, tokens). Also for making bland UI bolder, loud UI quieter, or ambitious visual effects. Not for backend-only or non-UI tasks.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

vecnode/vn-code102026년 10월 20일 업데이트

vecnode의 스킬 모두 보기

이 스킬의 문제 신고