Configurazione su convenzione all'interno di una libreria di grafica web

1

Sto cercando di dare un senso a un pattern usato con un framework webgl javascript.

sfondo

WebGL utilizza programmi shader per disegnare o calcolare cose. Questi programmi devono essere compilati in fase di runtime, quindi a un certo punto esistono in javascript come stringhe.

La libreria offre un livello di astrazione chiamato "Materiale". Espone proprietà di alto livello che descrivono di che colore è qualcosa, o quanto è brillante, che viene passato a uno shader specifico sotto il cofano.

API

Questi shader che appartengono ai materiali sono modelli pre-scritti, che assomigliano a questo:

#include <foo>                   //non standard shader syntax
#include <bar>
#include <baz>

void main(){                     //standard shader syntax

  #include <foo_vert>
  #include <bar_vert>
  #include <baz_vert>

  gl_Position = baz * bar + foo; //standard shader syntax
}

Alcuni FOO materiali possono consistere in foo e bar, mentre un altro materiale BAR può consistere solo di foo, mentre BAZ può consistere di tutti e tre, ecc.

La libreria analizza questo modello, non è un codice shader valido. Lo spazio dei nomi ha un dizionario di "shader chunks"

chunks = {
  foo: string,
  bar: string,
  baz: string,
  ...
}

Quindi, se si crea un Materiale, tutto lo shader è nascosto all'utente. La libreria stessa modifica questa stringa, sostituendo l'istruzione #include con il valore corrispondente dal dizionario chunks .

avanzate

Tuttavia, gli utenti possono avere familiarità con la struttura di questi modelli e il contenuto dei blocchi e spesso desiderano modificare alcune parti dello shader mantenendo tutto il resto così com'è.

Ho riscontrato un problema nel digerire la soluzione disponibile e ho notato che spesso viene richiesto un modello diverso.

Senza la necessità di modificare, l'astrazione di alto livello ha questo aspetto:

//not even aware that shaders happen beyond this
var redPlasticMaterial = new Material({
   shininess: veryShiny,
   color: red
})

Con la necessità di modificare, è necessario conoscere la logica all'interno dei blocchi e il modello del Materiale da modificare (dove vengono utilizzati i blocchi). Uno identifica che "foo", "bar" e "baz" devono cambiare, alias "defaultChunk [nome] non funziona" e si possono definire queste modifiche chunkName -> string :

var myShaderChunk_foo = 'lots of GLSL'
var myShaderChunk_bar = defaultChunks[bar] + 'lots of GLSL'
var myShaderChunk_baz = 'baz glsl'

la soluzione

var myCustomRedPlasticMaterial = new Material({
  color: red,
  shininess: veryShiny
})


//a callback that is called before processing the template
myCustomRedPlasticMaterial.onBeforeCompile = function( shader ){
   shader.vertexShader // template for one shader program
   shader.fragmentShader // template for another shader program

   //if i want to replace foo bar baz
   shader.vertexShader = shader.vertexShader.replace( '#include <foo>', myShaderChunk_foo) 
   shader.vertexShader = shader.vertexShader.replace( '#include <bar>', myShaderChunk_bar) 
   shader.vertexShader = shader.vertexShader.replace( '#include <baz>', myShaderChunk_baz) 
   ...
}

Per ottimizzare le chiamate grafiche, la libreria memorizza nella cache questi programmi shader. Se questa logica di ramificazione di callback, due materiali con rami diversi avrebbero lo stesso hash perché utilizza il codice sorgente della funzione per l'hash .

//cant be used since body of the function is used for hashing
onBeforeCompile( shader ) {
  if( qux ){
    //replace shader foo
  } else {
    //replace shader bar
  }
}
// i have to do
onBeforeCompileQux( shader ) {
  //replace foo
}
onBeforeCompileNonQux( shader ){
  //replace bar
}

Che cosa sta succedendo qui e ha un nome? L'API ti fornisce una stringa non elaborata e la lingua ti dà la possibilità di modificarla. Ho notato che cercare l'intera frase #include <name> sembra ridondante, ma c'è ancora qualcosa da dire a riguardo?

Senza il mio intervento - cercando questo e sostituendolo, la libreria sa come gestire #include <> e sa dove trovare chunk[name] .

Dovendo associare manualmente queste chiamate alla stringa, le chiamate sembrano "convenzione sulla configurazione"? Ma poi questa cosa del codice grezzo sembra più configurabile rispetto alla convenzione. La funzione stessa diventa dati?

stessa cosa attraverso la configurazione?

Il diverso modello che ho visto è quello di fornire questi pezzi come dati al materiale, attraverso una diversa API. Se sono a conoscenza di chunk che si tratta di un dizionario, qualunque cosa lo usi può essere detto di guardare qualcos'altro.

myMaterial.chunks.foo = foo
myMaterial.chunks.bar = bar
myMaterial.chunks.baz = baz

^ questa configurazione è su convenzione? In entrambi i casi vedo l'intento:

  1. So che il materiale utilizza chunks.foo
  2. Voglio che il materiale utilizzi un diverso foo

Nella richiamata noto:

  1. Devo essere consapevole dell'evento di analisi / compilazione
  2. Devo conoscere l'intera #include <> sintassi
  3. Devo manipolare esplicitamente una stringa
  4. Devo fornire "pippo"

Con le proprietà:

  1. (devo sapere dove va, ma dovrebbe essere lo stesso di defaultChunk di cui sono a conoscenza, quindi non sono sicuro se conta, ma posso anche vedere che è "devo conoscere l'API di sostituzione" che può forse essere più complicato)
  2. Devo fornire "pippo"

D'altra parte, "ecco una stringa fare qualsiasi cosa" sembra davvero flessibile e funziona con i metodi primitivi della lingua. Quest'ultimo ha bisogno di familiarità con un'altra api? Che tipo di terminologia posso applicare qui per spiegare meglio la situazione e quali modelli possono essere nominati?

    
posta Dusan Bosnjak 'pailhead' 03.03.2018 - 00:47
fonte

1 risposta

0

La tua domanda è essenzialmente "come si chiama quando un'API funziona in questo modo?" E anche "quali modelli sarebbero coinvolti?" Se è così, direi che rientra in un paio di categorie.

Sviluppo guidato dai dati

In sviluppo guidato dai dati , invece di scrivere codice per fare una cosa specifica, scrivi codice per elaborare i dati che dice al programma cosa fare. In questo caso specifico, i dati sono l'insieme di modelli glsl-like e i pezzi di quei modelli che l'utente può sostituire. Piuttosto che dire alla GPU di elaborare ogni vertice o frammentare un particolare modo, il framework Material (apparentemente) ha metodi per pre-processare alcuni template di shader, sostituendo parti qua e là e generando lo shader finale che viene effettivamente inviato alla GPU.

Lingua specifica per il dominio

glsl è esso stesso un linguaggio specifico del dominio in quanto si occupa principalmente dell'elaborazione dei vertici e dell'elaborazione dei frammenti (e poche altre cose correlate). Ma i modelli utilizzati da questo framework sono un linguaggio ancora più specifico del dominio. Offrono alcuni shader preesistenti in cui puoi combinare pezzi specifici o sostituire pezzi specifici con i tuoi.

Penso che la tua valutazione della convenzione rispetto alla configurazione sia corretta.

    
risposta data 04.03.2018 - 02:12
fonte

Leggi altre domande sui tag