Roger
Fredy Larico Chavez
Texturas Procedimentais
Computação Gráfica
UNICAMP
2004
Texturas Processuais ou Procedimentais
As texturas
procedimentais são imagens que não são armazenadas no disco duro como, por
exemplo, os mapas de bits (BMP, JPG, EPS, PSD...), eles são armazenados em
procedimentos ou algoritmos (matemáticos) que são capazes de gera-los.
Uma textura procedimental é desde um simples tabuleiro de xadrez ate textura de nuvens e montanhas.
É uma função Rn è R2 (duas dimensões)
É uma função Rn è R3 (3D)...
Existem infinidades de maneiras de fazer texturas procedimentais, mas os algoritmos utilizados são matematicamente comprovados, dado que uma desvantagem é o custo computacional.
Nesta monografia serão apresentadas somente às texturas de Perlin Noise e Cellular. Por ser as mais usadas para geração de texturas de água, montanhas, nuvens e outros fenômenos da natureza presentes freqüentemente em cenas virtuais. Na natureza há uma infinidade de funções matemáticas capazes de criar texturas ou modelos de elementos da natureza. Existe, no entanto, um conjunto de funções, as quais se costuma chamar de funções básicas, que funcionam como primitivas para uma grande classe de funções mais complexas a serem criadas posteriormente. Estas funções devem obedecer a algumas propriedades, sendo uma das principais o fato de apresentarem resultados aleatórios. Inicialmente, pode-se tomar como uma função básica à função random(), que gera valores aleatórios independente de qualquer parâmetro de entrada. Um exemplo gráfico do resultado gerado por esta função é o chuvisco de uma TV, não sintonizada em canal algum. Contudo, esta função possui uma série de inconvenientes, principalmente devido ao fato de que não possui parâmetros de entrada, não havendo, portanto controle algum sobre o seu resultado. Assim, não é possível garantir algo a respeito de uma coerência temporal do resultado, o que é de extrema importância para funções que irão gerar texturas ou modelos [Clua 99].
Finalmente, se apresentara alguns algoritmos para um processo inicial de aprendizado de texturas procedimentais de Perlin Noise e Cellular.
Figura gerada com Perlin Noise
Algumas propriedades que são fundamentais para serem obedecidas ao projetar uma função procedimental, e que, portanto são necessárias para qualquer função básica a ser construída:
· Pseudo-randômico: Os valores gerados pela função podem ser randômicos, mas sempre, para um mesmo valor dos parâmetros de entrada, a função deve retornar o mesmo resultado;
· Não pode haver periodicidade de padrões;
· Estas funções devem ser estacionárias, não variantes com relação à translação, e isotrópicas, não variantes com relação à rotação.
Existem inúmeras abordagens diferentes na tentativa de implementar funções que
obedeçam estas propriedades. A função noise é sem dúvida a função que se tornou mais
popular e deve-se a Ken Perlin [Perlin 85].
Para fazer cenas mais realistas possíveis, e com o avanço da tecnologia é necessário o apoio de ferramentas matemáticas e combinação com as atuais ferramentas e técnicas. Todo isto sempre tendo presente a poupança de recursos computacionais.
As principais vantagens das texturas processuais são:
Densidade, como um mapa de bits está definido de maneira discreta (sabemos que a cor do ponto [1,2]) as texturas processuais estão definidas para todo Rn, então, é possível obter a cor do ponto [1.1415.., 2.1232..]. Então é possível obter o valor dela, e não só isso, não perceberá os quadradinhos molestos. Alem disso, as texturas esta bem feitas, poderão observar novos detalhes em quanto mais nos aproximemos.
São Infinitas: estão definidas para todo o espaço, quer dizer, que podemos consultar sem problemas que cor tem o ponto [2e234,-5e324], aliás, a textura não se repete (só se precisaremos) como a texturas de um mapa de bits.
Ocupam pouco espaço: Uma imagem grande, em mapa de bits ocupa muito. Por exemplo, uma imagem de 256*256 pixels com cor de 24 bits são 1.5 Megas, Na melhor compressão pode baixar até 5%. No entanto, as texturas procedimentais são de poucas linhas de código. Alem disso, onde as texturas procedimentais são mais úteis que os mapas de bits (as texturas de maior tamanho, ou de muita resolução e as de 3D).
Figura 1: Textura Procedimental 3D
Figura 2: corte em 3D a um sólido com textura
Texturas 3D: Pelo geral as texturas sempre vão estar coladas às superfícies por tanto, bidimensionais, no entanto, em algumas ocasiões é útil que a textura esteja definida para todo o espaço, e o que vejamos seja um 'corte' desta textura que ocupa todo um volume. Por exemplo, todos sabemos que a madeira de um tronco tem uma textura de círculos concêntricos marrões, na realidade sabemos que esses círculos estão no centro do tronco, que são cilindros concêntricos marrões. Definindo a textura em 3D, poderemos talhar a arvore e observar em todo momento como vai ficando a madeira cortada. Um mapa de bits (bitmap) não pode fazer isto, geraria arquivos muito grandes. Por exemplo, um bitmap de 256*256*256 pixels a 24 bits ocuparia 402 Megas.
Não se desenham, inventam-se: As texturas processuais estão definidas por um algoritmo matemático capaz de cria-las, e alguém deve fazer este algoritmo. Não se podem pintar texturas processuais com o Photoshop. Pintam-se com o Visual C++, Renderman, Builder...
CPU: Todo o que se liberou no disco duro, agora o tem que fazer o CPU processando o algoritmo que gera a textura. Quer dizer, que uma vez que temos sintetizado uma textura, falta otimiza-la.
As texturas processuais nunca substituirão aos mapas de bits, mas poderão releva-las em algumas tarefas.
Pelo geral, as texturas processuais estão compostas de mapas. Os mapas são texturas simples, pelo geral o branco e preto, que se fusionam com outros produzindo texturas complexas, enchendo canais de cor, canais de brilho entre outros.
Repetisse, pois não tem aleatoriedade. Algumas texturas com este tipo de mapas:
Figura 3: Aplicação de Mapas Geométricos em sólidos
São (pelos na teoria) fáceis de sintetizar, fazem muito uso da função MOD ou de operadores binários.
Fazem uso de funções aleatórias. Não podem predizer a baixa escala, mas conseguem seguir patrões regulares: Em um caminho de pedras sempre há pedras, mas não há duas pedras iguais. Algumas texturas com mapas aleatórios:
Figura 4: Aplicação de mapas aleatorios em solidos
Estas texturas são muito mais complicadas de sintetizar e também precisam mais ciclos de processo, no entanto existem alguns algoritmos 'chave' de muito interesse. Por exemplo, o algoritmo do mapa Cellular é usado na Figura 4. E as nuvens são geradas usando o mapa Perlin Noise.
Para estas texturas é interessante uma Função Matemática (para cada elemento devolve outro) de aparência aleatória.
O exemplo principal é mais utilizado, é o fundo de uma piscina, o refletir da luz e da piscina (Sombra Cáustica), faz uso dos mapas básicos muito típicos: Perlin Noise, e Cellular.
Conselhos básicos para fazer uma textura procedimental:
void
dibujar_ajedrez(int tam_casillas){
//partiendo
de un fon7do blanco
for(int y=1;
y<8;y++){ //for vertical de casillas
for (int x< =0; x<8;x++){ //for
horizontal de casillas
if((x+y) mod 2 = 1){ //si la casilla es negra
for (int j=0;j< tam_casillas){ //for
vertical de píxeles de una casilla
for (int i=0;i< tam_casillas{ //for
horizontal de píxele es una casilla
Pintar(x*tam_casillas+i,y*tam_casillas+j)=RGB(0,0,0);
}}
}
}
}
-----------------------------------------------------
int
ajedrez(x,y,tam_casillas){
if((int(x/tam_casillas)+int(y/tam_casillas))mod 2 = 1)
{return 0;} //negro
else
{return = 255;} // blanco
void dibujar_ajedrez_bien(int
tam_casillas){
for(int y=1;
y< fin_y;y++){ //for vertical de todos los pixels
for (int x<=0; x< fin_x;x++){ //for
horizontal de todos los pixels
int color=ajedrez(x,y,tam_casillas);
Pintar(x,y)=RGB(color,color,color);
}}
Ambos produzem mais ou menos a mesma imagem (quadrados brancos e pretos alternativamente), no entanto as estratégias são diferentes, no primeiro caso, considerando incorreto, utiliza-se uma ordem conveniente à hora de pintar os pontos, no segundo caso, utiliza-se uma ordem estrita (de arriba para baixo, de esquerda para direita, no entanto poderia ser qualquer outro), e uma função que devolve a cor tão só sabendo de que ponto se trata, independentemente da ordem de petição. Para os que tenham conhecimentos de matemáticas, existem similitudes entre o primeiro exemplo e una curva, ou superfície paramétrica, e o segundo com uma curva ou superfície implícita.
O segundo exemplo é recomendável: na tela, em uma área retangular plana, podemos escolher a ordem de pintado, mas 'na realidade', quando utilizemos a textura em uma cena 3D, ou como uma capa de outra textura, provavelmente só temos interesse na cor de alguns pontos, e não de todos (as caras visíveis, ou os que apareçam em um reflexo com RayTracing...). Poderíamos calcula-los todos na ordem da nossa conveniência, armazenamos em uma matriz, e depois consultar os necessários, mas como as texturas processuais são (ou podem ser) infinitas, não saberíamos ate onde teríamos que pré-calcular a textura.
Os mapas de texturas serão FUNCOES de consulta de cores e não de PROCEDIMENTOS de pintado.
Usualmente a técnica consistia em, antes de desenhar a textura, chamar a um procedimento inicialization; que pré-calculara esses números aleatórios e os deixara, pelo geral, em uma matriz em uma ordem de conveniência. Não podemos calcular infinitos números aleatórios e depois usa-los. Tampouco podemos usar simplesmente uma função aleatória baseada no relógio do sistema, pois é obvio que a textura não pode, por exemplo, cambiar de um frame a outro:
- Matriz pré-calculada de números aleatórios: é estável, mas finita, não se podem calcular demasiados números aleatórios.
- Calculo de números aleatórios em tempo real com função Rnd: é infinito, mas incontrolável, no próximo frame a pedra de oito cm do caminho será de quatro cm, o de seis..., alem disso, devido a que as funções dos mapas são implícitas, seria complicado reunir um grupo de pontos num só valor aleatório.
- IntNoise: é infinita, e é função, o número aleatório IntNoise(5) é o mesmo que IntNoise(5) em outro momento. IntNoise(x)=IntNoise(x)=aleatório
- IntNoise, como boa função aleatória não é continua, (não poderia ser porque esta definida para números inteiros). IntNoise(x) não tem porque aparentar se em nada a IntNoise(x+1). Proba disso é que a textura 255*IntNoise(i+j*ancho) é um Noise, de aparência similar a o que aparece quando ligamos a televisão sem conectar à antena.
///
Algoritmo con y sin IntNoise de una textura de cuadrados en distintos grises
aleatorios.
// ******** Sem IntNoise *********
int Colors[QuadradosX][QuadradosY]
void
initialization(){
for(j=0,j< QuadradosY; j++){
for(i=0;i< CuadradosX; i++){
Colors[i][j]=int(Rnd*255); //Donde Rnd da valores entre [0..1] aleatorios
}
}
void Pintar(int
tam_celda){
initialization();
int color;
for(y=0,y< MaxY; y++){
for(x=0;x< MaxX; x++){
color=Colors[int(x/tam_celda)][int(y/tam_celda)];
Pintar[x,y]=RGB(color,color,color)
}}
//********* Com IntNoise********
void Pintar(int tam_celda){
int color;
for(y=0,y< MaxY; y++){
for(x=0;x< MaxX; x++){
color=int(255*IntNoise[int(x/tam_celda)*const+int(y/tam_celda)]);
Pintar[x,y]=RGB(color,color,color)
}}
Como podemos observar, com a função IntNoise, nós poupamos a função
‘initialization’ e agora temos um
algoritmo que pode desenhar quadradinhos de distintos grises, sem nenhuma limitação, e com a seguridade de que, quando
volvamos a consultar as cores da textura, seguirão sendo os mesmos.
A variável const que não está declarada, indica que ali há que pôr um número. Para poder indexar coordenadas dos integers num só integer: isto é, para fazer uma aplicação bijectiva entre R^2 e R. Isto é algo realmente complicado para um plano infinito (há métodos mas muito caros no cálculo e não vale a pena). Sabendo que a textura vai ter um ancho definido, e que vamos precisar N números aleatórios por cada fila, então N é o valor para a constante (qualquer numero maior também é valido), basta com pôr um numero suficientemente alto, 5253 por exemplo, que garanta que não será apreciável a repetição de os números aleatórios.
O núcleo deste programa está na linha:
IntNoise[int(x/tam_celda)*const+int(y/tam_celda)]. Se temos um grupo de pixels que hão de compartir um mesmo numero aleatório (a cor de uma pedra, ou a altura relativa de uma montanha...) temos que procurar que todos estes chamem a IntNoise com o mesmo argumento.
Sabendo que propriedades têm a nossa função (matemática) IntNoise, temos que procurar uma implementação. A implementação embaixo é para a função IntNoise com [0..1]
float
IntNoise(int x)
{
x = (x<<13)^x;
return (((x * (x * x * 15731 + 789221) +
1376312589) & 0x7fffffff) / 2147483648.0);
}
|
Este mapa é provavelmente o mais útil e famoso de todos os que existem. O mapa Perlin é interessante porque tem duas propriedades: - Completa aleatoriedade e, - Coerência (quer dizer, que não há muita variação entre dos pontos contínuos, como acontece em Noise). |
É muito útil pois há muitos efeitos naturais que são seu fiel reflexo (água, nuvens ou montanhas são o mais típico), outras são pequenas modificações, como a madeira, e também as sombras do fundo da piscina fazem uso dela.
|
A criação do efeito é relativamente simples, consiste em
fazer uma quadricula com quadros de N pixels de lado e, a cada vértice dos
quadrados, associa-lo com um número aleatório. Os pixels que não estejam
exatamente acima de um destes vértices (a maioria), no interior de algum
quadrado, tomarão o valor da interpolação dos números aleatórios dos vértices
deste. Com este algoritmo se conseguem imagens como as da esquerda (Noise). |
Há muitas fórmulas matemáticas para a interpolação de pontos, algumas são:
E rápida, mas produz visíveis picos nos vértices, quedando uma imagem de não boa qualidade. Faz uso de 2 Pontos em 1D, 4 em 2D, 8 em 3D.
Não produz picos nos vértices, mas todos eles tem derivada 0, depois se observa claramente onde se encontram. Usa 2 Pontos em 1D, 4 em 2D, 8 em 3D.
De boa qualidade, é devagar no seu calculo, pois o algoritmo é complexo e faz uso de muitos pontos: 4 em 1D, 16 em 2D, 64 em 3D. Alem disso, pode fazer interpolações de pontos num recorrido ([0...255] por exemplo) que saiam um pouco desse recorrido.
Neste exemplo se utiliza uma interpolação similar à cossenoidal mas é mais rápida. Baseada no polinômio 3x^2-2x^3.
Este polinômio tem f '(0)=f '(1)=0, f(0)=0 y f(1)=0. Alem disso, é o menor polinômio que cumpre estas características.
Este polinômio é usado porque é muito mais rápido de avaliar que o equivalente cossenoidal: 0.5-0.5*cos(x*pi) e tem uma forma muito similar. Este é o código: a e b são valores qualquer, e x é a proporção de cercania entre um e outro: 0 se esta em a, 1 se esta em b.
float InterPol(float a, float b, float x){ //a altura 1a
return a+(b-a)*x*x*(3-2*x); //b altura 2 a
} //c si = 0 entonces
a, si = 1 entonces b
Figura 5: interpolação para imagens em 2D
interpretação geométrica da Figura :
O quadriculado grosso indica os quadrados em os que ha sido partida a imagem.(o período).
Os quadrinhos finos são os pixels. Neste caso pode se observar que o período é de 16, e que tanto cada quadrado tem 16^2 pixels.
As 4 barras verticais amarelas são os valores da função IntNoise para cada vértice.
As 2 barras verdes encostadas no plano indicam os valores de frac_x y frac_y, pode se observar neste caso frac_x= 4/16 e frac_y=4/16 mais o menos. O ponto de interseção é o pixel do qual estamos calculando Noise(x,y)
As Curvas vermelhas são as curvas do polinômio de interpolação, e os pontos verdes que têm elas estão na altura indicada pelas variáveis a e b.
Finalmente a curva azul é o polinômio do último estagio, o ponto verde está na altura do valor final.
O tamanho dos quadrados (período) e a amplitude dos valores se conseguem distintas imagens Noise, superpondo todas se consegue uma imagem pseudofractal com distintos niveles de detalhe.
Nuvens geradas por Perlin Noise.
Perlin Noise é uma técnica que consiste na suma de diferentes funciones de tipo Noise. Uma função do tipo Noise é basicamente uma função pseudo-randômica (Noise(1,t)= Noise(1,t’)). Como são funções pseudo - randômicas , é importante não escolher duas vezes a mesma semente,porque a função Perlin não teria valores adequados de entrada. As funções Noise tem dois parâmetros, amplitude e longitude de onda.
Quando geremos a função Perlin Noise temos que saber que tipo de terreno que queremos criar, a relação existente entre a amplitude e freqüência é importante. Por exemplo, se desejamos gerar uma paisagem plana; temos que escolher para freqüências altas, amplitudes baixas; e para freqüências baixas, amplitudes altas. Um terreno de pedras é gerado escolhendo freqüências e amplitudes baixas.
Figura: Esferas 3D definidas pelo ruído de Perlin
Una vez que tenhamos os pontos da função Perlin Noise, nosso objetivo é interpolar. As interpolações anteriores podem ser utilizadas.
float
PerlinNoise(float x,float y,int width,int octaves,int seed){
if (octaves>12)
{octaves=12;} // octaves:
double
a,b,valor,freq,tam_pas,cachox,cachoy;
int
casilla,num_pasos,pasox,pasoy;
int amplitud=256; //La amplitud es 128,64,32,16... para
cada pasada
int periodo=256; //El periodo es similar a la amplitud
for (int
s=0;s<octaves;s++){
amplitud>>=1; //Manera rápida de dividir entre 2
periodo>>=1;
freq=1/float(periodo);
num_pasos=int(width*freq);//Para el const que vimos en IntNoise
pasox=int(x*freq); //Indices del vértice superior izquerda
del cuadrado
pasoy=int(y*freq); //en el que nos encontramos
cachox=x*freq-pasox; //frac_x y
frac_y en el ejemplo
cachoy=y*freq-pasoy;
casilla=pasox+pasoy*num_pasos;
// índice final del IntNoise
a=InterPol(IntNoise(casilla+seed),IntNoise(casilla+1+seed),cachox);
b=InterPol(IntNoise(casilla+num_pasos+seed),IntNoise(casilla+1+num_pasos+seed),cachox);
valor+=InterPol(a,b,cachoy)*amplitud;
//superposicion del valor
final con anteriores
}
return valor;
//seed es un numero
que permite generar imagenes distintas
} //pues
IntNoise dará valores distintos
CELLULAR
Este é outro mapa (o grupo de mapas) dentro das texturas procedimentais, a invenção do algoritmo pertence, a Steven Worley. É um mapa muito útil em todas as texturas que tenham aparência de células, escamas, etc. e em geral qualquer textura de padrões de elementos individuais.
O algoritmo é simples mais ele tem muitas variações. Normalmente se fala de 4 fases:
Se marcam as coordenadas dos pixels donde consideramos que tem um ponto, cada uno desses pontos será o pseudocentro de nossas células.
O Calculo da distancia entre dois
pontos pode ser feito de muitas maneiras. Calcular a distancia entre dois
pontos, no espaço euclidiano se faz com o teorema de Pitágoras, no entanto em
outros espaços poderíamos definir a distancia de maneira distinta. Da definição
da função distancia dependerá, obviamente, o aspecto da textura:
|
|
|
Euclidea |
Manhattan |
Quadrada |
Para cada pixel do mapa, calcula-se a distancia a cada ponto.
dist_aux=sqrt((x-xpunto)*(x-xpunto)+(y-ypunto)*(y-ypunto));
//Distancia Euclidea |
if
(primero>dist_aux){
tercero=segundo;
segundo=primero;
primero=dist_aux;
} else {
if (segundo>dist_aux) {
tercero= segundo;
segundo=dist_aux;
} else {
if (tercero>dist_aux)
{tercero=dist_aux;}}
}
Segundo nosso processador e nossa
tentativa, escolhemos a distancia do ponto mais perto ou dos dois mais pertos
ou três ou quatro pontos mais pertos.
if (primero>dist_aux){
tercero=segundo;
segundo=primero;
primero=dist_aux;
} else {
if (segundo>dist_aux) {
tercero= segundo;
segundo=dist_aux;
} else {
if (tercero>dist_aux)
{tercero=dist_aux;}}
}
O mapa Cellular permite utilizar os 3 ou 4 valores das distancias menores para conseguir curiosos efeitos.
Dependendo de que fórmula que utilizemos para conseguir o valor final a partir dos valores primeiro (P), segundo (S), y terceiro (T) e um fator de correção se for necessário (tam). Podemos conseguir uma variedade de efeitos.
Variações finais com distancia euclideana
|
|
|
|
255*P/tam |
255*S/tam |
255*T/tam |
255*2*P/(S+T) |
|
|
|
|
255*P/S |
255*(S-P)/tam |
255*(T-P)/tam |
255*(T-S)/tam |
Variações finais com distancia Manhattan
|
|
|
|
255*P/(2*tam) |
255*S/(2*tam) |
255*T/(2*tam) |
255*2*P/(S+T) |
|
|
|
|
255*P/S |
255*(S-P)/(2*tam) |
255*(T-P)/(2*tam) |
255*(T-S)/(2*tam) |
Como toda textura, as aplicações vão desde as texturas em imagens estáticas, dinâmicas ate cenas de realidade virtual. Mais pela complexidade computacional normalmente são só utilizadas em grandes computadores (potentes).
Algumas aplicações em função à dimensão que pertencem:
Na natureza, é difícil encontrar objetos que estejam totalmente quietos, por isso poderemos utilizar estas funções para controlar seres virtuais.
A aplicação perfeita de Perlin Noise em 2D é a criação de terrenos ou superfícies. A propriedade do terreno pode ser calculada facilmente sem ter o modelo na memória. São utilizadas em interfaz humano-computador.
Com este tipo de funções é muito fácil a criação de nuvens.
Geração e animação de nuvens em 3D e criação de texturas 3D e 2D.
Para fazer animações 3D de texturas sólidas.
- As texturas procedimentais são mais compactas que qualquer textura conhecida.
- Mas, este tipo de textura é de alto custo computacional no processamento.
- As texturas procedimentais são geralmente utilizadas em jogos antigos, isto porque o armazenamento agora não é obstáculo. A qualidade de antes era simples.
- Se ela for mais utilizada em jogos atuais, a sua implementação é complicada, mas quitaria os efeitos ruins ao zoom das texturas normalmente usadas.
- O avanço da tecnologia faz computadores mais velozes, também com mais capacidade do disco. Mas o CPU sempre tem disponível a capacidade de processar dados, mas o dispositivo de armazenamento é limitado.
- A textura de Perlin Noise mostrada é a melhor textura para nuvens em geral e seu processamento é relativamente rápido.
- As texturas processuais nunca substituirão aos mapas de bits, mas poderão releva-las em algumas tarefas.
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#define ImageWidth 524
#define ImageHeight 524
GLubyte MatrixImage[ImageHeight][ImageWidth][3];
float RGB [3];
static GLint height;
float InterPolation(float a, float b, float c)
{
return
a+(b-a)*c*c*(3-2*c);
}
float InterLinear(float a, float b, float c)
{
return
a*(1-c)+b*c;
}
float Noise( int x)
{
x = (x<<13)^x;
return (((x * (x * x * 15731 + 789221) + 1376312589) &
0x7fffffff) / 2147483648.0);
}
float PerlinNoise(float x,float y,int width,int octaves,int seed,double
periode){
double
a,b,value,freq,tam_pas,zone_x,zone_y;
int
s,box,num,step_x,step_y;
int amplitude=120;
int noisedata;
freq=1/(float)(periode);
for (
s=0;s<octaves;s++){
num=(int)(width*freq);
step_x=(int)(x*freq);
step_y=(int)(y*freq);
zone_x=x*freq-step_x;
zone_y=y*freq-step_y;
box=step_x+step_y*num;
noisedata=(box+seed);
a=InterPolation(Noise(noisedata),Noise(noisedata+1),zone_x);
b=InterPolation(Noise(noisedata+num),Noise(noisedata+1+num),zone_x);
value=InterPolation(a,b,zone_y)*amplitude;
}
return value;
}
void colour (float valor){
int r=InterLinear(valor,0,0 );
int g=InterLinear(0,0,valor);
int b=InterLinear(0,0,valor);
RGB[0]=r;
RGB[1]=g;
RGB[2]=b;
}
void makeImage(void)
{
int x,y;
float color;
int seed;
int width;
float disp1,disp2,disp3,disp4,disp5,disp6,scale;
for(y=0;y< ImageWidth; y++){
for(x=0;x<ImageHeight; x++){
scale=1;
width=12413;
seed=63;
disp1= PerlinNoise(x*scale,y*scale,width,1,seed,100);
disp2= PerlinNoise(x*scale,y*scale,width,1,seed,25);
disp3= PerlinNoise(x*scale,y*scale,width,1,seed,12.5);
disp4= PerlinNoise(x*scale,y*scale,width,1,seed,6.25);
disp5= PerlinNoise(x*scale,y*scale,width,1,seed,3.125);
disp6= PerlinNoise(x*scale,y*scale,width,1,seed,1.56);
colour((int)disp1+(int)(disp2*.25)+(int)(disp3*.125)+(int)(disp4*.0625)+(int)(disp5*.03125)+(int)(disp6*.0156));
MatrixImage[x][y][0] = RGB[0];
MatrixImage[x][y][1] = RGB[1];
MatrixImage[x][y][2] = RGB[2];
}}
}
void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
makeImage();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, 0);
glDrawPixels(ImageWidth, ImageHeight, GL_RGB,
GL_UNSIGNED_BYTE, MatrixImage);
glFlush();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
height = (GLint) h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
default:
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(524, 524);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
Função Cellular.
float Cellular(float x,float y,int width,int tam_cas, int seed){
double primero=2*tam_cas,
segundo=2*tam_cas,tercero=2*tam_cas,dist_aux;
int casilla_pto;
double xpunto, ypunto;
int
n_casillas=int(width/tam_cas)+1;
int casillax=int(x/tam_cas);
int casillay=int(y/tam_cas);
int
casilla=n_casillas*casillay+casillax;
for (int j=-1;j<2;j++){
for (int i=-1;i<2;i++){
casilla_pto=casilla+i+j*n_casillas;
xpunto=(casillax+i)*tam_cas+IntNoise(casilla_pto+seed)*tam_cas;
ypunto=(casillay+j)*tam_cas+IntNoise(casilla_pto+10+seed)*tam_cas;
dist_aux=sqrt((x-xpunto)*(x-xpunto)+(y-ypunto)*(y-ypunto));
if (primero>dist_aux){
tercero=segundo;
segundo=primero;
primero=dist_aux;
}else {
if (segundo>dist_aux)
{
tercero=
segundo;
segundo=dist_aux;
} else {
if (tercero>dist_aux)
{tercero=dist_aux;}}
}
}}
return
256*2*primero/(segundo+tercero);
}
[Perlin 85]
Ken Perlin. An image synthesizer. In B. A. Barsky, editor, Computer Graphics
(SIGGRAPH ’85 Proceedings), volume 19, pp. 287-296, July 1985.
[Perlin et
al 89] Ken Perlin and Eric M. Hoffert. Hypertexture. In Jeffrey Lane, editor,
Computer Graphics (SIGGRAPH’89 Proceedings), volume 23, pp. 253-262, 1989.
[Worley 96]
Steve Worley. A Cellular Texture Basis Function, Computer Graphics (SIGGRAPH
’96 Proceedings), volume 30, pp 291-294, August 1996.
[Clua 99] Esteban Walter Gonzalez Clua. Modelagem Procedimental para Visualização de Elementos da Natureza, 1999.
http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
http://www.kenperlin.com/talk1
http://pgrafica.webideas4all.com/cellular.html