1#version 130 2 3/* 4 Colorimetry shader 5 Ported from Drag's NES Palette Generator 6 http://drag.wootest.net/misc/palgen.html 7*/ 8 9// Parameter lines go here: 10#pragma parameter color_mode "Colorimetry mode" 0.0 0.0 2.0 1.0 11#pragma parameter white_point_d93 "Use D93 white point" 1.0 0.0 1.0 1.0 12#pragma parameter clipping_method "Color clipping method" 0.0 0.0 2.0 1.0 13 14#if defined(VERTEX) 15 16#if __VERSION__ >= 130 17#define COMPAT_VARYING out 18#define COMPAT_ATTRIBUTE in 19#define COMPAT_TEXTURE texture 20#else 21#define COMPAT_VARYING varying 22#define COMPAT_ATTRIBUTE attribute 23#define COMPAT_TEXTURE texture2D 24#endif 25 26#ifdef GL_ES 27#define COMPAT_PRECISION mediump 28#else 29#define COMPAT_PRECISION 30#endif 31 32COMPAT_ATTRIBUTE vec4 VertexCoord; 33COMPAT_ATTRIBUTE vec4 COLOR; 34COMPAT_ATTRIBUTE vec4 TexCoord; 35COMPAT_VARYING vec4 COL0; 36COMPAT_VARYING vec4 TEX0; 37 38vec4 _oPosition1; 39uniform mat4 MVPMatrix; 40uniform COMPAT_PRECISION int FrameDirection; 41uniform COMPAT_PRECISION int FrameCount; 42uniform COMPAT_PRECISION vec2 OutputSize; 43uniform COMPAT_PRECISION vec2 TextureSize; 44uniform COMPAT_PRECISION vec2 InputSize; 45 46void main() 47{ 48 gl_Position = MVPMatrix * VertexCoord; 49 COL0 = COLOR; 50 TEX0.xy = TexCoord.xy; 51} 52 53#elif defined(FRAGMENT) 54 55#ifdef GL_ES 56#ifdef GL_FRAGMENT_PRECISION_HIGH 57precision highp float; 58#else 59precision mediump float; 60#endif 61#define COMPAT_PRECISION mediump 62#else 63#define COMPAT_PRECISION 64#endif 65 66#if __VERSION__ >= 130 67#define COMPAT_VARYING in 68#define COMPAT_TEXTURE texture 69out COMPAT_PRECISION vec4 FragColor; 70#else 71#define COMPAT_VARYING varying 72#define FragColor gl_FragColor 73#define COMPAT_TEXTURE texture2D 74#endif 75 76uniform COMPAT_PRECISION int FrameDirection; 77uniform COMPAT_PRECISION int FrameCount; 78uniform COMPAT_PRECISION vec2 OutputSize; 79uniform COMPAT_PRECISION vec2 TextureSize; 80uniform COMPAT_PRECISION vec2 InputSize; 81uniform sampler2D Texture; 82COMPAT_VARYING vec4 TEX0; 83 84// compatibility #defines 85#define Source Texture 86#define vTexCoord TEX0.xy 87 88#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize 89 90#ifdef PARAMETER_UNIFORM 91uniform COMPAT_PRECISION float color_mode; 92uniform COMPAT_PRECISION float white_point_d93; 93uniform COMPAT_PRECISION float clipping_method; 94#else 95#define color_mode 0.0 96#define white_point_d93 0.0 97#define clipping_method 0.0 98#endif 99 100vec3 huePreserveClipDarken(float r, float g, float b) 101{ 102 float ratio = 1.0; 103 if ((r > 1.0) || (g > 1.0) || (b > 1.0)) 104 { 105 float max = r; 106 if (g > max) 107 max = g; 108 if (b > max) 109 max = b; 110 ratio = 1.0 / max; 111 } 112 113 r *= ratio; 114 g *= ratio; 115 b *= ratio; 116 117 r = clamp(r, 0.0, 1.0); 118 g = clamp(g, 0.0, 1.0); 119 b = clamp(b, 0.0, 1.0); 120 121 return vec3(r, g, b); 122} 123 124vec3 huePreserveClipDesaturate(float r, float g, float b) 125{ 126 float l = (.299 * r) + (0.587 * g) + (0.114 * b); 127 bool ovr = false; 128 float ratio = 1.0; 129 130 if ((r > 1.0) || (g > 1.0) || (b > 1.0)) 131 { 132 ovr = true; 133 float max = r; 134 if (g > max) max = g; 135 if (b > max) max = b; 136 ratio = 1.0 / max; 137 } 138 139 if (ovr) 140 { 141 r -= 1.0; 142 g -= 1.0; 143 b -= 1.0; 144 r *= ratio; 145 g *= ratio; 146 b *= ratio; 147 r += 1.0; 148 g += 1.0; 149 b += 1.0; 150 } 151 152 r = clamp(r, 0.0, 1.0); 153 g = clamp(g, 0.0, 1.0); 154 b = clamp(b, 0.0, 1.0); 155 156 return vec3(r, g, b); 157} 158 159vec3 ApplyColorimetry(vec3 color) 160{ 161 //http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html 162 //http://www.dr-lex.be/random/matrix_inv.html 163 // Convert the (x,y) values to X Y Z. 164 165 float R = color.r; 166 float G = color.g; 167 float B = color.b; 168 169 float Rx = 0.0; 170 float Ry = 0.0; 171 float Gx = 0.0; 172 float Gy = 0.0; 173 float Bx = 0.0; 174 float By = 0.0; 175 float Wx = 0.0; 176 float Wy = 0.0; 177 178 if (white_point_d93 > 0.5) 179 { 180 Wx = 0.31; 181 Wy = 0.316; 182 } 183 else 184 { 185 Wx = 0.3127; 186 Wy = 0.329; 187 } 188 189 if (color_mode < 0.5) 190 { 191 // FCC 1953 192 // Original FCC standard for the color of the phosphors 193 Rx = 0.67; 194 Ry = 0.33; 195 Gx = 0.21; 196 Gy = 0.71; 197 Bx = 0.14; 198 By = 0.08; 199 } 200 else if (color_mode == 1.0) 201 { 202 // SMPTE C (1987) 203 // A newer standard for the color of the phospors. (Not used in Japan) 204 Rx = 0.63; 205 Ry = 0.34; 206 Gx = 0.31; 207 Gy = 0.595; 208 Bx = 0.155; 209 By = 0.07; 210 } 211 else 212 { 213 //sRGB (PC Monitors) 214 //The colorimetry used in PC monitors, like the one you're (probably) looking at right now. 215 Rx = 0.64; 216 Ry = 0.33; 217 Gx = 0.3; 218 Gy = 0.6; 219 Bx = 0.15; 220 By = 0.06; 221 } 222 223 float Xr = Rx / Ry; 224 float Xg = Gx / Gy; 225 float Xb = Bx / By; 226 float Xw = Wx / Wy; 227 float Yr = 1.0; 228 float Yg = 1.0; 229 float Yb = 1.0; 230 float Yw = 1.0; 231 float Zr = (1.0 - Rx - Ry) / Ry; 232 float Zg = (1.0 - Gx - Gy) / Gy; 233 float Zb = (1.0 - Bx - By) / By; 234 float Zw = (1.0 - Wx - Wy) / Wy; 235 236 // Get ready for a bunch of painful math. I need to invert a matrix, then multiply it by a vector. 237 // Determinant for inverse matrix 238 239 float sDet = (Xr*((Zb*Yg)-(Zg*Yb)))-(Yr*((Zb*Xg)-(Zg*Xb)))+(Zr*((Yb*Xg)-(Yg*Xb))); 240 241 float Sr = ((((Zb*Yg)-(Zg*Yb))/sDet)*Xw) + ((-((Zb*Xg)-(Zg*Xb))/sDet)*Yw) + ((((Yb*Xg)-(Yg*Xb))/sDet)*Zw); 242 float Sg = ((-((Zb*Yr)-(Zr*Yb))/sDet)*Xw) + ((((Zb*Xr)-(Zr*Xb))/sDet)*Yw) + ((-((Yb*Xr)-(Yr*Xb))/sDet)*Zw); 243 float Sb = ((((Zg*Yr)-(Zr*Yg))/sDet)*Xw) + ((-((Zg*Xr)-(Zr*Xg))/sDet)*Yw) + ((((Yg*Xr)-(Yr*Xg))/sDet)*Zw); 244 245 // This should be the completed RGB -> XYZ matrix. 246 // Multiply each of the first three members by R, then add them together to get X 247 float convMatrix[9] = float[] (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); 248 249 convMatrix[0] = Sr*Xr; 250 convMatrix[1] = Sg*Xg; 251 convMatrix[2] = Sb*Xb; 252 convMatrix[3] = Sr*Yr; 253 convMatrix[4] = Sg*Yg; 254 convMatrix[5] = Sb*Yb; 255 convMatrix[6] = Sr*Zr; 256 convMatrix[7] = Sg*Zg; 257 convMatrix[8] = Sb*Zb; 258 259 // Convert RGB to XYZ using the matrix generated with the specified RGB and W points. 260 float X = (convMatrix[0] * R) + (convMatrix[1] * G) + (convMatrix[2] * B); 261 float Y = (convMatrix[3] * R) + (convMatrix[4] * G) + (convMatrix[5] * B); 262 float Z = (convMatrix[6] * R) + (convMatrix[7] * G) + (convMatrix[8] * B); 263 264 // This is the conversion matrix for CIEXYZ -> sRGB. I nicked this from: 265 // http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html 266 // and I know it's right because when you use the sRGB colorimetry, this matrix produces identical results to 267 // just using the raw R, G, and B above. 268 float xyztorgb[9] = float[] (3.2404, -1.5371, -0.4985, -0.9693, 1.876, 0.0416, 0.0556, -0.204, 1.0572); 269 270 // Convert back to RGB using the XYZ->sRGB matrix. 271 R = (xyztorgb[0]*X) + (xyztorgb[1]*Y) + (xyztorgb[2]*Z); 272 G = (xyztorgb[3]*X) + (xyztorgb[4]*Y) + (xyztorgb[5]*Z); 273 B = (xyztorgb[6]*X) + (xyztorgb[7]*Y) + (xyztorgb[8]*Z); 274 275 vec3 corrected_rgb; 276 277 // Apply desired clipping method to out-of-gamut colors. 278 if (clipping_method < 0.5) 279 { 280 //If a channel is out of range (> 1.0), it's simply clamped to 1.0. This may change hue, saturation, and/or lightness. 281 R = clamp(R, 0.0, 1.0); 282 G = clamp(G, 0.0, 1.0); 283 B = clamp(B, 0.0, 1.0); 284 corrected_rgb = vec3(R, G, B); 285 } 286 else if (clipping_method == 1.0) 287 { 288 //If any channels are out of range, the color is darkened until it is completely in range. 289 corrected_rgb = huePreserveClipDarken(R, G, B); 290 } 291 else if (clipping_method == 2.0) 292 { 293 //If any channels are out of range, the color is desaturated towards the luminance it would've had. 294 corrected_rgb = huePreserveClipDesaturate(R, G, B); 295 } 296 297 return corrected_rgb; 298} 299 300void main() 301{ 302 vec4 c = COMPAT_TEXTURE(Source, vTexCoord.xy); 303 vec3 rgb = vec3(c.r, c.g, c.b); 304 305 vec3 out_color = ApplyColorimetry(rgb); 306 FragColor = vec4(out_color, 1.0); 307} 308#endif 309