Thursday, October 26, 2017

Reverse engineering the rendering of The Witcher 3, part 3 - chromatic aberration

This post is a part of the series "Reverse engineering the rendering of The Witcher 3".


Hello!

Welcome to the third episode of my mini series where I demystify some rendering techniques from The Witcher 3.

Today we will take a closer look at chromatic aberration.

Chromatic aberration is an effect known mostly from cheaper lenses. It occurs because lenses have different refractive index for different wavelenghts of visible light. The result of this is visible distortion.

Not everyone likes it though. Luckily in The Witcher 3 this effect is very slight and therefore is not disturbing during gameplay  (at least for me). However, you can disable it if you want to.

Let's take a closer look at an example scene with and without chromatic aberration:
Chromatic aberration: on

Chromatic aberration: off
Okay, do you see any difference near the corners? Me neither. Let's try different scene:

Chromatic aberration: On (#2). Notice slight "red" distortion in marked region.

Ah! Much better! There is bigger contrast between dark and bright regions and in the corner we can see slight distortion.

As you can see, this effect is really slight. Anyway, I was curious how this was implemented.
So let's go now to the most interesting part: code!

Implementation
The first thing to do is to find proper draw call with pixel shader.
Actually, chromatic aberration is part of a bigger "final postprocess" pixel shader, which consists of chromatic aberration, vignette and gamma correction, all in one PS.

So let's take a closer look at pixel shader assembly:
 ps_5_0  
    dcl_globalFlags refactoringAllowed  
    dcl_constantbuffer cb3[18], immediateIndexed  
    dcl_sampler s1, mode_default  
    dcl_resource_texture2d (float,float,float,float) t0  
    dcl_input_ps_siv v0.xy, position  
    dcl_input_ps linear v1.zw  
    dcl_output o0.xyzw  
    dcl_temps 4  
   0: mul r0.xy, v0.xyxx, cb3[17].zwzz  
   1: mad r0.zw, v0.xxxy, cb3[17].zzzw, -cb3[17].xxxy  
   2: div r0.zw, r0.zzzw, cb3[17].xxxy  
   3: dp2 r1.x, r0.zwzz, r0.zwzz  
   4: sqrt r1.x, r1.x  
   5: add r1.y, r1.x, -cb3[16].y  
   6: mul_sat r1.y, r1.y, cb3[16].z  
   7: sample_l(texture2d)(float,float,float,float) r2.xyz, r0.xyxx, t0.xyzw, s1, l(0)  
   8: lt r1.z, l(0), r1.y  
   9: if_nz r1.z  
  10:  mul r1.y, r1.y, r1.y  
  11:  mul r1.y, r1.y, cb3[16].x  
  12:  max r1.x, r1.x, l(0.000100)  
  13:  div r1.x, r1.y, r1.x  
  14:  mul r0.zw, r0.zzzw, r1.xxxx  
  15:  mul r0.zw, r0.zzzw, cb3[17].zzzw  
  16:  mad r0.xy, -r0.zwzz, l(2.000000, 2.000000, 0.000000, 0.000000), r0.xyxx  
  17:  sample_l(texture2d)(float,float,float,float) r2.x, r0.xyxx, t0.xyzw, s1, l(0)  
  18:  mad r0.xy, v0.xyxx, cb3[17].zwzz, -r0.zwzz  
  19:  sample_l(texture2d)(float,float,float,float) r2.y, r0.xyxx, t0.xyzw, s1, l(0)  
  20: endif  
 ...  

And cbuffer values:


Okay, let's try to understand what's going on here.

cb3_v17.xy is essentialy center of chromatic aberration, so the first lines are essentially calculating 2d vector from texel coords (cb3_v17.zw = inverse viewport size) to "chromatic aberration center" and its length, then some maths, test and branching.

When chromatic aberration is applied, we calculate offsets using some values from constant buffer and we distort R and G channels.

Generally, the closer to corners of screen, the more intense the effect is. Line 10 is quite an interesting one, because it makes pixels to "come closer", especially when we exaggerate the aberration.

And I'm pleased to share with you with my implementation of it. As always, please take names of variables with (large) grain of salt. And note this effect is done *prior* to gamma correction.

 void ChromaticAberration( float2 uv, inout float3 color )  
 {  
   // User-defined params  
   float2 chromaticAberrationCenter = float2(0.5, 0.5);  
   float chromaticAberrationCenterAvoidanceDistance = 0.2;  
   float fA = 1.25;  
   float fChromaticAbberationIntensity = 30;  
   float fChromaticAberrationDistortionSize = 0.75;  
   
   // Calculate vector  
   float2 chromaticAberrationOffset = uv - chromaticAberrationCenter;  
   chromaticAberrationOffset = chromaticAberrationOffset / chromaticAberrationCenter;  
     
   float chromaticAberrationOffsetLength = length(chromaticAberrationOffset);  
    
   // To avoid applying chromatic aberration in center, subtract small value from  
   // just calculated length.  
   float chromaticAberrationOffsetLengthFixed = chromaticAberrationOffsetLength - chromaticAberrationCenterAvoidanceDistance;  
   float chromaticAberrationTexel = saturate(chromaticAberrationOffsetLengthFixed * fA);  
   
   float fApplyChromaticAberration = (0.0 < chromaticAberrationTexel);  
   if (fApplyChromaticAberration)  
   {  
     chromaticAberrationTexel *= chromaticAberrationTexel;  
     chromaticAberrationTexel *= fChromaticAberrationDistortionSize;  
   
     chromaticAberrationOffsetLength = max(chromaticAberrationOffsetLength, 1e-4);  
       
     float fMultiplier = chromaticAberrationTexel / chromaticAberrationOffsetLength;  
   
     chromaticAberrationOffset *= fMultiplier;  
     chromaticAberrationOffset *= g_Viewport.zw;  
     chromaticAberrationOffset *= fChromaticAbberationIntensity;  
   
     float2 offsetUV = -chromaticAberrationOffset * 2 + uv;  
     color.r = TexColorBuffer.SampleLevel(samplerLinearClamp, offsetUV, 0).r;  
   
     offsetUV = uv - chromaticAberrationOffset;  
     color.g = TexColorBuffer.SampleLevel(samplerLinearClamp, offsetUV, 0).g;  
   }  
 }  

I've added "fChromaticAberrationIntensity" to increase size of offset, therefore, intensity of the effect, as name suggets (TW3 = 1.0).

Intensity = 40:



So this is it! I hope you have enjoyed this post.
Stay tuned for more, at least few more effects are waiting to be reverse engineered! :)

Have a good day,
M.

Tuesday, October 3, 2017

Reverse engineering the rendering of The Witcher 3, part 2 - eye adaptation

This post is a part of the series "Reverse engineering the rendering of The Witcher 3".


Hi everyone!

Welcome to the second part of my mini series where I demystify some rendering techniques from The Witcher 3. This time it's gonna be much, much simpler than before.

In the first part I showed you how tonemapping is done in TW3. While explaining theoretical basics, I briefly mentioned about eye adaptation. And guess what? Today I'll show how this eye adaptation is handled.

But wait, what is this eye adaptation all about and why do we need that? Wikipedia knows all about this, but let's imagine that you are in dark room (Life is Strange, anyone? :) ) or cave and you go outside, where is bright. The primary source of this brightness can be Sun, for instance.

In darkness our pupils are big to let more light through them to retinas. When it gets brightly, our pupils are becoming smaller and sometimes we blink, because it "hurts".
This change doesn't happen immediately. Eye has to adapt to changes of brightness. This is exactly why we perform eye adaptation in real time rendering.

Good example where lack of eye adaptation is noticeable is HDRToneMappingCS11 from DirectX SDK. Abrupt changes of average luminance are rather unpleasant and unnatural.

Let's get started!
For consistency, we will be analyzing the same frame as before, from Novigrad City.



And now some diving into RenderDoc frame capture. Eye adaptation is usually done just before tonemapping and The Witcher 3 is no exception.

And look at the pixel shader state:


We have 2 inputs - 2 textures, R32_FLOAT, 1x1 (one pixel).
texture0 contains average scene luminance from previous frame.
texture1 contains average scene luminance from current frame (computed just before in compute shader - I marked this in blue color).

Not surprisingly, 1 output, R32_FLOAT, 1x1.

Let's take a look at pixel shader.

 ps_5_0  
    dcl_globalFlags refactoringAllowed  
    dcl_constantbuffer cb3[1], immediateIndexed  
    dcl_sampler s0, mode_default  
    dcl_sampler s1, mode_default  
    dcl_resource_texture2d (float,float,float,float) t0  
    dcl_resource_texture2d (float,float,float,float) t1  
    dcl_output o0.xyzw  
    dcl_temps 1  
   0: sample_l(texture2d)(float,float,float,float) r0.x, l(0, 0, 0, 0), t1.xyzw, s1, l(0)  
   1: sample_l(texture2d)(float,float,float,float) r0.y, l(0, 0, 0, 0), t0.yxzw, s0, l(0)  
   2: ge r0.z, r0.y, r0.x  
   3: add r0.x, -r0.y, r0.x  
   4: movc r0.z, r0.z, cb3[0].x, cb3[0].y  
   5: mad o0.xyzw, r0.zzzz, r0.xxxx, r0.yyyy  
   6: ret  

Wow, so easy! Only 7 lines of assembly :)
What is going on here? Explanation line by line:

0) Get average luminance from current frame.
1) Get average luminance from previus frame.
2) Perform a test: Is the current luminance less than or equal to luminance from previous frame?
If yes - luminance is going down, if no - luminance is getting higher.
3) Calculate difference: difference = currentLum - previousLum.
4) This conditional move (movc) assignes speed factor from constant buffer. Depending on the test from line #2, two different values can be assigned. This is smart, because you can have different adaptation speeds for both falling and rising of luminance. But in every single frame I investiagated, both values are the same, ranging from about 0.11 to 0.3.
5) Final calculation of adapted Luminance:
   adaptedLuminance = speedFactor * difference + previousLuminance.
6) End of the shader

Simple enough to implement in HLSL:
 // The Witcher 3 eye adaptation shader  
   
 cbuffer cBuffer : register (b3)  
 {  
   float4 cb3_v0;  
 }
  
 struct VS_OUTPUT_POSTFX  
 {  
   float4 Position                                             : SV_Position;  
 };  
  
 SamplerState samplerPointClamp : register (s0);  
 SamplerState samplerPointClamp2 : register (s1);  
   
 Texture2D TexPreviousAvgLuminance  : register (t0);  
 Texture2D TexCurrentAvgLuminance  : register (t1);  
   
 float4 TW3_EyeAdaptationPS(VS_OUTPUT_POSTFX Input) : SV_TARGET  
 {  
   // Get current and previous luminance.  
   float currentAvgLuminance = TexCurrentAvgLuminance.SampleLevel( samplerPointClamp2, float2(0.0, 0.0), 0 );  
   float previousAvgLuminance = TexPreviousAvgLuminance.SampleLevel( samplerPointClamp, float2(0.0, 0.0), 0 );  
     
   // Scale factor. Can be different for both falling down and rising up of luminance.  
   // It affects speed of adaptation.  
   // Small conditional test is performed here, so different speed can be set differently for both these cases.  
   float adaptationSpeedFactor = (currentAvgLuminance <= previousAvgLuminance) ? cb3_v0.x : cb3_v0.y;  
   
   // Calculate adapted luminance.  
   float adaptedLuminance = lerp( previousAvgLuminance, currentAvgLuminance, adaptationSpeedFactor );  
   return adaptedLuminance;  
 }  

It gives us the same assembly. I would suggest only changing output type to float instead of float4. No need to waste bandwidth.

So this is how eye adaptation is done in Witcher 3. Pretty easy, huh? :)
I hope you enjoyed this post! Stay tuned for more.


Edit - Decemeber 15, 2018
Hi, at the time of writing this post, I haven't recognized HLSL compiler patterns well enough to notice there is no need of writing "difference" and so on.
This is simply a linear interpolation, lerp/mix, you name it.
The way lerp(x, y, s) is performed on HLSL assembly is simply

 x + s(y-x).

And the (y-x) difference has to be stored somewhere.


Have a good day,
M.

PS. Huge thanks to Baldur Karlsson ( Twitter: @baldurk ) for RenderDoc. It simply rocks.

Thursday, September 7, 2017

Reverse engineering the rendering of The Witcher 3, part 1 - tonemapping

This post is a part of the series "Reverse engineering the rendering of The Witcher 3".


Hi!

In most of modern AAA games one of rendering stages you can encounter for sure is tonemapping.
Quick memory refreshment: In real life, there is a pretty huge luminance range, while our computer screens usually have a limited one (8bits per pixel, which gives 0-255). This is where tonemapping comes to party, because it allows to fit wider range of illumination into a limited one. Usually there are two inputs into this process: floating-point HDR image with color values exceeding 1.0 and an average luminance of scene (the latter can be calculated in a few ways, possibly with eye adaptation to simulate human's eye behavior, but this is not important here).

The next (and final) step consists of obtaining an exposure, calculating exposed color and processing it through tonemapping curve. This is where things start to be a bit messy, because new concepts appear, like "white point" and "middle gray". There are at least few popular curves and Matt Pettineo's article "A Closer Look at Tone Mapping" investigates some of them.

To be honest, I've alvays had problems with proper implementation of tonemapping in my code. There are at least a few different examples online which luckily turned out to be helpful... well, to some point. Some of them incorporate HDR luminance/white point/middle gray to account, some do not - which doesn't really help. I wanted to have a "battle-proven" implementation.

Recently I've started messing around rendering of The Witcher 3. This game has some awesome rendering trickery. And it's great, in terms of story/music/gameplay/eveything.


Ah, before I forget! This post is the first of short series which investigates some rendering solutions from The Witcher 3. It absolutely will not be as comprehensive, as Adrian Courrèges's GTA V graphics study, at least for now :)
We'll start by reverse-engineering tonemapping. Let's start!

We will be working on RenderDoc's capture from this frame from one of main quests from Novigrad City. All settings maxed:



After some search, there is a draw call for tonemapping! As I mentioned earlier, there is a HDR color buffer (texture #0, full res) and average luminance of scene (texture #1, 1x1, floating-point, calculated earlier by compute shader).


Let's take a look at pixel shader assembly:

 ps_5_0  
    dcl_globalFlags refactoringAllowed  
    dcl_constantbuffer cb3[17], immediateIndexed  
    dcl_resource_texture2d (float,float,float,float) t0  
    dcl_resource_texture2d (float,float,float,float) t1  
    dcl_input_ps_siv v0.xy, position  
    dcl_output o0.xyzw  
    dcl_temps 4  
   0: ld_indexable(texture2d)(float,float,float,float) r0.x, l(0, 0, 0, 0), t1.xyzw  
   1: max r0.x, r0.x, cb3[4].y  
   2: min r0.x, r0.x, cb3[4].z  
   3: max r0.x, r0.x, l(0.000100)  
   4: mul r0.y, cb3[16].x, l(11.200000)  
   5: div r0.x, r0.x, r0.y  
   6: log r0.x, r0.x  
   7: mul r0.x, r0.x, cb3[16].z  
   8: exp r0.x, r0.x  
   9: mul r0.x, r0.y, r0.x  
  10: div r0.x, cb3[16].x, r0.x  
  11: ftou r1.xy, v0.xyxx  
  12: mov r1.zw, l(0, 0, 0, 0)  
  13: ld_indexable(texture2d)(float,float,float,float) r0.yzw, r1.xyzw, t0.wxyz  
  14: mul r0.xyz, r0.yzwy, r0.xxxx  
  15: mad r1.xyz, cb3[7].xxxx, r0.xyzx, cb3[7].yyyy  
  16: mul r2.xy, cb3[8].yzyy, cb3[8].xxxx  
  17: mad r1.xyz, r0.xyzx, r1.xyzx, r2.yyyy  
  18: mul r0.w, cb3[7].y, cb3[7].z  
  19: mad r3.xyz, cb3[7].xxxx, r0.xyzx, r0.wwww  
  20: mad r0.xyz, r0.xyzx, r3.xyzx, r2.xxxx  
  21: div r0.xyz, r0.xyzx, r1.xyzx  
  22: mad r0.w, cb3[7].x, l(11.200000), r0.w  
  23: mad r0.w, r0.w, l(11.200000), r2.x  
  24: div r1.x, cb3[8].y, cb3[8].z  
  25: add r0.xyz, r0.xyzx, -r1.xxxx  
  26: max r0.xyz, r0.xyzx, l(0, 0, 0, 0)  
  27: mul r0.xyz, r0.xyzx, cb3[16].yyyy  
  28: mad r1.y, cb3[7].x, l(11.200000), cb3[7].y  
  29: mad r1.y, r1.y, l(11.200000), r2.y  
  30: div r0.w, r0.w, r1.y  
  31: add r0.w, -r1.x, r0.w  
  32: max r0.w, r0.w, l(0)  
  33: div o0.xyz, r0.xyzx, r0.wwww  
  34: mov o0.w, l(1.000000)  
  35: ret  

Some things to notice here. First of all, the loaded luminance does not have to be the used one, as it is being clamped (max/min calls) to values (from constant buffer) set by artists. This is handy, because it prevents overexposing or underexposing our scene. Sounds pretty obvious, but I've never done this before. And second - anyone familiar with tonemapping curves will quickly recognize this "11.2", as it is essentialy white point value from John Hable's Uncharted2 tonemapping curve.
A-F params are loaded from cbuffer.
Okay, there are also three more parameters: cb3_v16.x, cb3_v16.y, cb3_v16.z. We can investigate their values:

Some guessing:
I think the 'x' is some sort of 'white scale' or middle gray, as it is multiplied by 11.2 (line 4), and then this is numerator in calculation of exposure adjustment (line 10).
'y' - I called it "u2 numerator multiplier", you'll see why in a moment.
'z' - "exponent param", as it is used in log/mul/exp triple (essentialy exponentiation).
Please take these variable names with a grain of salt!

Also:
cb3_v4.yz - min/max values of allowed luminance,
cb3_v7.xyz - A-C params of Uncharted2 curve,
cb3_v8.xyz - D-F params of Uncharted2 curve.


Now the hard part - writing HLSL shader with will give us exactly the same assembly.
This can be very tricky, and the longer shader = the harder this task is. Luckily, some time ago I've written a tool which allows me to quickly view hlsl->asm.
Ladies and gentlemen... please give a warm welcome to D3DShaderDisassembler! :)



After some playing with code, here is the final "The Witcher 3 Tonemapping" HLSL:

 cbuffer cBuffer : register (b3)  
 {  
   float4 cb3_v0;  
   float4 cb3_v1;  
   float4 cb3_v2;  
   float4 cb3_v3;  
   float4 cb3_v4;  
   float4 cb3_v5;  
   float4 cb3_v6;  
   float4 cb3_v7;  
   float4 cb3_v8;  
   float4 cb3_v9;  
   float4 cb3_v10;  
   float4 cb3_v11;  
   float4 cb3_v12;  
   float4 cb3_v13;  
   float4 cb3_v14;  
   float4 cb3_v15;  
   float4 cb3_v16, cb3_v17;  
 }  
   
 Texture2D     TexHDRColor          : register (t0);  
 Texture2D     TexAvgLuminance     : register (t1);  
   
 struct VS_OUTPUT_POSTFX  
 {  
   float4 Position : SV_Position;  
 };  
   
 float3 U2Func( float A, float B, float C, float D, float E, float F, float3 x )  
 {  
      return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F)) - E/F;  
 }  
   
 float3 ToneMapU2Func( float A, float B, float C, float D, float E, float F, float3 color, float numMultiplier )  
 {  
      float3 numerator =  U2Func( A, B, C, D, E, F, color );  
      numerator = max( numerator, 0 );  
      numerator.rgb *= numMultiplier;  
   
      float3 denominator = U2Func( A, B, C, D, E, F, 11.2 );  
      denominator = max( denominator, 0 );  
   
      return numerator / denominator;  
 }  
   
   
   
 float4 ToneMappingPS( VS_OUTPUT_POSTFX Input) : SV_Target0  
 {  
      float avgLuminance = TexAvgLuminance.Load( int3(0, 0, 0) );  
      avgLuminance = clamp( avgLuminance, cb3_v4.y, cb3_v4.z );  
      avgLuminance = max( avgLuminance, 1e-4 );  
   
      float scaledWhitePoint = cb3_v16.x * 11.2;  
   
      float luma = avgLuminance / scaledWhitePoint;  
      luma = pow( luma, cb3_v16.z );  
   
      luma = luma * scaledWhitePoint;  
      luma = cb3_v16.x / luma;  
   
      float3 HDRColor = TexHDRColor.Load( uint3(Input.Position.xy, 0) ).rgb;  
   
      float3 color = ToneMapU2Func( cb3_v7.x, cb3_v7.y, cb3_v7.z, cb3_v8.x, cb3_v8.y,   
         cb3_v8.z, luma*HDRColor, cb3_v16.y);  
   
      return float4(color, 1);  
 }  


And a quick screenshot from my tool to prove it:

VoilĂ ! :)
I believe this is quite proper implementation of TW3 Tonemapping, at least in terms of assembly.
I already have this in my framework and it works well! Stay tuned for more!

I said "quite", because I have no heck idea why denominator in ToneMapU2Func is maxed with zero. Division by 0 is undefined, right?


Well... we could end right now, but quite accidentally I've found another variant of tonemapping shader in TW3 at this frame, at beautiful dusk (interestingly, minimum graphics settings!)


Let's check this out. At first, shader assembly:

 ps_5_0  
    dcl_globalFlags refactoringAllowed  
    dcl_constantbuffer cb3[18], immediateIndexed  
    dcl_resource_texture2d (float,float,float,float) t0  
    dcl_resource_texture2d (float,float,float,float) t1  
    dcl_input_ps_siv v0.xy, position  
    dcl_output o0.xyzw  
    dcl_temps 5  
   0: ld_indexable(texture2d)(float,float,float,float) r0.x, l(0, 0, 0, 0), t1.xyzw  
   1: max r0.y, r0.x, cb3[9].y  
   2: max r0.x, r0.x, cb3[4].y  
   3: min r0.x, r0.x, cb3[4].z  
   4: min r0.y, r0.y, cb3[9].z  
   5: max r0.xy, r0.xyxx, l(0.000100, 0.000100, 0.000000, 0.000000)  
   6: mul r0.z, cb3[17].x, l(11.200000)  
   7: div r0.y, r0.y, r0.z  
   8: log r0.y, r0.y  
   9: mul r0.y, r0.y, cb3[17].z  
  10: exp r0.y, r0.y  
  11: mul r0.y, r0.z, r0.y  
  12: div r0.y, cb3[17].x, r0.y  
  13: ftou r1.xy, v0.xyxx  
  14: mov r1.zw, l(0, 0, 0, 0)  
  15: ld_indexable(texture2d)(float,float,float,float) r1.xyz, r1.xyzw, t0.xyzw  
  16: mul r0.yzw, r0.yyyy, r1.xxyz  
  17: mad r2.xyz, cb3[11].xxxx, r0.yzwy, cb3[11].yyyy  
  18: mul r3.xy, cb3[12].yzyy, cb3[12].xxxx  
  19: mad r2.xyz, r0.yzwy, r2.xyzx, r3.yyyy  
  20: mul r1.w, cb3[11].y, cb3[11].z  
  21: mad r4.xyz, cb3[11].xxxx, r0.yzwy, r1.wwww  
  22: mad r0.yzw, r0.yyzw, r4.xxyz, r3.xxxx  
  23: div r0.yzw, r0.yyzw, r2.xxyz  
  24: mad r1.w, cb3[11].x, l(11.200000), r1.w  
  25: mad r1.w, r1.w, l(11.200000), r3.x  
  26: div r2.x, cb3[12].y, cb3[12].z  
  27: add r0.yzw, r0.yyzw, -r2.xxxx  
  28: max r0.yzw, r0.yyzw, l(0, 0, 0, 0)  
  29: mul r0.yzw, r0.yyzw, cb3[17].yyyy  
  30: mad r2.y, cb3[11].x, l(11.200000), cb3[11].y  
  31: mad r2.y, r2.y, l(11.200000), r3.y  
  32: div r1.w, r1.w, r2.y  
  33: add r1.w, -r2.x, r1.w  
  34: max r1.w, r1.w, l(0)  
  35: div r0.yzw, r0.yyzw, r1.wwww  
  36: mul r1.w, cb3[16].x, l(11.200000)  
  37: div r0.x, r0.x, r1.w  
  38: log r0.x, r0.x  
  39: mul r0.x, r0.x, cb3[16].z  
  40: exp r0.x, r0.x  
  41: mul r0.x, r1.w, r0.x  
  42: div r0.x, cb3[16].x, r0.x  
  43: mul r1.xyz, r1.xyzx, r0.xxxx  
  44: mad r2.xyz, cb3[7].xxxx, r1.xyzx, cb3[7].yyyy  
  45: mul r3.xy, cb3[8].yzyy, cb3[8].xxxx  
  46: mad r2.xyz, r1.xyzx, r2.xyzx, r3.yyyy  
  47: mul r0.x, cb3[7].y, cb3[7].z  
  48: mad r4.xyz, cb3[7].xxxx, r1.xyzx, r0.xxxx  
  49: mad r1.xyz, r1.xyzx, r4.xyzx, r3.xxxx  
  50: div r1.xyz, r1.xyzx, r2.xyzx  
  51: mad r0.x, cb3[7].x, l(11.200000), r0.x  
  52: mad r0.x, r0.x, l(11.200000), r3.x  
  53: div r1.w, cb3[8].y, cb3[8].z  
  54: add r1.xyz, -r1.wwww, r1.xyzx  
  55: max r1.xyz, r1.xyzx, l(0, 0, 0, 0)  
  56: mul r1.xyz, r1.xyzx, cb3[16].yyyy  
  57: mad r2.x, cb3[7].x, l(11.200000), cb3[7].y  
  58: mad r2.x, r2.x, l(11.200000), r3.y  
  59: div r0.x, r0.x, r2.x  
  60: add r0.x, -r1.w, r0.x  
  61: max r0.x, r0.x, l(0)  
  62: div r1.xyz, r1.xyzx, r0.xxxx  
  63: add r0.xyz, r0.yzwy, -r1.xyzx  
  64: mad o0.xyz, cb3[13].xxxx, r0.xyzx, r1.xyzx  
  65: mov o0.w, l(1.000000)  
  66: ret  
   

It may look intimidating at first, but actually it's not that bad. After a quick analysis we can notice that there are 2 calls to Uncharted2 func with different sets of input data
(A-F, min/max luminance...). I haven't encountered such solution before.

And HLSL:
 cbuffer cBuffer : register (b3)  
 {  
   float4 cb3_v0;  
   float4 cb3_v1;  
   float4 cb3_v2;  
   float4 cb3_v3;  
   float4 cb3_v4;  
   float4 cb3_v5;  
   float4 cb3_v6;  
   float4 cb3_v7;  
   float4 cb3_v8;  
   float4 cb3_v9;  
   float4 cb3_v10;  
   float4 cb3_v11;  
   float4 cb3_v12;  
   float4 cb3_v13;  
   float4 cb3_v14;  
   float4 cb3_v15;  
   float4 cb3_v16, cb3_v17;  
 }  
   
 Texture2D     TexHDRColor     : register (t0);  
 Texture2D     TexAvgLuminance     : register (t1);  
   
 float3 U2Func( float A, float B, float C, float D, float E, float F, float3 x )  
 {  
      return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F)) - E/F;  
 }  
   
 float3 ToneMapU2Func( float A, float B, float C, float D, float E, float F, float3 color, float numMultiplier )  
 {  
      float3 numerator =  U2Func( A, B, C, D, E, F, color );  
      numerator = max( numerator, 0 );  
      numerator.rgb *= numMultiplier;  
   
      float3 denominator = U2Func( A, B, C, D, E, F, 11.2 );  
      denominator = max( denominator, 0 );  
   
      return numerator / denominator;  
 }  
   
 struct VS_OUTPUT_POSTFX  
 {  
   float4 Position : SV_Position;  
 };  
   
 float getExposure(float avgLuminance, float minLuminance, float maxLuminance, float middleGray, float powParam)  
 {  
      avgLuminance = clamp( avgLuminance, minLuminance, maxLuminance );  
      avgLuminance = max( avgLuminance, 1e-4 );  
   
      float scaledWhitePoint = middleGray * 11.2;  
   
      float luma = avgLuminance / scaledWhitePoint;  
      luma = pow( luma, powParam);  
   
      luma = luma * scaledWhitePoint;  
      float exposure = middleGray / luma;  
      return exposure;  
 }  
   
 float4 ToneMappingPS( VS_OUTPUT_POSTFX Input) : SV_Target0  
 {  
      float avgLuminance = TexAvgLuminance.Load( int3(0, 0, 0) );  
     
   
      float exposure1 = getExposure( avgLuminance, cb3_v9.y, cb3_v9.z, cb3_v17.x, cb3_v17.z);  
      float exposure2 = getExposure( avgLuminance, cb3_v4.y, cb3_v4.z, cb3_v16.x, cb3_v16.z);  
   
        
      float3 HDRColor = TexHDRColor.Load( uint3(Input.Position.xy, 0) ).rgb;  
   
      float3 color1 = ToneMapU2Func( cb3_v11.x, cb3_v11.y, cb3_v11.z, cb3_v12.x, cb3_v12.y,   
         cb3_v12.z, exposure1*HDRColor, cb3_v17.y);  
   
      float3 color2 = ToneMapU2Func( cb3_v7.x, cb3_v7.y, cb3_v7.z, cb3_v8.x, cb3_v8.y,   
         cb3_v8.z, exposure2*HDRColor, cb3_v16.y);  
      
      float3 finalColor = lerp( color2, color1, cb3_v13.x ); 
      return float4(finalColor, 1);  
 }  
   

So basically we have 2 sets of control params, then calculate two tonemapped colors and at the end we interpolate them. Smart!

Feel free to comment, maybe there's something I have missed.
I hope you enjoyed this post :)

Have a good day,
M.

Thursday, June 11, 2015

How to get started with wxWidgets these days

Hi! :)

This post aims to be a painless introduction to wxWidgets with C++.

Introduction

When you code in C++ (or other language) - after all these console programs there comes a point when you start to ask yourself: "Alright, I understand whole syntax, but what about GUI programs - buttons, checkboxes, tabs etc.?".
Quick jump to Google and you know what you are looking for - Win32 API. Then, after some tutorials revealing basic concepts of it - like message loop etc. something is happening!
At least for a short period and only for basic programs.

This is exactly my story. I quickly found adding new controls (HWND, CreateWindow) at least cumbersome, breaking at the same time laws of object-oriented coding. Moreover, it was riddiculous to handle size messages. In fact, I was spending more time caring for small details in GUI instead of dealing with true problems.

WinAPI was written in C, before C++98 (which introduced concept of classes).
Let's be honest: You want to code GUI apps under Windows - you should be familiar with it. It has a lot of great features; every function has its description at MSDN.

If you are tired of fighting with WinAPI, you should really consider using one of available GUI toolkits.
 

GUI Toolkits

Luckily for us, there are GUI toolkits. How do they work? They are mostly wrappers. For instance, class "Window" has HWND something with it. A very trivial example, forgive me :) Right now, they are full-featured chunks of code which makes coding GUI a true pleasure.

But let's go straight to the point. There are many of them, like MFC (object-oriented version of Win32), Qt (used by Autodesk and many others), GTK (used by Gimp) and wxWidgets.

I decided to learn wxWidgets for a couple of reasons. First of all, it's free (open source) which is always nice. It's multiplatform (yes, with the same code you can code under Mac).
wxWidgets is widely used (CD Projekt Red's REDkit uses it, for instance).
What's important for learning, there is a rich documentation and samples.

Not convinced yet? :)
See http://wxwidgets.org/about/

Downloading & Building

You shouldn't have problems with downloading wxWidgets. Download the library from here: http://wxwidgets.org/downloads/
I prefer "Windows Installer". At the moment of writing this, the latest version is 3.0.2.

I will use Visual Studio 2013 in this tutorial, but in fact it's very similar for older versions of MSVC (2008-2012).

1. Run Visual Studio 2013.
2. Open proper solution file. It's located at (for instance) C:\wxWidgets-3.0.2\build\msw

 For VS 2013 choose "wx_vc12.sln". For VS 2012 - "wx_vc11.sln" and so on, depending on version of your compiler.

You will see something similar to this:


OK. Think now - do you prefer to link wxWidgets statically (.lib) or dynamically (.dll) to your application? In the first scenario, the final .exe file will be larger, but you will not have to keep it together with a dll files of wxWidgets. In the second one, exe will be smaller, but you will have to store proper DLLs in the same directory.

You should also think about CRT library (DLL or LIB) that wxWidgets will use. It's critical. Both your project and other libraries you use with it have to have the same CRT library. You can change it in "Configuration Settings" ->"C/C++"->"Code Generation"


You also probably want to build both debug/release configurations.
So: If you build LIBs for static linking - you are interested in Debug & Release build configurations.
If you build DLLs for dynamic linking - DLL Debug & DLL Release are for you.
To build one, click right mouse button on "Solution" in Solution Explorer, and click "Build Solution".


It will take some time. Don't forget about the second configuration!
Output files are stored in %WXWIN%\lib\vc_lib
As I said before, I prefer Windows Installer. One of the reasons is creating the WXWIN environment variable. We will take advantage of it later.

Alright, the next thing you should do is building samples. wxWidgets has over 85+ of them, covering different aspects of the library (from minmal code to threads and advanced controls). The best way to learn is to learn by practice - and playing and modifying existing code samples is pretty fun.
It is very similar to building wxWidgets itself. You just have to open proper solution file:


The notification above simply means that we are opening solution file in a newer version of Visual Studio than it was created in. You can backup the original "samples.sln" if you want to.

Migrating projects within solution can take some time. When it's finished, build the solution. Basically you will need only "Release" build. Of course, you can buld "Debug" also, if you really need to.

Tip: Various versions of the library

The library is built, samples are built.
If you have various versions of Visual Studio on your computer, as me, you probably want to avoid conflicts with wrong versions of libraries.
I propose you a simple solution: For each version of Visual C++ compiler create a different directory!

You see, the order matters. At first, we build the library, then samples. At the end we can safely move library files to, for instance, "vc12" directory. Of course, building samples only once is enough :)



Creating a project (and template)

Now the only thing we need to do is to create a basic project to get things running! :)
Moreover, we will create it as a template! It has a lot of advantages, there is no point in rewritting all basic code from scratch every time. NO!


There is much better way to deal with it: After creating a basic project and saving it as a template, this template can be reused (and expanded in future).



Okay, let's go!

1. Run Visual Studio. Click File->New->Project
2. Create a new empty project.
3. add a *.cpp source file, like "program.cpp"
4. At this point you can copy-paste "minimal.cpp" from samples\minimal to the file you just created.
Do not try to compile this right now. We have to set include directories, linking settings and so on.
We will cover here only necessary settings.

5. Open "Project Settings" window.
Set "All configurations" and choose "Use Unicode Character Set" from "Character Set" drop-down menu.

Now we have to set proper settings for either Debug and Release configurations.
Therefore, set "Configuration" to "Debug". Go to "Configration Properties"->"C/C++"->General.

In "Additional Include Directories" insert the following entries:
  • $(WXWIN)\Include
  • $(WXWIN)\lib\vc_lib\mswud
It should look pretty like this:



Alright, now go to the "Preprocessor" tab.
Add the following definitions:
  • WIN32
  • _DEBUG
  • __WXMSW__
  • _WINDOWS
  • NOPCH
(Plus, if you are using DLLs, define also WXUSINGDLL)

It's time for linker settings.
Go to "Linker"->"General".

In field named "Additional Library Directories" enter proper path to *.lib files. In my case this is:

(The screen above comes from VS2012)

Okay, now go to the "Input" tab and in the field "Additional Dependencies" we have to enter neccesary *.lib files to make linking run smoothly.
Here we go:
  • wxmsw30ud_propgrid.lib
  • wxmsw30ud_adv.lib
  • wxmsw30ud_core.lib
  • wxbase30ud_xml.lib
  • wxmsw30ud_html.lib
  • wxmsw30ud_xrc.lib
  • wxbase30ud.lib
  • wxtiffd.lib
  • wxjpegd.lib
  • wxpngd.lib
  • wxzlibd.lib
  • wxregexud.lib
  • wxexpatd.lib
  • winmm.lib
  • comctl32.lib
  • rpcrt4.lib
  • wsock32.lib
  • wininet.lib


6. Okay, now time for *Release* build configuration. This time it will be faster. Remember to set "Release" build configuration!

In "Additional Include Directories" insert the following entries:
  • $(WXWIN)\Include
  • $(WXWIN)\lib\vc_lib\mswu
Preprocessor Definitions:
  • WIN32
  • __WXMSW__
  • NDEBUG
  • _WINDOWS
  • NOPCH
  • (optionally) WXUSINGDLL
"Additional Library Directories" - the same as in Debug.

"Additional Dependencies":
  • wxmsw30u_propgrid.lib
  • wxmsw30u_adv.lib
  • wxmsw30u_core.lib
  • wxmsw30u_html.lib
  • wxbase30u_xml.lib
  • wxmsw30u_xrc.lib
  • wxbase30u.lib
  • wxtiff.lib
  • wxjpeg.lib
  • wxpng.lib
  • wxzlib.lib
  • wxregexu.lib
  • wxexpat.lib
  • winmm.lib
  • comctl32.lib
  • rpcrt4.lib
  • wsock32.lib
  • wininet.lib

OK! This is it! :-)
Make sure that both configurations can be built correctly.
All you need to do now is (after some minor tweaks, if you want) click File -> "Export Template".
Then.. just use it. Of course, you can always modify your project and export it again if you want to change some code or settings.

This Minimal Sample

I think wxWidgets has an excellent documentation - it makes no sense in writing the same over and over. After some practice, you will catch what's going on.

The trick I propose:
You may ask yourself: Where is WinMain?
In fact, it's deeply hidden within IMPLEMENT_APP macro.

Sometimes you want to do something "before" GUI starts.
It's kinda simple in wxWidgets:

IMPLEMENT_APP_NO_MAIN(CMyApp)

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
#if defined (_DEBUG) | defined (DEBUG)
 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

 return wxEntry(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
} 
 
As you can see, all you need to do is use IMPLEMENT_APP_NO_MAIN macro. Then, in WinMain you have to call wxEntry to start wxWidgets.

Summary

OK, this is the end of this post. I'm exhausted :) It's my really first one; therefore, I'm curious: Did you like it? Was it useful? Maybe you have any questions?
In the nearest future I will write about XRC (resources system), wxpgex (my property grid library for wxWidgets - more info soon :-) ) and - of course - I'll show how to write advanced color picker.
Preview:
 

Until the next time!

Friday, March 13, 2015

Hello World!

Hello World!

It's time to start a new (well, another one for you) programming blog!

My name is Mateusz (eng: Matthew/Matt).

Waiting for new posts will take some time. In the meantime, you can take a look at my "musical" site:
http://astralis.cba.pl/
"Astralis" is my musical alias.

What you can expect in the future on this blog:
- wxWidgets tutorials (basics and some advanced ones, like state-of-the-art color picker)
- Various coding tutorials (PhysX, NVIDIA GameWorks...)
- General thoughts about programming, gamedev and games (of course)
- Basically everything that interests me (music production, coding, astronomy, sound design... )

Feel free to contact me.

So... prepare for it :)
Cheers!