memoscan
← all memos

Memo 0xe7d8a500…f62c80 on Ethereum

his.cellsWide=t(Y/this.cellSize),this.cellsHigh=t(P/this.cellSize);const{cellsWide:l,cellsHigh:i}=this;this.segmentMetaDataTexture={texture:new e.DataTexture(new Float32Array(l*i*2),l,i,e.RGFormat,e.FloatType),width:l,height:i},this.segmentMetaDataTexture.texture.needsUpdate=!0,this.segmentIndexTexture={texture:new qt(n,n,e.RedFormat,e.FloatType,new Float32Array(n*n)),width:n,height:n},this.segmentIndexTexture.texture.setRenderer(X)}updatePolylines(e,t=!1){this.isClosed=t,this.polyLines=e,this.texturesInitialised||(this.initializeTextures(),this.texturesInitialised=!0),this.buildSegmentRecords(),this.buildSegmentCellIndex(),this.buildSegmentDataTextures()}buildSegmentRecords(){const e=this.polyLines.reduce(((e,t)=>{const n=this.isClosed?t.length:t.length-1;return n>0?e+n:e}),0);this._segmentRecords=new Float32Array(8*e);let t=0;this.polyLines.forEach(((e,n)=>{let l=0;for(let t=0;t<e.length-1;t++)l+=$t(e[t],e[t+1]);this.isClosed&&e.length>1&&(l+=$t(e[e.length-1],e[0]));let i=0;const a=this.isClosed?e.length:e.length-1;for(let s=0;s<a;s++){const a=e[s],o=e[(s+1)%e.length],r=$t(a,o);this._segmentRecords[t++]=a[0],this._segmentRecords[t++]=a[1],this._segmentRecords[t++]=o[0],this._segmentRecords[t++]=o[1],this._segmentRecords[t++]=n,this._segmentRecords[t++]=i,this._segmentRecords[t++]=i+r,this._segmentRecords[t++]=l,i+=r}}))}buildSegmentCellIndex(){const e=this.cellsWide*this.cellsHigh;this._cellCounts=new Float32Array(e),this._cellOffsets=new Float32Array(e);const n=this._segmentRecords.length/8;let l=0;for(let e=0;e<n;e++){const e=this._segmentRecords[l+0],n=this._segmentRecords[l+1],i=this._segmentRecords[l+2],a=this._segmentRecords[l+3];l+=8;const o=t(e/this.cellSize),r=t(n/this.cellSize),c=t(i/this.cellSize),d=t(a/this.cellSize);let u=s(c-o),h=s(d-r),m=o,p=r,b=1+u+h;const g=c>o?1:-1,f=d>r?1:-1;let x=u-h;for(u*=2,h*=2;b--;){if(m>=0&&m<this.cellsWide&&p>=0&&p<this.cellsHigh){const e=p*this.cellsWide+m;this._cellCounts[e]++}if(x>0)m+=g,x-=h;else if(x<0)p+=f,x+=u;else{if(m+=g,p+=f,x+=u-h,b--,b<0)break;if(m>=0&&m<this.cellsWide&&p>=0&&p<this.cellsHigh){const e=p*this.cellsWide+m;this._cellCounts[e]++}}}}let i=0;for(let t=0;t<e;t++){const e=this._cellCounts[t];this._cellOffsets[t]=i,i+=e}this._indexData=new Float32Array(i),l=0;for(let e=0;e<n;e++){const n=this._segmentRecords[l+0],i=this._segmentRecords[l+1],a=this._segmentRecords[l+2],o=this._segmentRecords[l+3];l+=8;const r=t(n/this.cellSize),c=t(i/this.cellSize),d=t(a/this.cellSize),u=t(o/this.cellSize);let h=s(d-r),m=s(u-c),p=r,b=c,g=1+h+m;const f=d>r?1:-1,x=u>c?1:-1;let Z=h-m;for(h*=2,m*=2;g--;){if(p>=0&&p<this.cellsWide&&b>=0&&b<this.cellsHigh){const t=b*this.cellsWide+p,n=this._cellOffsets[t];this._indexData[n]=e,this._cellOffsets[t]=n+1}if(Z>0)p+=f,Z-=m;else if(Z<0)b+=x,Z+=h;else{if(p+=f,b+=x,Z+=h-m,g--,g<0)break;if(p>=0&&p<this.cellsWide&&b>=0&&b<this.cellsHigh){const t=b*this.cellsWide+p,n=this._cellOffsets[t];this._indexData[n]=e,this._cellOffsets[t]=n+1}}}}}buildSegmentDataTextures(){const e=this.segmentDataTexture.texture.texture.image.data,t=this.segmentDataTexture.width,n=this.segmentDataTexture.height,l=this._segmentRecords.length/8,i=o(2*l,t*n);let a=0,s=0;for(let t=0;t<l&&!(2*t+1>=i);t++){const n=2*t*4,l=4*(2*t+1),i=this._segmentRecords[a+0],o=this._segmentRecords[a+1],r=this._segmentRecords[a+2],c=this._segmentRecords[a+3],d=this._segmentRecords[a+4],u=this._segmentRecords[a+5],h=this._segmentRecords[a+6],m=this._segmentRecords[a+7];e[n+0]=i,e[n+1]=o,e[n+2]=r,e[n+3]=c,e[l+0]=d,e[l+1]=u,e[l+2]=h,e[l+3]=m,a+=8,s+=8}this.segmentDataTexture.texture.uploadSubData(s);const r=this.segmentMetaDataTexture.texture.image.data,c=this.segmentMetaDataTexture.width*this.segmentMetaDataTexture.height;for(let e=0;e<c;e++){const t=this._cellCounts[e],n=this._cellOffsets[e]-t,l=2*e;r[l+0]=n,r[l+1]=t}this.segmentMetaDataTexture.texture.needsUpdate=!0;const d=this.segmentIndexTexture.texture.texture.image.data,u=d.length,h=Math.min(this._indexData.length,u);d.set(this._indexData.subarray(0,h)),this.segmentIndexTexture.texture.uploadSubData(this._indexData.length)}getTextures(){return{segmentMetaDataTexture:this.segmentMetaDataTexture.texture,segmentDataTexture:this.segmentDataTexture.texture.texture,segmentIndexTexture:this.segmentIndexTexture.texture.texture}}reset(){this._segmentRecords=new Float32Array(0),this._cellCounts=new Float32Array(0),this._cellOffsets=new Float32Array(0),this._indexData=new Float32Array(0)}dispose(){this.segmentDataTexture.texture.texture.dispose(),this.segmentMetaDataTexture.texture.dispose(),this.segmentIndexTexture.texture.texture.dispose()}}let tn,nn=new en(4),ln=new en(20),an=0,sn=!1,on=null;function rn(t){if(0===t.lines.length)return;tn=tn||new e.ShaderMaterial({uniforms:{resolution:{value:new e.Vector2(Q,j)},uInternalResolution:{value:new e.Vector2(Y,P)},uFaceIndex:{value:0},tPaperTexture:{value:Ht.paperTexture.target.texture},uSegmentDataTex:{value:null},uSegmentIndexTex:{value:null},uSegmentMetaDataTex:{value:null},uBrightness:{value:1},...ce(),uAccumulationStrength:{value:1},uEdgeSharpness:{value:1},uTaperingFadeIn:{value:0},uTaperingFadeOut:{value:0},uTaperingScaleIn:{value:0},uTaperingScaleOut:{value:0},uFadeAtLineSegmentVertices:{value:new Float32Array([0,0,0,0])},uFadeAtLineSegmentDistance:{value:0},uFadeAtLineSegmentStrength:{value:1},uEnableFadeAtLineSegment:{value:!1},uIsShading:{value:!0},uDisableSaturationShift:{value:!1},uIsClosedPoly:{value:!1},tScreenBuffer:{value:Ft.getReadBuffer().texture},tPolyEdgeDistance:{value:Ht.polyEdgeDistance.target.texture},uNoiseOffset:{value:new ie(0,0)},tWarpTex:{value:Ht.displacementMap.target.texture},uLineColor:{value:new e.Vector3(1,0,0)},uLineHardness:{value:1},uLineWeight:{value:1},uMaterialHardness:{value:1},uLighting:{value:{center:Ie.default,min:ge(Ie.default,Ie.min,Ie.influencesBrightnessMult),max:ge(Ie.default,Ie.max,Ie.influencesBrightnessMult)}},tPolyLineGrid:{value:{width:0,height:0,cellSize:0}}},vertexShader:le,glslVersion:e.GLSL3,fragmentShader:`\n precision highp float;\n precision highp int;\n\n uniform highp sampler2D tWarpTex;\n uniform highp sampler2D tScreenBuffer;\n uniform highp sampler2D tPaperTexture;\n uniform highp sampler2D tPolyEdgeDistance;\n\n uniform highp sampler2D uSegmentDataTex;\n uniform highp sampler2D uSegmentMetaDataTex;\n uniform highp sampler2D uSegmentIndexTex;\n\n struct TPolyLineGrid {\n float width;\n float height;\n float cellSize;\n };\n\n uniform TPolyLineGrid tPolyLineGrid;\n\n\n struct Lighting {\n float center;\n float min;\n float max;\n };\n\n uniform Lighting uLighting;\n \n // uniform int uPositionsCount;\n uniform bool uIsClosedPoly;\n \n uniform vec2 resolution;\n uniform vec2 uTileOffset;\n uniform vec2 uTileSize;\n\n uniform vec2 uInternalResolution;\n uniform vec2 uNoiseOffset;\n uniform bool uIsShading;\n // uniform bool uBulge;\n\n uniform float uTaperingFadeIn;\n uniform float uTaperingFadeOut;\n uniform float uTaperingScaleIn;\n uniform float uTaperingScaleOut;\n\n uniform bool uEnableFadeAtLineSegment;\n uniform vec2 uFadeAtLineSegmentVertices[2];\n uniform float uFadeAtLineSegmentDistance;\n uniform float uFadeAtLineSegmentStrength;\n\n uniform float uBrightness;\n uniform float uEdgeSharpness;\n uniform float uAccumulationStrength;\n uniform float uMaterialHardness;\n uniform vec3 uLineColor;\n uniform float uLineHardness;\n uniform float uLineWeight;\n uniform float uFaceIndex;\n uniform bool uDisableSaturationShift;\n\n // Max number of lines to consider for this fragment\n const int MAX_LINE_COUNT = 64;\n\n in vec2 vUv;\n out vec4 outColor;\n\n ${Et}\n ${te}\n ${Vt}\n ${Ct}\n ${ee}\n ${ne}\n ${At}\n\n //-----------------------------------------\n \n\n vec2 pointLineSDFInfinite(vec2 p, vec2 a, vec2 b) {\n vec2 pa = p - a;\n vec2 ba = b - a;\n float t = dot(pa, ba) / dot(ba, ba);\n float dist = length(pa - ba * t);\n return vec2(dist, t);\n }\n\n // 1) Helper to fetch offset+count from segmentMetaDataTexture\n vec2 getSegmentMeta(ivec2 cellCoords) {\n // each pixel stores (offset, count)\n return texelFetch(uSegmentMetaDataTex, cellCoords, 0).rg;\n }\n \n ivec2 getCellCounts() {\n return ivec2(\n int(tPolyLineGrid.width / tPolyLineGrid.cellSize),\n int(tPolyLineGrid.height / tPolyLineGrid.cellSize)\n );\n }\n\n ivec2 getGridCoords(vec2 uv) {\n ivec2 cellCount = getCellCounts();\n \n // Flip Y so top-left is (0,0)\n float flippedY = 1.0 - uv.y;\n \n int x = int(floor(uv.x * float(cellCount.x)));\n int y = int(floor(flippedY * float(cellCount.y)));\n \n return ivec2(x, y);\n }\n\n\n // 2) Helper to read the segment ID from segmentIndexTexture\n int getSegmentID(int globalIndex) {\n ivec2 size = textureSize(uSegmentIndexTex, 0);\n // clamp the fetch\n globalIndex = clamp(globalIndex, 0, size.x*size.y - 1);\n int x = globalIndex % size.x;\n int y = globalIndex / size.x;\n return int(texelFetch(uSegmentIndexTex, ivec2(x,y), 0).r);\n }\n\n // 3) Helper to read the 2 texels for the given segment\n struct SegmentData {\n vec2 a; // x1,y1\n vec2 b; // x2,y2\n float lineIndex; \n float segStart;\n float segEnd;\n float totalLen;\n };\n\n SegmentData getSegmentData(int segmentID) {\n SegmentData seg;\n\n // each segment uses 2 RGBA texels = 8 floats\n // texel1 => (x1, y1, x2, y2)\n // texel2 => (lineIndex, segStartLen, segEndLen, totalLineLen)\n int texelIndex1 = segmentID * 2;\n int texelIndex2 = segmentID * 2 + 1;\n\n ivec2 size = textureSize(uSegmentDataTex, 0);\n\n // clamp fetch\n int maxTexels = size.x * size.y;\n texelIndex1 = clamp(texelIndex1, 0, maxTexels-1);\n texelIndex2 = clamp(texelIndex2, 0, maxTexels-1);\n\n // decode coordinates\n int x1 = texelIndex1 % size.x;\n int y1 = texelIndex1 / size.x;\n int x2 = texelIndex2 % size.x;\n int y2 = texelIndex2 / size.x;\n\n vec4 d1 = texelFetch(uSegmentDataTex, ivec2(x1,y1), 0);\n vec4 d2 = texelFetch(uSegmentDataTex, ivec2(x2,y2), 0);\n\n seg.a = d1.xy / uInternalResolution;\n seg.b = d1.zw / uInternalResolution;\n\n seg.a = vec2(seg.a.x, 1.0 - seg.a.y);\n seg.b = vec2(seg.b.x, 1.0 - seg.b.y);\n\n seg.lineIndex = d2.x;\n seg.segEnd = d2.z / uInternalResolution.x;\n seg.segStart = d2.y / uInternalResolution.x;\n seg.totalLen = d2.w / uInternalResolution.x; // scale to pixel space\n\n return seg;\n }\n\n vec2 pointLineSegSDF(vec2 p, vec2 a, vec2 b) {\n vec2 pa = p - a;\n vec2 ba = b - a;\n float t = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);\n float dist = length(pa - ba * t);\n return vec2(dist, t);\n }\n\n\n // This struct stores the final line-based result\n struct LineDistanceResult {\n float dist; // nearest distance for this line\n float lineT; // fraction along the entire line\n float lineIndex; // which line\n };\n \n const ivec2 offsets[9] = ivec2[](\n ivec2(-1,-1), ivec2(0,-1), ivec2(1,-1),\n ivec2(-1, 0), ivec2(0, 0), ivec2(1, 0),\n ivec2(-1, 1), ivec2(0, 1), ivec2(1, 1)\n );\n\n // This function will find the closest lines to the given uv coordinate\n void lineDistanceSDFSegments(\n vec2 uv,\n float distanceThreshold,\n out LineDistanceResult linesWithin[MAX_LINE_COUNT],\n out int linesCount\n ){\n linesCount = 0;\n \n // Keep track of each line's nearest distance\n float lineDistances[MAX_LINE_COUNT];\n float lineTvalues[MAX_LINE_COUNT];\n int lineIDs[MAX_LINE_COUNT];\n \n // Initialize with defaults\n for (int i = 0; i < MAX_LINE_COUNT; i++) {\n lineDistances[i] = 1e20;\n lineTvalues[i] = 0.0;\n lineIDs[i] = -1;\n }\n \n // Figure out the base cell coords from uv\n ivec2 baseCellCoords = getGridCoords(uv);\n ivec2 cellCount = getCellCounts();\n \n // For each of the 9 offsets, gather segments from neighboring cells.\n for (int c = 0; c < 9; c++) {\n ivec2 cellCoords = baseCellCoords + offsets[c];\n \n // clamp cellCoords to grid boundaries\n if(cellCoords.x < 0 || cellCoords.x >= cellCount.x) continue;\n if(cellCoords.y < 0 || cellCoords.y >= cellCount.y) continue;\n \n // 1) fetch offset+count from the segmentMetaDataTexture\n vec2 meta = getSegmentMeta(cellCoords);\n int segOffset = int(meta.r);\n int segCount = int(meta.g);\n \n // 2) read each segment\n for (int i = 0; i < 1024; i++) {\n if (i >= segCount) break;\n \n int segID = getSegmentID(segOffset + i);\n SegmentData seg = getSegmentData(segID);\n \n // compute distance to this segment\n vec2 dt = pointLineSegSDF(uv, seg.a, seg.b);\n float dist = dt.x;\n float localT = dt.y;\n \n float segLen = seg.segEnd - seg.segStart;\n float absoluteDistanceAlongLine = seg.segStart + localT * segLen;\n float fraction = (seg.totalLen > 0.0) ? (absoluteDistanceAlongLine / seg.totalLen) : 0.0;\n \n // if within threshold\n if (dist <= distanceThreshold) {\n int lineID = int(seg.lineIndex);\n \n // check if we already have that line\n int foundIndex = -1;\n for (int j = 0; j < MAX_LINE_COUNT; j++) {\n if (lineIDs[j] == lineID) {\n foundIndex = j;\n break;\n }\n }\n \n if (foundIndex == -1) {\n // new line\n for (int j = 0; j < MAX_LINE_COUNT; j++) {\n if (lineIDs[j] < 0) {\n lineIDs[j] = lineID;\n lineDistances[j] = dist;\n lineTvalues[j] = fraction;\n break;\n }\n }\n } else {\n // line already exists => keep closer distance\n if (dist < lineDistances[foundIndex]) {\n lineDistances[foundIndex] = dist;\n lineTvalues[foundIndex] = fraction;\n }\n }\n }\n }\n }\n \n // Final pass: gather valid lines into linesWithin\n int validCount = 0;\n for (int i = 0; i < MAX_LINE_COUNT; i++) {\n if (lineIDs[i] >= 0) {\n linesWithin[validCount].dist = lineDistances[i];\n linesWithin[validCount].lineT = lineTvalues[i];\n linesWithin[validCount].lineIndex = float(lineIDs[i]);\n validCount++;\n if (validCount >= MAX_LINE_COUNT) break;\n }\n }\n\n // Now 'validCount' lines are in linesWithin[0..validCount-1].\n // Sort them by dist ascending.\n for (int i = 0; i < MAX_LINE_COUNT - 1; i++) {\n // If 'i' >= validCount - 1, break\n if (i >= validCount - 1) break;\n\n for (int j = i + 1; j < MAX_LINE_COUNT; j++) {\n // If 'j' >= validCount, break\n if (j >= validCount) break;\n\n if (linesWithin[j].dist < linesWithin[i].dist) {\n // Swap\n LineDistanceResult tmp = linesWithin[i];\n linesWithin[i] = linesWithin[j];\n linesWithin[j] = tmp;\n }\n }\n }\n \n linesCount = validCount;\n }\n\n\n float easeOut(float t, float strength) {\n return 1.0 - pow(1.0 - t, strength);\n }\n\n vec4 render(vec2 uv, vec2 screenSpaceUV, vec2 msaaOffset, vec3 frameBuffer) {\n\n float scaleFactor = (1. / uInternalResolution.x);\n vec2 tileScaleFactor = resolution / uTileSize;\n \n \n vec2 uvWarp = lookupWarpSample(screenSpaceUV + msaaOffset).xy;\n vec2 ssLookupWarp = uvWarp * tileScaleFactor;\n\n uv += uvWarp;\n\n float sdf = (texture(tPolyEdgeDistance, screenSpaceUV + ssLookupWarp + msaaOffset).r - 0.5) * 2.0;\n\n \n vec3 color = uLineColor;\n float rampStart = 0.7;\n float rampEnd = 1.0;\n float lowValue = 0.015;\n float highValue = 0.06;\n\n float t = clamp((color.x - rampStart) / (rampEnd - rampStart), 0.0, 1.0);\n \n float accumulationClampMax = 0.15;\n float accumulationIncrement = mix(lowValue, highValue, smoothstep(rampStart, rampEnd, color.x));\n vec2 accumulationClamp = vec2(accumulationClampMax, accumulationClampMax * 0.4);\n float chromaIncrement = (accumulationIncrement * 0.4) * uAccumulationStrength;\n float lightnessIncrement = (-accumulationIncrement) * uAccumulationStrength;\n float alphaIncrement = uIsShading ? .95 : 1.5;\n \n if(uIsShading) {\n float colorShift = uBrightness - uLighting.center;\n float shiftFactor = (colorShift > 0. && color.x > 0.6) ? (1.0 - clamp(color.x / 0.82, 0., 1.)) : 1.0;\n float newMult = ((colorShift * 1.5 * shiftFactor) + 1.0);\n \n color.x *= newMult;\n \n color.x -= 0.0;\n color.y -= 0.025;\n \n color.x = clamp(color.x, 0., 0.82);\n }\n\n color.x = clamp(color.x, 0., .81);\n color.y = clamp(color.y, 0., .4);\n \n vec3 originalColor = color;\n\n // SHAPE SDF\n \n float alpha = 0.0;\n \n float segmentDistanceAlphaMult = 1.;\n\n if(uEnableFadeAtLineSegment) {\n vec2 fadeStartVert = uFadeAtLineSegmentVertices[0] / resolution.xy;\n vec2 fadeEndVert = uFadeAtLineSegmentVertices[1] / resolution.xy;\n float fadeDistance = uFadeAtLineSegmentDistance * scaleFactor;\n float fadeAlpha = pointLineSDFInfinite(uv, fadeStartVert, fadeEndVert).x;\n float fadeLerp = smoothstep(0., fadeDistance, fadeAlpha);\n fadeLerp = clamp(remap(fadeLerp, 0., 1., 1. - uFadeAtLineSegmentStrength, 1.), 0., 1.);\n segmentDistanceAlphaMult = fadeLerp;\n }\n\n float edgeSharpness = remap(uEdgeSharpness, 0., 1., -0.1, -0.0015);\n\n segmentDistanceAlphaMult *= smoothstep(edgeSharpness, -0.0, sdf);\n\n if(segmentDistanceAlphaMult <= 0.) {\n return vec4(frameBuffer, 1.);\n }\n\n\n float accumulationValue = 0.0;\n float lineSampleDistanceMax = scaleFactor * (uLineWeight * 2.);\n\n LineDistanceResult linesWithin[MAX_LINE_COUNT];\n int linesCount;\n lineDistanceSDFSegments(uv, lineSampleDistanceMax, linesWithin, linesCount);\n\n for(int i = 0; i < linesCount; i++) {\n vec3 localColor = color;\n \n float lineSDF = linesWithin[i].dist;\n float lineT = linesWithin[i].lineT;\n float lineIndex = linesWithin[i].lineIndex;\n\n float currentLineNoiseOffset = float(lineIndex) * 10.;\n\n // Line Tapering and Fade\n float fadeLineCap = 1.;\n float scaleLineCap = 1.;\n \n if(uTaperingFadeIn > 0. || uTaperingFadeOut > 0.) {\n float fadeWarp = abs(fbm(uv + uNoiseOffset + currentLineNoiseOffset, 1, 10., 0.9)) * 0.05;\n \n if(uTaperingFadeIn > 0.) {\n fadeLineCap = easeOut(smoothstep(0.0, (uTaperingFadeIn / 2.) - fadeWarp, lineT), 10.);\n }\n\n if(uTaperingFadeOut > 0.) {\n fadeLineCap *= easeOut(smoothstep(1., 1. - (uTaperingFadeOut / 2.) - fadeWarp, lineT), 10.);\n }\n }\n\n if(uTaperingScaleIn > 0. || uTaperingScaleOut > 0.) {\n float scaleWarp = abs(fbm(uv + uNoiseOffset + currentLineNoiseOffset, 1, 10., 0.9)) * 0.05;\n if(uTaperingScaleIn > 0.) {\n scaleLineCap = easeOut(smoothstep(0.0, (uTaperingScaleIn / 2.), lineT + scaleWarp), 1.);\n }\n\n if(uTaperingScaleOut > 0.) {\n scaleLineCap *= easeOut(smoothstep(1., 1. - (uTaperingScaleOut / 2.), lineT + scaleWarp), 1.);\n }\n }\n\n float lineHardness = uLineHardness * fadeLineCap;\n float lineWeight = uLineWeight;\n \n float lineDistanceMax = (scaleFactor * lineWeight) * scaleLineCap;\n float lineWeightWobble = fbm(uv + currentLineNoiseOffset, 3, 8., 0.6) * (lineDistanceMax / (uIsShading ? 2. : 2.));\n \n float lineWeightEdgeNoise = fbm(uv + uNoiseOffset + currentLineNoiseOffset, 3, 300., 0.54) * (lineDistanceMax / 10.);\n\n float lineDistance = (lineSDF + lineWeightWobble + lineWeightEdgeNoise); \n \n \n float lineFineNoise = clamp(\n smoothstep(0., 0.9, fbm(uv + currentLineNoiseOffset, 2, 700., 0.9) - 0.1) + remap(lineHardness, 1., 0., 1. - uMaterialHardness, 0.5),\n 0., 1.);\n\n float lineFineNoise2 = clamp(\n smoothstep(0., 0.9, fbm(uv, 1, 900., 0.9)) + remap(lineHardness, 1., 0., 0.01, 0.5),\n 0., (1. - uMaterialHardness) + .1);\n \n\n float alphaWobblePerLine = fbm(uNoiseOffset + currentLineNoiseOffset, 1, 300., 0.9) * 0.015;\n\n float lineColorWobble = 0.008;\n \n float lightnessWobblePerLine = ((fbm(uNoiseOffset + (currentLineNoiseOffset / 1.), 1, 1000., 0.9) + .5) / 2. ) * lineColorWobble;\n localColor.x += lightnessWobblePerLine;\n\n float saturationWobblePerLine = ((fbm(uNoiseOffset + (currentLineNoiseOffset / 1.1), 1, 1100., 0.9) + .5) / 2. ) * (lineColorWobble * 0.4);\n localColor.y += saturationWobblePerLine;\n \n float maxLineHardness = clamp(smoothstep(0., 1., lineHardness - alphaWobblePerLine), 0., 1.);\n\n float lineRolloff = 0.0001;\n float alphaHardLineSpread = lineDistanceMax * 1.;\n float hardLineSS = smoothstep(alphaHardLineSpread + lineRolloff, alphaHardLineSpread - lineRolloff, lineDistance);\n\n float alphaHardLine = clamp(\n (hardLineSS * (1. - lineFineNoise) * (1. - lineFineNoise2)) * maxLineHardness,\n 0., 1.);\n\n float alphaSoftLine = 0.;\n\n if(!uIsShading) {\n float alphaSoftLineNoise = clamp((fbm(uv + currentLineNoiseOffset, 1, 800., 0.9) - 0.3), 0., 1.);\n\n\n float alphaSoftLineSpread = lineDistanceMax * 1.5;\n float softLineSS = smoothstep(alphaSoftLineSpread + lineRolloff, alphaSoftLineSpread - lineRolloff, lineDistance);\n\n alphaSoftLine