Three.js: Material: Hinzugefügt beiBeforeCompile()

Erstellt am 9. Juni 2017  ·  75Kommentare  ·  Quelle: mrdoob/three.js

Im Laufe der Jahre bestand eine häufige Featureanforderung darin, die eingebauten Materialien modifizieren zu können. Heute wurde mir klar, dass es ähnlich wie Object3D onBeforeRender() implementiert werden könnte.

https://github.com/mrdoob/three.js/commit/e55898c27a843f69a47e602761c60d9bbe91ee35

WebGLPrograms fügt onBeforeCompile.toString() zum Programm-Hash hinzu, sodass dies keine Auswirkungen auf andere in der Szene verwendete eingebaute Materialien hat.

Hier ist ein Beispiel für die Funktion in Aktion:

http://rawgit.com/mrdoob/three.js/dev/examples/webgl_materials_modified.html

material.onBeforeCompile = function ( shader ) {

    // console.log( shader )

    shader.uniforms.time = { value: 0 };

    shader.vertexShader = 'uniform float time;\n' + shader.vertexShader;
    shader.vertexShader = shader.vertexShader.replace(
        '#include <begin_vertex>',
        'vec3 transformed = vec3( position.x + sin( time + position.y ) / 2.0, position.y, position.z );'
    );

    materialShader = shader;

};

Wir können nicht nur mit dem Shader-Code herumspielen, sondern auch benutzerdefinierte Uniformen hinzufügen.

if ( materialShader ) {

    materialShader.uniforms.time.value = performance.now() / 1000;

}

Ist es zu hackig?

/cc @WestLangley @bhouston @tschw

Enhancement

Hilfreichster Kommentar

@mrdoob Ich denke, es ist schwierig (oder unmöglich), Materialien zu serialisieren, die mit .onBeforeCompile() gehackt wurden. Glauben Sie, dass Benutzer ShaderMaterial wenn sie voll funktionsfähige (rendern, kopieren, klonen, serialisieren usw.) modifizierte integrierte Materialien wünschen?

Ich habe nicht erwartet (in Betracht gezogen), dass die Verwendung von onBeforeCompile() serialisiert wird...

Alle 75 Kommentare

Es scheint ein bisschen hackig zu sein, eine benutzerdefinierte String-Manipulation zu erfordern, wenn WebGLProgram.js bereits viel Verarbeitung durchführt. Es gibt einen verwandten Anwendungsfall, der darin besteht, neue #includes hinzuzufügen, anstatt vorhandene zu überschreiben. Sie können dies heute tun, indem Sie THREE.ShaderLib zur Laufzeit ändern, aber es fühlt sich genauso schmutzig an.

Vielleicht ist eine schönere Lösung für beides, Ihnen zu ermöglichen, eine benutzerdefinierte ShaderLib für ein Material bereitzustellen, die die eingebauten Chunks ohne manuelle String-Manipulation und ohne sie dauerhaft zu überschreiben, überschreiben/erweitern würde. Das schließt einen onBeforeCompile-Hook für andere Zwecke nicht aus, aber es scheint eher dem zu entsprechen, was Ihr Beispiel zu tun versucht.

Allerdings wird die Shader-Komposition, die auf Raw-Includes basiert, von einer Version zur nächsten immer sehr fragil sein. Wenn es Ihnen nichts ausmacht, den gesamten Skelettcode (wie meshphong_vert.glsl ) einzufügen, können Sie die integrierten Materialien bereits mit etwas wie diesem erweitern:

export class MyPhongMaterial extends ShaderMaterial {
  constructor({ color, radius, map, normalMap, emissiveMap }) {
    super();
    this.vertexShader = "...."
    this.fragmentShader = "....";
    this.uniforms = UniformsUtils.clone(ShaderLib.phong.uniforms);

    this.isMeshPhongMaterial = true;
    this.lights = true;

    this.uniforms.time = { value: 1 };
    //...
  }
}

In allen Fällen müssen Sie sich darauf verlassen, dass die interne Organisation der eingebauten Shader nicht zu viel von einer Version zur nächsten ändert. Jedes Include ist bereits eine getarnte Funktion mit einer falsch spezifizierten Signatur. Ich vermute, dass es auf beiden Seiten viel sauberer und wartungsfreundlicher sein wird, Überschreibungen auf Funktionsebene statt auf Einschlussebene bereitzustellen.

Heute dachte ich..

Schön! Das dachte ich auch am 10. Februar, als ich diese Pull-Anfrage stellte:

https://github.com/mrdoob/three.js/pull/10791

Es scheint der einzige Unterschied zu sein, dass Sie es über einen Rückruf tun? Ich habe das nicht verwendete Argument im WebGLRenderer angezapft.

Ich verwende dies zum Beispiel gerne, um Threejs mit normalen Maps zum Laufen zu bringen, aber ich habe einige Probleme verlinkt, die scheinbar trivial damit zu lösen wären.

Sie können sich meinen Zweig ansehen und damit eine Runde drehen oder sich dieses supereinfache Beispiel ansehen (testen Sie einfach eine benutzerdefinierte Verformung mit Beleuchtung / Schatten usw. und fügen Sie ein paar Uniformen hinzu).

http://dusanbosnjak.com/test/webGL/three-material-shader-override/webgl_materials_shader_override.html

Vielleicht ist eine schönere Lösung für beides, Ihnen zu ermöglichen, eine benutzerdefinierte ShaderLib für ein Material bereitzustellen, die die eingebauten Chunks ohne manuelle String-Manipulation und ohne sie dauerhaft zu überschreiben, überschreiben/erweitern würde.

Fasst genau zusammen, was passiert in: https://github.com/mrdoob/three.js/pull/10791 Ich wünschte, ich könnte bessere Beschreibungen schreiben :)


Ich muss zugeben, dass ich nicht weiß, was hier vor sich geht. Ich glaube, ich kenne jemanden, der es aufgegeben hat, an drei zu arbeiten, es damals nicht verstanden hat, aber jetzt finde ich es irgendwie frustrierend.

Frustrierend vor allem, weil ich ein Déjà-vu habe, wenn ich den ersten Absatz lese:

...ähnlich wie onBeforeRender() von Object3D.

Genau das gleiche passierte mit dem onBeforeRender() https://github.com/mrdoob/three.js/pull/9738 , als es ein Jahr dauerte, das Feature zu überzeugen.

Gott, zweimal, mit demselben Entwickler? Ich mache hier offensichtlich etwas falsch, aber was? Beim ersten Mal wurden die Build-Dateien eingecheckt, ich wusste nicht, was ich tun sollte, und es wurde einfach vergessen. Aber dieses Mal war ich bei diesem Pull-Request oben, es gibt gerade einen Konflikt, aber er ist leicht zu lösen, es gab seit Monaten keine Konflikte.

Versteh mich nicht falsch, ich denke nicht, dass hier Eitelkeit in Arbeit ist, ich denke, ich möchte nur Ideen von Leuten umsetzen und Feedback bekommen. @unconed es ist offensichtlich, dass Sie mit dem Problem vertraut sind und wahrscheinlich verschiedene Dinge ausprobiert haben. Was könnte dazu geführt haben, dass ein Nutzer wie Sie die andere PR verpasst hat?

Ich fühlte mich ziemlich gut mit der Shaderlib pro Material, aber ich fand eine Funktion im Renderer, die ganze Shader als Strings aussah / verglich, um das Caching herauszufinden. Ich wünschte, es gäbe einige Rückmeldungen...

War das Beispiel fehlerhaft? Der Kopf macht vielleicht deutlicher, was vor sich geht als mein abstrakter Stachelball, aber das ganze Beispiel arbeitet mit Schatten, die mehr Boden bedecken. Es ist aus irgendeinem Grund schwer, aber ich denke, es liegt daran, dass Sin/Cos auf vielen Vers ist.

Ich schätze, wenn Sie von vorne anfangen ... Auch wenn das Szenen-Kit absolut schrecklich ist, ist diese API wirklich nett:

https://developer.apple.com/documentation/scenekit/scnshadable

Obwohl schrecklich dokumentiert, kann ich nicht den Teil finden, der von größerem Interesse ist, aber im Grunde haben sie in vielen verschiedenen Phasen des Shaders abstrahierte Hooks.

Anfang dieses Jahres habe ich eine Pull-Anfrage gestellt, die etwas Ähnliches, wenn nicht sogar dasselbe zu tun scheint:

https://github.com/mrdoob/three.js/pull/10791

Es scheint der einzige Unterschied zu sein, dass Sie es über einen Rückruf tun? Ich habe das nicht verwendete Argument im WebGLRenderer angezapft.

Tut mir leid, dass ich mich noch nicht um diese PR kümmern kann @pailhead. Ich denke, der Hauptvorteil meines Ansatzes besteht darin, dass nur 3 neue Zeilen erforderlich sind.

Nachdem dies gesagt wurde, werde ich jetzt einige Zeit damit verbringen, Ihre und @unconed Vorschläge zu

Dieser fühlt sich definitiv eleganter an, nur drei Linien. Bei dem anderen folgte ich einem anderen Muster. Wollte sagen, dass es vielleicht etwas ausführlicher ist, aber nicht so sicher. Ich denke, der einzige Vorteil des anderen ist, dass es gut war, vor ein paar Monaten zu gehen :)

Gott, zweimal, mit demselben Entwickler? Ich mache hier offensichtlich etwas falsch, aber was?

Das tut mir leid. Das einzige, woran ich denken kann, ist, dass ich wahrscheinlich überfordert war. Vielleicht eine lange Diskussion oder eine komplizierte PR, die zu viel meiner Energie verbrauchen würde, also beschließe ich, es für später zu belassen und zu einfacheren PRs überzugehen.

Nicht nur du @pailhead. @bhouston hatte viele @WestLangley und @Mugen87.

Auch hier ist nicht so, dass ich die PR nicht mag, sondern dass die PR etwas Aufmerksamkeit erfordert, die ich derzeit nicht bieten kann. Ein gutes Beispiel ist die Instancing PR. Ich fand Zeit, es durchzulesen, experimentierte selbst und schlug Vereinfachungen vor. Hoffentlich kann ich es bald nachholen.

Es ist schwierig, all dies zu managen und jeder PR die Aufmerksamkeit zu schenken, die sie verdient.

War das Beispiel fehlerhaft? Der Kopf macht vielleicht deutlicher, was vor sich geht als mein abstrakter Stachelball, aber das ganze Beispiel arbeitet mit Schatten, die mehr Boden bedecken. Es ist aus irgendeinem Grund schwer, aber ich denke, es liegt daran, dass Sin/Cos auf vielen Vers ist.

Jetzt erwähnst du es... Ja, läuft mit 10 fps auf einem neuen MacBook Pro beim Heranzoomen 😮

Ich denke, es sind die Schatten und der Rand, Sündenbock und so, aber ja, es sieht so aus, als ob es zu lange dauert.

Wie auch immer, ich bin überrascht, dass Sie dies in drei Zeilen geschafft haben, ich habe mich zu sehr darauf konzentriert, wie materielle Parameter in Uniformen umgewandelt werden und diesem Muster gefolgt sind. Der Unterschied ist, dass ich ein alternatives ShaderLib wie ein Wörterbuch bereitstelle, also bringt #include <> anderen Code ein. Bevor dies geschieht, entfernen Sie das Include und ersetzen es durch glsl. Ich denke, wenn Sie eine Art Wrapper um den Shader zurückgeben, könnte diese Syntax vielleicht sauberer sein (geben Sie einfach den Chunk-Namen + glsl an, anstatt replace() . Aber es kann am Ende viel mehr wie der andere aussehen .

Es wäre wirklich schön, das zu haben, ich habe nicht mit so vielen 3D-Engines gearbeitet, aber Unity und Scene Kit scheinen so etwas zu haben.

@unconed

Ich denke, ein Vorteil von onBeforeCompile() ist, dass die Materialeigenschaften unverändert bleiben. Es fühlt sich also eher an, die eingebauten Materialien zu erweitern.

export class MyMeshPhongMaterial extends MeshPhongMaterial {
  constructor( parameters ) {
    super( parameters );
    this.onBeforeCompile = function ( shader ) {
      shader.vertexShader = shader.vertexShader.replace(
        '#include <begin_vertex>',
        'vec3 transformed = vec3( position.x + sin( position.y ) / 2.0, position.y, position.z );'
      );
    };
  }
}

var material = new MyMeshPhongMaterial();
material.color.setRGB( 1, 0, 0 ); // this still works

Mit ShaderLib herumzuspielen ist in der Tat knifflig. Das Hinzufügen von für immer unveränderten Hooks sollte jedoch machbar sein:

    #include <begin_vertex>
    % vertex %
    #include <morphtarget_vertex>
    #include <skinning_vertex>
    % transformed_vertex %
    #include <project_vertex>
    % projected_vertex %

Der Ersetzungscode wird wie folgt aussehen:

this.onBeforeCompile = function ( shader ) {
  shader.vertexShader = shader.vertexShader.replace(
    '% vertex %',
    'transformed.x += sin( position.y ) / 2.0;'
  );
);

Wir entfernen dann natürlich alle Hooks, die vor dem Kompilieren nicht verwendet wurden.

So sieht es im Vergleich zu den ShaderChunks pro Instanz aus

//given some material
var material = new THREE.MeshNormalMaterial();

//and some shader snippet
var myShader = [
    'float theta = sin( time + position.y ) / 2.0;',
    'float c = cos( theta );',
    'float s = sin( theta );',
    'mat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );',
    'vec3 transformed = vec3( position ) * m;', //and making assumptions about THREE's shader framework
    'vNormal = vNormal * m;'
].join( '\n' );

https://github.com/mrdoob/three.js/pull/10791 wie bei Material.defines Ansatz:

material.shaderIncludes = {

        begin_vertex: myShader,

    //uv_pars_vertex: [
    //  THREE.ShaderChunk['uv_pars_vertex'], //this doesn't have to be
    //  "uniform float time;",
    //].join('\n')
};

material.shaderUniforms = { time: { value: 0, type: 'f' || 'float' } }; //because this could just inject it in the right place (but needs type)

_Es ist nur ein Wörterbuch mit Chunk-Namen und das Uniformen-Objekt. Die String-Manipulation hier ist eher in der Art von "Ich möchte diesen Teil wiederverwenden und etwas darüber hinaus tun"_

diese PN:

material.onBeforeCompile = function ( shader ) {

    shader.uniforms.time = { value: 0 };

    shader.vertexShader = 'uniform float time;\n' + shader.vertexShader; //this feels hacky

    shader.vertexShader = shader.vertexShader.replace( //this is more verbose
        '#include <begin_vertex>',
        myShader
    );

};

Ich denke, ein Vorteil von onBeforeCompile() besteht darin, dass die Materialeigenschaften unverändert bleiben.

In den anderen PR funktioniert es genauso, also denke ich, dass es egal ist, wie es gemacht wird.


Ein wahrscheinlich irrelevanter Vorteil, den ich mir vorstellen kann, ist, dass Sie die Logik zur Manipulation des Materials im selben Block und auf derselben Ebene halten können. Wenn Sie irgendwo einen Code hatten, der die Farbe änderte, und Sie diesem Material später benutzerdefiniertes GLSL hinzufügen, können Sie den Code hinzufügen, um die Uniform mit der Farbe zu ändern. (Textwand überspringen)


Wenn das uv_pars_vertex verwirrend ist, denke ich, eine gute Frage ist:

"Wo füge ich meine benutzerdefinierte GLSL-Funktion ein, wie float rand( vec2) ?"

Dann macht dieser vielleicht Sinn https://github.com/mrdoob/three.js/pull/11050


Ein weiteres Argument könnte sein - vorausgesetzt, Sie kennen GLSL, vorausgesetzt, Sie kennen das Shader-Framework von THREE, müssen Sie immer noch kreativ sein und darüber nachdenken, wo Sie was injizieren. Ich musste ein wenig nachdenken und die meisten Shader durchsehen, um herauszufinden, dass uv_pars_vertex ein bequemer Ort für meine Materialerweiterung ist.

Es wäre besser, von so etwas zu einer Abstraktion überzugehen, lightingPhase , objectTransformation , perspectiveTransformation usw. zu haben. Oder zumindest ein guaranteedToBeAboveMainButBelowCommonsAndExtensionCalls Dummy-Chunk, der in jedem Shader-Programm enthalten ist. Es sieht so aus, als ob Sie das mit % vertex % anstreben? Aber mit dieser Syntax kenne ich mich nicht aus.

Diese PR, so wie sie ist, scheint in die entgegengesetzte Richtung zu gehen. Sie müssen nicht nur wissen, wo Sie eintippen müssen, sondern auch explizit sein und Strings manipulieren und einige der Arbeiten wiederholen, die der Renderer bereits für Sie erledigt. Auch wenn Sie neben der ersten noch eine weitere Frage stellen

Wie verwende ich eine Funktion, die im Commons-Chunk deklariert ist, in der Funktion, die ich injiziere?

Sie könnten feststellen, dass Sie mehr String-Manipulationen durchführen, als nur die Uniformen dem gesamten Shader voranzustellen.

Ich musste ein wenig nachdenken und die meisten Shader durchgehen, um herauszufinden, dass uv_pars_vertex ein bequemer Ort für meine Materialerweiterung ist.

Ich glaube, es gibt keinen Weg, das zu umgehen, es sei denn, wir überarbeiten. Die Idee mit % vertex % ist, ein wenig dabei zu helfen, indem man die Hooks benennt und Ihnen mehr oder weniger sagt, in welchem ​​Zustand Sie Code injizieren, aber es wäre schwierig zu dokumentieren, welche Variablen zu welchem ​​Zeitpunkt verfügbar sind .

Wir öffnen eine Tür für das Hacken von eingebauten Materialien, aber ich glaube nicht, dass wir angemessene "Unterstützung" bieten können. Der Benutzer sollte sich bewusst sein, dass die Möglichkeit besteht, dass Dinge kaputt gehen.

Ich habe versucht, es in der anderen PR anzugehen. Zumindest für die Funktionen, Variationen, Attribute und Uniformen habe ich einen Dummy-Hook hinzugefügt. Ein Großteil der GLSL-Logik findet bereits auf Structs statt, Scenekit hat jede Variable dokumentiert (kann jedoch nicht mehr dieselbe Dokumentation finden).

Es müsste wahrscheinlich im Laufe der Zeit umgestaltet werden, aber etwas in der Art:

#ifdef PHASE_FOO

#include <shader_foo> 
//someGlobalStruct.mvPosition = modelViewMatrix * myLogic( transformed );
//if there is glsl provided for phase "foo" document that it should operate on "transformed" 
//and that it should return "mvPosition" 
#end including <shader_foo>

#else 

  //default
  #ifdef USE_SKINNING

    vec4 mvPosition = modelViewMatrix * skinned;

  #else

    vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );

  #endif

#ifdef PHASE_BAR

#include <something_like_this>
//mvPosition = myPostDefaultTransformationLogic( mvPosition );

#endif

gl_Position = projectionMatrix * mvPosition;

#endif

Natürlich würden ifdefs und include so nicht funktionieren. Aber ich kann sehen, dass es mit dem anderen Ansatz relativ einfach ist. Wir könnten #include <some_phase> immer mit leeren Strings hinzufügen. Etwas könnte dabei helfen, zu verwalten, was durch die Verwendung von #ifdef s ausgelöst wird. Wenn Sie beispielsweise Logik für die Phase "vertex_transformation" bereitstellen, definiert der Shader alles, was Sie bereitstellen. Es wäre insgesamt hilfreich zu dokumentieren, was jeder Chunk mit GLSL macht. Ohne weiter in die Abstraktion einzusteigen, wäre es meiner Meinung nach großartig, diese Karte zu haben, wie die Chunks jetzt strukturiert sind.

Wir öffnen eine Tür für das Hacken von eingebauten Materialien, aber ich glaube nicht, dass wir angemessene "Unterstützung" bieten können. Der Benutzer sollte sich bewusst sein, dass die Möglichkeit besteht, dass Dinge kaputt gehen.

Wir haben bereits das definierte Material für jedes Material verfügbar. https://github.com/mrdoob/three.js/issues/10764 schlug vor, THREE.ShaderLib zu ändern und dann eine .defines Eigenschaft für eine Instanz von Material . Zugegeben, die Eigenschaft war undokumentiert, bis ich den Eintrag geschrieben habe (aber Sie haben ihn genehmigt :)) und ist immer noch undefiniert .

Aber so wie dies derzeit funktioniert und von jedem Material geerbt wird, wird es unterstützt - wenn Sie Dinge hineinlegen, wirkt sich dies auf den Shader für dieses Material aus . Wenn Sie außerdem etwas definieren, das bereits Teil der Shader-Bibliothek ist, kann dies zu Fehlern führen .

Versuchen Sie nicht, ein Klugscheißer zu sein, sondern versuchen Sie, ein Beispiel zu finden. Ich habe meistens Defines mit ShaderMaterial aber es betrifft alle Materialien, nur weil WebGLRenderer funktioniert. Es gibt keine Logik, die sagt:

Wenn Sie eine viel schlauere, oberflächenähnliche Abstraktion eines Materials wie Phong dann berücksichtigen Sie keine Definitionen. Da drei nicht dafür ausgelegt ist, dass seine Shader-Chunks überschrieben werden, und das Phong-Template die letzte Autorität dafür ist, was GLSL tun muss, und dazu dient, dies vor der Verwendung zu verbergen, ist es sinnvoll, dass GLSL-Definitionen dies nicht stören.

Ich bin mir nicht ganz sicher, ob es kaputt gehen kann, ich muss es versuchen.

Abgesehen davon würde ich gerne die Möglichkeit haben, Dinge zu zerstören, wenn es viel Gewinn bringt. Mit einem ziemlich gekapselten und winzigen GLSL-Schnipsel könnte ich meine Version von drei leicht ändern, um normale Karten anders zu rendern. Eine Normale zu nehmen und sie in eine andere Normalität umzuwandeln ist super einfach, es sei denn, wir beginnen Computergrafiken auf eine völlig andere Weise zu machen, ich kann mir nicht vorstellen, dass dies jemals kaputt geht :)

Instanzierung war ein weiteres Beispiel:

oh, ich würde gerne ANGLE_INSTANCED_ARRAYS verwenden, drei scheinen es mit seinen Schatten oder PBR-Materialien nicht zu unterstützen, lass mich einfach diese beiden Zeilen hinzufügen und es für mich arbeiten lassen."

Ich freue mich zu sehen, dass diese Funktion endlich in three.js kommt :). Ja, ich habe auch ein yet another material modifier PR (https://github.com/mrdoob/three.js/pull/7581). Es war so alt, dass der Code nicht mehr relevant ist, sondern im Grunde Hooks wie @mrdoob injiziert vorschlägt und dann einfach durch Ihren eigenen Code ersetzen.
Ich mag die Idee vordefinierter Hooks, da es leicht zu verstehen ist, was Sie ändern, da ich glaube, dass die meisten Leute, die diese Funktion verwenden möchten, das Standardmaterial "leicht" ändern möchten, anstatt es vollständig neu zu schreiben.

Ich mag die Einfachheit dieser PR, aber wenn wir eine strukturiertere/sauberere Methode suchen, würde ich es vorziehen, bereits ein Wörterbuch mit Hooks zu haben, die durch Ihren Code ersetzt werden können, und eine einfachere Möglichkeit, Uniformen oder benutzerdefinierten Code hinzuzufügen als nur Saiten ersetzen.

@fernandojsg Jede Chance, dass Sie #10791 ausprobieren könnten, geben Feedback, es scheint dem, was Sie getan haben, sehr ähnlich zu sein, außer dass ich den zusätzlichen Hook direkt außerhalb von main (so etwas wie Ihr "pre_vertex") platziert habe und es etwas mehr Verwaltung gibt.

@fernandojsg Deine PR hatte ich vergessen 😚. Ich denke, Ihre PR sieht ziemlich so aus, wie ich angefangen habe, dies zu sehen. Unterbewusstsein?

Versuchen Sie nicht, ein Klugscheißer zu sein, sondern versuchen Sie, ein Beispiel zu finden. Ich habe meistens Defines mit ShaderMaterial aber es betrifft alle Materialien, nur weil WebGLRenderer funktioniert.

Ich bin froh, dass die Bibliothek auf diese Weise gehackt werden kann, aber wir sollten Missbrauchsfunktionen nicht intern für Dinge verwenden, für die nicht gedacht war. Es ist leicht, auf diese Weise mit fragilem Code zu enden.

#7581 noch einmal lesen... Zusätzlich zu https://github.com/mrdoob/three.js/commit/e55898c27a843f69a47e602761c60d9bbe91ee35 können wir % HOOKS % hinzufügen und dann eine Klasse wie diese erstellen:

THREE.ExtendedMaterial = function ( material, hooks ) {

    material.onBeforeCompile = function ( shader ) {
        var vertexShader = shader.vertexShader;
        var fragmentShader = parameters.fragmentShader;
        for ( var name in hooks ) {
           vertexShader = vertexShader.replace( '%' + name + '%', hooks[ name ] );
           fragmentShader = fragmentShader.replace( '%' + name + '%', hooks[ name ] );
        }
        shader.vertexShader = vertexShader;
        shader.fragmentShader = fragmentShader;
    };

    return material;

};

Dann können wir dies tun:

```js
var material = new THREE.ExtendedMaterial(
neues DREI.MeshBasicMaterial(),
{ Scheitelpunkt: 'transformiert.x += sin( position.y ) / 2.0;' }
);

Die Frage ist also, welche Haken sollten wir haben?

VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

FRAGMENT_UNIFORMS
INPUT_FRAGMENT
OUTPUT_FRAGMENT

@mrdoob Als Beispiel können Sie in diesem Kern sehen, wo Code in den MeshBasicMaterial Vertex-Shader eingefügt werden muss, um die Instanzierung zu unterstützen.

Und hier für den Rest der Materialien :)

https://github.com/mrdoob/three.js/pull/10750

VERTEX_UNIFORMS klingt zu eigensinnig, wo sollte man eine Funktion, ein Attribut oder eine Variation hinzufügen? PRE_VERTEX oder so?

NORMAL_MAP wird auf jeden Fall benötigt

vert:

PRE_VERTEX
VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

UV
fragment:

PRE_FRAGMENT
INPUT_FRAGMENT
LIGHT
NORMAL

OUTPUT_FRAGMENT

VERTEX_UNIFORMS klingt zu eigensinnig, wo sollte man eine Funktion, ein Attribut oder eine Variation hinzufügen? PRE_VERTEX oder so?

Hm, eigensinnig? Wie so?

attribute vec4 aMyAttribute; ist keine Uniform :)

float myRand( vec4 foo ) { /*logic*/ return vec4(bar,baz)} ist keine Uniform

varying vec3 vMyVarying; ist auch keine Uniform

entweder kein Plural oder gar keine Uniformen

Was ist der Anwendungsfall für das "Injizieren" von Attributen?
Sollten Sie nicht stattdessen geometry.addAttribute( 'aMyAttribute', ... ) verwenden?

Ich wusste nicht, dass Sie es nicht deklarieren müssen. Lässt noch Variationen und Funktionen übrig?

Lässt noch Variationen und Funktionen übrig?

Guter Punkt.

muss man in GLSL wirklich keine Attribute mehr deklarieren? Ich dachte, es funktioniert eher wie Uniformen, Sie deklarieren in GLSL, müssen es aber nicht in JS eingeben.

Ich denke, diese Liste kann ziemlich schnell ziemlich groß werden, wenn es um bestimmte Dinge geht, wird der Code ziemlich verstreut. Eine Möglichkeit, Normal Maps zu erstellen, beinhaltet zwei zusätzliche Attribute, Variationen und Transformationslogik sowohl in Vert als auch in Frag.

Ich mag die neuesten Änderungen, es ist immer noch einfach und elegant. Ich stimme @pailhead zu, dass wir einen Ort brauchen, um Code in den globalen Bereich https://github.com/mrdoob/three.js/pull/7581 musste ich preMainVertex preMainFragment global injizieren, damit Sie sie für Variationen und Funktionen verwenden können. Wahrscheinlich könnte ein Name wie globalVertex/Fragment oder ähnlich besser sein.

GLOBAL_VERTEX und GLOBAL_FRAGMENT klingen gut

genau wie ich sie in der anderen PR genannt habe 😆 👍

@pailhead Aber jetzt kennen Sie das Geheimnis, um Ihren Code zusammenzuführen: Öffnen Sie einfach eine PR, lassen Sie sie einige Zeit dort, warten Sie dann, bis

image

Jetzt im Ernst, ich mag diese Namen auch und damit glaube ich, dass wir bereit sind, oder übersehe ich etwas? Ich werde versuchen, das Beispiel aus meiner PR in diesen neuen Code umzuwandeln ;)

Der Teil über den Code, der reinkommt, ist in Ordnung, die Zeitleiste ist irgendwie daneben. #7581 wurde vor fast zwei Jahren vorgeschlagen. Wurde in https://github.com/mrdoob/three.js/issues/10789 nicht referenziert, ist also wahrscheinlich vom Radar verschwunden. Ich hätte es gestoßen oder gegabelt.

Selbst wenn Sie Module importieren und Ihre eigenen drei Rosinen auswählen, sind die Leute nicht allzu glücklich darüber, inoffizielle Kernmodule zu verwenden. Viel einfacher zu sagen: "Hey, es gibt ein npm-Paket, das mit Foo umgeht". Deshalb finde ich PRs wie diese wichtig, drei sind nicht flexibel genug.

profitieren!

Welcher Gewinn? 😆

Der Teil über den Code, der reinkommt, ist in Ordnung, die Zeitleiste ist irgendwie daneben. #7581 wurde vor fast zwei Jahren vorgeschlagen. Wurde in https://github.com/mrdoob/three.js/issues/10789 nicht referenziert, ist also wahrscheinlich vom Radar verschwunden. Ich hätte es gestoßen oder gegabelt.

Was? Sie haben nicht alle offenen PRs überprüft, bevor Sie eine neue erstellt haben? #trololol

wahr :)

ok wähle die Liste ein und lass uns das so schnell wie möglich haben :D

Wird dieser Shader (zum Beispiel):

#include <shadowmap_pars_vertex>

void main() {

    #include <begin_vertex>
    #include <project_vertex>
    #include <worldpos_vertex>
    #include <shadowmap_vertex>

}

sieht aus wie das:

#include <shadowmap_pars_vertex>

void main() {

    % begin_vertex %
    % project_vertex %
    % worldpos_vertex %
    % shadowmap_vertex %

}

Ähm, ich bin mir nicht sicher, ob Sie alle ersetzen oder einfach den Code einfügen, um einen Teil des aktuellen Verhaltens zu hinterlassen:

#include <shadowmap_pars_vertex>

%GLOBAL_VERTEX% 
void main() {
       %PRE_VERTEX% 
    #include <begin_vertex>
...
}

Mein erster Ansatz bestand darin, den zusätzlichen Code einzufügen, der den Rest des Includes unberührt ließ.

% hook % ist also nur für die eigentlichen Hooks, wenn man <begin_vertex> ersetzen möchte, muss man es immer noch in einem String suchen, dh. wird es keinen begin_vertex Hook geben?

In meiner PR wollte ich nur das Verhalten des Materials leicht modifizieren, normalerweise einige Farbkorrekturen vornehmen, nachdem die fs vollständig ausgeführt wurde, oder vs-Transformationen ähnlich der von @mrdoob in seinem Beispiel
Aber ja, wenn wir jedes der Includes vollständig ersetzen möchten, sollten wir die Hooks anstelle der Includes verwenden und die Logik hinzufügen, um das Include selbst einzufügen, falls Sie den Hook nicht gebunden haben.

Ich würde dies gerne für Normals tun (keine Derivate verwenden und tatsächliche TBN-Informationen von 3ds max oder Maya oder überall dort verwenden, wo die Normal Map generiert wird). Die Anpassung von MeshPhongMaterial an die Arbeit mit Instanzen erforderte auch das Ersetzen einiger Chunks, aber das fühlt sich wie ein Grenzfall an.

Das sind die beiden, die mir einfallen, ich frage mich, ob es noch mehr gibt.

% hook % ist also nur für die eigentlichen Hooks, wenn man <begin_vertex> ersetzen möchte, muss man es immer noch in einem String suchen, dh. wird es keinen begin_vertex Hook geben?

Sie können beide ersetzen. Mit dem aktuellen Code können Sie bereits alles ersetzen. %HOOK% dienen der Bequemlichkeit.

Wie in #11562 erwähnt, wie wäre es mit dem Konzept der austauschbaren Shader-Chunks?

Beispiel:

const material = new THREE.MeshPhongMaterial({
  color: 0xffffff,
  map: texture,

  parts: {
    frag: {
      map_pars_fragment: `
        uniform sampler2D map;
        uniform sampler2D map2;
     `,

      map_fragment: `
        vec4 texelColor = texture2D( map, vUv );
        vec4 texelColor2 = texture2D( map2, vUv );

    texelColor = mapTexelToLinear( 
          mix( texelColor, texelColor2, progress)
        );

    diffuseColor *= texelColor;
      `
    }
  }
});

material.uniforms.progress = {value: 1};
material.uniforms.map2 = {value: texture2};

So einfach kann der Übergangscode zwischen mehreren Texturen auf einem Material sein.

Es kann einfach implementiert werden und vermeidet unnötigen Code.

@sasha240100

https://github.com/mrdoob/three.js/pull/10791 sieht genau so aus wie das, was Sie vorschlagen, außer dass parts shaderChunks und es nicht als Streit.
:roll_eyes:

Sie brauchen auch das Wörterbuch frag{} , da die Chunks bereits markiert sind. Alle haben _fragment und _vertex .

Dieser macht dasselbe, außer dass Sie die Zeichenfolge durchsuchen müssen, um zu finden, was Sie ersetzen müssen.

@pailhead Ja, lassen Sie uns frag: {} . Ich habe es als Argument verwendet, weil es übergeben werden sollte, bevor der Material-Shader verkettet wird. In diesem Fall können wir einfach vermeiden, vorhandene Teile nach der Verkettung zu ersetzen.

Wir können tatsächlich beides hinzufügen: als Argument und als Eigenschaft. Wie color tut

Wo steht das gerade? Ich sehe, dass das Beispiel das .replace() tut und ich sehe keine Hooks / austauschbaren Chunks? @mrdoob

Konnte noch nicht darauf zurückkommen.

Möchten Sie eine PR für das % hook % Ding machen?

Möchten Sie das erweiterte Material aus dem obigen Kommentar?

THREE.ExtendedMaterial = function ( material, hooks ) {

    material.onBeforeCompile = function ( shader ) {
        var vertexShader = shader.vertexShader;
        var fragmentShader = parameters.fragmentShader;
        for ( var name in hooks ) {
           vertexShader = vertexShader.replace( '%' + name + '%', hooks[ name ] );
           fragmentShader = fragmentShader.replace( '%' + name + '%', hooks[ name ] );
        }
        shader.vertexShader = vertexShader;
        shader.fragmentShader = fragmentShader;
    };

    return material;

};

Den Spot für die Hooks zu wählen und zu dokumentieren, ist vielleicht gar nicht so trivial? Ich würde vielleicht hooks durch chunks ersetzen und nach den Einschlüssen suchen, mit dem Hinzufügen von global_vert und global_frag für den Moment? Was jetzt, wo ich es geschrieben habe, so klingt, als ob es nicht einmal in der Verantwortung von drei liegen sollte, aber andererseits würde es die THREE.ExtendedMaterial für % hook % s in der Zukunft stopfen?

Ich weiß nicht, ob Ihnen die Erweiterung THREE.BAS bekannt ist. Es verwendet Platzhalter in den Originalmaterialien, auf die aus dem benutzerdefinierten Material als Arrays zugegriffen werden kann, wie zum Beispiel:

  var material = new BAS.PhongAnimationMaterial({
    flatShading: true,
    vertexColors: THREE.VertexColors,
    side: THREE.DoubleSide,
    uniforms: {
      uTime: {type: 'f', value: 0}
    },
    vertexFunctions: [
      // cubic_bezier defines the cubicBezier function used in the vertexPosition chunk
      BAS.ShaderChunk['cubic_bezier'],
      BAS.ShaderChunk['quaternion_rotation']
    ],
    vertexParameters: [
      'uniform float uTime;',
      'attribute vec2 aDelayDuration;',
      'attribute vec3 aStartPosition;',
      'attribute vec3 aEndPosition;',
      'attribute vec3 aControl0;',
      'attribute vec3 aControl1;',
      'attribute vec4 aAxisAngle;'
    ],
    vertexInit: [
      'float tProgress = mod((uTime + aDelayDuration.x), aDelayDuration.y) / aDelayDuration.y;',
      'vec4 tQuat = quatFromAxisAngle(aAxisAngle.xyz, aAxisAngle.w * tProgress);'
    ],
    vertexPosition: [
      'transformed = rotateVector(tQuat, transformed);',
      'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
    ]
  });

Ich mag die Tatsache, dass es auf einfachen Konventionen basiert: vertexInit und fragmentInit werden zum Beispiel oben in der main()-Funktion stehen. vertexParameters und fragmentParameters befinden sich oben im Shader, um Uniformen oder Attribute zu definieren. vertexFunctions und fragmentFunctions stehen vor der Funktion main(). Und so weiter für Normalen, Position usw...

Wie Sie an der Position sehen können, ändern Sie die Variable transformed , um die endgültige Position zu ändern. Ähnliches zum Beispiel für Normalen ( objectNormal ) oder Farben ( diffuseColor ) im Fragment-Shader.

So sieht das Phong-Material aus, fügt nur die Platzhalter hinzu: https://github.com/zadvorsky/three.bas/blob/master/src/materials/PhongAnimationMaterial.js

Hallo Leute, ich habe versucht, der Convo zu folgen, aber ehrlich gesagt bin ich verloren, da ich vom Frontend komme und nicht vom Spieleentwickler.

Ich versuche, eine MTL zu laden und bekomme:

material.onBeforeCompile.toString() ist undefiniert.

Meine MTL-Datei zeigte ursprünglich auf eine .psd-Datei und ich habe sie in eine der wenigen PNG-Dateien geändert, die den gleichen Ordner enthalten (Normal). Ich denke, es ist nicht möglich, die PSD zu laden, aber trotzdem schlägt auch mit PSD fehl.

# Blender MTL File: 'goblin.blend'
# Material Count: 1

newmtl Material
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.640000 0.640000 0.640000
Ks 0.001617 0.005682 0.002517
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd Normal.png

Die Konsolenausgabe ist:

THREE.WebGLRenderer 87
bundle.js:54570 OBJLoader: 29.447998046875ms
bundle.js:28429 Uncaught TypeError: Cannot read property 'toString' of undefined
    at WebGLPrograms.getProgramCode (bundle.js:28429)
    at initMaterial (bundle.js:32350)
    at setProgram (bundle.js:32542)
    at WebGLRenderer.renderBufferDirect (bundle.js:31605)
    at renderObject (bundle.js:32335)
    at renderObjects (bundle.js:32308)
    at WebGLRenderer.render (bundle.js:32072)
    at bundle.js:9658
    at bundle.js:53976
    at XMLHttpRequest.<anonymous> (bundle.js:40153)

und der Code, den ich verwende, ist hauptsächlich:

OBJLoader(THREE);

const modelLoader = new THREE.OBJLoader();
const textureLoader = new MTLLoader();
textureLoader.setTexturePath( 'models/' );
    textureLoader.load('models/goblin.mtl', function( materials ) {

            materials.preload();

            modelLoader.setMaterials( materials );
            modelLoader.load('models/goblin.obj', function ( obj ) {
                 scene.add( obj );
                renderer.render( scene, camera );


            });

        }); 


    renderer.render( scene, camera );

}

Ist das ein Problem im Modell? in drei? im mtl-loader? es hat lösung?

Jede Hilfe wäre dankbar.
Vielen Dank.

Müssen wir etwas wie material.shader = shader in material.onBeforeCompile(shader=>...) ?

Ich denke, es fühlt sich etwas klobig an, vielleicht lohnt es sich, den anderen Vorschlag irgendwann noch einmal zu überdenken :)

Es wird dadurch verursacht, aber was führt dazu, dass Material diese Funktion nicht hat?
https://github.com/mrdoob/three.js/blob/35a26f178c523514673d992da1aece23c1cfca6e/src/renderers/webgl/WebGLPrograms.js#L244

@mrdoob

Ist es möglich, dass diese erweiterten Shader nicht richtig zwischengespeichert werden? Ich versuche, zwei verschiedene Materialien zu verwenden, eines hat dithering_fragment geändert, das andere nicht. Wenn ich beide zur Szene hinzufüge, wird nur diejenige ohne den dithering_fragment Block verwendet.

Ich konnte es mit dieser Geige nicht nachstellen
https://codepen.io/anon/pen/KQPBjd

Aber mit diesem Code in meinem Beispiel

const dithering_fragment = 
`
gl_FragColor.xyz *= foobar
`
// in onBeforeCompile
console.log(shader.fragmentShader)



md5-0f1a3ac67268b230968df20abdbd03d1



/**
#if defined( DITHERING )
  gl_FragColor.rgb = dithering( gl_FragColor.rgb );
#endif

gl_FragColor.xyz *= foobar

}
*/

Aber es kommt nicht zu einem Fehler, wenn ich der Szene weiteres Material hinzufüge, das andere enthält und definiert.

Das liegt daran:

https://github.com/mrdoob/three.js/blob/35a26f178c523514673d992da1aece23c1cfca6e/src/renderers/webgl/WebGLPrograms.js#L244

Ich habe die gleiche Funktion mit einer Logik für beide Materialien, die Variablen für die Logik liegen außerhalb des Geltungsbereichs des Rückrufs, daher erhalten meine beiden Materialien den gleichen Hash?

array.push( material.onBeforeCompile( obtainShaderSomehow ) )

https://github.com/mrdoob/three.js/pull/10791 hat es so gelöst:

https://github.com/pailhead/three.js/blob/879d52349bd5ef72c64fc962d8ca26bacaf489bf/src/renderers/webgl/WebGLPrograms.js#L242

Würden Sie die andere Pull-Anfrage überdenken, wenn ich die Platzhalter/Hooks global_vertex und global_fragment entferne? Mit denen hat es viele Dateien berührt, ohne sie ist es weniger Code. Nicht drei Zeilen, aber es hasht richtig :)

Kommentiere #41 in dieser Geige https://codepen.io/anon/pen/KQPBjd
und das Material des anderen Netzes in der Szene ändert sich.

Ist dies in einer neueren Version enthalten oder befindet sich alles noch in der Entwicklung?

@mrdoob Ich denke, es ist schwierig (oder unmöglich), Materialien zu serialisieren, die mit .onBeforeCompile() gehackt wurden. Glauben Sie, dass Benutzer ShaderMaterial wenn sie voll funktionsfähige (rendern, kopieren, klonen, serialisieren usw.) modifizierte integrierte Materialien wünschen?

_Bitte lesen Sie das Folgende auf die netteste, konstruktivste und nicht konfrontative Art und Weise_ :)

@takahirox warum wird das immer wieder behauptet?

Ich denke, es ist schwierig (oder unmöglich), gehackte Materialien zu serialisieren

Wenn Sie Materialien mit onBeforeCompile "hacken", fügen Sie einfach Uniformen hinzu, die mit jedem anderen Material identisch sind. Es gibt keinen Unterschied. Wenn Sie material.color serialisieren können, warum können Sie material.userData.myColor nicht serialisieren?

Ich versuche nicht, einen Kampf zu beginnen, Textwände oder so. Können Sie mir bitte in den einfachsten und kürzesten Worten erklären oder zumindest in die richtige Richtung weisen, warum dies unmöglich oder schwierig ist? Ich bin offen für die Möglichkeit, dass ich etwas Offensichtliches übersehe, aber wenn ich es bin, würde ich gerne verstehen, was es ist :)

Aus einer sehr kleinen Stichprobe, die von Artikeln generiert wurde , scheint dieses Experiment nicht so zu sein, als ob die Leute den ShaderMaterial Ansatz verwenden möchten.


Das ist mir gerade eingefallen? Gibt es eine Art Test, der beweist, dass dies schwer oder unmöglich ist? Mögen:

runSerializationTest( someMaterialWithOnBeforeCompile ).expect( something )

Da die Methode onBeforeCompile sehr willkürlichen Code enthalten kann, ist es sicherlich unmöglich, diesen Rückruf robust zu serialisieren. Die Methode könnte beispielsweise einen synchronen XMLHttpRequest erstellen und etwas mit dem Ergebnis tun. 😅

Aber... für die Art und Weise, wie es praktisch verwendet wird (um die Shader zu patchen), stimme ich zu, dass es nicht unmöglich ist. Aber die einfachen APIs (zB fn.toString() ) sind hackig, und die robusten APIs sind komplexer.

Ich verstehe das Problem immer noch nicht. Wie wäre es mit dieser Sichtweise:

Nehmen Sie rN , das MeshPhongMaterial und MeshStandardMaterial .

Sie können MeshPhongMaterial serialisieren, dh. schreibe einen JSON wie folgt:

{
  color: 'red',
  specular: 'very',
  glossy: 'not much'
}

Nehmen Sie rN+1 , das beide Materialien enthält:

Sie können beide weiterhin auf die gleiche Weise serialisieren:

//phong
{
  color: 'red',
  specular: 'very',
  glossy: 'not much'
}

//standard
{
  color:'red',
  shininess: 'shiny',
  metalness: 'not much'
}

Nirgendwo haben Sie die GLSL von MeshStandardMaterial serialisiert.

Auf die gleiche Weise können Sie alle erweiterten Materialien serialisieren.

//extended PhongMaterial
{
   color: 'red',
   specular: 'very',
   glossy: 'not much',
   hamburger: 'medium rare'
}

Deserialisierung:

if ( data.userData.hamburger && HamburgerPhongMaterial )
   mat = new HamburgerPhongMaterial( data ) 
else{ 
   console.warn(`I'm terribly sorry, but you don't have the HamburgerPhongMaterial extension, using default fallback`)
   mat = new PhongMaterial( data ) 
}

Was fehlt mir hier?


Wie onBeforeCompile ... diesen Rückruf serialisieren.

Warum, warum würdest du es überhaupt in Betracht ziehen :) Ich denke, das ist der Kern meiner Verwirrung, warum wird über diesen Haken nachgedacht, es ist nicht Teil der Materialdefinition, Beschreibung, irgendetwas. Es hat etwas mit der Engine, Plugins, Apps usw. zu tun, nicht mit den Daten.

Das obige Beispiel sieht für mich gut aus, es erfordert nur, dass der Benutzercode, der das Material deserialisiert, über die Existenz von HamburgerPhongMaterial Bescheid weiß. Ich habe damit kein Problem, es sind nicht einmal unbedingt API-Änderungen erforderlich, wenn Sie einen ähnlichen Ansatz wie #14099 verfolgen und toJSON und fromJSON überschreiben.

Vielleicht haben wir die Frage von @takahirox hier anders interpretiert?

...es ist schwierig (oder unmöglich), Materialien zu serialisieren, die mit .onBeforeCompile() gehackt wurden. Glauben Sie, dass Benutzer ShaderMaterial verwenden sollten, wenn sie voll funktionsfähige (rendern, kopieren, klonen, serialisieren usw.) modifizierte integrierte Materialien wünschen?

Ich versuche das mal aufzuteilen:

  • Wenn der Benutzer toJSON() für ein einfaches MeshStandardMaterial aufruft, bei dem die GLSL durch onBeforeCompile geändert wurde, werden die Änderungen an der GLSL nicht automatisch serialisiert.
  • Wenn der Benutzer ein MeshStandardMaterial mit einigen zusätzlichen Eigenschaften serialisiert, die auf eine Änderung der GLSL hinweisen, und es mit benutzerdefiniertem Code lädt, der weiß, was mit diesen zusätzlichen Eigenschaften zu tun ist, ist das sicherlich in Ordnung.
  • verschiedene andere Optionen (ShaderMaterial, NodeMaterial, glTF's KHR_techniques_webgl , ...) sind verfügbar, um beliebige benutzerdefinierte Materialien zu serialisieren.

Wenn der Fall, über den wir sprechen, keiner davon ist, dann verstehe ich das hier wohl falsch. Wenn es um #14099 geht, bin ich (noch) dafür, das zusammenzuführen.

Hmmm, ich habe nicht wirklich zwischen den ersten beiden unterschieden. Ich versuche mir etwas auszudenken, für das keine Uniformen erforderlich wären, es könnte zum Beispiel ein gl_FragColor.xyz /= gl_FragColor.a; . Aber ich kann mir kein Szenario vorstellen, in dem Sie so etwas serialisieren möchten ( zusammen mit dem Material? ). Es scheint, als würde es in den Bereich eines Effektrenderers oder so fallen, nicht in die materiellen Daten.

materials = loadSerializedMaterials()

effect = new Effect()

effect.load( 'alphaDivide').then(()=>materials.forEach(mat.onBeforeCompile = effect.getOnBeforeCompile('alphaDivide')))

Das zweite Beispiel ist das, was ich die ganze Zeit im Sinn hatte. Serialisieren Sie zusammen mit verschiedenen Eingaben einen Hinweis, was mit ihnen zu tun ist userData.extension === 'hamburger' .

Ich glaube, ich habe schlecht geschrieben. Was ich erwähnen wollte ist

  • Eingebautes Material, das mit .onBeforeCompile() gehackt wurde, wird nicht korrekt in derselben API kopiert, geklont oder serialisiert wie die eingebauten Materialien. Es wird als nicht gehacktes Material behandelt.
  • Wenn Benutzer das richtige Ergebnis wünschen, benötigen sie zusätzliche Arbeit auf der Benutzerseite.
  • Wenn Benutzer das gehackte integrierte Material mit der Three.js-Kern-API ohne zusätzliche benutzerseitige Arbeit wünschen, sollten sie an andere Optionen wie ShaderMaterial denken.

Meine Frage bezieht sich nur auf die Richtlinie von .onBeforeCompile() und nicht auf den speziellen Fall. Ich wollte die Einschränkung verdeutlichen und einen Hinweis im Dokument hinzufügen.

Wenn ich Three.js und das gesamte Rendering-System vergesse, denke ich, dass ich den Punkt verstehe. Als generisches Objekt, wenn es .clone() hat, möchten Sie in der Lage sein zu klonen und konsistente Erwartungen zu haben. Das Problem ist, dass beide Lösungen (klonen, nicht klonen) ein gültiges Argument zu haben scheinen. Sie klonen keine Zuhörer, aber Sie erwarten auch, dass etwas, das das Zeichnen beeinflusst, konsistent ist.

Ich denke immer noch, dass mein Vorschlag von oben eine gültige Möglichkeit ist, dies zu tun, da ich in anderen Bereichen auf das gleiche Problem gestoßen bin (wie das Teilen von Tiefenpuffern zwischen Zielen kann auch mit ownThing || outsideThing gelöst werden.

Anstatt die Funktion zwischenzuspeichern, was nicht funktioniert, würden Sie im Grunde userData aber eine Teilmenge davon. Es macht keinen Sinn, userData.hamburger: 'rare' , aber userData.effectOn: true". The ownInput | ownChunks | ownWhatever` würde dieses Problem lösen.

Es löst auch das Problem von:

Da die onBeforeCompile-Methode sehr willkürlichen Code enthalten kann, ist es sicherlich unmöglich, diesen Rückruf robust zu serialisieren.

Es ist lange her, die Leute haben onBeforeCompile wir sollten jetzt wissen, was für willkürlichen Code darin zu finden ist. Ich denke, es funktioniert nur auf shader Objekt, das übergeben wird. Sie mutieren dieses Objekt, aber nur Mutationen, die mit ShaderMaterial Sinn machen, würden sowieso eine Wirkung haben. Einige gl Sachen selbst einzustellen wäre wahrscheinlich überschrieben, und das ist so ziemlich das Einzige, was mir in den Sinn kommt.

Es ist im Wesentlichen ein pre three parse Schritt, bei dem Sie die Möglichkeit haben, den Shader zusammen mit der obligatorischen uniforms:{} Schnittstelle selbst zu analysieren, da Sie ihn auf Material ( defines:{} auf der anderen Seite, und beide sind Geschwister in ShaderMaterial ).

Wie Sie es machen, dh was auch immer der beliebige Code ist, spielt keine Rolle. Es gibt nichts zurück, aber es mutiert shader:{} und der Renderer verwendet es dann.

Ansätze die ich vorschlage:

  1. Füge ownThing zu verschiedenen Klassen hinzu. THREE.WebGLRenderTarget könnte .ownStencilDepthBuffer . Hier, wie ich oben vorgeschlagen habe, wäre es eine Version von ownGLSL:{} ownInput | ownUniforms . Sie beseitigen die Notwendigkeit für willkürlichen Code, um das shader:{} Objekt zu mutieren:

Gegebene Eingaben wie diese, die aus dem WebGLRenderer (super privat):

const shader = {
  uniforms: buildFromMaterial(mat),
  vs: getVSTemplate(key),
  fs: getFSTemplate(key)
}

Nuke das:

shader = onBeforeCompile( shader )

shader.vs //invalid GLSL
shader.uniforms //you got some extra ones that onBeforeCompile may have mutated, maybe removed some others

shader = ___parse(shader) //private step

shader.vs //valid glsl, (not a template)

compile(shader)

Benutze das:

function parseShader(shader){
   return {
     uniforms: shader.uniforms,
     vs: parseChunks(shader.vs)
     fs: parseChunks(shader.fs)
   }
}
//FIX FOR HASHING BUG
function parseChunk( material, chunkName ){
  return material.ownChunks[chunkName] || THREE.ShaderChunks[chunkName]
}

//do whatever you want with the templates, maybe remove an `#include <>`
shader = onBeforeParse(shader)

shader.vs //template || valid glsl

shader = parseShader(shader) //sample OWN || PRIVATE 

shader.vs //valid GLSL 

//if you want to apply a regex on the entire valid glsl, or compile node material or something else
shader = onBeforeCompile( shader )

compile(shader)
  1. Lass WebGLRenderer nichts anderes wissen als ShaderMaterial .

Wo immer du hättest

const mat = new THREE.MeshBasicMaterial()
const sm = new THREE.ShaderMaterial()

Verfügen über

const mat = nodeMaterial.compile() //returns ShaderMaterial, with a friendly interface (same as StandardMaterial for example)

const mat = chunkMaterial.compile()

Die tl: dr von dem Zeug ist oben Benutzer nicht über den bestimmten Zeitpunkt kümmern , in: shaderString, out: shaderString , isoliert werden. Der Grund, warum es an diesem bestimmten Punkt in der Pipeline getan werden muss, ist meiner Meinung nach wert, untersucht und verstanden zu werden. Es scheint den Dingen eher im Weg zu stehen, als dass es hilft.

@mrdoob Ich denke, es ist schwierig (oder unmöglich), Materialien zu serialisieren, die mit .onBeforeCompile() gehackt wurden. Glauben Sie, dass Benutzer ShaderMaterial wenn sie voll funktionsfähige (rendern, kopieren, klonen, serialisieren usw.) modifizierte integrierte Materialien wünschen?

Ich habe nicht erwartet (in Betracht gezogen), dass die Verwendung von onBeforeCompile() serialisiert wird...

Ich wurde gerade hart gebissen, indem ich onBeforeCompile verwendet habe. Ich habe eine Shader-Hilfsklasse, die es mir ermöglicht, auf definierte Weise Änderungen an integrierten Shadern vorzunehmen. Dazu verwende ich die Methode onBeforeCompile. Anscheinend verwendet WebGLProgram, wie im ersten Kommentar erwähnt, onBeforeCompile.toString() als Hash. Da meine Funktion jedoch generisch ist (sie ersetzt Teile der Vertex- und Fragment-Shader durch Variablen), sieht onBeforeCompile.toString() für verschiedene Shader nicht anders aus. Dies bedeutete, dass alle meine verschiedenen Shader als dasselbe Programm zwischengespeichert wurden. Ein eval call und eine uuid und jetzt sehen meine Funktionen alle anders aus. Es hat ewig gedauert, das herauszufinden.

@donaldr Siehe https://github.com/mrdoob/three.js/issues/13192. Ich denke, es wäre gut, diese Einschränkungen zu dokumentieren.

Könnten Sie mitteilen, wie Sie die Auswertung verwenden? Ich habe darüber nachgedacht, aber es schien einfach zu schmutzig. Sie könnten im Grunde const fdscxnmdrek435rkjl zum oberen Rand des Körpers hinzufügen und dann auswerten?

Dies bedeutete, dass alle meine verschiedenen Shader als dasselbe Programm zwischengespeichert wurden. Ein eval call und eine uuid und jetzt sehen meine Funktionen alle anders aus. Es hat ewig gedauert, das herauszufinden.

Das klingt sehr frustrierend, tut mir leid. 😞 Wenn die Änderungen, die Sie vornehmen mussten, in der Bibliothek selbst sinnvoll erscheinen würden, können Sie auch gerne Fragen öffnen.

Bitte fühlen Sie sich auch willkommen, Fragen zu öffnen.

Würden Sie in Erwägung ziehen, bestehende wieder zu eröffnen? #13192 scheint das gleiche zu sein, aber es wurde geschlossen :crying_cat_face: Es wäre wirklich schön, wenn dort ein Workaround gepostet würde, wenn es als Feature angesehen würde.

Hmmm...

const _obc = shader => {...}
const obc = `
function (shader){
  const _${ hash() }
  ${ _obc.toString() }
  _obc(shader)
}
`
material.onBeforeCompile = eval(obc)

^Würde das funktionieren? Ich habe nie eval .

Die Einführung von Material.customProgramCacheKey() über #17567 stellt sicher, dass Entwickler jetzt die Möglichkeit haben, dass Shader-Programme nicht für Materialien freigegeben werden, die über onBeforeCompile() mit bedingten Anweisungen modifiziert wurden.

Bitte diskutieren Sie andere bestehende Probleme oder Verbesserungen im Zusammenhang mit onBeforeCompile() in neuen Threads (wie #13446).

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen