Waldmann TEVISIO Leuchte
Eine kommerzielle Produktion der visionsbox bei der vorhandene CAD-Daten in 3ds Max aufbereitet und animiert wurden. Ich war hauptsächlich am Shading, Lighting und Rendering, welches mit Vray gelöst wurde, beteiligt.
Eine kommerzielle Produktion der visionsbox bei der vorhandene CAD-Daten in 3ds Max aufbereitet und animiert wurden. Ich war hauptsächlich am Shading, Lighting und Rendering, welches mit Vray gelöst wurde, beteiligt.
Meine Vorgabe für dieses Still war der restriktive Einsatz von ausschließlich prozeduralen Shadern die in RSL geschrieben wurden. Keiner der verwendeten Shader wurde mit Maya (Hypershade) oder anderen ShaderManagern (Slim, Shaderbox, usw.) gefertigt.
Das Still ist bis auf eine leichte Helligkeit- und Kontrastkorrektur völlig ohne Nachbearbeitung.
Da kommendes Still fast ausschließlich über GI ausgeleuchtet werden soll, hab ich den ursprünglichen volume- und pointbased Shader Kombination überarbeitet. Der Prozess ist nach wie vor noch in 2-Phasen unterteilt – allerdings nicht mehr alleine auf volume Shader fixiert.
In der ersten Phase wird eine pointcloud generiert – je nach Wunsch kann man dies mit Photonmapping noch koppeln, bzw. FinalGathering anstreben. Dieser Part ist nach wie vor noch mit einem volume Shader realisiert.
#include "pragmas/p_sg_ptcGI.h"
////////////////////////////////////////////////////////////////////////
V_SG_PTCWRITE_PRAGMAS
V_SG_PTCWRITE_PRAGMAS_GROUPS
////////////////////////////////////////////////////////////////////////
volume
v_ptcGI_write ( string ptcfile = "<project>/3delight/<scene>/ptc/<scene>.#.ptc";
string coordsys = "world";
//~ float CsKm = 1;
float photonKm = 1;
float estimator = 100;
string lookuptype = "";
float mindepth = 1;
output varying color cphotons = 0;
)
{
normal Nn = normalize(N);
color cincand = 0;
color csurface = 0;
surface("aov_surface_color", csurface);
surface("aov_incandescence", cincand);
string globalmap = "";
attribute("photon:globalmap", globalmap);
cphotons = photonKm * photonmap(globalmap, P, Nn, "estimator", estimator, "lookuptype", lookuptype, "mindepth", mindepth);
color cindirect = Oi * (Ci + cphotons * csurface + cincand);
bake3d(ptcfile, "", P, N, "coordsystem", coordsys, "_radiosity", cindirect, "interpolate", 1);
Ci = cindirect;
}
Den ursprünglichen 2. volume Shader, der die Global Illumination Anteile berechnete hab ich dieses Mal mit einem light Shader ersetzt. Er beherrscht nach wie vor noch IBL, colorbleeding und occlusion sowie einen Cache, der eine weitere pointcloud generiert, welche man dann auch in eine gefilterte brickmap wandeln könnte.
Desweiteren hab ich das gesamte Interface überarbeitet und den Funktionsumfang auf das Nötigste reduziert.
#include "pragmas/p_sg_ptcGI.h"
////////////////////////////////////////////////////////////////////////
L_SG_PTCRENDER_PRAGMAS
L_SG_PTCRENDER_PRAGMAS_GROUPS
////////////////////////////////////////////////////////////////////////
light
l_ptcGI_render( string operation = "GI",
ptcfile = "<project>/3delight/<scene>/ptc/<scene>.#.ptc",
ptcchannel = "_radiosity",
ptccoord = "world",
cache = "write",
cachefile = "<project>/3delight/<scene>/ptc/cached.<scene>.#.ptc",
envmap = "",
envcoord = "world",
hitsides = "both",
falloffmode = "exponential";
color occblack = 0,
occwhite = 1;
float Kind = 1, Kocc = 1, Kenv = 1,
maxdist = 1e15,
falloff = 1,
bias = 0.045,
clamping = 1,
sortbleeding = 1,
coneangle = 180,
maxsolidangle = .2,
maxvariation = 1;
)
{
normal Nsn = normalize(Ns);
varying color cenv, cocc, cindirect;
if(cache == "write") {
varying float occ;
uniform float fallmode;
if(falloffmode == "exponential")
fallmode = 1;
else fallmode = 0;
if(operation == "occlusion") {
occ = (1 - occlusion(Ps, Nsn, 0,
"pointbased", 1,
"filename", ptcfile,
"coordsystem", ptccoord,
"hitsides", hitsides,
"coneangle", radians(coneangle),
"clamp", clamping,
"sortbleeding", sortbleeding,
"maxdist", maxdist,
"falloff", falloff,
"falloffmode", fallmode,
"bias", bias,
"maxsolidangle", maxsolidangle,
"maxvariation", maxvariation));
cocc = mix(occblack, occwhite, occ);
bake3d(cachefile, "", Ps, Nsn, "coordsystem", ptccoord, "_occlusion", cocc, "interpolate", 1);
Cl = Kocc * cocc;
outputchannel("aov_occlusion", cocc);
}
else if(operation == "GI") {
cindirect = indirectdiffuse(Ps, Nsn, 0,
"pointbased", 1,
"filename", ptcfile,
"coordsystem", ptccoord,
"hitsides", hitsides,
"coneangle", radians(coneangle),
"clamp", clamping,
"sortbleeding", sortbleeding,
"maxdist", maxdist,
"falloff", falloff,
"falloffmode", fallmode,
"bias", bias,
"maxsolidangle", maxsolidangle,
"maxvariation", maxvariation,
"environmentmap", envmap,
"environmentspace", envcoord,
"environmentcolor", cenv,
"occlusion", occ,
"radiositychannel", ptcchannel);
cocc = mix(occblack, occwhite, 1 - occ);
cindirect -= cenv;
outputchannel("aov_indirect", cindirect);
outputchannel("aov_env_diffuse", cenv);
outputchannel("aov_occlusion", cocc);
bake3d(cachefile, "", Ps, Nsn, "coordsystem", ptccoord, "_GI", cindirect, "_occlusion", cocc, "_env", cenv, "interpolate", 1);
Cl += (Kind * cindirect + Kenv * cenv) * (Kocc * cocc);
}
}
else if(cache == "read") {
if(operation == "occlusion") {
texture3d(cachefile, Ps, Nsn, "coordsystem", ptccoord, "_occlusion", cocc);
Cl = Kocc * cocc;
}
else if (operation == "GI") {
texture3d(cachefile, Ps, Nsn, "coordsystem", ptccoord, "_GI", cindirect, "_occlusion", cocc, "_env", cenv);
Cl += (Kind * cindirect + Kenv * cenv) * (Kocc * cocc);
}
outputchannel("aov_indirect", cindirect);
outputchannel("aov_env_diffuse", cenv);
outputchannel("aov_occlusion", cocc);
}
}
Auch diesen Shader lass ich mal in der Kategorie “Baustelle” – so ganz zufrieden bin ich damit noch nicht.
Für das kommende Still brauch ich noch einen Shader der eine gewisse Ähnlichkeit mit bunten Bleiglasfenster wie in Kirchen hat.
Voraussetzung die der Shader erfüllen muss, ist neben den zellenartigen Flächen die fröhlich bunt sind, auch farbigen Schatten (Transmission) zu werfen.
Dafür hab ich die bekannte voronoi-shadeop aus Larry Gritz noises.h ein klein wenig modifiziert damit sie neben den Featuren (f1, f2) und points (pos1, pos2) auch die farbigen Zelle an der derzeitigen Position ausgibt.
void voronoi_2F_3D_C ( point P;
float jitter;
output float f1; output point pos1;
output float f2; output point pos2;
output color cell;
)
{
point thiscell = point (floor(xcomp(P)) + .5, floor(ycomp(P)) + .5, floor(zcomp(P)) + .5);
f1 = f2 = 1000;
uniform float i, j, k;
varying float dist;
for (i = -1; i <= 1; i += 1) {
for (j = -1; j <= 1; j += 1) {
for (k = -1; k <= 1; k += 1) {
point testcell = thiscell + vector(i,j,k);
point pos = testcell + jitter * (vector cellnoise(testcell) - .5);
vector offset = pos - P;
dist = offset . offset; /* actually dist^2 */
if (dist < f1) {
f2 = f1; pos2 = pos1;
f1 = dist; pos1 = pos;
/*give the current color*/
cell = cellnoise(testcell);
}
else if (dist < f2) {
f2 = dist; pos2 = pos;
}
}
}
}
f1 = sqrt(f1); f2 = sqrt(f2);
} /* Voronoi cell noise (a.k.a. Worley noise) -- 3-D, 2-feature, colored cells version. */
Für die Berechnung der Reflektionen und Refraktionen kommt fresnel() zum Einsatz.
Der gesamte Shader sieht dann so aus:
#include "h_sg_utils.h"
#include "h_sg_procedurals.h"
color
envtrace(point inP; vector inDir; string inEnv;) {
color cenv;
color ctraced = 0, trans;
ctraced = trace(inP, inDir, "transmission", trans);
if(inEnv != "" && (trans[0]>0 || trans[1]>0 || trans[2]>0) ) {
ctraced += trans * environment(inEnv, vtransform("world", inDir));
}
return ctraced;
}
surface
s_cellglas( float Ka = 0.2, Kd = 1, Ks = .5;
float power = 50;
color specularcolor = color (.9, .9, .9);
float Kr = 1, Kt = 1, eta = 1.5;
string envmap = "";
float cellscale = 1;
float cellgap = .05;
float Km = 1;
float octaves = 6, lacunarity = 2, gain = .35;
float scale = 1;
float truedisp = 1;
)
{
float f1, f2, fcells, dist, frost, disp;
varying color ccell;
point Pshad = transform("current", P);
float dPshad = filterwidthp(Pshad);
point pos1, pos2;
normal Nn = normalize(N);
//~ colored cells and cell outlines
voronoi_2F_3D_C(cellscale * Pshad, 1, f1, pos1, f2, pos2, ccell);
dist = distance(pos1, pos2) / (distance(pos1, Pshad) + distance(Pshad, pos2));
fcells = invertF(smoothstep(.5, 1, f1)) * filterstep(cellgap * dist, f2 - f1);
//~ additional fractional brownian motion to displace
frost = fBm(scale * Pshad, dPshad, octaves, lacunarity, gain) + .5;
frost = smoothstep(0, 1, frost);
disp = (invertF(f1) * frost) * fcells * .25;
//~ displace
N = displace(Nn, "world", Km * disp, 1);
//~ reflection and refraction
normal Ndisp = normalize(N);
vector In = normalize(I);
vector Rfldir, Rfrdir;
float kr, kt;
fresnel(In, Ndisp, eta, kr, kt, Rfldir, Rfrdir);
color crefl = (kr * Kr) * envtrace(P, Rfldir, envmap);
color crefr = (kt * Kt) * envtrace(P, Rfrdir, envmap);
color cspec = Ks * phong(Ndisp, -In, power);
color cdiff = mix(0, ccell, fcells) * (Ka * ambient() + Kd * diffuse(Ndisp));
Oi = mix(1, ccell, frost * fcells);
Ci = cdiff
+ (crefl
+ cspec
+ crefr) * fcells;
outputchannel("aov_surface_color", mix(0, ccell, fcells));
outputchannel("aov_diffuse", cdiff);
outputchannel("aov_specular", cspec * fcells);
outputchannel("aov_reflection", crefl * fcells);
outputchannel("aov_refraction", crefr * fcells);
}
//~ maya ui
#pragma annotation Ka "gadgettype=floatslider;min=0;max=1;label=Ka;"
#pragma annotation Kd "gadgettype=floatslider;min=0;max=1;label=Kd;"
#pragma annotation Ks "gadgettype=floatslider;min=0;max=1;label=Ks;"
#pragma annotation specularcolor "gadgettype=colorslider;label=specular;"
#pragma annotation Kr "gadgettype=floatslider;min=0;max=1;label=Kr;"
#pragma annotation Kt "gadgettype=floatslider;min=0;max=1;label=Kt;"
#pragma annotation envmap "gadgettype=inputfile;label=envmap"
#pragma annotation transparency "gadgettype=floatslider;min=0;max=1;label=transparency;"
#pragma annotation Km "gadgettype=floatslider;min=0;max=1;label=Km;"
#pragma annotation truedisp "gadgettype=checkbox:0:1=custom value;"
Allerdings stimmt dabei noch die finale Komposition nicht. Die farbigen Zellen werden in der Opacity des Shaders eingesetzt – dadurch wird das Objekt an diesen Stellen farbig durchsichtig. Die Refraktionen werden allerdings bisher nur addiert. Das hat zum Effekt, dass man zum einen das gebrochene Licht sieht und zum anderen durch die (ungebrochene) Transparenz des Objektes.
Der Shader ist also noch als “in der Entwicklung” zu betrachten. Ich werd ihn allerdings in seinem jetzigen Bestehen in der kommenden Szene verwenden.
Hier noch ein Test-Rendering.
Zur Zeit arbeite ich am nächsten Still. Im Rahmen dessen brauche ich einen anpassungsfähigen Shader der Steinwände in unterschiedlichen Variationen darstellen kann. Daher bin ich dabei, für mich eine Kombination von Displacement- und Surfaceshader zu entwickeln die vollständig auf prozeduralen Funktionen basieren.
Die bisherigen Grundfunktionen stellen mich soweit zufrieden. Doch es fehlen noch verschiedene Beleuchtungsfunktionen, ebenso sollen noch Verschmutzung, Kratzer, Risse und evtl noch eine Variation der Backsteine hinzugefügt werden.
Vorläufig werden Variablen via message-passing zwischen Displacement Shader und Surface Shader übermittelt, doch der letzte Schritt wird wohl sein, die beiden Shader vollständig zu kombinieren und einen Co-Shader daraus zu machen.