/******************************************************************** * file: pebblecolor.sl * author: Peter Stuart * date: 10/20/2002 * $Id: pebblecolor.sl,v 1.2 2003/11/18 19:01:40 ophi Exp $ * * description: This surface generates layers of pebbles using * bombing techniques. It also does some fake shadows and shading. * A texture can be used to control the distribution of the pebbles. * The greyscale value of the texture determines how many layers to place * in a particular region of the image. * * Todo: * frequency clamping: Calculate when the pebble size reaches the size * of a pixel. When this happens, blend pebble to average value. * tiling: The texture should be tilable... * speed: This thing is dog slow :( It would be nice if the pebbles could * packed together better, so that fewer layers are needed. * * ---- common parameters ------------- * layers: Number of layers of pebbles. Higher numbers will slow rendering * scale: Zoom out of the image - higher values makes rocks smaller * seed: Controls random characteristics * distimg: Image controlling distribution of pebbles. White areas will * cause "layers" layers to be drawn. No layers will be drawn * in black areas, and the number of layers will vary according * to the values in between. * * ---- surface parameters ---------- * shdw: Creates shadows - preferred values between [0,1] * shading: Shades the sides of the pebbles. The value is used as follows: * 0 - turn off shading * >0 - turn on shading and multiply color by the parameter value * minc, maxc: Range of pebble colors in HSV * * compile with QUALITY_PEBBLES defined to get better quality (but slower) ************************************************************/ /* Uncomment this to get better quality pebbles and shadows. If you see shadows or pebbles that stop abruptly, uncommenting this should give better quality, but adversely affect execution time */ /* #define QUALITY_PEBBLES */ #include "sl2vex.h" #include "rmannotes.sl" #include "xforms.h" #define RN_OFFS 0.25 #include "rn.h" #pragma hint "distimg" image #pragma hint "basecolor" color #pragma hint "baseopac" color /* ---- functions to create random transformation and inverse ----- * scale_min, scale_max: range of scaling * rot_min, rot_max: range of rotation * seed: controls transformation * row, col: row and column of current tile * s, t: current position * ss, tt: returned position * * the rand_xform_params provides a way of making sure both the forward and * inverse transforms use the same parameters. */ void rand_xform_params(float scale_min, scale_max; float rot_min, rot_max; float seed; float row, col; output float sx, sy, tx, ty, rot) { float scale1 = udn2(col+RN(1), row+RN(2), scale_min, scale_max) / 2; float scale2 = udn2(col+RN(3), row+RN(4), scale_min, scale_max) / 2; sx = 2*scale1; sy = 2*scale2; tx = udn2(col+RN(5), row+RN(6), scale1-0.5, 0.5-scale1) / 2; ty = udn2(col+RN(7), row+RN(8), scale2-0.5, 0.5-scale2) / 2; rot = udn2(col+RN(9), row+RN(10), rot_min, rot_max); } void rand_xform(float scale_min, scale_max; float rot_min, rot_max; float seed; float row, col, s, t; output float ss, tt;) { float sx, sy, tx, ty, rot; rand_xform_params(scale_min, scale_max, rot_min, rot_max, seed, row, col, sx, sy, tx, ty, rot); xform_rot(sx, sy, tx, ty, rot, s, t, ss, tt); } void rand_xform_inv(float scale_min, scale_max; float rot_min, rot_max; float seed; float row, col, s, t; output float ss, tt;) { float sx, sy, tx, ty, rot; rand_xform_params(scale_min, scale_max, rot_min, rot_max, seed, row, col, sx, sy, tx, ty, rot); xform_rot_inv(sx, sy, tx, ty, rot, s, t, ss, tt); } /********* surface shader *************/ surface pebblecolor(uniform float layers = 5, shdw = 0, shading = 0; uniform float scale = 1, seed = 1234; color minc = 0, maxc = 1; float Ka = 0.9, Kd = 0, Ks = 0, rough = 10; string distimg = ""; color basecolor = 1, baseopac = 1) { color layer_color, surface_color, surface_opac; color offs; float layer_disp, layer_z, layer_opac, shadow_opac; float radius, d; float ss, tt; /* local coordinates */ float xs, xt; /* transformed coordinates */ float gs, gt; /* global coordinates */ float row, col; uniform float loffs; /* layer offset - used to ensure random numbers generated for multiple layers don't overlap */ uniform float sfreq, tfreq; uniform float i=0, j=0, k=0; float distrib; layer_z = 0; surface_color = basecolor; surface_opac = baseopac; shadow_opac = 0; layer_color = 0; for (i=layers; i>0; i-=1){ loffs = i*100; /* increase the frequency of pebbles at higher layers - since the layers are generated in reverse order, the smaller pebbles appear below larger pebbles. */ sfreq = scale*(i + snoise(loffs+RN(1))); tfreq = scale*(i + snoise(loffs+RN(2))); /* give this layer a random position, size, and rotation */ rand_xform(0.5, 1, 0, radians(90), seed, loffs+RN(2), loffs+RN(2), s, t, gs, gt); col = whichtile(gs, sfreq); row = whichtile(gt, tfreq); ss = repeat(gs, sfreq); tt = repeat(gt, tfreq); #ifdef QUALITY_PEBBLES float true_col=col; float true_row=row; /* these loops help to draw portions of the pebble and its shadow that might overlap another row or column */ for (j=-1; j<=1; j+=1) { for(k=-1; k<=1; k+=1) { ss = repeat(gs, sfreq) - j; tt = repeat(gt, tfreq) - k; col = mod((true_col + j), sfreq); row = mod((true_row + k), tfreq); #endif /* QUALITY_PEBBLES */ /* determine shape of current rock */ rand_xform(0.25, 0.5, 0, radians(90), seed, row+loffs, col+loffs, ss, tt, xs, xt); /* get distribution of pebbles */ if (distimg != "") { /* This is the real tricky part. In order to get a complete pebble, each of the pebble needs to map to the same pixel in screen coordinates. The point 0.5,0.5 in pebble space is chosen, since it is the center of the pebble. This point has to be transformed back into screen space by performing the inverse of ALL of the previous transformations, including repeats and rand_xforms */ float iss, itt, igs, igt, is, it; rand_xform_inv(0.25, 0.5, 0, radians(90), seed, row+loffs, col+loffs, 0.5-j, 0.5-k, iss, itt); igs = (iss + col) / sfreq; igt = (itt + row) / tfreq; rand_xform_inv(0.5, 1, 0, radians(90), seed, loffs+RN(2), loffs+RN(2), igs, igt, is, it); /* now determine how many layers to draw */ distrib = comp(color(texture(distimg, is, it)), 0) * layers; } else { distrib = layers; } if (i < distrib) { layer_opac = 0; layer_disp = 0; /* give the pebble a low frequency noisy edge */ radius = 0.5 + 0.5*snoise2(xs+col+loffs+RN(3), xt+row+loffs+RN(4)); d = distance(setp(0.5, 0.5, 0), setp(xs, xt, 0)); /* draw shadow */ if (shdw > 0) { layer_opac = 1 - smoothstep(radius + (0.3*shdw)-0.6, radius + (0.3*shdw), d); /* don't include shadows in pebble color calculation since shadow should darken the layers underneath. */ layer_color = 0; shadow_opac = union(shadow_opac, layer_opac); surface_color = blend(surface_color, layer_color, layer_opac); } /* draw pebble */ layer_opac = 1 - smoothstep(radius-radius/20, radius+radius/20, d); /* shade edges */ layer_disp = layer_opac * sqrt(max(0, radius*radius - d*d)); if (shading > 0) layer_z = shading * layer_opac * layer_disp; else layer_z = 1; /* add a little bit of perlin noise to the surface to make it look a dirtier - also generate a color randomly */ /* modify the color slightly over the surface */ float xxs, xxt; rand_xform(0.5, 1, 0, 0, seed, xs*5, xt*5, xs, xt, xxs, xxt); offs = hsvtorgb(udn2(col+loffs+RN(5), row+loffs+RN(6), comp(minc, 0), comp(maxc, 0)) + udn2(xxs, xxt, -0.01, 0.01), udn2(col+loffs+RN(7), row+loffs+RN(8), comp(minc, 1), comp(maxc, 1)) + udn2(xxs, xxt, -0.1, 0.1), udn2(col+loffs+RN(9), row+loffs+RN(10), comp(minc, 2), comp(maxc, 2)) + udn2(xxs, xxt, -0.1, 0.1)); layer_color = (1 - 0.2*(float(noise(xs*7, xt*7)))) * (0.1 + 0.9*layer_z) * offs; surface_color = blend(surface_color, layer_color, layer_opac); surface_opac = union(surface_opac, layer_opac); } #ifdef QUALITY_PEBBLES } } #endif /* QUALITY_PEBBLES */ } Ci = surface_color; normal Nf = faceforward(normalize(N), I); Ci = surface_color * (Ka*ambient() + Kd*diffuse(Nf)) + Ks*specular(Nf, -normalize(I), rough); Oi = union(surface_opac, shadow_opac); }