Back to shaders

Shader test bench

CMYK Halftone.fs

vidvox-isf-files utility glsl runnable fragment MIT
Source
runnable fragment

Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.

Code

uniform sampler2D inputImage;
uniform float gridSize;
uniform float smoothing;
precision mediump float;
/*{
    "CATEGORIES": [
        "Halftone Effect",
        "Retro"
    ],
    "CREDIT": "by zoidberg",
    "INPUTS": [
        {
            "NAME": "inputImage",
            "TYPE": "image"
        },
        {
            "DEFAULT": 45,
            "MAX": 256,
            "MIN": 1,
            "NAME": "gridSize",
            "TYPE": "float"
        },
        {
            "DEFAULT": 0.15,
            "MAX": 1,
            "MIN": 0,
            "NAME": "smoothing",
            "TYPE": "float"
        }
    ],
    "ISFVSN": "2"
}
*/

const vec4		gridRot = vec4(15.0, 45.0, 0.0, 75.0);




vec4 RGBAtoCMYK(vec4 inColor)
{
	vec4		ret;
	ret.w = 1.0 - max(max(inColor.x, inColor.y), inColor.z);
	ret.x = (1.0-inColor.x-ret.w)/(1.0-ret.w);
	ret.y = (1.0-inColor.y-ret.w)/(1.0-ret.w);
	ret.z = (1.0-inColor.z-ret.w)/(1.0-ret.w);
	return ret;
}
vec4 CMYKtoRGBA(vec4 inColor)
{
	vec4		ret;
	ret.xyz = (1.0-inColor.xyz)*(1.0-inColor.w);
	ret.w = 1.0;
	return ret;
}

void main() {
	//	a halftone is an overlapping series of grids of dots
	//	each grid of dots is rotated by a different amount
	//	the size of the dots determines the colors. the shape of the dot should never change (always be a dot with regular edges)
	
	vec4		cmykAmounts = vec4(0.0);
	
	//	for each of the channels (i) of CMYK...
	for (int i=0; i<4; ++i)	{
		//	figure out the rotation of the grid in radians
		float		rotRad = radians(gridRot[i]);
		//	the grids are rotated counter-clockwise- to find the nearest dot, take the fragment pixel loc, 
		//	rotate it clockwise, and split by the grid to find the center of the dot. then rotate this 
		//	coord counter-clockwise to yield the location of the center of the dot in pixel coords local to the render space
		mat2		ccTrans = mat2(vec2(cos(rotRad), sin(rotRad)), vec2(-1.0*sin(rotRad), cos(rotRad)));
		mat2		cTrans = mat2(vec2(cos(rotRad), -1.0*sin(rotRad)), vec2(sin(rotRad), cos(rotRad)));
		
		//	render loc -> grid loc -> grid dot loc -> grid dot loc in render coords -> pixel color under grid dot loc
		vec2		gridFragLoc = cTrans * gl_FragCoord.xy;
		vec2		gridDotLoc = vec2(floor(gridFragLoc.x/gridSize)*gridSize, floor(gridFragLoc.y/gridSize)*gridSize);
		gridDotLoc = gridDotLoc + vec2(gridSize/2.0);
		vec2		renderDotLoc = ccTrans * gridDotLoc;
		vec4		renderDotImageColorRGB = texture2D(inputImage, (renderDotLoc) / u_resolution.xy);
		vec4		renderDotImageColorCMYK = RGBAtoCMYK(renderDotImageColorRGB);
		
		float		channelAmount = renderDotImageColorCMYK[i];
		float		dotRadius = channelAmount * (gridSize/2.0);
		float		fragDistanceToGridCenter = distance(gl_FragCoord.xy, renderDotLoc);
		//	the amount of the channel depends on the distance to the center of the grid, the size of the dot, and smoothing
		float		smoothDist = smoothing * (gridSize/6.0);
		cmykAmounts[i] += smoothstep(dotRadius, dotRadius-(dotRadius*smoothing), fragDistanceToGridCenter);
	}
	
	gl_FragColor = CMYKtoRGBA(cmykAmounts);
}