W trzeciej części chciałbym opisać technikalia tworzenia poszczególnych modyfikacji shadera do uzyskania efektów oraz przedstawić kod shadera stanowiącego podstawkę do dalszego rozwoju:
7. Technikalia
- Fraktal IFS – Menger-Sponge
- Niebo i Słońce
- PostProcessing i PreProcessing
- Generowanie terenu
- Światło i cienie
- Ambient occlusion
- Odbicia
- Mgła
- Metaball
8. RayMarch shader
Działającą wersję shadera można zobaczyć tutaj
//-------------------------------------------------- // RayMarch framework v0.1 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License // (c) 2014 http://shad.mobi/ // // Thanks for IQ for useful article // about raymarch technology // http://www.iquilezles.org/ //-------------------------------------------------- #ifdef GL_ES precision mediump float; #endif uniform float time; uniform vec2 mouse; uniform vec2 resolution; #define RENDER_MODE 0 #define CAMERA_MODE 4 #define SHADOW_MODE 0 #define ENABLE_LIGHT 1 #define ENABLE_AO 1 #define ENABLE_MATERIAL 1 #define ENABLE_TEXTURE 1 const float PI=3.14159; const int MAX_STEPS=128; const float STEPS_DELTA=1.0 / 32.; const float MAX_DISTANCE=100.0; const float DISTANCE_BOOST=1.0; const float EPSILON=0.01; const float NORMAL_EPSILON=0.001; const float SHADOW_EPSILON=0.001; const int SHADOW_ITERATION=8; const float SHADOW_POINT=0.1; const float SHADOW_MIN_DISTANCE=0.0; const float SHADOW_MAX_DISTANCE=8.0; const float SHADOW_BLUR=1.0; const float SHADOW_DELTA=0.1; const float SHADOW_BOOST=1.0; const int AO_ITERATION=5; const float AO_START=0.05; const float AO_STEP=0.1; const float AO_BLUR_START=0.70; const float AO_BLUR=0.75; //------------------------------------- // Data structures //------------------------------------- struct Material { vec3 ambient; vec3 diffuse; vec3 specular; float shininess; int texture; }; struct Object { float distance; float step; float grey; vec3 pos; vec3 normal; bool hit; Material mat; }; struct Light { vec3 posistion; vec3 ambientColor; vec3 color; }; float length2( vec2 p ) { return sqrt(p.x * p.x + p.y * p.y); } float length6( vec2 p ) { p=p * p * p; p=p * p; return pow(p.x + p.y, 1.0 / 6.0); } float length8( vec2 p ) { p=p * p; p=p * p; p=p * p; return pow(p.x + p.y, 1.0 / 8.0); } #if ENABLE_MATERIAL==1 Material materials[9]; #endif //------------------------------------- // RayMarch primitive object // (procedural function) //------------------------------------- vec2 box(vec3 p, vec3 pos, vec3 size, float material) { vec3 d=abs(p - pos) - size; float distance=min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0)); return vec2(distance, material); } vec2 sphere(vec3 p, vec3 pos, float radius, float material) { return vec2(length(p - pos) - radius, material); } vec2 plane(vec3 p, vec3 pos, vec3 normal, float material) { return vec2(dot(p - pos, normal), material); } vec2 roundBox(vec3 p, vec3 pos, vec3 size, float radius, float material) { return vec2(length(max(abs(p - pos) - size, 0.0)) - radius, material); } vec2 torus( vec3 p, vec3 pos, vec2 t, float material) { p=p - pos; vec2 q=vec2(length(p.xy) - t.x, p.z); return vec2(length(q) - t.y, material); } vec2 hexPrism(vec3 p, vec3 pos, vec2 h, float material) { vec3 q=abs(p - pos); return vec2(max(q.z - h.y, max(q.x + q.y * 0.57735, q.y * 1.1547) - h.x), material); } vec2 triPrism( vec3 p, vec3 pos, vec2 h, float material) { vec3 q=abs(p - pos); return vec2(max(q.z - h.y, max(q.x * 0.866025 + p.y * 0.5, -p.y) - h.x * 0.5), material); } vec2 capsule( vec3 p, vec3 pos, vec3 a, vec3 b, float r, float material) { p=p - pos; vec3 pa=p - a; vec3 ba=b - a; float h=clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return vec2(length(pa - ba * h) - r, material); } vec2 torus82(vec3 p, vec3 pos, vec2 t, float material) { p=p - pos; vec2 q=vec2(length2(p.xz) - t.x, p.y); return vec2(length8(q) - t.y, material); } vec2 torus88(vec3 p, vec3 pos, vec2 t, float material) { p=p - pos; vec2 q=vec2(length8(p.xz) - t.x, p.y); return vec2(length8(q) - t.y, material); } vec2 cylinder6(vec3 p, vec3 pos, vec2 h, float material) { p=p - pos; return vec2(max(length6(p.xz) - h.x, abs(p.y) - h.y), material); } //------------------------------------- // Operation //------------------------------------- vec2 smothJoin(vec2 a, vec2 b, float k ) { float h=clamp(0.5 + 0.5 * (b.x - a.x) / k, 0.0, 1.0); float distance=mix(b.x, a.x, h) - k * h * (1.0 - h); if (distance > b.x){ b.x=distance; return b; } a.x=distance; return a; } vec2 join(vec2 a, vec2 b) { if (a.x > b.x){ return b; } return a; } vec2 join(vec2 a, vec2 b, vec2 c) { if (a.x > b.x){ if (b.x > c.x){ return c; } return b; } if (a.x > c.x){ return c; } return a; } vec2 intersect(vec2 a, vec2 b) { if (a.x > b.x){ return a; } return b; } vec2 intersect(vec2 a, vec2 b, vec2 c) { if (a.x > b.x){ if (c.x > a.x){ return c; } return a; } if (c.x > b.x){ return c; } return b; } vec2 diff(vec2 a, vec2 b) { b.x=-b.x; if (b.x > a.x){ return b; } return a; } vec2 diff(vec2 a, vec2 b, vec2 c) { b.x=-b.x; if (b.x > a.x){ c.x=-c.x; if (c.x > c.x){ return c; } return b; } if (b.x > c.x){ a.x=-a.x; if (a.x > c.x){ return a; } return c; } c.x=-c.x; if (c.x > a.x){ return c; } return a; } vec3 repetition(vec3 pos, vec3 c) { return mod(pos, c) - 0.5 * c; } vec3 scale(vec3 p, float s) { // return primitive(p/s)*s; return (p / s) * s; } vec3 rotateX(vec3 p, float angle) { float c=cos(angle); float s=sin(angle); return vec3(p.x, c * p.y - s * p.z, s * p.y + c * p.z); } vec3 rotateY(vec3 p, float angle) { float c=cos(angle); float s=sin(angle); return vec3(c * p.x - s * p.z, p.y, s * p.x + c * p.z); } vec3 rotateZ(vec3 p, float angle) { float c=cos(angle); float s=sin(angle); return vec3(c * p.x - s * p.y, s * p.x + c * p.y, p.z); } vec3 rotateX(vec3 p, float sinus, float cosinus) { return vec3(p.x, cosinus * p.y - sinus * p.z, sinus * p.y + cosinus * p.z); } vec3 rotateY(vec3 p, float sinus, float cosinus) { return vec3(cosinus * p.x - sinus * p.z, p.y, sinus * p.x + cosinus * p.z); } vec3 rotateZ(vec3 p, float sinus, float cosinus) { return vec3(cosinus * p.x - sinus * p.y, sinus * p.x + cosinus * p.y, p.z); } vec3 twist(vec3 p, float tc, float ts ) { float c=-1. * cos(tc * p.y + ts); float s=1. * sin(tc * p.y + ts); mat2 m=mat2(c, -s, s, c); return vec3(m * p.xz, p.y); } vec3 bend(vec3 p, float tc, float ts ) { float c=-1. * cos(tc * p.y + ts); float s=1. * sin(tc * p.y + ts); mat2 m=mat2(c, -s, s, c); return vec3(m * p.xy, p.z); } //------------------------------------- // Displacement map //------------------------------------- vec2 sinDisplace(vec3 p, vec2 primitive, float steps, float scale) { primitive.x+=(sin(steps * p.x) * sin(steps * p.y) * sin(steps * p.z)) * scale; return primitive; } vec2 sinDisplace(vec3 p, vec2 primitive, float steps, float scale, float tim) { primitive.x+=(sin(steps * p.x + tim) * sin(steps * p.y + tim) * sin(steps * p.z + tim)) * scale; return primitive; } //------------------------------------- // Map function // Return 2 dimensional vector, where: // x - distance // y - material ID //------------------------------------- vec2 map(vec3 p) { vec2 hit=plane(p, vec3(0.0, 0.0, 0.0), vec3(0.1, 1.0, 0.1), 0.0); //vec2 hit=vec2(MAX_DISTANCE); hit=join(hit, roundBox(twist(p, 2., 0.), vec3(0.0, 0.0, 0.0), vec3(1.2, 1.2, 1.2), 0.2, 4.0)); hit=join(hit, roundBox(bend(p, 0.4, 0.2), vec3(4.0, 0.0, 0.0), vec3(1.2, 1.2, 1.2), 0.2, 2.0)); return hit; // return sinDisplace(p, sphere(p, vec3(0.0, 0.0, 0.0), 2.0, 0.0), 5., 0.2, time); // return roundBox(bend(p, 0.4, 0.), vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); // return roundBox(twist(p, 2., 0.), vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); // vec2 ob1=torus(rotateX(p, time), vec3(0.0,0.0,0.0), vec2(2.0,0.5), 0.0); // vec2 ob2=roundBox(rotateY(p, time * 2.0), vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); // return diff(ob1, ob2); // return roundBox(scale(p, 1.0), vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); // return roundBox(repetition(p, vec3(5.0, 10.0 ,5.0)), vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); //vec2 ob1=torus(p, vec3(0.0,0.0,0.0), vec2(2.0,0.5), 0.0); //vec2 ob2=roundBox(p, vec3(0.0,0.0,0.0), vec3(1.2,1.2,1.2), 0.2, 0.0); //return intersect(ob1, ob2); // return join(ob1, ob2); // return smothJoin(ob1, ob2, 0.5); // return diff(ob1, ob2); // return torus82(p, vec3(0.0,0.0,0.0), vec2(1.2, 0.4), 0.0); // return torus88(p, vec3(0.0,0.0,0.0), vec2(1.2, 0.4), 0.0); // return capsule(p, vec3(0.0,0.0,0.0), vec3(1.0,1.0,2.0), vec3(-1.0,2.0,1.0), 1.4, 0.0); // return triPrism(p, vec3(0.0,0.0,0.0), vec2(1.5,1.0), 0.0); // return hexPrism(p, vec3(0.0,0.0,0.0), vec2(1.5,1.0), 0.0); // return torus(p, vec3(0.0,0.0,0.0), vec2(2.0,0.5), 0.0); // return roundBox(p, vec3(0.0,0.0,0.0), vec3(1.0,1.0,1.0), 0.2, 0.0); // return box(p, vec3(0.0,0.0,0.0), vec3(1.0,1.0,1.0), 0.0); // return sphere(p, vec3(0.0,0.0,0.0), 2.0, 0.0); // return plane(p, vec3(0.0,0.0,0.0), vec3(0.1,1.0,0.1), 0.0); } //------------------------------------- // Ray Marching //------------------------------------- vec3 pointNormal(vec3 pos) { vec3 eps=vec3(NORMAL_EPSILON, 0.0, 0.0); vec3 nor=vec3( map(pos + eps.xyy).x - map(pos - eps.xyy).x, map(pos + eps.yxy).x - map(pos - eps.yxy).x, map(pos + eps.yyx).x - map(pos - eps.yyx).x ); return normalize(nor); } Object rayMarching(vec3 ray, vec3 rayDir) { float distance=0.0; Object sceneObject; vec2 mapOut; sceneObject.hit=false; for(int i=0; i < MAX_STEPS; ++i) { mapOut=map(ray); ray += rayDir * mapOut.x; distance += mapOut.x; sceneObject.step += STEPS_DELTA; if (mapOut.x < EPSILON){ sceneObject.hit=true; distance=0.0; break; } if (distance > MAX_DISTANCE){ sceneObject.hit=false; distance=MAX_DISTANCE; break; } } sceneObject.distance=distance / MAX_DISTANCE; sceneObject.pos=ray; sceneObject.grey=1.0 - sceneObject.distance; if (sceneObject.hit){ sceneObject.normal=pointNormal(ray); // // Assign material to object // #if ENABLE_MATERIAL==1 int matIndex=int(floor(mapOut.y)); for(int i=0; i < 9; i++) { if (matIndex == i){ sceneObject.mat=materials[i]; break; } } #else sceneObject.mat.ambient=vec3(0.3); sceneObject.mat.diffuse=vec3(0.6); sceneObject.mat.specular=vec3(1.0); sceneObject.mat.shininess=4.0; #endif } return sceneObject; } //------------------------------------- // Light, shadows //------------------------------------- vec3 pointLight(vec3 color, Light light, Object sceneObject) { vec3 lightDir=normalize(light.posistion - sceneObject.pos); float lightPower=dot(sceneObject.normal, lightDir); // Ambient color=color + light.ambientColor; // Diffuse color=color + lightPower * light.color; // Specular color=color + (light.color * sceneObject.mat.specular * pow(lightPower, sceneObject.mat.shininess)); return clamp(color, 0.0, 1.0); } float pointShadow(in vec3 ro, in vec3 rd) { float t=SHADOW_MIN_DISTANCE; for(int i=0; i < SHADOW_ITERATION; i++) { float h=map(ro + rd * t).x; if (h < SHADOW_EPSILON){ return 0.0; } t += h * SHADOW_BOOST; if (t > SHADOW_MAX_DISTANCE){ break; } } return 1.0; } float pointSoftShadow(in vec3 ro, in vec3 rd, float k) { float res=1.0; float t=SHADOW_MIN_DISTANCE; for(int i=0; i < SHADOW_ITERATION; i++) { float h=map(ro + rd * t).x; if (h < SHADOW_EPSILON){ return 0.0; } res=min(res, k * h / t); t += SHADOW_DELTA + h * SHADOW_BOOST; if (t > SHADOW_MAX_DISTANCE){ break; } } return res; } // // Ambient occlusion // float pointAO(in vec3 pos, in vec3 nor) { float totao=0.0; float sca=AO_BLUR_START; for(int aoi=0; aoi < AO_ITERATION; aoi++) { float hr=AO_START + AO_STEP * float(aoi); vec3 aoPos=nor * hr + pos; float distance=map(aoPos).x; totao += -(distance - hr) * sca; sca *= AO_BLUR; } return clamp(1.0 - 4.0 * totao, 0.0, 1.0); } //------------------------------------- // Texture //------------------------------------- vec3 debugSteps(float distance) { return vec3( (distance < 0.25) ? 1. - distance * 4. : ((distance > 0.24 && distance < 0.50) ? 0.0 + distance * 2. : 0.), (distance < 0.25) ? 1. - distance * 4. : ((distance > 0.49 && distance < 0.75) ? distance * 1.2 : 0.), (distance < 0.15) ? 1. - distance * 4. : ((distance > 0.75) ? distance : 0.) ); } vec3 checkers(vec3 pos, vec3 normal, float size) { vec3 p=cross(pos, normal); float c=floor(mod((p.x / size) + floor(p.y / size) + floor(p.z / size), 2.0)) * size; return vec3(clamp(c, 0.3, 1.0)); } vec3 distancecolor(vec3 pos, vec3 normal) { return normalize(pos); } vec3 plasma(vec3 pos, vec3 normal) { return vec3( pos.x * sin(pos.y * 50. + time / 0.4), pos.y * cos(pos.z * 10. + time / 0.2), pos.x * sin(pos.y * 15. + time / 0.10) ); } void main() { vec2 pos=(gl_FragCoord.xy * 2.0 - resolution.xy) / resolution.y; vec2 uv=pos * vec2(resolution.x / resolution.y, 1.0); // // Light config // Light light1; light1.posistion=vec3(sin(time * 2.25) * 4.0, 5.0, cos(time * 1.25) * 5.0); light1.ambientColor=vec3(0.0, 0.0, 0.0); light1.color=vec3(1.0, 1.0, 1.0); // // Material config // #if ENABLE_MATERIAL==1 materials[0]=Material(vec3(0.0, 0.0, 0.0), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), 16., 1); materials[1]=Material(vec3(0.0, 0.0, 0.0), vec3(0.6, 0.6, 0.6), vec3(1.0, 0.0, 0.0), 16., 3); materials[2]=Material(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0), 16., 0); materials[3]=Material(vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 1.0), 16., 0); materials[4]=Material(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(1.0, 1.0, 1.0), 16., 0); materials[5]=Material(vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 0.0), vec3(1.0, 1.0, 1.0), 16., 0); materials[6]=Material(vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 1.0), vec3(1.0, 1.0, 1.0), 16., 0); materials[7]=Material(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 1.0), vec3(1.0, 1.0, 1.0), 16., 0); #endif // // Camera config // #if CAMERA_MODE==0 vec3 camPos=vec3(12.0, 5.0, 12.0); vec3 camTarget=vec3(0.0, 0.0, 0.0); #elif CAMERA_MODE==1 vec3 camPos=vec3(10.0, 0.0, 10.0); vec3 camTarget=vec3(0.0, 0.0, 0.0); #elif CAMERA_MODE==2 vec3 camPos=vec3(mouse.x, mouse.y, 5.0); vec3 camTarget=vec3(0.0, 0.0, 0.0); #elif CAMERA_MODE==3 vec3 camPos=vec3(-sin(time / 10.0) * 5.0, 5, cos(time / 10.0) * 5.0); vec3 camTarget=vec3(0.0, 0.0, 0.0); #else vec3 camPos=vec3(cos(time * 0.5) * 4., 3. + sin(time * 0.5) * 3., 4.0); vec3 camTarget=vec3(0.0, 0.0, 0.0); #endif // // Calculate camera vectors // vec3 camUp=normalize(vec3(0.0, 1.0, 0.0)); vec3 camDir=normalize(camTarget - camPos); vec3 camSide=cross(camDir, camUp); float focus=1.8; vec3 rayDir=normalize(camSide * pos.x + camUp * pos.y + camDir * focus); vec3 ray=camPos; // // RayMarching // Object sceneObject=rayMarching(ray, rayDir); // // Color process // vec3 outColor=vec3(0.0, 0.0, 0.0); if (sceneObject.hit){ #if RENDER_MODE==0 outColor=sceneObject.mat.diffuse; #elif RENDER_MODE==1 outColor=sceneObject.mat.diffuse * sceneObject.grey; #elif RENDER_MODE==2 outColor=vec3(sceneObject.grey); #elif RENDER_MODE==3 outColor=sceneObject.normal; #elif RENDER_MODE==4 outColor=vec3(sceneObject.distance); #elif RENDER_MODE==5 outColor=sceneObject.pos; #endif #if ENABLE_TEXTURE if (sceneObject.mat.texture == 1){ outColor=outColor * checkers(sceneObject.pos, sceneObject.normal, 2.0); } else if (sceneObject.mat.texture == 2){ outColor=outColor * distancecolor(sceneObject.pos, sceneObject.normal); } else if (sceneObject.mat.texture == 3){ outColor=outColor * plasma(sceneObject.pos, sceneObject.normal); } #endif #if ENABLE_LIGHT vec3 lightColor=vec3(0.0); lightColor=pointLight(lightColor, light1, sceneObject); #else vec3 lightColor=vec3(1.0); #endif float baseShadow=1.0; #if ENABLE_AO float ao=pointAO(sceneObject.pos, sceneObject.normal); baseShadow=baseShadow * ao; #endif #if SHADOW_MODE==1 float shadow=pointShadow(sceneObject.pos + SHADOW_POINT * sceneObject.normal, light1.posistion); baseShadow=baseShadow * shadow; #endif #if SHADOW_MODE==2 float shadow=pointSoftShadow(sceneObject.pos + SHADOW_POINT * sceneObject.normal, light1.posistion, SHADOW_BLUR); baseShadow=baseShadow * shadow; #endif outColor=outColor * lightColor * baseShadow + sceneObject.mat.ambient; } else { // // Background color // //outColor=mix(vec3(0.0, 0.0, 0.0), vec3(1.0,0.0,0.0), 20.0/abs(sceneObject.pos.y)); //outColor=mix(vec3(0.0, 0.0, 0.0), vec3(1.0,0.0,0.0), sceneObject.pos.y/MAX_DISTANCE); //outColor=mix(vec3(0.0, 0.0, 1.0), vec3(1.0,0.0,0.0), rayDir.x*5.); //outColor=rayDir; //outColor=abs(rayDir); outColor=mix(vec3(0.0, 0.0, 0.5), vec3(0.0, 0.4, 0.5), rayDir.y * 3.); } gl_FragColor=vec4(outColor.r, outColor.g, outColor.b, 1.0); } |
9. Literatura
- http://www.pouet.net/topic.php?which=7931&page=1
- http://www.pouet.net/topic.php?which=7920&page=1
- http://www.iquilezles.org/www/articles/raymarchingdf/raymarchingdf.htm
- http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
Pozostałe części:
Część I – wstep, opis algorytmu
Część II – obiekty podstawowe, modyfikatory
Możliwość komentowania jest wyłączona.