Page 1 of 1

[HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 03 Jul 2015, 07:44
by kingeric1992
Just to get a better understanding on what happened in APPLYGAMECOLORCORRECTION in Skyrim enbeffect.fx
for DNI-K, looks final part.

First, here is the original code

Code: Select all

#ifdef APPLYGAMECOLORCORRECTION
	//apply original
    r0.x=1.0/_c2.y;
    r1=tex2D(_s2, _v0);
    r0.yz=r1.xy * _c1.y;
    r0.w=1.0/r0.y;
    r0.z=r0.w * r0.z;
    r1=tex2D(_s0, _v0);
    r1.xyz=r1 * _c1.y;
    r0.w=dot(_c7.xyz, r1.xyz);
    r1.w=r0.w * r0.z;
    r0.z=r0.z * r0.w + _c7.w;
    r0.z=1.0/r0.z;
    r0.x=r1.w * r0.x + _c7.w;
    r0.x=r0.x * r1.w;
    r0.x=r0.z * r0.x;
    if (r0.w<0) r0.x=_c6.x;
    r0.z=1.0/r0.w;
    r0.z=r0.z * r0.x;
    r0.x=saturate(-r0.x + _c2.x);
//    r2=tex2D(_s3, _v0);//enb bloom
    r2=tex2D(_s1, _v0);//skyrim bloom
    r2.xyz=r2 * _c1.y;
    r2.xyz=r0.x * r2;
    r1.xyz=r1 * r0.z + r2;
    r0.x=dot(r1.xyz, _c7.xyz);
    r1.w=_c7.w;
    r2=lerp(r0.x, r1, _c3.x);
    r1=r0.x * _c4 - r2;
    r1=_c4.w * r1 + r2;
    r1=_c3.w * r1 - r0.y; //khajiit night vision _c3.w
    r0=_c3.z * r1 + r0.y;
    r1=-r0 + _c5;
    _oC0=_c5.w * r1 + r0;

#endif //APPLYGAMECOLORCORRECTION
and with the help in Shader_ORIGINALPOSTPROCESS, here are separate parts with notations
*I've combined and rearrange some lines to get a better view.
*thanks to JawZ for additional info on imagespace & imagespace modifier
external registers:

Code: Select all

//   ColorRange   c1       float4( null, color range constant, null, null)
//   Param        c2       float4( bloom coeff(level related), brightness Level constant, null, null)  
//Post-Process
//   Cinematic    c3       float4( saturation, null, brightness, contrast)
//   Tint         c4       float4( Tint RGB, Tint alpha)
//   Fade         c5       float4( fade RGB, fade alpha)        //vfx includes night eye and other visual effects
the first part would be r0 initialization

Code: Select all

    r1    = tex2D(_s2, _v0) * _c1.y; // float4(L2 hysteresis average, L1 hysteresis average, instant average, null),  multiplied with color range multiplier.
    r0.x  = 1.0/_c2.y;   
    r0.yz = r1.xy;      
    r0.w  = 1.0/r0.y;           
    r0.z  = r0.w * r0.z;
    r1    = tex2D(_s0, _v0) * _c1.y;    //color, multiplied with color range multiplier
    r0.w  = dot(_c7.xyz, r1.xyz);       //lum(orig)
// r0 = float4( 1/ Brightness Level, L2 average, adaptation ratio, lum(orig))  

                                //                   L2 hysteresis average
    r1.w=r0.w * r0.z;           //r1.w = L(orig) *  ---------------------- = L(adp)
    r0.z=r0.z * r0.w + _c7.w;   //                   L1 hysteresis average
    r0.z=1.0/r0.z;              // 
    r0.x=r1.w * r0.x + _c7.w;   // 
    r0.x=r0.x * r1.w;           //                 1 + L(adp) / Level
    r0.x=r0.z * r0.x;           //r0.x = L(adp) * -------------------- = L'
                                //                     1 + L(adp)

and preparing to blend bloom

Code: Select all

    if (r0.w<0) r0.x=_c6.x;     // if Lum(orig) < 0, set  L' == 0
    r0.z=1.0/r0.w;              // invert Lum(orig)
    r0.z=r0.z * r0.x;           // == L' / Lum(orig)
    r0.x=saturate(-r0.x + _c2.x);   // saturate( bloom level - L'), 0 when  L' > bloom level
    r2=tex2D(_s1, _v0) * _c1.y; // Skyrim bloom, multiplied with color range multiplier
//r0 = float4( bloom brightpass, null, Lum old-new ratio, null)
blends bloom and color

Code: Select all

        r2.xyz = r0.x * r2; // bloom * saturate( _c2.x - L') == bloom * brightpass
        r1.xyz = r0.z * r1; // color * L'/ Lum(orig), maps original brightness to new brightness
/*
                    L'
color' = color * ---------
                  L(orig)
*/
        r1.xyz = r1 + r2;   // blending color and bloom
        r1.w = _c7.w;       // = 1 
then is the part for Skyrim post process

Code: Select all

        r0.x = dot(r1.xyz, _c7.xyz);            // gray scale
        r2   = lerp(r0.x, r1, _c3.x);           // saturation: lerp( gray, color, saturation weight)
        r1   = lerp(r2, r0.x * _c4, _c4.w);     // tint: lerp( color(saturation), gray * tint, tint weight)
        r0   = lerp(r0.y, _c3.w * r1, _c3.z);   // intensity, contrast: lerp( average, intensity * color(tint), contrast weight) 
        _oC0 = lerp(r0, _c5 , _c5.w);           // Fade: additional color correction for sfx, only > 0 when triggers imagespace modifier
one can observed with Enhanced ENB Diagnostics by number6/scegielski that c2 ~ c4 are controlled by game and often changes from cell to cell, which also altered by lighting mods.

c5 can be used as D-N-I-khajiit separation without whole set of identical color correction process just for khajiit.
*update:
c5 is NOT night eye specific, it is Fade in imagespace modifier and used in different events including night eye.

here are DNIK examples

Code: Select all

//for DNI-KDNI
    float parm   = lerp( lerp( parm_D, parm_N, ENightDayFactor), parm_I, EInteriorFactor);
    float parm_k = lerp( lerp( parm_k_D, parm_k_N, ENightDayFactor), parm_k_I, EInteriorFactor);
    parm = lerp(parm, parm_k, EKhajiitFactor);
//or DNIK
    float parm   = lerp( lerp( parm_D, parm_N, ENightDayFactor), parm_I, EInteriorFactor);
    parm = lerp(parm, parm_k, EKhajiitFactor);
and for the EKhajiitFactor, there are 3 options

option1:
may introduce unwanted color change on other event that uses Fade in imagespace modifier.

Code: Select all

    float EKhajiitFactor = _c5.w * KhajiitFactorMultiplier;
option2:
suggested by jawZ for unmoded night eye, will not affecting other game vfx , no transition time( probably doesn't matters, transition time is rather fast when switching night eye)

Code: Select all

     float EKhajiitFactor = (_c2.y >= 0.562498 && _c2.y <= 0.5625 && _c5.z >= 0.745096 && _c5.z <= 0.745099)? 1:0;
option3:
use enhanced night eye esp by number6.
also not affecting other game vfx, conserved night eye transition time.

Code: Select all

    float EKhajiitFactor = (_c5.r < 0.01 && _c5.g < 0.01)? _c5.b: 0;
anyway, hopes these are not too boring.
feel free to comment below.

update: April.3.16
As supplement,
here is the complete rephrase of game color correction.

Code: Select all

////////////////////////////////////////////////////
//  AGCC Inputs   
//////////////////////////////////////////////////// 
    float  AGCC_ColorRange = _c1.y; //Linear scaler, requires confirm on Imagespace Parm in CK.
    float  AGCC_Average    = tex2D(_s2, coord).x * _c1.y; // scene average (log average if following Reinhard's defines)
    float3 AGCC_LumCoeff   = _c7.rgb;
    float  AGCC_AdaptAvg   = tex2D(_s2, coord).y * _c1.y / AGCC_Average;
    float  AGCC_SqWhight   = _c2.y; //Square of minimum white, requires confirm on Imagespace Parm in CK.
    float  AGCC_BloomCoeff = _c2.x; //Bloom scaler, requires confirm on Imagespace Parm in CK.
    
    float  AGCC_Saturation = _c3.x;
    float3 AGCC_Tint       = _c4.rgb;
    float  AGCC_TintWeight = _c4.a;
    float  AGCC_Brightness = _c3.w;
    float  AGCC_Contrast   = _c3.z;
    float3 AGCC_Fade       = _c5.rgb;
    float  AGCC_FadeWeight = _c5.w;
    
    color = tex2D(_s0, _v0) * AGCC_ColorRange; // color
    bloom = tex2D(_s1, _v0) * AGCC_ColorRange; // Skyrim bloom
    
////////////////////////////////////////////////////
//  AGCC Tonemapper with adaptation (auto exposure).   
//////////////////////////////////////////////////// 
    float lum_orig   = dot(color.rgb, AGCC_LumCoeff);
    float lum_scaled = lum_orig * AGCC_AdaptAvg;
    float lum_mapped = lum_scaled * ( lum_scaled / AGCC_SqWhight + 1) / ( lum_scaled + 1);  //Reinhard
    
    lum_mapped = (lum_orig < 0)? 0: lum_mapped;
    color = color * lum_mapped / lum_orig;

////////////////////////////////////////////////////
//  AGCC Bloom Blending
////////////////////////////////////////////////////
    bloom = bloom * saturate(AGCC_BloomCoeff - lum_mapped);
    color = color + bloom;
/*
    bloom behavior:
    if bloom_coeff < lum
        color = color; <-- no bloom
    if bloom_coeff > lum + 1
        color = color + bloom; <-- add
    if lum + 1 > bloom_coeff > lum
        color = color + bloom * (difference);
*/

////////////////////////////////////////////////////
//  AGCC PostProcess/Cinematic settings
////////////////////////////////////////////////////

    float lum = dot(color.rgb, AGCC_LumCoeff);
    color = lerp(lum, color, AGCC_Saturation);
    color = lerp(color, lum * AGCC_Tint, AGCC_TintWeight);
    color = color * AGCC_Brightness;
    color = lerp(AGCC_Average, color, AGCC_Contrast);
    color = lerp(color, AGCC_Fade, AGCC_FadeWeight);
    
    color.a = 1;

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 03 Jul 2015, 14:49
by --JawZ--
Just a correction;

_c5.rgba -> Applies to Imagespace Modifiers and is a color tint addition instead of a multiplier like _c4, where Night Eye, blurred when hit, "hit by spriggan visual change" and similar time based screen changes occur. So it doesn't work as a night eye only function.
_c5.r = Red Fade Channel, 0-255
_c5.g = Green Fade Channel, 0-255
_c5.b = Blue Fade Channel, 0-255
_c5.a = Alpha Fade Channel, 0-1

For a Night Eye only change you would have to do something like this, with vanilla night eye Imagespace Modifiers;

Code: Select all

if (_c2.y >= 0.562498 && _c2.y <= 0.5625 && _c5.z >= 0.745096 && _c5.z <= 0.745099)
Those values are unique to the night eye effect in the vanilla game. The _c2.y value also occurs with the Brelynas Practice Spell

Imagespace Modifiers is an adjustment that is either animated or static and that either adds or overwrite the regular imagespaces values used in the current cell.
Imagespace vs. Imagespace Modifiers

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 03 Jul 2015, 19:41
by kingeric1992
Thanks for the _c5 supplement, looks like it works as special effect, will update the post.

But simply do the "if" statement will loses the transition process when using night eye, and using night eye specific values also means losing other color sfx in game mechanism.


about blending modes, judging by codes,
_c5.rgb is on alphablend and color is on inverse alphablend
_c4 is more like blending with colored grayscale, where color channel can interfere each others, not exactly a color filter multiplier.

Nice reference on Imagespace vs. Imagespace Modifiers, it seams to me that any modification here in enbeffect.fx on _c2 ~ _c5 is like " Global Imagespace Modifiers",
I wonder if _c5/ fade can be loaded in enbeffectprepass.fx, it can probably work as dof control like " when _c5.rba == 0, _c5.g == dof level" or any other hack.

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 04 Jul 2015, 12:06
by --JawZ--
Yeah precisely.

Yes but as you pointed, "simply". It was only a simple suggestion that's all.

Well yeah but visually it has very much the similar approach on how they are adjusted, again simple is used here.

Correct. It could be, but Boris would have to "hardcode forward" it to that .fx file. Lot's of things can be done if we got the opportunity to do so ;)

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 08 Aug 2015, 19:47
by kingeric1992
update on s2 average color, which changes everything.

done some testing on parameters, and here is what I've got on texture s2

s2.x == level2 hysteresis average
s2.y == level1 hysteresis average
s2.z == instant average

so now Lum(scaled) should be adaptation weighted luminence,

Code: Select all

                    L2 hysteresis average
L(adp) = L(orig) *  ----------------------
                    L1 hysteresis average
and with constant input _c2.y,
the entire first part has become simple level control (base on predefined parameter under time & cell)

Code: Select all

               1 + L(adp) / Level
L' = L(adp) * --------------------
                  1 + L(adp)
where Level == _c2.y,
then apply the new luminance to color

Code: Select all

                    L'
color' = color * ---------
                  L(orig)
Anyway, sorry about the confusion before, no Reinhard in the original postprocess.
Also btw, the code in APPLYGAMECOLORCORRECTION is identical to technique< vanilla>,
but what I don't get is that why original postprocess would have hdr output??

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 09 Aug 2015, 20:42
by Marty McFly
s2.x == level2 hysteresis average
s2.y == level1 hysteresis average

So .y has the average brightness over a small time delta and .x over a large? And you're correct, it seems like original game color correct outputs HDR data - I don't get that either... I never played/modded Skyrim intensively but is it necessary to tonemap there? I am used to GTASA where Boris forced HDR rendering but tonemapping is almost useless as most colors are in LDR range anyways (as it's an LDR game by design) and even with bloom they seldom exceed it. Oh and what range is that fixed Level parameter? If it's negative, numerator in your equation is larger than the denominator, resulting in a number < 1.0 so it will darken the result. Not sure if that counts as tonemapping though.

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 09 Aug 2015, 22:55
by kingeric1992
I've only tested a few times in whiterun.

Level == 1 during day (hence the only purpose left for the function is eye adaptation)
Level ~ 1.4 during night

according to function,
0 < Level < 1 == brighten
otherwise darken.
< 0 will cause serious artifacts so it definitely won't go that way in any case.

here is a pic on the curve with Level = 1.32
it probably just for reduce hdr color during night?
if that so, there's no need for that while other tonemapping function can also achieve DNI separated curve.
Image
https://www.desmos.com/calculator/jxe1mwyvur

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 14 Aug 2015, 16:02
by number6
I'm still trying to understand all of what kingeric1992 has written in the OP. I think it would probably greatly help to understand that code if it were re-written to use variables with meaningful names instead of using the components of r0, r1, r2, s1, s2, c1, c2, etc. It's tough enough to understand without having to remember what varaible is what.

In regards to the DNI stuff, one thing that I have found is that it seems that the image space modifiers (IMOD) will stack their effects. So if you are in an environment that has an IMOD already and you activate night eye, you are seeing the combination of the two in the registers. Can you guys verify that this is the case?

To get around this in my night eye/night vision mods, I check both the .w value of the register and the color .xyz value to make sure it is in the proper range for activation of the night eye effect. For the Skyrim night eye mod I went as far as to change the IMOD to be an extreme blue {0,0,1} tint when night eye is activated so that there is no mistaking it inside the shader when looking at the register values.

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 14 Aug 2015, 19:49
by --JawZ--
They only stack up on each other if the Imod's use the "add" value and leave the "Multiply" value alone. Tint and Fade falls into the "Multiply" category, overwriting the effects.
Image

Also if there are two Imods, one alters Brightness "Multiply" and the other Contrast "Multiply" they will be "combined" and use both values.

Re: [HLSL CODE] APPLYGAMECOLORCORRECTION & DNI-khajiit

Posted: 03 Apr 2016, 01:56
by kingeric1992
post update :
Add complete rephrase of AGCC.