Bordi rumorosi, uniformando i bordi tra le facce tramite shader di frammenti


8

Ho un terreno generato, con geometria esagonale, come da schermata seguente:

inserisci qui la descrizione dell'immagine

Genero quindi biomi, ma come puoi vedere i confini tra loro sono davvero brutti e dritti. Per nascondere quell'origine esagonale, avrei bisogno di appianare i confini tra i biomi. Ecco come appare ora in wireframe con facce tringolari reali:

inserisci qui la descrizione dell'immagine

Quello a cui sto puntando è qualcosa di più simile a questo:

inserisci qui la descrizione dell'immagine

Ogni vertice ha attibute che contiene il tipo di bioma, posso anche aggiungere attributi speciali ai vertici sul bordo tra due biomi, ma non riesco proprio a capire come risolverlo in codice shader, ovviamente rumore è coinvolto qui, ma come posso renderlo continuo su più facce e sull'intero bordo di più biomi?

Sto eseguendo il rendering con WebGL utilizzando THREE.js


Hai provato MSAA?
Milo Lu

@Milo Hai provato a leggere la domanda?
Bálint,

Come si inviano le informazioni sul bioma allo shader?
Bálint,

@ Bálint Biome In questo momento sto passando il colore tramite l'attributo vertice, quando potrò effettivamente campionare i colori da trame reali, invece di semplici colori, passerò il tipo di bioma come intero.
user1617735

Gli attributi del vertice non possono essere usati come vuoi, se passi alle trame, sarà molto più facile. Carica le informazioni sul bioma come trama, così sarai in grado di ottenere i biomi vicini
Bálint

Risposte:


9

Altre risposte qui suggeriscono di usare una trama. Ecco una tecnica che non usa trame.

Vuoi che i confini tra gli esagoni siano interessanti. È più facile creare confini interessanti quando li sposti al centro di ciò che stai disegnando. Invece di disegnare direttamente le tessere, si disegna il "doppio" della tessera. Questa tecnica è chiamata “porzioni d'angolo” ( qui e qui e qui ). Il doppio di un esagono è un triangolo, quindi disegneremmo questi triangoli invece degli esagoni:

doppio triangolo di un esagono

I confini tra gli esagoni sono ora nel mezzo dei triangoli renderizzati, quindi questo ci permetterà di fare cose più interessanti con loro. Bonus: devi solo pescarne due triangoli per esagono, anziché sei (o ventiquattro come stai facendo ora).

All'interno di ciascuno di quei triangoli vogliamo che lo shader di frammenti disegni gli esagoni. Possiamo farlo con coordinate baricentriche . Metti (1,0,0), (0,1,0) e (0,0,1) su ciascun vertice del triangolo. All'interno del triangolo, tali coordinate saranno interpolate. Lo shader di frammento riceverà (a, b, c) e può vedere per vedere quale valore è il più grande - questo ci dirà quale dei tre esagoni dovrebbe essere disegnato a questo punto.

float max_n = max(barycentric.r, max(barycentric.g, barycentric.b)); if (max_n == barycentric.r) { color = v_color0; } else if (max_n == barycentric.g) { color = v_color1; } else if (max_n == barycentric.b) { color = v_color2; }

Questo è per le linee rette.

Se vuoi bordi rumorosi, puoi aggiungere rumore alle coordinate baricentriche:

esagono con bordi rumorosi

Giocando con l'ampiezza della lunghezza d'onda / frequenza del rumore, puoi ottenere alcuni effetti fantastici:

esagono con bordi ancora più rumorosi

Devi stare attento con il rumore, assicurandoti che sia coerente attraverso i confini del triangolo. Un modo per farlo è passare un id esadecimale e usarlo come valore seed per ciascuno dei tre valori di rumore aggiunti alle coordinate baricentriche.

Ho fatto una demo interattiva qui . (Per la demo non ho implementato l'id esadecimale o alcune delle altre cose di cui potresti aver bisogno se facessi funzionare questo in un progetto reale - è solo una demo veloce e sporca)


Questo è un materiale di risposta di alta qualità. Punta del cappello
Quentin,

Bella risposta! Correzione sottile: i poligoni regolari, inclusi gli esagoni, sono auto-duali. Tuttavia, le tessellazioni di triangoli ed esagoni sono doppie l'una dell'altra, come illustra la tua risposta.
Erik Foss,

0

Sono sicuro che "potrebbe" essere risolto con qualche algoritmo di immagine, ma se fossi in me probabilmente lo risolverei con trame. Farei trame esagonali, le metterei tutte in un atlante di trama, quindi per ogni esagono guarderei i suoi vicini e decidere quale trama applicare.

Le trame dovrebbero avere versioni per ogni tipo di terreno più versioni per ogni tipo di transizione.

Questo è simile al numero di sistemi basati su piastrelle che fanno terreno. Ecco un esempio di giochi 2D .

Un'altra possibilità sarebbe quella di avere solo le trame per i vari tipi di terreno (acqua, neve, terra, erba) e aggiungere quantità di miscelazione a ciascun vertice di esagono per decidere come mescolarle.

Questo articolo mostra l'idea di mescolare trame del terreno. Non sto suggerendo di seguire la loro implementazione, ma mostra l'idea.


Bene, nel mio caso non è semplice preparare trame, poiché gli esagoni non sono uniformi, la forma e le dimensioni differiscono su tutta la mappa. Sfortunatamente questo è il prezzo che devo pagare per avere un pianeta, non solo una mappa piatta. Anche l'articolo sembra molto interessante, grazie. Anche se nel mio caso sto rendendo il terreno dall'alto, probabilmente da un'altitudine in cui, ad esempio, non puoi vedere un singolo albero e intere foreste sporgenti sembrano una massa greem.
user1617735

0

Innanzitutto, trasforma i tuoi biomi in una trama. Mappa i triangoli a texcoords. Puoi farlo usando una proiezione di un mercatore o, meglio, una mappa cubica . Ora, nello shader di frammenti, fai qualcosa del genere:

// frequency and magnitude of the noise in texels.
uniform float frequency;
uniform float magnitude;

// This function should just look like random smooth noise in x,y
// This one isn't great, but you can experiment.
vec2 noise(vec3 vertexPos) {
    return vec2(sin(vertexPos.x * frequency + vertexPos.y * frequency) * magnitude, 
                cos(vertexPos.y * frequency * 2 + vertexPos.z * frequency) * magnitude);
}

// Sample the texture and preturb it with noise based on the world
// position of the fragment.
vec4 frag(vec2 texCoord, vec3 fragmentPos) {
   return texture(mySampler, texCoord + noise(fragmentPos));
}

dove si noisetrova una funzione pseudo-casuale (usando, diciamo, sinusoidi) sulla posizione 3D del vertice nello spazio modello che restituisce un offset rumoroso alla coordinata della trama. Campionare la trama usando GL_NEARESTper mantenere bordi nitidi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.