crt/shaders.cpp
#include "shaders.hpp"
#include "stats.hpp"
DepthFog::DepthFog(const Img::rgb_t& c, coord_t s, unsigned d)
: col(c), start(s), sqstart(s*s), density(1.0/(double)d) {
}
void DepthFog::get(const Scene*, ShaderInfo::shader_info_t& info, Img::rgb_t& c) const {
// compute the two intersection points with this box and take its len
// TODO: per default acc. to DOV?
// http://blog.demofox.org/2014/06/22/analytic-fog-density/
// http://computergraphics.stackexchange.com/questions/227/how-are-volumetric-effects-handled-in-raytracing
// https://renderman.pixar.com/view/fog-and-atmospheric-effects
// http://www.cs.rpi.edu/~cutler/classes/advancedgraphics/S07/final_projects/fischc/fog_simulation.html
#if 1
coord_t len = info.ray.direction.sqlen();
if (len <= sqstart) {
return;
}
len = GETORSET(info.depth, sqrt(len)) - start;
#else
coord_t len = ray.direction.max() - start;
if (len <= 0.0) {
return;
}
#endif
float alpha = len*density;
alpha = MIN(1.0, alpha); // 1: only fog
// + some random value for cloudiness?
// TODO: config.img_lin_avg.d
c.r += (col.r - c.r) * alpha;
c.g += (col.g - c.g) * alpha;
c.b += (col.b - c.b) * alpha;
}
VolumetricLight::VolumetricLight(const vertex_t p, unsigned brightness, unsigned len)
: pos(p), intensity(MIN((double)brightness,100.0)/100.0), maxlen(len), maxsqlen(len*len), samples_max((2.0*maxlen)/stepsize) {
}
const coord_t VolumetricLight::stepsize = 50.0;
void VolumetricLight::get(const Scene* scene, ShaderInfo::shader_info_t& info, Img::rgb_t& c) const {
// TODO: blur/skip/interpolate/noise?
// volumetric effects, i.e. light rays? participating media?
// precomputed lightmap for voxels?
// config.img_lin_avg.d
// http://www.alexandre-pestana.com/volumetric-lights/
const coord_t raylen = GETORSET(info.depth, info.ray.direction.len()); // TODO: can move first sample pos to length boundary (as camera should be far away)
const int samples = raylen / stepsize; // TODO: round down? we start with 1 below, too
const vertex_t dir = info.ray.direction / raylen; // unit vector to travel along
ray_t lray(pos, vertex_t(0, 0, 0)); // cast from light to current sample
float l = 0.0;
for (int i=1; i<=samples; ++i) {
coord_t sample_len = ((coord_t)i) * stepsize;
sample_len -= (((coord_t)rand())/((coord_t)RAND_MAX)) * 0.5 * stepsize; // not too far beyond clipping
lray.direction = info.ray.origin + (dir * sample_len);
if (lray.direction.min() < 0.0) {
continue; // lightrays must not clip scene
}
lray.direction -= lray.origin;
coord_t rlen = lray.direction.sqlen();
if (rlen >= maxsqlen) {
continue;
}
if (scene->intersect(lray)) {
continue;
}
l += (maxsqlen-rlen)/maxsqlen; // TODO: account for angle? use non-squared len?
}
l /= (float)samples_max; // average light throughout all samples on the ray
l *= intensity;
c.r += (255 - c.r) * l;
c.g += (255 - c.g) * l;
c.b += (255 - c.b) * l;
}
DepthBlur::DepthBlur(unsigned mmin, unsigned mmax)
: mind(mmin), maxd(mmax), diffd(mmax-mmin) {
}
void DepthBlur::get(const Scene*, ShaderInfo& shader_info, Img& out) const {
for (unsigned y=0; y<out.h-1; ++y) {
for (unsigned x=0; x<out.w-1; ++x) {
Img::rgb_t& p = out.at(x, y);
ShaderInfo::shader_info_t& i = shader_info.at(x, y);
float blur = 1.0 - ((GETORSET(i.depth, i.ray.direction.len()) <= mind)? 0.0: ((i.depth >= maxd)? 1.0: ((i.depth - mind) / diffd))); // or hitpoint len?
// depth-map, actually
// FIXME: blur according to average surroundings, if its the same object or depth. Otherwise this could be simply a pixel shader or light source.
p.r *= blur;
p.g *= blur;
p.b *= blur;
}
}
}