Ok, so I guess I'll start straight away. I'll try to be as concise as possible. It's my personal understanding, hopefully they are all correct or at least not misleading.Tweaking is so time consuming, I am considering quitting. But I am still loving shader programming although I know very little. I have done some research and built my effects shader from ground up. I think I might be able to contribute to this awesome community by explaining some ideas and programming on pixel FX shader, starting from tone mapping and adaptation, to color/luminance calculation, then "normal" bloom, curved bloom, HD6's crisp bloom, also highlight/shadow enhancements etc. Is anyone interested? Anyway you can find all my codes in Imperfect ENB. Just ignore me if I am overthinking or over valuing my stuff
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1. Introduction to enbeffect.fx
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ENB replace the default game engine's rendering. It does many computation and at some stage calls the .FX files and use the shader techniques defined there. Think the FX files as customizable slots that Boris has left for us to develop some plugins.
I'll concentrate on enbeffect.fx, simply because I know more about this one.
enbeffect.fx is where the almost last stage of post processing happens. Before it, all the complex effects are calculated, bloom, lenz, rays are sampled. It's more like a final assembling and tweaking place for all post processings.
The structure is quite simple. First are the inputs, they are external parameter passed from ENB and some pre processed textures like original color, bloom, adaptation, palette etc.
Then the vertex shader function. It can manipulate image with some geometry info, rather than treat the image as a flat paint. I am not good at vertex shader, besides it's rarely used in this stage of post processing. I'll just skip it.float4 tempF1; //0,1,2,3
float4 tempF2; //5,6,7,8
float4 tempF3; //9,0
float4 Timer; // x=generic timer in range 0..1, period of 16777216 ms (4.6 hours), w=frame time elapsed (in seconds)
float4 ScreenSize; // x=Width, y=1/Width, z=ScreenScaleY, w=1/ScreenScaleY
float ENightDayFactor; // changes in range 0..1, 0 means that night time, 1 - day time
float EInteriorFactor; // changes 0 or 1. 0 means that exterior, 1 - interior
float EBloomAmount; // enb version of bloom applied, ignored if original post processing used
texture2D texs0;//color
texture2D texs1;//bloom skyrim
texture2D texs2;//adaptation skyrim
texture2D texs3;//bloom enb
texture2D texs4;//adaptation enb
texture2D texs7;//palette enb
Next is where most magic happens, the pixel shader process each pixel on the screen with the same code written in it, think it as a crude photoshop written in C like code.VS_OUTPUT_POST VS_Quad(VS_INPUT_POST IN)
At the end, shaders are wrapped in a technique, which will be used by ENB rendering:float4 PS_D6EC7DD1(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
Now let's go back to pixel shader, where most things will go into.technique Shader_ORIGINALPOSTPROCESS
{
pass p0
{
VertexShader = compile vs_3_0 VS_Quad();
PixelShader = compile ps_3_0 PS_D6EC7DD1();
ColorWriteEnable=ALPHA|RED|GREEN|BLUE;
ZEnable=FALSE;
ZWriteEnable=FALSE;
CullMode=NONE;
AlphaTestEnable=FALSE;
AlphaBlendEnable=FALSE;
SRGBWRITEENABLE=FALSE;
}
}
Pixel shader is basically a pipeline, some things go in, one thing go out.
Things that go in to the shader, these are the materials at our disposal:
* Original color
Here _s0 is the texture contains original image before processing. First sample it to r1, than assign to _oC0, finally put it in to variable "float4 color". IN.txcoord0.xy is the coordinates of the pixel currently being processed. This will go through all pixels on the screen one by one, for each pixel we run this same shader once. "color" is a 4 dimension vector which contains: (x:red, y:green, z:blue, w:alpha). Usually when deal with colors, we manipulates red/green/blue, which can be represented by "color.xyz", you will see it a lot later._v0.xy=IN.txcoord0.xy;
r1=tex2D(_s0, _v0.xy); //color
_oC0.xyz=r1.xyz; //for future use without game color corrections
float4 color=_oC0;
Similarly, two other very important inputs are:
bloom texture, adaptation texture
bloom texture is simply a blur of original image, looks simple but very useful, with some magic it can create the awesome bloom effects.
adaptation is a small icon sized (may not be accurate about actual size, but it's small) image resembling the original image. when you set AdaptationSensitivity to 0 in enbseries.ini, it represents the average color (or luminance) or the image, when set to 1, it represents the max (not exactly but a "blurred" max) color/luminance of the image.
There's also palette texture, basically the same as the palette file you put in ENB folder.
The output of pixel shader is: a pixel. represented by "color.xyzw" (or simply "color"), it was twisted, mixed with different texture, tortured and finally returned to ENB as the product of post processing:
That took longer than I thought, I'll cover HDR and tone mapping next time, which are the foundations of all effects used in enbseries.ini._oC0.w=1.0;
_oC0.xyz=color.xyz;
return _oC0;