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.