forked from sobotka/AgX
-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
AgX.hlsl
271 lines (222 loc) · 7.14 KB
/
AgX.hlsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
HLSL implementation of AgX (by Troy Sobotka) for OBS.
author = "Liam Collod"
repository = "https://github.com/MrLixm/AgXc"
References:
- [0] https://github.com/sobotka/AgX-S2O3/blob/main/AgX.py
- [1] https://github.com/Unity-Technologies/Graphics/blob/master/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl
- [2] https://video.stackexchange.com/q/9866
- [3] https://github.com/Fubaxiusz/fubax-shaders/blob/master/Shaders/LUTTools.fx
- [4] https://github.com/Unity-Technologies/Graphics/blob/master/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl#L574
- [5] https://github.com/colour-science/colour/blob/develop/colour/models/rgb/transfer_functions/srgb.py#L99
*/
#include "colorspace.hlsl"
// OBS-specific syntax adaptation to HLSL standard to avoid errors reported by the code editor
#define SamplerState sampler_state
#define Texture2D texture2d
// Uniform variables set by OBS (required)
uniform float4x4 ViewProj; // View-projection matrix used in the vertex shader
uniform Texture2D image; // Texture containing the source picture
uniform int INPUT_COLORSPACE = 1;
uniform int OUTPUT_COLORSPACE = 1;
/*
colorspaceid_Passthrough = 0;
colorspaceid_sRGB_Display_EOTF = 1;
colorspaceid_sRGB_Display_2_2 = 2;
colorspaceid_sRGB_Linear = 3;
colorspaceid_BT_709_Display_2_4 = 4;
colorspaceid_DCIP3_Display_2_6 = 5;
colorspaceid_Apple_Display_P3 = 6;
*/
uniform int DRT = 1;
uniform float INPUT_EXPOSURE = 0.75;
uniform float INPUT_GAMMA = 1.0;
uniform float INPUT_SATURATION = 1.0;
uniform float INPUT_HIGHLIGHT_GAIN = 0.0;
uniform float INPUT_HIGHLIGHT_GAIN_GAMMA = 1.0;
uniform float PUNCH_EXPOSURE = 0.0;
uniform float PUNCH_SATURATION = 1.0;
uniform float PUNCH_GAMMA = 1.3;
uniform bool USE_OCIO_LOG = false;
uniform bool APPLY_OUTSET = true;
// LUT AgX-default_contrast.lut.png
uniform texture2d AgXLUT;
#define AgXLUT_BLOCK_SIZE 32
#define AgXLUT_DIMENSIONS int2(AgXLUT_BLOCK_SIZE * AgXLUT_BLOCK_SIZE, AgXLUT_BLOCK_SIZE)
#define AgXLUT_PIXEL_SIZE 1.0 / AgXLUT_DIMENSIONS
#define colorspaceid_working_space colorspaceid_sRGB_Linear
uniform int drt_id_none = 0;
uniform int drt_id_agx = 1;
/*=================
OBS BOILERPLATE
=================*/
// Interpolation method and wrap mode for sampling a texture
sampler_state linear_clamp
{
Filter = Linear; // Anisotropy / Point / Linear
AddressU = Clamp; // Wrap / Clamp / Mirror / Border / MirrorOnce
AddressV = Clamp; // Wrap / Clamp / Mirror / Border / MirrorOnce
BorderColor = 00000000; // Used only with Border edges (optional)
};
sampler_state LUTSampler
{
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
AddressW = Clamp;
};
struct VertexData
{
float4 pos : POSITION; // Homogeneous space coordinates XYZW
float2 uv : TEXCOORD0; // UV coordinates in the source picture
};
struct PixelData
{
float4 pos : POSITION; // Homogeneous screen coordinates XYZW
float2 uv : TEXCOORD0; // UV coordinates in the source picture
};
/*=================
Main processes
=================*/
float3 applyInputTransform(float3 Image)
/*
Convert input to workspace colorspace (sRGB)
*/
{
return convertColorspaceToColorspace(Image, INPUT_COLORSPACE, colorspaceid_working_space);
}
float3 applyGrading(float3 Image)
/*
Apply creative grading operations (pre-display-transform).
*/
{
float ImageLuma = powsafe(get_luminance(Image), INPUT_HIGHLIGHT_GAIN_GAMMA);
Image += Image * ImageLuma.xxx * INPUT_HIGHLIGHT_GAIN;
Image = saturation(Image, INPUT_SATURATION);
Image = powsafe(Image, INPUT_GAMMA);
Image *= powsafe(2.0, INPUT_EXPOSURE);
return Image;
}
float3 applyAgXLog(float3 Image)
/*
Prepare the data for display encoding. Converted to log domain.
*/
{
float3x3 agx_compressed_matrix = {
0.84247906, 0.0784336, 0.07922375,
0.04232824, 0.87846864, 0.07916613,
0.04237565, 0.0784336, 0.87914297
};
Image = max(0.0, Image); // clamp negatives
// why this doesn't work ??
// Image = mul(agx_compressed_matrix, Image);
Image = apply_matrix(Image, agx_compressed_matrix);
if (USE_OCIO_LOG)
Image = cctf_log2_ocio_transform(Image);
else
Image = cctf_log2_normalized_from_open_domain(Image, -10.0, 6.5);
Image = clamp(Image, 0.0, 1.0);
return Image;
}
float3 applyAgXLUT(float3 Image)
/*
Apply the AgX 1D curve on log encoded data.
The output is similar to AgX Base which is considered
sRGB - Display, but here we linearize it.
-- ref[3] for LUT implementation
*/
{
float3 lut3D = Image * (AgXLUT_BLOCK_SIZE-1);
float2 lut2D[2];
// Front
lut2D[0].x = floor(lut3D.z) * AgXLUT_BLOCK_SIZE+lut3D.x;
lut2D[0].y = lut3D.y;
// Back
lut2D[1].x = ceil(lut3D.z) * AgXLUT_BLOCK_SIZE+lut3D.x;
lut2D[1].y = lut3D.y;
// Convert from texel to texture coords
lut2D[0] = (lut2D[0]+0.5) * AgXLUT_PIXEL_SIZE;
lut2D[1] = (lut2D[1]+0.5) * AgXLUT_PIXEL_SIZE;
// Bicubic LUT interpolation
Image = lerp(
AgXLUT.Sample(LUTSampler, lut2D[0]).rgb, // Front Z
AgXLUT.Sample(LUTSampler, lut2D[1]).rgb, // Back Z
frac(lut3D.z)
);
// LUT apply the transfer function so we remove it to keep working on linear data.
Image = cctf_decoding_Power_2_2(Image);
return Image;
}
float3 applyOutset(float3 Image)
/*
Outset is the inverse of the inset applied during `applyAgXLog`
and restore chroma.
*/
{
float3x3 agx_compressed_matrix_inverse = {
1.1968790, -0.09802088, -0.09902975,
-0.05289685, 1.15190313, -0.09896118,
-0.05297163, -0.09804345, 1.15107368
};
Image = apply_matrix(Image, agx_compressed_matrix_inverse);
return Image;
}
float3 applyDRT(float3 Image)
/*
Apply the DRT selected by the user.
*/
{
if (DRT == drt_id_agx){
Image = applyAgXLog(Image);
Image = applyAgXLUT(Image);
if (APPLY_OUTSET)
Image = applyOutset(Image);
}
return Image;
}
float3 applyODT(float3 Image)
/*
Apply Agx to display conversion.
:param color: linear - sRGB data.
*/
{
return convertColorspaceToColorspace(Image, colorspaceid_working_space, OUTPUT_COLORSPACE);
}
float3 applyLookPunchy(float3 Image)
/*
Applies the post "Punchy" look to display-encoded data.
Input is expected to be in a display-state.
*/
{
Image = powsafe(Image, PUNCH_GAMMA);
Image = saturation(Image, PUNCH_SATURATION);
Image *= powsafe(2.0, PUNCH_EXPOSURE); // not part of initial cdl
return Image;
}
PixelData VERTEXSHADER_AgX(VertexData vertex)
{
PixelData pixel;
pixel.pos = mul(float4(vertex.pos.xyz, 1.0), ViewProj);
pixel.uv = vertex.uv;
return pixel;
}
float4 PIXELSHADER_AgX(PixelData pixel) : TARGET
{
float4 OriginalImage = image.Sample(linear_clamp, pixel.uv);
float3 Image = OriginalImage.rgb;
Image = applyInputTransform(Image);
Image = applyGrading(Image);
Image = applyDRT(Image);
Image = applyODT(Image);
Image = applyLookPunchy(Image);
Image = convertColorspaceToColorspace(Image, 1, 4);
return float4(Image.rgb, OriginalImage.a);
}
technique Draw
{
pass
{
vertex_shader = VERTEXSHADER_AgX(vertex);
pixel_shader = PIXELSHADER_AgX(pixel);
}
}