Há três importantes processos que afetam a
distribuição da radiância em um ambiente com meio
participativo: absorção, emissão e dispersão. Agora as
características de estes processos são
homogêneos e não homogêneos.
Processos homogêneos: são constantes no espaço.
Processos não homogêneos: podem variar em todas partes
do espaço.
Observações.
A função fase é normalizada tal que
Agora lembremos que
Esta expresão tem uma vantagem de separar as noçes de
probabilidade de colisão por unidade de comprimento e o resultado
é
uma distribuição de direções.
A função fase frequentemente só depende do ângulo,
, entre o raio de chegada ao ponto de disperão ,
e
raio de de dispersão
.
Podemos escrever como
onde
é o (dispersão para frente) e
é (dispersão para atrais)
[3].
A forma como as funções fase podem ser caracterizadas é por o
número
é chamado de parametro
[2] ou
[4] e
varia entre 1 e -1, e 0 para disperperção isotropica.
Alem o valor
é sera positivo para dispersão para
frente (forward scattering) e negativo para dispersão para atrais
(backward scattering) [4].
3.1 Função de Fase isotropica
Descreve igual dispersão em todas as direções, ie, é
independiente de qualquer dos sentidos.
Porque as funções fase são normalizadas, existe soamente
uma de tais funções
Todas as outras funções fase são
.
3.2 Função fase de Rayleigh:
descreve a dispersão de muitas e pequenas
partículas (como as partículas da atmosfera). Se as partículas
tem
radio menor que o comprimento de onda da luz ,
, o modelo descreve con precisão a
distribuição da
dispersão da luz.
A dependencia do comprimento de onda é a razão do que ceu é
azul e os raios do por do sol vermelhos.
E é
3.3 Função fase de Mie:
A teoria é derivada das equações de Maxwell, e podem
descrever a dispersão de um rango amplo de tamano de
partículas.
É um bom modelo para dispersão na atmosfera devido as gotas de
agua e bruma.
3.4 Função fase mais usada é Henyey e Greenstein
É usada para descrever dispersão em oceanos, nuvens, pele, pedra
e mais, é
onde é o parametro asimetrico (anisotropico).
Blasi, e Schlick (1993) desenvolvieram uma aproximação
eficiente à função de Henyey-Greenstein. Esta
aproximação
evita a função
cara de powf()
Relação do Volume.
A abstração chave para descrever a dispersão
volumétrica no pbrt [4] é a classe VolumeRegion.
Todo VolumeRegion pode calcular sua caixa limitada
com eixos alinhados ao espaço mundo.
Este limite pode ser usado para calcular VolumeRegion
em estruturas de aceleração.
Porque os VolumeIntegrators necessitam saber a escala
paramétrica de um raio do espaço mundo que passa
através de una região do volume, um
VolumeRegion::IntersectP() separado do método retorna
a escala paramétrica.
00035 virtual bool IntersectP(const Ray &ray, float *t0, 00036 float *t1) const = 0;
Dado um ponto e um sentido do espaço mundo,
VolumeRegion::sigma_a(), VolumeRegion::sigma_s(), e
VolumeRegion::sigma_
() retornam a
absorção, a
dispersão e as propriedades correspondentes da
emissão e o método VolumeRegion::p() retorna o valor
da função fase em um ponto dado.
VolumeRegion::sigma_t() retorna o coeficiente de
atenuação
mas a maioria das
execuções do VolumeRegion cancelarão esta suma e
calcularão diretamente.
00037 virtual Spectrum sigma_a(const Point &, 00038 const Vector &) const = 0; 00039 virtual Spectrum sigma_s(const Point &, 00040 const Vector &) const = 0; 00041 virtual 00042 Spectrum Lve(const Point &, const Vector &) const = 0; 00043 virtual float p(const Point &, const Vector &, 00044 const Vector &) const = 0; 00045 virtual Spectrum sigma_t(const Point &,const Vector &) const; 00046 virtual Spectrum Tau(const Ray &ray, 00047 float step = 1.f, float offset = 0.5) const = 0;
O método VolumeRegion::Tau() calcula a espessura
óptica do volume, entre dois pontos ray(ray.mint) e
ray(ray.maxt).
00046 virtual Spectrum Tau(const Ray &ray, 00047 float step = 1.f, float offset = 0.5) const = 0;
Volume Homogêneo
É a representação mais simples do volume.
HomogeneousVolume descreve uma região box-shaped do
espaço com propriedade de dispersão homogêneas. Os
valores para , , o valor de da
função fase e a quantidade de emissão
são passadas ao construtor.
O construtor inicializa as variáveis do membro
copiando os parametros correspondentes.
00057 Spectrum sig_a, sig_s, le; 00058 float g; 00059 BBox extent; 00060 Transform WorldToVolume;
Porque e são constantes em
todas partes do volume, a espessura óptica que de um
raio pode ser calculada usando a lei de Beer.
00050 Spectrum Tau(const Ray &ray, float, float) const { 00051 float t0, t1; 00052 if (!IntersectP(ray, &t0, &t1)) return 0.; 00053 return Distance(ray(t0), ray(t1)) * (sig_a + sig_s);
Exemplo.
Seja o coeficiente de atenuação fixo,
. Logo em regiões onde a densidade da
partícula foi 1, então o valor de será
0.2.
Agora se a densidade é 3, então o valor de será
0.6.
Definimos a classe DensityRegion que
fornecera um novo método para obter a densidade da
partícula em um ponto.
O construtor DensityRegion toma os valores básicos
das propriedades de dispersão e as armazena nas
variáveis correspondentes.
00074 Transform WorldToVolume; 00075 Spectrum sig_a, sig_s, le; 00076 float g;
00048 DensityRegion::DensityRegion(const Spectrum &sa, 00049 const Spectrum &ss, float gg, 00050 const Spectrum &emit, 00051 const Transform &VolumeToWorld) 00052 : sig_a(sa), sig_s(ss), le(emit), g(gg) { 00053 WorldToVolume = VolumeToWorld.GetInverse();
O método de DensityRegion::Density, retorna a
densidade do volume no ponto dado no espaço do objeto.
A densidade deve ser não negativa em toda parte porque
é usada para medir os parametros básicos de
dispersão.
00054 virtual float Density(const Point &Pobj) const = 0;
O método de DensityRegion::sigma_a() ilustra como
DensityRegion trabalha.
DensityRegion::sigma_a() leva em conta a densidade
local no ponto.
00055 Spectrum sigma_a(const Point &p, const Vector &) const { 00056 return Density(WorldToVolume(p)) * sig_a;
Uma excepção é DensityRegion::p() o método não
calcula o valor da função de fase pela densidade local. A
quantidade de dispersão em um ponto estão já no valor de
.
00067 float p(const Point &p, const Vector &w, 00068 const Vector &wp) const { 00069 return PhaseHG(w, wp, g);
A classe VolumeGrid armazena densidades em uma grade
3D de posições. Estas amostras são interpoladas para
calcular os valores de densidades. VolumeGrid é uma
subclasse de DensityRegion.
O construtor toma os valores de ,
, e . Armazena uma caixa limitada
do espaço do objeto para a região, e faz uma copia
local dos valores de densidade. A implemtação do VolumeGrid
esta em volumes/volumegrid.cpp no pbrt [4].
00040 VolumeGrid::VolumeGrid(const Spectrum &sa, 00041 const Spectrum &ss, float gg, 00042 const Spectrum &emit, const BBox &e, 00043 const Transform &v2w, 00044 int x, int y, int z, const float *d) 00045 : DensityRegion(sa, ss, gg, emit, v2w), 00046 nx(x), ny(y), nz(z), extent(e) { 00047 density = new float[nx*ny*nz]; 00048 memcpy(density, d, nx*ny*nz*sizeof(float)); 00049 }
00035 float *density; 00036 const int nx, ny, nz; 00037 const BBox extent;
A tarefa do método de Density() do VolumeGrid é usar
as amostras para reconstruir a função de densidade do
volume no ponto dado.
00050 float VolumeGrid::Density(const Point &Pobj) const { 00051 if (!extent.Inside(Pobj)) return 0;
Dado os oito valores da amostra que cercam um ponto em
3D, este método interpola trilinearmente para calcular
os valores da função densidade no ponto. Começa
encontrando a amostra mais próxima do volume cujas
coordenadas do interior são todas são menos do
que posição da amostra e usam então as distancias de
Manhattan ao longo de cada linha central, (dx,dy, e
dz).
00053 float voxx = (Pobj.x - extent.pMin.x) / 00054 (extent.pMax.x - extent.pMin.x) * nx - .5f; 00055 float voxy = (Pobj.y - extent.pMin.y) / 00056 (extent.pMax.y - extent.pMin.y) * ny - .5f; 00057 float voxz = (Pobj.z - extent.pMin.z) / 00058 (extent.pMax.z - extent.pMin.z) * nz - .5f; 00059 int vx = Floor2Int(voxx); 00060 int vy = Floor2Int(voxy); 00061 int vz = Floor2Int(voxz); 00062 float dx = voxx - vx, dy = voxy - vy, dz = voxz - vz;
Estas distancias podem ser usadas diretamente em uma
serie de chamadas de lerp() para estimar a densidade
em um ponto da amostra.
00064 float d00 = Lerp(dx, D(vx, vy, vz), D(vx+1, vy, vz)); 00065 float d10 = Lerp(dx, D(vx, vy+1, vz), D(vx+1, vy+1, vz)); 00066 float d01 = Lerp(dx, D(vx, vy, vz+1), D(vx+1, vy, vz+1)); 00067 float d11 = Lerp(dx, D(vx, vy+1, vz+1),D(vx+1, vy+1, vz+1)); 00068 float d0 = Lerp(dy, d00, d10); 00069 float d1 = Lerp(dy, d01, d11); 00070 return Lerp(dz, d0, d1);
O método D() retorna a densidade na posição dada da
amostra.
00027 float D(int x, int y, int z) const { 00028 x = Clamp(x, 0, nx-1); 00029 y = Clamp(y, 0, ny-1); 00030 z = Clamp(z, 0, nz-1); 00031 return density[z*nx*ny + y*nx + x];
Os valores de e de são os parametros que
controlam a densidade total e como rapidamente tem uma
queda em função da altura, respectivamente.
Esta função da densidade é um modelo bom para a
atmosfera visto da superfície da Terra,
onde a curvatura da atmosfera pode generalmente ser
omitida (Ebert et al 2003). Pode também ser usada para
modelar névoa baixa encontrando se com o nível da
Terra. E é definido em volumes/exponential.cpp no pbrt
[4].
00014 class ExponentialDensity : public DensityRegion { 00015 public: 00016
00035 private: 00036
O construtor de ExponentialDensity inicializa suas
variáveis do membro diretamente de seus argumentos.
Além as propriedades de dispersão volumétrica passam
ao construtor de DensityRegion, também a fronteira do volume,
e os valores dos parametros de e de . Este
construtor toma um vetor dado na direção ``acima''
que orienta o volume e é usado para calcular a
altura de pontos para o calculo da densidade. Agora o
sentido ascendente não é estritamente necessário, mas
especificar um vetor para ``acima'' explicito pode ser
mais fácil de entender.
00017 ExponentialDensity(const Spectrum &sa, const Spectrum &ss, 00018 float gg, const Spectrum &emit, const BBox &e, 00019 const Transform &v2w, float aa, float bb, 00020 const Vector &up) 00021 : DensityRegion(sa, ss, gg, emit, v2w), 00022 extent(e), a(aa), b(bb) { 00023 upDir = Normalize(up); 00024 }
00037 BBox extent; 00038 float a, b; 00039 Vector upDir;
Para a classe ExponecialDensity é necessário encontrar
uma projeção perpendicular de um ponto sobre o vetor
direção acima e determinar a distancia ao longo de
do ponto de projeção.
A distancia esta dada por o produto ponto
onde é o vetor esquina na caixa (e o vetor direção
começa ali).
00030 float Density(const Point &Pobj) const { 00031 if (!extent.Inside(Pobj)) return 0; 00032 float height = Dot(Pobj - extent.pMin, upDir); 00033 return a * expf(-b * height); 00034 }