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.
| Table of Contents |
|---|
Basic Shader Overview
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 diffuse.
How Does NWN Load a Shader
There are several entry points for shaders:
- To load a custom shader for custom content you need to add a customshaderVS and customshaderFS line in the MTR file for that specific texture. There used to be an old way to do this in TXI files as well (but don't use TXI files any more!)
- You should be able to do this for most textures in the game. Exceptions may be things like GUI textures which are a bit odd (they replace a given texture reference internally) but it is worth experimenting. Skies for instance do not have a specific shader file but the MTR option works fine for them.
- To use some shader things like Standard material inputs then only specifying a renderhint is required
- The game has several default ones loaded - some are only loaded when certain Game Options are selected such as "Depth of Field" or "Sharpen". Others are loaded as general stock ones for different models.
See below for what stock shader files there are and where they are used.
Generally you don't want to override the games default shaders; need to edit in here when tested since it might only be possible to do at game launch.
Stock Shader Files
Shader files are generally in the base_shaders.bif file - extract this most easily with NWN Explorer. Up until relatively recently the shader files may have undergone significant changes and were included in the /ovr folder in the game installation directory. These were folded back into the bifs later.
From looking at them there are not many comments and no easy to follow examples for new modders. You probably want to learn about shaders - an old tutorial where some of this is from is here and hopefully a few tutorials will be added to this wiki in time.
The game ships with a set of stock shaders. Many of these emulate behaviour of the old 1.69 NWN engine. First step in modifying one of the stock shaders is figuring out which one is used for the model in question. The shaders mostly follow a naming scheme based on what they take as input:
- lit means the shader uses dynamic lighting
- _sm means the object is sphere-mapped,
- _cm means the object is cube-mapped
- t means the shader has texture coordinates
- c means the shader is given a specific color by the engine
- v means the (vertex) shader gets the vertex coordinates
- _ls means the shader is using long-distance fog
- _sk means the shader is using the m_bones skeleton
- fb tends to be post-processing game options
A list of standard shaders included in the game files and what they're used on - when someone gets around to fully testing.
...
No comments. PLT file related, from Merricksdad:
The shader code is only 4 lines long.
The first line gets the black and white color for the uv coordinate.
The second line replaces the green channel with the correct row from the provided color scheme data. This converts the black and white data to new UV coords into the palette file
The third line just fetches the final color from the palette file.
The last line just finishes setting the color for the fragment.
...
Fragment shader using color input with long-distance fog. The long-distance fog is useful for shadows: it needs to fade out unless it's on the top portion of the screen.
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
There are several entry points for shaders:
- To load a custom shader for custom content you need to add a customshaderVS and customshaderFS line in the MTR file for that specific texture. There used to be an old way to do this in TXI files as well (but don't use TXI files any more!)
- You should be able to do this for most textures in the game. Exceptions may be things like GUI textures which are a bit odd (they replace a given texture reference internally) but it is worth experimenting. Skies for instance do not have a specific shader file but the MTR option works fine for them.
- To use some shader things like Standard material inputs then only specifying a renderhint is required
- The game has several default ones loaded - some are only loaded when certain Game Options are selected such as "Depth of Field" or "Sharpen". Others are loaded as general stock ones for different models.
See below for what stock shader files there are and where they are used.
Generally you don't want to override the games default shaders; but if you have to you'll also have to gate what version you're loading for since engine changes can render older shaders inoperable on newer versions of the game.
Stock Shader Files
Shader files are generally in the base_shaders.bif file - extract this most easily with NWN Explorer. Up until relatively recently the shader files may have undergone significant changes and were included in the /ovr folder in the game installation directory. These were folded back into the bifs later.
From looking at them there are not many comments and no easy to follow examples for new modders. You probably want to learn about shaders - an old tutorial where some of this is from is here and hopefully a few tutorials will be added to this wiki in time.
The game ships with a set of stock shaders. Many of these emulate behaviour of the old 1.69 NWN engine. First step in modifying one of the stock shaders is figuring out which one is used for the model in question. The shaders mostly follow a naming scheme based on what they take as input:
- lit means the shader uses dynamic lighting
- _sm means the object is sphere-mapped,
- _cm means the object is cube-mapped
- t means the shader has texture coordinates
- c means the shader is given a specific color by the engine
- v means the (vertex) shader gets the vertex coordinates
- _ls means the shader is using long-distance fog
- _sk means the shader is using the m_bones skeleton
- fb tends to be post-processing game options
A list of standard shaders included in the game files and what they're used on - when someone gets around to fully testing.
| Shader Filename | Type | Descriptive Name | Includes | Usage in Game | Notes |
|---|---|---|---|---|---|
| fs.shd | Fragment Shader | inc_standard | Looks to the the standard fragment shader when no texture, lighting or PBR maps are applied. | ||
| fs_beamvol | Fragment Shader | None | Related to beaming (light ray effects on tiles). | ||
| fs_cg_btn_col | Fragment Shader | inc_standard | No comments. cg_btn_col is the button to select "colour" - PLT colours for the player - at chargen. So something to do with that potentially. | ||
| fs_invalid | Fragment Shader | Invalid | None | Provides the RGB colour 1.0, 0.0, 1.0 (alpha 1.0) to the output. This is bright pink. | |
| fs_pltgen | Fragment Shader | PLT Generation | None | No comments. PLT file related, from Merricksdad: The shader code is only 4 lines long. The first line gets the black and white color for the uv coordinate. The second line replaces the green channel with the correct row from the provided color scheme data. This converts the black and white data to new UV coords into the palette file The third line just fetches the final color from the palette file. The last line just finishes setting the color for the fragment. | |
| fs_shadowplane | Fragment Shader | Shadow Plane | inc_standard | No comments. Presumably shadow related. | |
| fs_shadowvol | Fragment Shader | Shadow Volume | None | "This is just for debugging output." presumably for debugging shadow volumes | |
| fsc | Fragment Shader | Colour | inc_standard | Fragment colour matches the vertex colour, no PBR and no Textures - shadows possibly use this. | |
| fsc_ls | Fragment Shader | Colour, Long Distance Fog | None | Fragment shader using color input with long-distance fog. The long-distance fog is useful for shadows: it needs to fade out unless it's on the top portion of the screen. | |
| fsc_sm | Fragment Shader | Colour, Sphere Mapped | None | ||
| fsfbdof | Fragment Shader | Depth of Field | None | Depth of Field shader. Depth of field is generally used for screenshots only since it makes everything but the camera focus point blurry. | |
| fsfbgam | Fragment Shader | Gamma | None | Sets the gamma. The gamma option in-game defaults to 2.2 | |
| fsfblvls | Fragment Shader | Dynamic contrast shader | None | Adds contrast to the current scene with a dynamic midpoint based on area lighting. | |
| fsfbpostpr | Fragment Shader | Post Processor | inc_postpr | See include file "inc_postpr". Does this in fact do all the post-processing shader effects and the other files are legacy? or do the other files do it and this is legacy? or a mix? | |
| fsfbshrp | Fragment Shader | Sharpen | None | Sharpens the image based on differences in luma. | |
| fsfbssao | Fragment Shader | SSAO | None | Screen Space Ambient Radiosity, Highlight and Occlusion for NWN:EE. Mimics the ambient light radiation and occlusion that would realistically affect the appearance of objects in a scene, adding to the sense of depth and detail. | |
| fsfbvib | Fragment Shader | Vibrance | None | Seems to find the min and max colour and saturates it by the difference. Unknown if used in game. | |
| fsglu | Fragment Shader | inc_standard | |||
| fsgrass | Fragment Shader | Grass | inc_standard | Presumably the shader for grass | |
| fslit | Fragment Shader | Lighting | inc_standard | No textures, but has fog, lighting and keyholing, so general lighting shader? | |
| fslit_nm | |||||
| fslit_sm | |||||
| fslit_sm_nm | |||||
| fsparticle | Main entrypoint for emitter particle planes. Does not handle chunk emitters. | ||||
| fst | Main entrypoint for many older GUI panels, as well as the skybox. Does not include debug GUI. NewUI not tested. | ||||
| fstc | |||||
| fstc_sm | |||||
| fst_sm | |||||
| fswater | Main entrypoint for fancy water applied via TXI. Can also be called directly to make custom fancy waters, but will not work exactly the same. | ||||
| fs_beamvol | |||||
| fs_cg_btn_col | |||||
| fs_invalid | |||||
| fs_pltgen | |||||
| fs_shadowplane | |||||
| fs_shadowvol | |||||
| inc_common | Includes the most common functions and sets up variables used by all shader subtypes. Also preps communication with textures and negotiates flags that were no explicitly set. Calls inc_config. | ||||
| inc_config | Sets up some specific shader flags, such as which shader quality to use, and some stuff about lighting. Called by inc_common. | ||||
| inc_keyhole | Sets up keyholing functions. Called by inc_standard. | ||||
| inc_lighting | Includes functions for rendering light from point sources and from certain types of self-illumination maps. Calls inc_material. Called by inc_standard. | ||||
| inc_material | Includes functions for deriving numeric values from textures. Handles things like specular and roughness mapping, as well as texturespace occlusion based on height mapping. Calls inc_lighting. Called by | ||||
| inc_postpr | Includes the main hub file for all of the post processing functions. If options are set to run those functions, it will include the other postpr includes and run their functions. | ||||
| inc_postpr_dof | Includes functions to draw depth of field effects. | ||||
| inc_postpr_dyn_c | Includes function for screenspace dynamic contrast correction. | ||||
| inc_postpr_gam | Includes functions for screenspace gamma correction. | ||||
| inc_postpr_shrp | Includes functions for screenspace sharpening. | ||||
| inc_postpr_vibr | Includes functions for screenspace vibrance enhancement. | ||||
| inc_standard | Includes functions to run the standard shader. Calls inc_lighting and inc_keyhole. Called by shader entrypoint files like fs_lit and vs_lit. | ||||
| inc_water | Includes functions to produce fancy water. Includes code for handling things like wind sources and using the built-in noise texture. Called by fs_water. | ||||
| vsfbdof | |||||
| vsfbgam | |||||
| vsfblvls | |||||
| vsfbpostpr | |||||
| vsfbshrp | |||||
| vsfbssao | |||||
| vsfbvib | |||||
| vsglu | |||||
| vsgrass | |||||
| vslit | |||||
| vslitc | |||||
| vslitc_nm | |||||
| vslitc_sm | |||||
| vslitc_sm_nm | |||||
| vslitnotex | |||||
| vslitnt_sm | |||||
| vslit_nm | |||||
| vslit_sk | |||||
| vslit_sk_nm | |||||
| vslit_sm | |||||
| vslit_sm_nm | |||||
| vsparticle | Main entrypoint for emitter particle planes. Does not handle chunk emitters. | ||||
| vsv | |||||
| vsvc | |||||
| vsvt | Main entrypoint for many older GUI panels, as well as the skybox. Does not include debug GUI. NewUI not tested. | ||||
| vsvtc | |||||
| vsvt_sm | |||||
| vswater | Main entrypoint for fancy water applied via TXI. | ||||
| vswaterc | |||||
| vs_beamvol | Includes code for projecting beaming volumes. | ||||
| vs_invalid | |||||
| vs_pltgen | |||||
| vs_shadowplane | |||||
| vs_shadowvol | Includes code for projecting shadow volumes. Not to be confused with texturespace or screenspace shadow-ing from occlusion. |
If you're not sure which stock shader is used for a given model, you can:
a) Load a GL debugger/tracer and check glUseProgram() calls
b) Modify each FS to show a different solid color, then check the color of the model, eg:
| Code Block |
|---|
void main()
{
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
} |
Define / Default Values List
(Note should move this to Shader Engine Support at some point for single source of truth)
This is a list of defines. You can use them in if statements:
| Code Block |
|---|
#if NAME == VALUE
#endif |
To block out sections of code based on these. The main one to note here that isn't used in the games default shaders may be BUILD_REVISION which is 34, 35, 36 etc. and useful for making shaders compatible with older game versions.
Since these are clientside fields you can retrieve some of them via. script commands but not necessarily all of them.
The Version Added field only is filled in if the value wasn't available to begin with when shaders were first added.
| 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 |
Shader Uniforms
These are essentially variables in a sense of they are determined at that particular frame for a particular pixel depending on what the engine has there at the time. These need adding if not already defined in the shader (many default game shaders define these already).
See Shader Engine Support for more details on this and a complete list of what is available.
To utilise them sometimes they need defining since a default include file isn't using them.
For instance vCustomColor is usable by this style of code, setting in the top level that you want the uniform attribute vCustomColor to be sent to the shader file and then allocate it to the VertexColor variable, which just replaces the contents of that output first sorted in inc_standard and changed with ApplyStandardShader() with the variable contents of vCustomColor directly.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define VERTEX_COLOR 1
#include "inc_standard"
attribute vec4 vCustomColor;
void main ()
{
ApplyStandardShader();
VertexColor = vCustomColor;
} |
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define VERTEX_COLOR 1
#include "inc_standard"
void main ()
{
FragmentColor = vec4(1.0);
SetupStandardShaderInputs();
ApplyStandardShader();
gl_FragColor = VertexColor;
|
...
Screen Space Ambient Radiosity, Highlight and Occlusion for NWN:EE. Mimics the ambient light radiation and occlusion that would realistically affect the appearance of objects in a scene, adding to the sense of depth and detail.
...
If you're not sure which stock shader is used for a given model, you can:
a) Load a GL debugger/tracer and check glUseProgram() calls
b) Modify each FS to show a different solid color, then check the color of the model, eg:
| Code Block |
|---|
void main()
{
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
} |
SHD Format
Shaders are relatively standard text files, written in c-like code. See Shader Engine Support for what shader uniforms and defines are available from the engine.
Generally each shader you'll want to use for model texture changes will include "inc_standard". They also will use #define to setup what the shader file will do. For instance fslit_nm has the normal, specular, roughness height and self-illumination maps set (the "nm" means "normal maps"):
...