Version 1.1 composite package includes shaders for Sprite Animations, Texture Translation, Basic Distortion, Three-channel Self-Illumination, Environment Map Mixing with Alpha, Minor Fire-related Effects (attenuate + redshift), Fancy Water, and Fancy Fog.
About the Kit
The base kit includes fragment and vertex shader replacements for the following texture combinations:
Those are meant to be full replacements for the EE shaders offered in Soren's NWN:EE Shader Prototypes package. If you add them to a hak, they'll be global overrides of the base shaders. Doing that will have the main effect of modifying how your area fog works.
The demo module comes complete with a hak file. This also means the hak file includes shaders which may upset the toolset. You might want to dump the shaders out of the hak into your development directory. Leave the rest in the hak until you're sure you can use it. Be careful of FS* shader overwrite if you have important other shaders in your development folder. You may also want to move the FXPA_ material files to the development folder. You can then delete the SHD and MTL files from the hak and open the module without warnings. Not a requirement, but should help if the toolset actually crashes instead of just giving warnings.
This v1.1 demo module showcases Fancy Water, Sprite Animation, Light Flicker, Fancy Fog, Three-channel Self-Illumination, Texture Translate, Environment/Alpha Mixing and Fire Effects. It does not showcase the Crystal/Glass shader yet.
About Fog
I wasn't happy with fog increasing as I zoomed out. In a game like this, being turn based and often DM-driven, I wanted a system of fog that was unit-based rather than camera based. By modifying the base fog system, I was able to tap into the per-pixel GL fogEnd and fogStart values and backward calculate area fog strength by negating the effects of camera distance from the PC's position. That allowed me to manipulate the area appearance using the area fog properties.
Using the fog strength, I devised a system where area fog strength represented a percent, making fog strenght 30 = 30% fog density and so on. I then added a level of complexity by allowing the user to specify a maximum or base fog height, and then allowing that fog height to also scale by fog density.
Since the effects of lighting can be modified by fog both locally and at distance, I then added a bit of modification to light rendering so that fog gets glowy nearest the lights.
Keep in mind, this is all an illusion. There is no fog particle system in NWN. As with the original fog, I'm simply painting the surfaces with a value representative of some atmospheric transmicity percent, making it look like there is actually fog. Since fog is applied before some later shader types (such as Ambient Occlusion) dense fog may appear cartoony when those later effects are applied. I suggest turning off Ambient Occlusion, at least in fog areas, or at least until the occlusion order is placed before fog rendering.
In this system, fog is much thicker at high values than OC fog. A tiny bit of saturation goes a VERY long way. I highly suggest turning down your fog saturation if you want to use the super dense fog over 70%, otherwise your area base luminance will be increased drastically.
Low fog percents from 30 to 0 have very little effect up close, but can be seen at a distance. Such low values are effective mainly at covering the dark ground in edgetiles which are usually darkened out by the engine.
Keep in mind that fog is applied per shader, meaning it is applied per TYPE of mesh. For instance, one type of mesh requires the FSLIT shader because it doesn't have fancymapping. This in turn means that any modification to fog systems must be at least area-wide and span all types of texture and fancymap combinations. At best, any modification to fog must be global to also affect all characters and placeables equally. Because of that, fog modifications are found in a shared system between all shaders.
If you want to modify base fog effects, I've moved all the fog stuff to the file MD_INC_FOG. At the top of that file, you'll find the following customizable keys:
// turn on fancy fog // if disabled just do OC fog #define FOG_FANCY 1 // set the maximum opacity of fog // good for debugging distant objects but still using fog // normal fog max opacity is 1.0 const float FOG_OPACITY_MAX = 1.0; // change fog rendering so it becomes player-relative // OC is camera-relative* #define FOG_PLAYER_CENTERED 1 // set the maximum height of area fog // this makes fog heavy instead of linear // supply values in meters above or below module zero-level // use negative values to only fill pits (not tile-relative) const float FOG_HEIGHT_MAX = 3.0; // change how fog is calculated // set this to use FOG_HEIGHT_MAX as a ratio instead of cap // if FOG_HEIGHT_MAX = 1.0, then total fog height will be set to the fog strength (in meters) #define FOG_HEIGHT_RELATIVE_TO_STRENGTH 1 // set this to force fog darkening vs dark textures // disable to have uniform thickness of fog #define FOG_RESPECT_DARKNESS 1 // set this to force negative fog to represent a sandstorm #define FOG_SPECIAL_IS_SANDSTORM 1
* if FOG_PLAYER_CENTERED is disabled, FOG_HEIGHT_MAX should probably be modified to about 14000.0 to make sure the fog affects the entire area as in OC fog.
As of v1.2, fancy fog has been greatly improved to:
About Special Fog Functions
As of v1.2 I've added a special fog function potential. Normally, users use a range of 0 to 255 (integer value) fog, which represents something similar to a percent in the OC fog shader. My fancy fog instead uses integer values directly as percents, so 0 to 100 is actually 0 to 100%, meaning 155 additional values were now open for something else. So what I did was take the other values and make them do something else.
Because the strength of fog differs when you are zooming in and out, I have to leave a gap. The speed at which you zoom in and out defines how wide the gap should be. What I ended up doing was setting a gap of 25 values. This means that I have two 100% scales now imbedded within the range of 0 to 255. The first set is 0-100, as expected, but the second set starts at 125 and goes to 225.
Now if you supply a value of 125 or greater, my fog system will think you entered a value of X-125, but it will turn on a special fog type. If no special fog type is written in, then it just draws normal fog again.
I've supplied a proof of concept fog called "Sandstorm". If FOG_SPECIAL_IS_SANDSTORM is enabled, then the fog will be rendered with grainy noise, and the area overall brightness will be somewhat reduced. Areas that get fully filled in by sandstorm may appear exactly the same as normal fog (just darker), but areas that are only partially filled will have a blowing sand appearance.
While this appearance needs some improvement, this is just a proof of concept stage showing you what you can now do. Other examples you might write yourself include but are certainly not limited to:
Remember, at the moment, setting area fog applies to all players in the area, so you shouldn't write fog effects for a single character unless you're making a single-character module.
About Other Uses
The rest of the shader can be copied off to other files for specific use. Current other uses packed into the shader base include: sprite rendering, texture translation, fancy water options, and a few things I'm working on for textures that are supposed to be lights, or otherwise glow without using self-illumation.
The current package includes version 0.10 of my Wavey Water Shader, and the primary release of my Sprite Fire Shader. I've also included the functional but not perfected crystal and glass shader, which allows light through a glassy surface to be drawn on the opposite faces of the mesh, somewhat modified.
At the top of the FS* shaders you'll find these keys. To use the FS* shader as one of these, just copy the file you need and set one of these equal to 1 to convert the shader into that subtype. Don't mix the shaders, or you'll probably break it.
// CHOOSE ONLY ONE OF THESE AT A TIME // used for rendering water waves on the surface of a mesh #define WATER_SHADER 0 // used for transparent obects that you want light to pass through #define CRYSTAL_SHADER 0 // used to animate sprite sheets #define SPRITE_SHADER 0 // used to translate/slide textures over time #define SLIDE_SHADER 0 // used to mimic TXI distortions #define DISTORTION_SHADER 0
About Wavey Water
Wavey water is wind-driven, so it requires a modified STEP_SPLASH model which emits more wind longer (4 s).
The footstep model can also double as a wind placeable if you add it to placeables.2da, or even as a visual effect with customizable duration. This will let you mimic wind events at any duration.
The purpose of this package is to show users how water can be animated using the new shaders, and without actually using the bumpyshiny water feature in the Advanced Options menu.
I've tried using MTR files to simply replace existing OC water textures, but it doesn't work so simply. To use this in OC tilesets you'll have to make some fake empty TXI files to undo the animations and other TXI effects. One for each water texture you are replacing.
As mentioned, this is not a replacement for the standard water shader. Instead, this is called via MTR material files (see packaged MTR files). This shader lets you make any texture into a fluid.
The options from my Wavey Water shader remain mostly unchanged from version 0.10. The only difference is in the constant naming scheme. Instead of GLOBAL_WIND*, constants have been renamed WIND_GLOBAL*.
// multiply POINT wind wave length (approximately meters) // suggested range 0.8 to 1.6; // higher wavelength looks like really cold almost frozen water // lower wavelength looks like rubbing alcohol to quicksilver // **Note that changing wavelength also automatically changes the ripple radius const float WATER_BASE_WAVELENGTH = 1.0; // multiply GLOBAL wind wave length (approximately meters) // suggested range 0.8 to 1.6; const float WATER_GLOBAL_BASE_WAVELENGTH = 1.0; // multiply wave speed (approximately meters per second) // effectively changes the frame speed of wave animations // **Note that changing wavespeed also automatically changes the ripple radius and wavelength const float WATER_BASE_SPEED = 1.0; // change how far wave ripples travel (a coefficient -> use fractions) // (Changes distance, not duration. Duration is set in the footstep model emitter.) const float WATER_BASE_RIPPLE_RADIUS = 1.0; // change how far away pointsource waves will be drawn (in meters) // does not affect global wind const float WIND_FALLOFF = 50.0; // change how far away global waves will be drawn (in meters) // does not affect global wind const float WIND_GLOBAL_FALLOFF = 100.0; // change how far away global waves will be drawn // if set, the global wind falloff will be set exactly to the fog cutoff distance const bool WIND_GLOBAL_FALLOFF_MATCHES_FOG_FALLOFF = true; // change how far away pointsouce waves will be drawn // if set, the pointsource wind falloff will be set exactly to the fog cutoff distance const bool WIND_FALLOFF_MATCHES_FOG_FALLOFF = true;
About Animating Sprites
The goal was to show how a shader script could substitute for some particle effects. The benefit of using a shader to animate a sprite sheet is that you have more control over the blending mode. In addition, shaded meshes can be seen through transparent materials, which is currently not possible with emitted particle fire. This gives you the ability to make animated fire behind a glass window, such as on homes, or in a lantern.
Within the customization section near the top of any of the FS* shader files, you'll find the following customizable keys:
// Sprite frames per second const int SPRITE_FPS = 16; // Sprite sheet number of columns const int SPRITE_GRID_X = 4; // Sprite sheet number of rows const int SPRITE_GRID_Y = 4; // Sprite sheet first frame (1-based) // Ex. if you want to skip the first frame, then set this value to 2 const int SPRITE_FIRST_FRAME = 1; // Sprite sheet last frame (1-based) // Ex. if you have a sheet of 16 sprites, but only want to use the first 12, then set this value to 12, and set first frame to 1 const int SPRITE_LAST_FRAME = 16; // Sprite frames will be picked for play at random const int SPRITE_RANDOM_PLAYBACK = 0; // Sprite will be sent for tinting using associated normal and specular maps // Use "_MIXING" factors below to modify // If you don't want full shading, but still want fog, just apply fog // !!! setting flame textures to tintable will make them darker !!! // !!! use double saturation property to enforce brightness !!! const int SPRITE_IS_TINTABLE = 0; const int SPRITE_APPLY_FOG = 0; // Doubles the saturation of the sprite color // For use with tintable sprites, but where you still want sprites to be visible at night over long distances // Good for use with lights, flares, and fires const int SPRITE_DOUBLE_SATURATION = 0; // Sprite blend mode // !!! Not the same as TXI or emitter blend modes !!! const int SPRITE_BLEND_MODE = 0; // 0 = use texture alpha channel // 1 = draw any non-zero value // 2 = alpha equals luminance // 3 = alpha equals sqrt luminance // 4 = alpha equals pi-luminance, brightness is multiplied // 5 = alpha equals pythagorean luminance <--very useful for black-background sprites without alpha channels
About Texture Translation
The goal here was to replace Animesh and TXI with the shader. I've made it so you can slide textures either across the UV coordinates of the mesh, or you can replace the UV coordinates with a scaled copy of the mesh's world XY position.
// set the texture translation speed in 1xUV/second // if SLIDE_USING_GLOBAL_POSITION = 1 then speed is in meters/second // note that UV/second will mean variable speed across some faces not properly UV-mapped const vec2 SLIDE_SPEED = vec2(0.5, 0.5); // Set this flag to use global XY rather than UV coordinates // Setting this flag will cause SLIDE_SPEED_* varables to represent direction of flow across the module/"world" // Default is to use mesh-relative UV coordinates #define SLIDE_USING_GLOBAL_POSITION 0 // set the size of the global repeating texture in meters const float SLIDE_GLOBAL_TEXTURE_SIZE = 2.5;
About Fire Visual Properties
This is just a small set of properties I've tinkered with for textures that are supposed to act like light, but that don't use self-illumination. I'm very likely to add a bunch more of these for different light types.
// used for glowing things and attenuating the color of a sprite/decal over distance as if it was a light
#define FIRE_SHADER 0
// Treats the fire sprite/decal as a light that will attenuate with distance from camera
const int FIRE_ATTENUATE_BY_DISTANCE = 0;
// If the fire is attenuated by distance, should it also be redshifted?
const int FIRE_APPLY_REDSHIFT_BY_DISTANCE = 0;
About Distortion Effects
The goal behind distortion effects is to eventually replace TXI functions like Arturo and Water animation. This is experimental and not finished. Right now (v 1.1) water distortion somewhat works. Don't get comfortable with with the current effects as they will definitely change.
Eventually I want to make this work with other shader types too (ie. sprite+distortion, glow-layer + distortion, specular-channel distortion, etc.)
// Set distortion type // 1 = Water // 2 = Random // 3 = Arturo // 4 = Fractal const int DISTORTION_TYPE = 1; const float DISTORTION_AMPLITUDE = 1.0; const float DISTORTION_WIDTH = 1.0; const float DISTORTION_SPEED = 1.0;
About Environment Map Mixing With Transparency
NWN hasn't really allowed for any ability to have both Environment Mapping and Transparency on the same mesh before EE. We've had to split the mesh, or even do texture overrides with TXI to force character units to have mixed visual effects.
Now you can just use texture layer 13 in a MTR file to supply two mixing factors. I want you to be able to have up to 100% transparency AND up to 100% environment mapping. I know you won't ever get to that level, but there will be times you might want something greater than 50/50, so I've given you the red and blue channels to work with in texture layer 13.
At the top of the shader files there is a key to turn on ENV_XPARENT_RATIO_MAP. The shader will then expect to find something in the MTR file for texture 13.
The green and alpha channels are not used.
To build a proper mixing map, you can take many approaches. One approach is to make separate R G B A channel layers and then use a channel combine function in an art program to build the final RGBA bitmap. It currently doesn't matter what is in your green or alpha layer at all.
Another approach is to use layers. Make three layers. Set the bottom layer to all black. Paint only red (hue 0) in the second transparent layer, and only blue (hue 169) in the third transparent layer. Use a channel mixing function on your blue layer such that a 100% red layer and a 100% blue layer should produce 100% magenta, not 50% purple. Such a function is sometimes called "SCREEN".
I offer you one other property:
// if this flag is set, texture 13 blue channel will be used as an override to texture transparency // ONLY WHEN AN ENVIRONMENT-MAPPED PIXEL IS ENCOUNTERED WITH LESS THAN 100% OPACITY // normally that layer would be multplied by the diffuse alpha layer, but that can cause transparency bugs // this allows you to gaurantee an alpha value over or under the internal 20% cutoff #define ENVMAP_MIXING_XPARENT_AS_OVERRIDE 1
About Three-Channel Self-Illumination (not quite Light Mapping)
I've opened up texture channel 12 for use as three-channel self-illumination. Soren's texture channel (5 I think) self-illumination is single-channel only. It doesn't allow you to make something glow differently at a distance than it appears up close. This option does. This option will eventually be combined with distortion and translation effects so you can animation the self-illumination on a mesh, and with proximity effects.
At the top of the shader files there is a key to turn on LIGHT_MAP. The shader will then expect to find something in the MTR file for texture 12.
The only channel currently not used in three-channel self-illumination is the alpha channel, but I may open that up too.
This isn't quite light mapping because it doesn't illuminate anything but the object it is applied to. It casts no light, and can't be picked up by the shader as a light source.
That being said, future versions will allow this channel to flicker like flames, animate, distort, and use additional fire effects. In fact, the concept portal placeable already uses additional fire effects of light attenuation per distance.
About Light Flicker Hacks
I've included a neat little system for hacking the RADIUS value of light nodes. Normally the light radius is in meters, but I figured why do we need partial meters? Just use whole numbers and us the fractional portion for something else. Since I can't currently get the light radius back directly from the GL shaders, I can at least backward calculate it from the GL attenuation factors. By doing so, I can reconstruct the tenths digit of the original light radius with some certainty. This allows me to offer NINE and only NINE tricks you can do with light.
Keep in mind that many of these things are better done with animations if you are making your own models. However, some of these can't be done without direct access to position and world timer per pixel, so they have to be done in the shader. If you can duplicate this in animation, then just do that. But for compiled models that already exist, I offer you this option.
Below is the current list of tricks, along with their tenths digit code.
This works almost 100% of the time on models with well-defined radius values and many trailing zeroes. It may not work very well for other radius values.
Where this doesn't work is with radiuskey animations. Any time your radius is animated, it will pass through all these tricks, and you're bound to hit lightning and arc welder for a split second. It's not pretty. But if you put this in lights that are always on, or have animation lengths of exactly 0.0 with only one radiuskey, it works just fine.
The problem with this is that it's a global effect because it must function while painting all types of objects shaded with any other shader in the kit. That means standard torches and darkvision may flicker when enabled or disabled unless otherwise modified. So, I've supplied a modified FX_LIGHT_CLR model with all the animation lengths set to 0.0 to reduce flickering in the case you want to use this. In addition, any fractional values were set to whole numbers.
It has only one key in the customization section, which is enabled for all the FS* shaders in the kit, just to show them off. If you want to disable this "feature/bug", you need to modify all the FS* shaders. In Notepad ++ you can just do an all files find/replace for "_FLICKER 1" and set it to "_FLICKER 0" if you unpack these to development folder.
// enable/disable flickering lights // set light radii in any mdl light node to achieve various effects // decimal values enable various effects, ie. ####.1 enables candle-like flicker #define LIGHT_ENABLE_FLICKER 1
About Additional Light-Related Keys
I've merged the light-mixing keys from the Sprite Fire and Wavey Water shaders. They now work on ALL the shaded mesh types.
// change how much ambient color goes into the TEXTURES (use values from 0.00 to 1.00) // increasing this value allows the sun/moon to tint water volume more const float LIGHT_AMBIENT_MIXING = 1.0; // change how much diffuse color goes into the TEXTURES (use values from 0.00 to 1.00) // increasing this value allows the sun/moon to tint water surfaces more const float LIGHT_DIFFUSE_MIXING = 1.0; // change how much specular color goes into the TEXTURES (use values from 0.00 to 1.00) // increasing this value allows the sun/moon to tint water surfaces more const float LIGHT_SPECULAR_MIXING = 1.0; // change how specular TEXTURES can be // increasing this value makes water more glossy // values up to 4.0 still seem reasonable for high noon on a clear summer day const float LIGHT_BASE_SPECULAR = 1.0; // add some color to the scene without having to change the texture file or area colors // suggested values from -1 to +1 (negative pulls out color) // example: sewer green water might be vec4(-0.05, 0.05, -0.05, 0.00); const vec4 EXTRA_COLOR_RGBA = vec4(-0.00, 0.00, -0.00, +0.00);
In v1.1 I've added a global key that allows you to not draw objects within a certain distance of the camera. This is experimental, and has some internal issues which don't allow theses shaders access to objects closer than 0.42 m. Hopefully those pixels can be opened up to the public later, so we can choose how to handle them ourselves. Right now they only paint the fog color, which can be really annoying if they're bright fog in an otherwise low-light area. This creates strobing if the camera strikes things like trees or rocks as you walk. The goal is to 100% kil that effect, but EE isn't quite there yet.
// set the shader minimum camera distance // Prevents seeing the "paper" of any mesh whenever the camera is too close to an object // Only works on textures which pass through the standard shader (might not work on some sprites) // Attempts to stop some of the flashing that happens when you walk between trees in the forest tileset // Does not fully work, and does not apparently happen for all video cards const float SHADER_MIN_DISTANCE = 0.45;
This is gold. I hope it launches a thousand shaders.
I have been watching this unfold since the very early test screens. Awesome that is had come from out of the shade and into the light!
FP!