Skip to content

Instantly share code, notes, and snippets.

@akshayloke
Created October 7, 2016 16:36
Show Gist options
  • Save akshayloke/5da8d84140cbfcd95178da52539921f3 to your computer and use it in GitHub Desktop.
Save akshayloke/5da8d84140cbfcd95178da52539921f3 to your computer and use it in GitHub Desktop.
Implementation for converting RGB data to YUV I420 data. Very useful when you want to encode RGB video feed for writing to file or sending over WebRTC..
varying lowp vec2 vUV;
uniform sampler2D compositeTexture;
uniform highp vec2 screenSize;
uniform highp vec2 halfScreenSize;
uniform highp vec2 doubleScreenSize;
uniform highp vec2 invScreenSize;
uniform highp vec3 numYUVPixels;
lowp float getLuma(highp float inArrayIndex1D) {
highp float sampleDiv = floor(inArrayIndex1D / screenSize.x) + 0.5;
highp float sampleMod = floor(mod(inArrayIndex1D, screenSize.x)) + 0.5;
lowp vec2 yUV = vec2(sampleMod ,sampleDiv) * invScreenSize;
lowp vec3 rgb = texture2D(compositeTexture, yUV).rgb;
lowp float Y = rgb.r * 0.257 + rgb.g * 0.504 + rgb.b * 0.098 + 16.0/255.0;
return Y;
}
lowp float getChromaSubsample(highp float subsampleIndex, bool getU) {
highp float subsampleDiv = floor(subsampleIndex / screenSize.x) + 0.5;
highp float subsampleMod = floor(mod(subsampleIndex, screenSize.x)) + 0.5;
lowp vec2 subsampleUV = vec2(subsampleMod ,subsampleDiv) * invScreenSize;
lowp vec3 rgb = texture2D(compositeTexture, subsampleUV).rgb;
lowp float Y = rgb.r * 0.257 + rgb.g * 0.504 + rgb.b * 0.098 + 16.0/255.0;
lowp float chromaVal = 0.0;
if (getU) {
chromaVal = -0.148 * rgb.r - 0.291 * rgb.g + 0.439 * rgb.b + 0.5;
}
else {
chromaVal = 0.439 * rgb.r - 0.368 * rgb.g - 0.071 * rgb.b + 0.5;
}
return chromaVal;
}
lowp float getChroma(highp float offsetIndex, bool getU) {
highp float offsetDiv = floor(offsetIndex / halfScreenSize.x);
highp float offsetMod = floor(mod(offsetIndex, halfScreenSize.x));
highp float subsampleIndex = doubleScreenSize.x * offsetDiv + offsetMod * 2.0;
lowp float chromaVal = 0.0;
chromaVal += getChromaSubsample(subsampleIndex, getU);
chromaVal += getChromaSubsample(subsampleIndex+1.0, getU);
chromaVal += getChromaSubsample(subsampleIndex+screenSize.x, getU);
chromaVal += getChromaSubsample(subsampleIndex+screenSize.x+1.0, getU);
chromaVal *= 0.25;
return chromaVal;
}
void main() {
lowp vec2 uv = vUV;
//gl_FragCoord goes from 0.5 to w-0.5.. hence need to subtract 0.5 so the indices are calculated correctly
highp vec2 outArrayIndex2D = gl_FragCoord.xy - vec2(0.5);
highp float outArrayIndex1D = outArrayIndex2D.y * screenSize.x + outArrayIndex2D.x;
if (outArrayIndex1D < numYUVPixels.x) { //Luma
highp float inArrayIndex1D = (outArrayIndex1D) * 4.0;
lowp float Y_R = getLuma(inArrayIndex1D + 0.0);
lowp float Y_G = getLuma(inArrayIndex1D + 1.0);
lowp float Y_B = getLuma(inArrayIndex1D + 2.0);
lowp float Y_A = getLuma(inArrayIndex1D + 3.0);
//output format is BGRA
gl_FragColor = vec4(Y_B, Y_G, Y_R, Y_A);
}
else if (outArrayIndex1D < numYUVPixels.y) { //Chroma U
highp float offsetIndex = (outArrayIndex1D - numYUVPixels.x) * 4.0;
lowp float U_R = getChroma(offsetIndex+0.0, true);
lowp float U_G = getChroma(offsetIndex+1.0, true);
lowp float U_B = getChroma(offsetIndex+2.0, true);
lowp float U_A = getChroma(offsetIndex+3.0, true);
//output format is BGRA
gl_FragColor = vec4(U_B, U_G, U_R, U_A);
}
else if (outArrayIndex1D < numYUVPixels.z) { //Chroma V
highp float offsetIndex = (outArrayIndex1D - numYUVPixels.y) * 4.0;
lowp float V_R = getChroma(offsetIndex+0.0, false);
lowp float V_G = getChroma(offsetIndex+1.0, false);
lowp float V_B = getChroma(offsetIndex+2.0, false);
lowp float V_A = getChroma(offsetIndex+3.0, false);
//output format is BGRA
gl_FragColor = vec4(V_B, V_G, V_R, V_A);
}
else {
discard;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment