{"slug": "modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics", "title": "Modern GLSL Shader Development in Zed & VS Code: Zero Setup, Real-Time Diagnostics", "summary": "A developer built an open-source, Rust-based GLSL language server that provides zero-setup shader development in both Zed and VS Code, using AI assistance during development. The tool validates shaders against the glslang compiler backend in real time, offering instant diagnostics, autocompletion, signature help, go-to-definition, and background #include resolution for shared GLSL headers. It supports per-file targeting of Desktop OpenGL and Vulkan SPIR-V via a // @target: vulkan directive.", "body_md": "Writing GLSL shaders often feels like stepping ten years back in time compared to modern web or backend development. Whether you are targeting modern Desktop OpenGL or Vulkan, finding an editor setup that just works out of the box is surprisingly difficult.\n\nRecently, while trying to get a decent shader workflow running across **Zed** and **VS Code**, I kept hitting the same roadblocks:\n\n`#version 330` to `460 core` or Vulkan SPIR-V, they start throwing false, confusing errors.`#include` resolution:\nInstead of constantly fighting with fragmented tools, I decided to build a unified solution with the help of AI: a lightweight Rust-based language server supporting both Zed and VS Code with zero manual setup.\n\nThe project is completely open source. If you want to jump straight into the code or install it for your editor, you can find the repository here:\n\n`.xyzw`, `.rgba`), functions, and structs.` glslang`) in the background.\nTo see how this works, let's create a basic vertex shader inside a `glsl/` directory named `standart.vert`:\n\n```\n#version 460 core\n\nlayout(location = 0) in vec3 aPos;\n\nvoid main()\n{\n    gl_Position = vec4(aPos, 1.0);\n}\n```\n\nNow, let's say we accidentally omit the semicolon on the `gl_Position` assignment:\n\n```\nvoid main()\n{\n    gl_Position = vec4(aPos, 1.0) // Missing semicolon\n}\n```\n\nThe moment you stop typing, the language server validates the shader against the compiler backend and immediately highlights the syntax error with precise line and column information:\n\nNo need to recompile your engine or run command-line tools—you catch typos the second you make them.\n\nIn real-world projects, shaders share math helpers, camera matrices, and lighting calculations. To keep things modular, let's create a shared header named `common.glsl`:\n\n```\nmat4 calculateMVP(mat4 modelMatrix, mat4 viewMatrix, mat4 projectionMatrix) {\n    return projectionMatrix * viewMatrix * modelMatrix;\n}\n\nvec3 TransformToWorldSpace(mat4 modelMatrix, vec3 position) {\n    return vec3(modelMatrix * vec4(position, 1.0));\n}\n\nvec4 TransformToClipSpace(mat4 modelMatrix, mat4 viewMatrix, mat4 projectionMatrix, vec4 position) {\n    return projectionMatrix * viewMatrix * modelMatrix * position;\n}\n\nvec3 TransformNormalToWorldSpace(mat3 normalMatrix, vec3 normal) {\n    return normalize(normalMatrix * normal);\n}\n\nvec3 TransformNormalToWorldSpace(mat4 normalMatrix, vec3 normal) {\n    return normalize(mat3(normalMatrix) * normal);\n}\n```\n\n*(We assume normal matrix inversion/transposition is pre-calculated on the CPU side).*\n\nStandard GLSL does not support `#include` without compiler extensions. By enabling `GL_ARB_shading_language_include` (or `GL_GOOGLE_include_directive`), we can cleanly include our shared header inside `standart.vert`:\n\n```\n#version 460 core\n\n#extension GL_ARB_shading_language_include : enable\n#include \"common.glsl\"\n\nlayout(location = 0) in vec3 aPos; out vec3 FragPos;\nlayout(location = 0) in vec3 aNormal; out vec3 Normal;\nlayout(location = 0) in vec2 aTexCoords; out vec2 TexCoords;\n\nuniform mat4 model, normal;\nuniform mat4 view, projection;\n\nvoid main()\n{\n    TexCoords = aTexCoords;\n    FragPos = TransformToWorldSpace(model, aPos);\n    Normal = TransformNormalToWorldSpace(normal, aNormal);\n    gl_Position = TransformToClipSpace(model, view, projection, vec4(aPos, 1.0));\n}\n```\n\nThe language server immediately parses `common.glsl` in the background. You get instant autocompletion for all helper functions:\n\nAlong with full signature help and parameter hints while typing function arguments:\n\nYou can also press `F12` (Go to Definition) on functions like `TransformToClipSpace` to jump straight to their definition in `common.glsl`.\n\nIf your engine targets Vulkan, your shader requirements are different: you use descriptor sets `(set = X, binding = Y)`, push constants, and compile directly to SPIR-V.\n\nYou can tell the language server to validate specifically for Vulkan on a per-file basis using the `// @target: vulkan` directive at the top of your shader:\n\n```\n// @target: vulkan\n#version 460\n\nlayout(location = 0) in vec3 inPosition;\nlayout(location = 1) in vec3 inNormal;\nlayout(location = 2) in vec2 inTexCoord;\n\nlayout(set = 0, binding = 0) uniform CameraBuffer {\n    mat4 view;\n    mat4 projection;\n} camera;\n\nlayout(push_constant) uniform PushConstants {\n    mat4 model;\n} pc;\n\nlayout(location = 0) out vec3 outFragPos;\nlayout(location = 1) out vec3 outNormal;\nlayout(location = 2) out vec2 outTexCoord;\n\nvoid main() {\n    outFragPos = vec3(pc.model * vec4(inPosition, 1.0));\n    outNormal = mat3(pc.model) * inNormal;\n    outTexCoord = inTexCoord;\n\n    gl_Position = camera.projection * camera.view * vec4(outFragPos, 1.0);\n}\n```\n\nWith `// @target: vulkan`, the language server automatically switches the compiler backend to strict SPIR-V validation mode. It properly checks descriptor sets, push constant alignment, and Vulkan-specific qualifiers without complaining about missing OpenGL uniforms:\n\nYou do not even need to write per-file directives. You can configure the target API globally or per-workspace in your `settings.json`:\n\n```\n{\n  \"lsp\": {\n    \"glsl_validator\": {\n      \"initialization_options\": {\n        \"target_api\": \"vulkan\"\n      }\n    }\n  }\n}\n```\n\n*(You can always override this per-file using `// @target: vulkan` or `// @target: opengl` at the top of any shader).*\n\nGetting a fast, zero-configuration GLSL environment working across Zed and VS Code solved a huge daily pain point in my graphics workflow.\n\nNext up, I built the exact same zero-bloat workflow for **HLSL and Unity ShaderLab**, powered by Microsoft's DirectX Shader Compiler (`dxc`). I will be sharing a breakdown of that setup in a follow-up post.\n\nIf you want to try it out, you can find the project, installation guides, and full documentation on GitHub:\n\n👉 [glsl-extended on GitHub](https://github.com/zyr1on/glsl-extended)\n\nFeel free to open an issue if you encounter edge cases or have feature requests.\n\nWhat does your current shader development workflow look like? Which editor do you rely on most for writing shaders? Let me know in the comments!", "url": "https://wpnews.pro/news/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics", "canonical_source": "https://dev.to/semihozdmirr/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics-59di", "published_at": "2026-09-19 21:41:28+00:00", "updated_at": "2026-09-19 22:24:27.093634+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Zed", "VS Code", "GLSL", "Vulkan", "OpenGL", "glslang", "Rust"], "alternates": {"html": "https://wpnews.pro/news/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics", "markdown": "https://wpnews.pro/news/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics.md", "text": "https://wpnews.pro/news/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics.txt", "jsonld": "https://wpnews.pro/news/modern-glsl-shader-development-in-zed-vs-code-zero-setup-real-time-diagnostics.jsonld"}}