// created by florian berger (flockaroo) - 2018 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. // oil paint brush drawing // calculating and drawing drawing the brush strokes #define MULTI_STROKE //#NumTriangles 0x10000 #ifndef __UNITY3D__ ivec2 ivec2i(int x) { return ivec2(x,x); } ivec2 ivec2i(vec2 x) { return ivec2(x); } vec2 vec2i(int x) { return vec2(x,x); } vec2 vec2i(ivec2 x) { return vec2(x); } vec2 vec2i(float x) { return vec2(x,x); } vec3 vec3i(float x) { return vec3(x,x,x); } vec3 vec3i(vec2 a, float b) { return vec3(a,b); } vec3 vec3i(vec2 a, int b) { return vec3(a,b); } vec4 vec4i(vec3 v,int x) { return vec4(v,x); } #define mul(m,v) (v*m) #endif #ifdef __UNITY3D__ uniform int NumTriangles; #else #define NumTriangles 0x10000 #endif #define Res (iResolution.xy) #define Res0 vec2(textureSize(iChannel0,0)) #define Res1 vec2(textureSize(iChannel1,0)) #define PI 3.1415927 #define N(v) (v.yx*vec2(1,-1)) vec4 getRand(vec2 pos) { return textureLod(iChannel1,pos/Res1,0.); } vec4 getRand(int idx) { ivec2 rres=textureSize(iChannel1,0); idx=idx%(rres.x*rres.y); return texelFetch(iChannel1,ivec2(idx%rres.x,idx/rres.x),0); } uniform float SrcContrast; uniform float SrcBright; uniform float SrcColor; uniform float SrcBlur; vec4 getCol(vec2 pos, float lod) { // use max(...) for fitting full image or min(...) for fitting only one dir vec2 uv = (pos-.5*Res)*min(Res0.y/Res.y,Res0.x/Res.x)/Res0+.5; vec2 mask = step(vec2i(-.5),-abs(uv-.5)); vec4 c=textureLod(iChannel0,uv,lod+SrcBlur*(log2(Res.x)-1.)); float br=dot(c.xyz,vec3(.333,.333,.333)); c=(c-br)*SrcColor+br; c=(c-.5)*SrcContrast+.5; c*=SrcBright; return clamp(c,0.,1.)*mask.x*mask.y; //return clamp(((textureLod(iChannel0,uv,lod+SrcBlur*(log2(Res.x)-1.))-.5)*SrcContrast+.5*SrcBright),0.,1.)*mask.x*mask.y; } uniform float FlickerStrength; uniform float FlickerFreq; float flickerParam; vec3 getValCol(vec2 pos, float lod) { return getCol(pos,1.5+log2(Res0.x/600.)+lod).xyz*.7+getCol(pos,3.5+log2(Res0.x/600.)+lod).xyz*.3+.003*getRand(pos*.1+flickerParam*10.).xyz; //return getCol(pos,.5+lod).xyz*.7+getCol(pos,lod+2.5).xyz*0.3+.003*getRand(pos*.1+iTime*FlickerStrength*10.).xyz; } float compsignedmax(vec3 c) { vec3 s=sign(c); vec3 a=abs(c); if (a.x>a.y && a.x>a.z) return c.x; if (a.y>a.x && a.y>a.z) return c.y; return c.z; } vec2 getGradMax(vec2 pos, float eps) { vec2 d=vec2(eps,0); float lod = log2(2.*eps*Res0.x/Res.x); lod=0.; return vec2( compsignedmax(getValCol(pos+d.xy,lod)-getValCol(pos-d.xy,lod)), compsignedmax(getValCol(pos+d.yx,lod)-getValCol(pos-d.yx,lod)) )/eps/2.; } vec2 quad(vec2 p1, vec2 p2, vec2 p3, vec2 p4, int idx) { #ifdef __UNITY3D__ vec2 p[6] = {p1,p2,p3,p2,p4,p3}; #else vec2[6] p = vec2[6](p1,p2,p3,p2,p4,p3); #endif return p[idx%6]; } uniform float BrushDetail; uniform float StrokeBend; int bitinv(int x, int bits) { int ret=0; for(int i=0;i>i)&1)<<(bits-1-i); return ret; } int SCRAMBLE(int idx, int num) { return idx; // sort of a generalized bit conversion - exchange half domains until smallest scale int idx0=0; for(int i=0;i<15;i++) { if(idx-idx0>=num-num/2) { idx=idx-(num-num/2); idx0+=0; num=num/2; } else { idx=idx+num/2; idx0+=num/2; num=num-num/2; } if (num<=0) break; } return idx; } uniform float BrushSize; //uniform float StrokeThresh; uniform float LayerScale; uniform float StrokeAng; uniform float ColorSpread; #define CS(ang) cos(ang-vec2(0,PI/2.)) mat2 ROT2(float ang) { vec2 b=CS(ang); return mat2(b,b.yx*vec2(-1,1)); } // HSV <-> RGB from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void mainGeom( out vec4 vertCoord, inout vec4 vertAttrib[3], int vertIndex ) { flickerParam = ((iTime-mod(iTime,1.0/max(FlickerFreq,1.)))*FlickerStrength); vertCoord=vec4(0,0,0,1); int pidx = vertIndex/6; float idxFact = float(pidx)/float(NumTriangles/2); vec3 brushPos; int layerScalePercent = int(floor(LayerScale*100.)); float ls = pow(float(layerScalePercent)/100.,2.); //float pow(ls, int NumGrid=int(float(NumTriangles/2)*(1.-ls)); float aspect=Res.x/Res.y; int NumX = int(sqrt(float(NumGrid)*aspect)); int NumY = int(sqrt(float(NumGrid)/aspect)); //int pidx2 = NumX*NumY*4/3-pidx; int pidx2 = NumTriangles/2-pidx; int NumX2=NumX; int NumY2=NumY; int layer=0; //int maxLayer=int(-log(float(NumY))/log(float(layerScalePercent)/100.)); for(int i=0; i<20; i++) { if(pidx2max(Res.x,Res.y)*.1) ? 0. : wh; //float StrokeThresh = iMouse.x/iResolution.x; //wh = (layer!=int(StrokeThresh*20.)-1 && int(StrokeThresh*20.)>0) ? 0. : wh; wh = (gl*BrushDetail<.003/wh0 && wh0