$input v_color0 $input v_normal $input v_uv0 $input v_wpos #include "../common.sh" SAMPLER2D(s_texColor, 0); SAMPLER3D(s_ditherSampler, 1); SAMPLER2D(s_rampSampler, 2); uniform vec4 u_time; uniform vec4 u_dotColor; uniform vec4 u_texInfo; uniform vec4 u_baseColor; float calcBrightness(vec3 lightPos, vec3 vertPos, vec3 normal) { vec3 lightDir = normalize(lightPos - vertPos); float diffuse = max(0.0, dot(lightDir, normal)); return diffuse * clamp(1.0 - distance(lightPos, vertPos) * 0.01, 0.0, 1.0); } float calcBrightnessDirectional(vec3 sunDir, vec3 normal) { return max(0.0, dot(sunDir, normal)); } vec3 desaturate(vec3 color) { return vec3_splat(dot(color, vec3(0.33, 0.34, 0.33))); } float dither(float brightness, vec2 inputUv) { float globalScale = 6; // constants float xRes = u_texInfo.z; float dotsPerSide = xRes / 16; float dotsTotal = dotsPerSide * dotsPerSide; float invXRes = 1 / xRes; float zRes = dotsTotal; float invZRes = 1 / zRes; vec2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5); float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r; // Magic dot frequency calculation vec2 dx = dFdx(inputUv); vec2 dy = dFdy(inputUv); mat2 mat = mat2(dx, dy); vec4 vectorized = vec4(dx, dy); float qq = dot(vectorized, vectorized); float rr = determinant(mat); float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr); float discriminant = sqrt(discriminantSqr); vec2 freq = sqrt(vec2(qq + discriminant, qq - discriminant) / 2.0); // Figuring out how many dots we want float scaleExp = exp2(globalScale); float spacing = freq.y * scaleExp; spacing *= dotsPerSide * (1 / globalScale); // todo: just guessed that globalScale is the right variable here float brightnessSpacingMultiplier = pow(brightnessCurve * 2 + 0.001, -(1 - 0.5)); spacing *= brightnessSpacingMultiplier; float spacingLog = log2(spacing); int patternScaleLevel = int(floor(spacingLog)); float patternFractional = spacingLog - patternScaleLevel; vec2 uv = inputUv / exp2(patternScaleLevel); // patternFractional *= patternFractional; // patternFractional = smoothstep(0, 1, patternFractional); float subLayer = lerp(0.25 * dotsTotal, dotsTotal, 1 - patternFractional); subLayer = (subLayer - 0.5) * invZRes; float pattern = texture3D(s_ditherSampler, vec3(uv, subLayer)).r; float contrast = 0.5 * scaleExp * brightnessSpacingMultiplier * 0.1; contrast *= pow(freq.y / freq.x, 1.0); float baseVal = lerp(0.5, brightness, saturate(1.05 / (1 + contrast))); float threshold = 1 - brightnessCurve; return saturate((pattern - threshold) * contrast + baseVal); } vec4 rgbToCmyk(vec3 rgb) { return vec4(1.0, 1.0, 1.0, 1.0); } vec3 cmykToRgb(vec4 cmyk) { return vec3(1.0, 1.0, 1.0); } vec2 rotateUV(vec2 uv, vec2 angle) { return uv; } void main() { // setup bool isTextured = u_dotColor.x < 0.02; float testRadius = 30.0; float testSpeed = 1.0; vec3 testOffset = vec3(0.0, 0.0, 50.0); vec3 lightPos = vec3(sin(u_time.x * testSpeed) * testRadius, 5.0, cos(u_time.x * testSpeed) * testRadius); vec3 baseColor = u_baseColor.xyz; vec3 texColor = isTextured ? texture2D(s_texColor, v_uv0).xyz : u_dotColor.xyz; // lighting float brightness = lerp(0.2, 0.8, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal)); // dither float r = dither(texColor.r * brightness, v_uv0); float g = dither(texColor.g * brightness, v_uv0); float b = dither(texColor.b * brightness, v_uv0); vec3 ditheredColor = vec3(r,g,b); vec4 cmyk = rgbToCmyk(texColor * brightness); cmyk.x = dither(cmyk.x, rotateUV(v_uv0, float2(0.966, 0.259))); cmyk.y = dither(cmyk.y, rotateUV(v_uv0, float2(0.259, 0.966))); cmyk.z = dither(cmyk.z, rotateUV(v_uv0, float2(1.000, 0.000))); cmyk.w = dither(cmyk.w, rotateUV(v_uv0, float2(0.707, 0.707))); // vec3 ditheredColor = cmykToRgb(cmyk); // finalize vec3 finalColor = mix(baseColor, ditheredColor, ditheredColor); gl_FragColor = vec4(finalColor, 1.0); // gl_FragColor = vec4(texColor * brightness, 1.0); }