Shaders are SHD files loaded by the NWN:EE engine to perform graphical changes to textures. There are vertex (per-vertex) and fragment (per-pixel) shaders that are run by the game. They are OpenGL GLSL based, a c-like language.
Using shaders you can alter the graphical presentation of textures and models. See Shader Engine Support for details on how the engine pushes uniforms and default defines to the shader files.
...
Shaders are text files loaded by the engine and compiled onto the GPU for changing how textures and models look at run time. Note that Mobile is mobile GLSL so slightly different. Additionally NVIDIA, Intel and AMD may be compatible with shaders in different ways. Worth testing on every platform you can.
There's really just a few real separate shaders. The vs and fs prefixed files just have key flags that get turned on and off to make the shaders do different things, like yes or no use normal, spec, roughness, etc. In general the VS ones get your vertex properties all set up. The FS ones basically paint your screen pixels using the information gleaned from the nearest verts. And then there's a post-processing layer that does your stuff like sharpen and screen depth type stuff. And finally there's your shadow and beaming projectors.
...
If you want to add something like an alpha override instead of just putting the alpha in the diffuse alpha layer like you should, then you can pick a texture # between 6 and 10 and add that texture in there. Then write a super simple shader mod to just get your alpha from that B&W map instead of the diffuseinstead of the diffuse.
Debugging Shaders
Errors with shaders tend to make everything pink (eg the fog or object you're applying a shader to), or it fails to load and everything goes black or other similar looking problems occur.
You can find compile errors in the engine logs; these will show a line ID of a problem, however due to the way #include works (see below) it will not necessarily make any sense.
Additional debug is more difficult - eg outputting what values you are using to a console - but you can do outputs such as changing changing the fragment colours specifically based on the values you're generating.
You can check the performance of shaders with Tracy especially if you turn vsync off and do a before/after picture of FPS with the same scene being rendered.
Mobile / Different Versions
As noted Mobile GLSL is sometimes very different from desktop GLSL, and even in the base engine shaders it is gated with a #if MOBILE gate. It is recommended to test both on mobile and desktop shaders and gate any problematic desktop things so mobile do not activate them (else mobile likely get either pink everywhere - ie shaders didn't compile - or some error in the code mobile doesn't support).
Different game versions also have very different shaders. To work around this and support multiple versions of the game you must supply valid versions for every overriden or new shader file, so duplicating the include files (eg having "inc_standard.shd" kept as the base game one, but "inc_standard35.shd" be one for version 8193.35 might be necessary). Alternatively keep only one version of the game possible to use to join and kick those not on it (eg; GetPlayerBuildVersionMinor in the join script).
For singleplayer modules it is impossible to stop someone loading it so recommending the minimum version or warning them with GetPlayerBuildVersionMinor usage in a script may be recommended.
Include Files
Compared to usual GLSL shaders you can use #include to have include files. It can help organise your code and any similar code can be put into include files instead of the base shader file. The main thing is when #include is found it will copy the entire file contents to where it is defined, it won't act like a proper include file and - say on a compilation error - show the file name and line in that file, instead it basically generates several thousand lines of file then compiles / runs it.
One major thing that won't work with them is #if blocks - #include files are always included - so make sure you instead #if block all the include if it is specific to a particular platform/uniform/define.
How Does NWN Load a Shader
...
| Define Name | Example | Compatibility | Notes | ||
|---|---|---|---|---|---|
#version 300 es precision highp float; | Mobile | If mobile this will be defined (open GL version etc.) | |||
#version 330 core #define mediump #define lowp #define highp | Non-Mobile | If not mobile this will be defined (open GL version etc.) | |||
#define MAX_NUM_LIGHTS | 3 | Same as the client setting for Maximum Number of Lights | |||
#define MAX_NUM_BONES | 64 | This can vary across some versions but now is standardised to 64. | |||
#define GAMMA_CORRECTION | 1 or 0 | Gamma Correction game setting on/off | |||
#define FRAGMENT_LIGHTING | 1 or 0 | Fragment Lighting game setting on/off | |||
#define SHADER_QUALITY_MODE | 0, 1 or 2 | Shader quality mode game setting (low, medium, high) | |||
#define KEYHOLING_ENABLED | 1 or 0 | Keyholing game setting on/off | |||
#define SHADER_DEBUG_MODE | 1 or 0 | Shader debug mode on/off | |||
#define BUILD_VERSION | 8193 | This is very unlikely to change for MP servers - since the server version must match the client version - but can perhaps matter for SP modules if they want to support older versions. You can still safely use this with the BUILD_REVISION to get a more accurate picture. | |||
#define BUILD_REVISION | 35 | This is the incremental patch number at the end of the patch version. Omits the last part, eg: 1.87.8193.35.5 won't show the 5, just be "35" | |||
#define NO_DISCARD | 1 or 0 | Discard enabled or not | |||
#define POSTPROCESSING_TYPES_ENABLED | Bitmask value | See inc_postpr.shd for the bitmask values below. If 0 none are enabled. If post-processing is enabled (either the default-on option to enable post-processing shaders regardless of them being enabled, or one or more is enabled) the vsfbpostpr and fsfbpostpr shader files are run.
Note for some things it simply runs additional shader files explicitly;
| |||
#define varying | out or in | out = is the for types GL_VERTEX_SHADER and in = not vertex shader | |||
#define attribute | in | ||||
#define texture2D | texture | ||||
#define textureCube | texture | ||||
#define gl_FragColor compat_glFragColor out vec4 compat_glFragColor; | Only added if a fragment shader |
...