Posted By: 3run
2d shadows - 05/12/20 22:27
Hey! I've recently played around with shadertoy and converted some 2d shadows into HLSL
Maybe someone may find this useful! And if you guys have any ideas, how to convert any of those into 3d scene, share your ideas!
https://www.shadertoy.com/view/4dSXR1
https://www.shadertoy.com/view/4dfXDn
https://www.shadertoy.com/view/4sKBRD
Some of them take some time to get compiled, and all of them are pp shaders.
Best regards!
Maybe someone may find this useful! And if you guys have any ideas, how to convert any of those into 3d scene, share your ideas!
https:/
Code
const float4 vecTime; const float4 vecViewPort; const float4 vecSkill1; // Checks if spesific pixel is located within these bounds float pixelInCube(float2 p, float4 t) { return float(p.x > t.x-t.z*.5 && p.x < t.x+t.z*.5 && p.y > t.y-t.w*.5 && p.y < t.y+t.w*.5); } // Checks if spesific pixel is located within these bounds float pixelInCircle(float2 p, float3 t) { float hypT = pow(t.z*.5,2.)+pow(t.z*.5,2.); float hypP = pow(p.x-t.x,2.)+pow(p.y-t.y,2.); return float(hypP < hypT); } // Creates a cone for the light float shadow_isInBounds(float2 p, float3 l, float4 t, float side){ float ty = t.y - t.w*.5*(float(l.x > t.x)-.5)*2.*side; float tx = t.x - t.z*.5*(float(l.y < t.y)-.5)*2.*side; float rot = atan2(ty-l.y, tx-l.x)+45.; float lx = l.x + cos(rot) * l.z*side; float ly = l.y + sin(rot) * l.z*side; float angle = (ly-ty)/(lx-tx); float f = float(p.y > ly+((p.x-lx)*angle) && lx>tx); f += float(p.y < ly+((p.x-lx)*angle) && lx<tx); return f; } // Culls the "front" of the cone so that only the "shadow" is visible float shadow_cullLight(float f, float2 p, float3 l, float4 t){ float ty = t.y - t.w*.5*(float(l.y > t.y)-.5)*2.; float tx = t.x - t.z*.5*(float(l.x > t.x)-.5)*2.; float c = 1.0; c*= float((p.y < ty && ty > l.y) || (p.y > ty && ty < l.y)); c*= float((p.x < tx && tx > l.x) || (p.x > tx && tx < l.x)); return clamp(f-c,0.0,1.0); } // Checks if spesific pixel is located within these bounds float pixelInShadow(float2 p, float3 l, float4 t){ return shadow_cullLight(shadow_isInBounds(p, l, t, 1.)-shadow_isInBounds(p, l, t, -1.),p,l,t); } float4 FP(float2 fragCoord: VPOS) : COLOR { float4 color = float4(0.3, 0.3, 0.3, 0.3); float4 cube; float4 T = float4(fragCoord.xy, vecViewPort.xy); float3 light = float3(vecSkill1.x+float(vecSkill1.x == 0.0)*T.z*.4,vecSkill1.y+float(vecSkill1.y == 0.0)*T.w*.5,20.0); float time = vecTime.w * 0.01; // Controlls the rotationspeed of the circle color.rg += pixelInCircle(T.xy,light); float circleScale = 1.01; // Draw rotating circle for(int i = 0; i < 6; i+=1){ cube = float4(cos(float(i)*circleScale+time)*T.w*.3+T.z*.7,sin(float(i)*circleScale+time)*T.w*.3+T.w*.5,40.0,40.0); color.r += pixelInCube(T.xy,cube); color -= pixelInShadow(T.xy,light,cube)*.95; } // Draw the corner objects for(int i = 0; i < 4; i+=1){ cube = float4(fmod(floor(float(i)*.5),2.0)*-T.z*.8 + T.z*.9,fmod(float(i),2.0)*-T.w*.8 + T.w*.9,40.0,40.0); color.r += pixelInCube(T.xy,cube); color -= pixelInShadow(T.xy,light,cube)*.95; } // Draw the wall cube = float4(T.z*.2,0.0,10.0,T.w); color.r += pixelInCube(T.xy,cube); color -= pixelInShadow(T.xy,light,cube)*.95; float4 fragColor = color; return fragColor; } technique vhs { pass one { PixelShader = compile ps_3_0 FP(); } }
https:/
Code
/* Hi all, This is just my playground for a bunch of 2D stuff: Some distance functions and blend functions Cone marched 2D Soft shadows Use the mouse to control the 3rd light */ const float4 vecTime; const float4 vecViewPort; const float4 vecSkill1; const float TIME_FACTOR = 0.1; ////////////////////////////////////// // Combine distance field functions // ////////////////////////////////////// float smoothMerge(float d1, float d2, float k) { float h = clamp(0.5 + 0.5*(d2 - d1)/k, 0.0, 1.0); return lerp(d2, d1, h) - k * h * (1.0-h); } float merge(float d1, float d2) { return min(d1, d2); } float mergeExclude(float d1, float d2) { return min(max(-d1, d2), max(-d2, d1)); } float substract(float d1, float d2) { return max(-d1, d2); } float intersect(float d1, float d2) { return max(d1, d2); } ////////////////////////////// // Rotation and translation // ////////////////////////////// float2 rotateCCW(float2 p, float a) { float2x2 m = float2x2(cos(a), sin(a), -sin(a), cos(a)); return mul(p, m); } float2 rotateCW(float2 p, float a) { float2x2 m = float2x2(cos(a), -sin(a), sin(a), cos(a)); return mul(p, m); } float2 translate(float2 p, float2 t) { return p - t; } ////////////////////////////// // Distance field functions // ////////////////////////////// float pie(float2 p, float angle) { angle = radians(angle) / 2.0; float2 n = float2(cos(angle), sin(angle)); return abs(p).x * n.x + p.y*n.y; } float circleDist(float2 p, float radius) { return length(p) - radius; } float triangleDist(float2 p, float radius) { return max(abs(p).x * 0.866025 + p.y * 0.5, -p.y) - radius * 0.5; } float triangleDist(float2 p, float width, float height) { float2 n = normalize(float2(height, width / 2.0)); return max(abs(p).x*n.x + p.y*n.y - (height*n.y), - p.y); } float semiCircleDist(float2 p, float radius, float angle, float width) { width /= 2.0; radius -= width; return substract(pie(p, angle), abs(circleDist(p, radius)) - width); } float boxDist(float2 p, float2 size, float radius) { size -= float2(radius, radius); float2 d = abs(p) - size; return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius; } float lineDist(float2 p, float2 start, float2 end, float width) { float2 dir = start - end; float lngth = length(dir); dir /= lngth; float2 proj = max(0.0, min(lngth, dot((start - p), dir))) * dir; return length((start - p) - proj) - (width / 2.0); } /////////////////////// // Masks for drawing // /////////////////////// float fillMask(float dist) { return clamp(-dist, 0.0, 1.0); } float innerBorderMask(float dist, float width) { //dist += 1.0; float alpha1 = clamp(dist + width, 0.0, 1.0); float alpha2 = clamp(dist, 0.0, 1.0); return alpha1 - alpha2; } float outerBorderMask(float dist, float width) { //dist += 1.0; float alpha1 = clamp(dist, 0.0, 1.0); float alpha2 = clamp(dist - width, 0.0, 1.0); return alpha1 - alpha2; } /////////////// // The scene // /////////////// float sceneDist(float2 p) { float c = circleDist(translate(p, float2(100, 250)), 40.0); float b1 = boxDist(translate(p, float2(200, 250)), float2(40, 40), 0.0); float b2 = boxDist(translate(p, float2(300, 250)), float2(40, 40), 10.0); float l = lineDist(p, float2(370, 220), float2(430, 280), 10.0); float t1 = triangleDist(translate(p, float2(500, 210)), 80.0, 80.0); float t2 = triangleDist(rotateCW(translate(p, float2(600, 250)), vecTime.w * TIME_FACTOR), 40.0); float m = merge(c, b1); m = merge(m, b2); m = merge(m, l); m = merge(m, t1); m = merge(m, t2); float b3 = boxDist(translate(p, float2(100, sin((vecTime.w * TIME_FACTOR) * 3.0 + 1.0) * 40.0 + 100.0)), float2(40, 15), 0.0); float c2 = circleDist(translate(p, float2(100, 100)), 30.0); float s = substract(b3, c2); float b4 = boxDist(translate(p, float2(200, sin((vecTime.w * TIME_FACTOR) * 3.0 + 2.0) * 40.0 + 100.0)), float2(40, 15), 0.0); float c3 = circleDist(translate(p, float2(200, 100)), 30.0); float i = intersect(b4, c3); float b5 = boxDist(translate(p, float2(300, sin((vecTime.w * TIME_FACTOR) * 3.0 + 3.0) * 40.0 + 100.0)), float2(40, 15), 0.0); float c4 = circleDist(translate(p, float2(300, 100)), 30.0); float a = merge(b5, c4); float b6 = boxDist(translate(p, float2(400, 100)), float2(40, 15), 0.0); float c5 = circleDist(translate(p, float2(400, 100)), 30.0); float sm = smoothMerge(b6, c5, 10.0); float sc = semiCircleDist(translate(p, float2(500,100)), 40.0, 90.0, 10.0); float b7 = boxDist(translate(p, float2(600, sin((vecTime.w * TIME_FACTOR) * 3.0 + 3.0) * 40.0 + 100.0)), float2(40, 15), 0.0); float c6 = circleDist(translate(p, float2(600, 100)), 30.0); float e = mergeExclude(b7, c6); m = merge(m, s); m = merge(m, i); m = merge(m, a); m = merge(m, sm); m = merge(m, sc); m = merge(m, e); return m; } float sceneSmooth(float2 p, float r) { float accum = sceneDist(p); accum += sceneDist(p + float2(0.0, r)); accum += sceneDist(p + float2(0.0, -r)); accum += sceneDist(p + float2(r, 0.0)); accum += sceneDist(p + float2(-r, 0.0)); return accum / 5.0; } ////////////////////// // Shadow and light // ////////////////////// float shadow(float2 p, float2 pos, float radius) { float2 dir = normalize(pos - p); float dl = length(p - pos); // fraction of light visible, starts at one radius (second half added in the end); float lf = radius * dl; // distance traveled float dt = 0.01; for(int i = 0; i < 64; ++i) { // distance to scene at current position float sd = sceneDist(p + dir * dt); // early out when this ray is guaranteed to be full shadow if (sd < -radius) { return 0.0; } // width of cone-overlap at light // 0 in center, so 50% overlap: add one radius outside of loop to get total coverage // should be '(sd / dt) * dl', but '*dl' outside of loop lf = min(lf, sd / dt); // move ahead dt += max(1.0, abs(sd)); if (dt > dl) { break; } } // multiply by dl to get the real projected overlap (moved out of loop) // add one radius, before between -radius and + radius // normalize to 1 ( / 2*radius) lf = clamp((lf * dl + radius) / (2.0 * radius), 0.0, 1.0); lf = smoothstep(0.0, 1.0, lf); return lf; } float4 drawLight(float2 p, float2 pos, float4 color, float dist, float range, float radius) { // distance to light float ld = length(p - pos); // out of range if (ld > range) { return float4(0.0, 0.0, 0.0, 0.0); } // shadow and falloff float shad = shadow(p, pos, radius); float fall = (range - ld)/range; fall *= fall; float source = fillMask(circleDist(p - pos, radius)); return (shad * fall + source) * color; } float luminance(float4 col) { return 0.2126 * col.r + 0.7152 * col.g + 0.0722 * col.b; } void setLuminance(inout float4 col, float lum) { lum /= luminance(col); col *= lum; } float AO(float2 p, float dist, float radius, float intensity) { float a = clamp(dist / radius, 0.0, 1.0) - 1.0; return 1.0 - (pow(abs(a), 5.0) + 1.0) * intensity + (1.0 - intensity); return smoothstep(0.0, 1.0, dist / radius); } ///////////////// // The program // ///////////////// float4 FP(float2 fragCoord: VPOS) : COLOR { float2 p = fragCoord.xy + float2(0.5, 0.5); float2 c = vecViewPort.xy / 2.0; //float dist = sceneSmooth(p, 5.0); float dist = sceneDist(p); float2 light1Pos = vecSkill1.xy; float4 light1Col = float4(0.75, 1.0, 0.5, 1.0); setLuminance(light1Col, 0.4); float2 light2Pos = float2(vecViewPort.x * (sin((vecTime.w * TIME_FACTOR) + 3.1415) + 1.2) / 2.4, 175.0); float4 light2Col = float4(1.0, 0.75, 0.5, 1.0); setLuminance(light2Col, 0.5); float2 light3Pos = float2(vecViewPort.x * (sin((vecTime.w * TIME_FACTOR)) + 1.2) / 2.4, 340.0); float4 light3Col = float4(0.5, 0.75, 1.0, 1.0); setLuminance(light3Col, 0.6); // gradient float4 col = float4(0.5, 0.5, 0.5, 1.0) * (1.0 - length(c - p) / vecViewPort.x); // grid col *= clamp(min(fmod(p.y, 10.0), fmod(p.x, 10.0)), 0.9, 1.0); // ambient occlusion col *= AO(p, sceneSmooth(p, 10.0), 40.0, 0.4); //col *= 1.0-AO(p, sceneDist(p), 40.0, 1.0); // light col += drawLight(p, light1Pos, light1Col, dist, 150.0, 6.0); col += drawLight(p, light2Pos, light2Col, dist, 200.0, 8.0); col += drawLight(p, light3Pos, light3Col, dist, 300.0, 12.0); // shape fill col = lerp(col, float4(1.0, 0.4, 0.0, 1.0), fillMask(dist)); // shape outline col = lerp(col, float4(0.1, 0.1, 0.1, 1.0), innerBorderMask(dist, 1.5)); float4 fragColor = col; return fragColor; } technique vhs { pass one { PixelShader = compile ps_3_0 FP(); } }
https:/
Code
const float4 vecTime; const float4 vecViewPort; const float4 vecSkill1; const float TIME_FACTOR = 0.1; //spheres and coloured lights from an orthographic projection, with analytical antialiasing and shadows. #define NUMBER_OF_SPHERES 20 #define NUMBER_OF_LIGHTS 7 #define SURFACE_BRIGHTNESS 0.01 struct light { float3 pos; float3 col; }; //hsl to rgb conversion adapted from https://gist.github.com/mjackson/5311256 float hue2rgb(float p, float q, float h) { h = frac(h); if(h<0.1616) return p+(q-p)*6.0*h; else if(h<0.5) return q; else if(h<0.666) return p+(q-p)*(0.666-h)*6.0; else return p; } float3 hsltorgb(float3 hsl) { float h = hsl.x; float s = hsl.y; float l = hsl.z; float q = l<0.5 ? l*(1.0+s) : l+s-l*s; float p = 2.0*l-q; float r = hue2rgb(p, q, h+0.333); float g = hue2rgb(p, q, h); float b = hue2rgb(p, q, h-0.333); return float3(r,g,b); } // https://www.shadertoy.com/view/4djSRW float3 hash(float p) { float3 p3 = frac(float3(p, p, p) * float3(0.1031, 0.1030, 0.0973)); p3 += dot(p3, p3.yzx+19.19); return frac((p3.xxy+p3.yzz)*p3.zyx); } float3 getnearestpointonlinesegment(float3 pa, float3 pb, float3 t) { float3 l = pb-pa; float h = clamp(dot(t-pa,l)/dot(l,l), 0.0, 1.0 ); return pa+l*h; } float2 orbit(float r, float s, float p) { r *= sin((vecTime.w * TIME_FACTOR) * p); s *= (vecTime.w * TIME_FACTOR); return float2( sin(s)*r, cos(s)*r ); } float distancetocoverage(float d) { return clamp(d*vecViewPort.y, 0.0, 1.0); } float circle(float2 uv, float2 p, float r) { float d = r-distance(uv,p); return distancetocoverage(d); } float sphereshadow(float3 uv, float3 lp, float3 cp, float cr) { float3 np = getnearestpointonlinesegment(uv, lp, cp); float d = cr-distance(np,cp); return distancetocoverage(d); } float4 FP(float2 fragCoord: VPOS) : COLOR { float2 uv = fragCoord / vecViewPort.y; float2 center = float2(0.5*(vecViewPort.x / vecViewPort.y), 0.5); float3 spheres[NUMBER_OF_SPHERES]; for (int i = 0; i<NUMBER_OF_SPHERES; i++ ) { float3 h = hash(float(i+500)); float px = h.x * vecViewPort.x/vecViewPort.y; float py = h.y; float r = lerp(0.025, 0.1, h.z); spheres[i] = float3( px, py, r ); } light lights[NUMBER_OF_LIGHTS]; for (int i = 0; i < NUMBER_OF_LIGHTS; i++) { float3 h = hash(float(i+200)); float d = lerp(0.25, 0.8, h.x); //orbital distance float s = lerp(0.12, 0.5, abs(h.y*2.0-1.0))*sign(h.y-0.5); //orbital speed float p = lerp(0.1, 0.35, h.z); //occilation speed lights[i].pos = float3(center+orbit(d,s,p), 0.101+sin((vecTime.w * TIME_FACTOR)+float(i))*0.1); float3 hsl = float3( (float(i)+(vecTime.w * TIME_FACTOR)*0.5)/float(NUMBER_OF_LIGHTS), 0.95, 0.2 ); lights[i].col = hsltorgb( hsl ); } if(vecSkill1.w > 0.0) lights[0].pos.xy = vecSkill1.xy / vecViewPort.y; float3 col = float3(0.0, 0.0, 0.0); int id = -1; // index of sphere under pixel (-1 if there is none) float pz = 0.0; // height of pixel above floor plane for(int i = 0; i<NUMBER_OF_SPHERES; i++) { float2 sr = spheres[i].xy-uv; float cz = spheres[i].z * spheres[i].z - dot(sr, sr); // (pixel height on spheres[i]) ^ 2 if(cz > pz * pz) { pz = sqrt(cz); id = i; } } float coverage = 0.0; // sphere coverage of pixel float3 nor = float3(0.0, 0.0, 1.0); // surface normal if(id != -1) { coverage = circle(uv, spheres[id].xy, spheres[id].z); nor = normalize(float3(uv-spheres[id].xy, pz)); } for(int i = 0; i<NUMBER_OF_LIGHTS; i++) { // get shadow coverage float illumination = 1.0; for(int j = 0; j<NUMBER_OF_SPHERES && illumination>0.0; j++) { illumination *= 1.0 - sphereshadow(float3(uv,pz), lights[i].pos, float3(spheres[j].xy,0.0), spheres[j].z); } float3 rv = lights[i].pos-float3(uv,pz); float3 rd = normalize(rv); // light ray direction (from surface to light) float gr = max(rd.z,0.0) * (1.0-sphereshadow(float3(uv,0.0), lights[i].pos, float3(spheres[id].xy,0.0), spheres[id].z)); // Lambertian reflection from ground plane float sr = max(dot(rd, nor),0.0) * coverage; // Lambertian reflection from sphere surface float f = gr + sr; f *= illumination * SURFACE_BRIGHTNESS / dot(rv,rv); col += f * lights[i].col; } col *= 2.0; //filmic tone mapping -- http://filmicworlds.com/blog/filmic-tonemapping-operators/ col = max(col-0.004, 0.0); col = (col*(6.2*col+0.5))/(col*(6.2*col+1.7)+0.06); col += (hash(uv.x*uv.y*(vecTime.w * TIME_FACTOR)*1000.0)-0.5) / 128.0; //grain float4 fragColor = float4(col,1.0); return fragColor; } technique vhs { pass one { PixelShader = compile ps_3_0 FP(); } }
Some of them take some time to get compiled, and all of them are pp shaders.
Best regards!