Three.js: Matériel: Ajouté onBeforeCompile()

Créé le 9 juin 2017  ·  75Commentaires  ·  Source: mrdoob/three.js

Au fil des ans, une demande de fonctionnalité commune a été de pouvoir modifier les matériaux intégrés. Aujourd'hui, j'ai réalisé qu'il pouvait être implémenté de la même manière que le Object3D de onBeforeRender() .

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

WebGLPrograms ajoute onBeforeCompile.toString() au hachage du programme, donc cela ne devrait pas affecter les autres matériaux intégrés utilisés dans la scène.

Voici un exemple de la fonctionnalité en action :

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;

};

Non seulement nous pouvons jouer avec le code du shader, mais nous pouvons également ajouter des uniformes personnalisés.

if ( materialShader ) {

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

}

Est-ce trop bidon ?

/cc @WestLangley @bhouston @tschw

Enhancement

Commentaire le plus utile

@mrdoob Je pense qu'il est difficile (ou impossible) de sérialiser des matériaux piratés avec .onBeforeCompile() . Pensez-vous que les utilisateurs devraient utiliser ShaderMaterial s'ils veulent des matériaux intégrés entièrement modifiés (rendu, copie, clonage, sérialisation, etc.) ?

Je ne m'attendais pas (considéré) à ce que l'utilisation de onBeforeCompile() soit sérialisée...

Tous les 75 commentaires

Il semble un peu difficile d'exiger une manipulation de chaîne personnalisée lorsque WebGLProgram.js effectue déjà beaucoup de traitements. Il existe un cas d'utilisation connexe, qui consiste à ajouter de nouveaux #includes plutôt que de remplacer ceux existants. Vous pouvez le faire aujourd'hui en modifiant THREE.ShaderLib au moment de l'exécution, mais cela semble tout aussi sale.

Peut-être qu'une solution plus intéressante pour les deux est de vous permettre de fournir une ShaderLib personnalisée pour un matériau, qui remplacerait/augmenterait les morceaux intégrés sans manipulation manuelle des cordes et sans les écraser de façon permanente. Cela n'empêche pas un hook onBeforeCompile pour d'autres utilisations, mais cela semble plus conforme à ce que votre exemple essaie de faire.

Cela dit, la composition des shaders à base de raw includes va toujours être très fragile d'une version à l'autre. Si cela ne vous dérange pas d'inclure tout le code squelette (comme meshphong_vert.glsl ), vous pouvez déjà étendre les matériaux intégrés avec quelque chose comme ceci :

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 };
    //...
  }
}

Dans tous les cas, il faut compter sur l'organisation interne des shaders intégrés pour ne pas trop changer d'une version à l'autre. Chaque inclusion est déjà une fonction déguisée, avec une signature mal spécifiée. Je soupçonne que ce sera beaucoup plus propre et maintenable des deux côtés pour fournir des remplacements au niveau de la fonction plutôt que du niveau d'inclusion.

Aujourd'hui j'ai pensé..

Joli! Je le pensais aussi le 10 février lorsque j'ai fait cette pull request :

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

Il semble que la seule différence est que vous le faites via un rappel ? J'ai puisé dans l'argument inutilisé dans le WebGLRenderer.

J'aime l'utiliser par exemple pour que threejs fonctionne avec des cartes normales, mais j'ai lié certains problèmes qui semblent être triviaux à résoudre avec cela.

Vous pouvez regarder ma branche et faire un tour ou consulter cet exemple super simple (teste juste une déformation personnalisée avec éclairage/ombres, etc., et ajoute quelques uniformes).

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

Peut-être qu'une solution plus intéressante pour les deux est de vous permettre de fournir une ShaderLib personnalisée pour un matériau, qui remplacerait/augmenterait les morceaux intégrés sans manipulation manuelle des cordes et sans les écraser de façon permanente.

Résume exactement ce qui se passe dans : https://github.com/mrdoob/three.js/pull/10791 J'aimerais pouvoir écrire de meilleures descriptions :)


Je dois admettre que je ne sais pas ce qui se passe ici. Je pense que je connais quelqu'un qui a renoncé à contribuer à trois, ne l'a pas compris à l'époque, mais maintenant je trouve ça un peu frustrant.

Frustrant surtout parce que j'ai un déjà-vu quand je lis le premier paragraphe :

... manière similaire à onBeforeRender() d'Object3D.

C'est exactement la même chose qui s'est produite avec le onBeforeRender() https://github.com/mrdoob/three.js/pull/9738 , quand il a fallu un an pour convaincre pour intégrer la fonctionnalité.

Bon sang, deux fois, avec le même développeur ? Je fais évidemment quelque chose de mal ici, mais quoi? La première fois, les fichiers de construction étaient archivés, je ne savais pas quoi faire et cela a été oublié. Mais cette fois-ci, j'étais au top de cette pull request, il y a un conflit en ce moment mais il est facilement résolu, il n'y a eu aucun conflit pendant des mois.

Ne vous méprenez pas, je ne pense pas que ce soit de la vanité dans les travaux ici, je pense que je veux juste lancer des idées par des gens et obtenir des commentaires. @unconed, il est évident que vous connaissez le problème et que vous avez probablement essayé différentes choses. Qu'est-ce qui a pu faire qu'un utilisateur comme vous manque l'autre PR ?

Je me sentais plutôt bien avec la shaderlib par matériau, mais j'ai trouvé une fonction dans le moteur de rendu qui ressemblait/comparait des shaders entiers comme des chaînes pour comprendre la mise en cache, je ne me sentais pas très bien à ce sujet. J'aimerais qu'il y ait des commentaires donnés...

L'exemple était-il erroné ? La tête peut rendre ce qui se passe beaucoup plus évident que ma balle à pointes abstraite, mais l'exemple entier fonctionne avec des ombres qui couvrent plus de terrain. C'est lourd pour une raison quelconque, mais je pense que c'est parce que sin/cos sur de nombreux verts.

Je suppose que si vous recommencez... Même si le kit de scène est absolument horrible, cette API est vraiment sympa :

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

Bien qu'horriblement documenté, je ne trouve pas la partie la plus intéressante, mais en gros, ils ont des crochets abstraits à de nombreuses étapes différentes du shader.

Plus tôt cette année, j'ai fait une pull request qui semble faire quelque chose de similaire sinon exactement la même chose :

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

Il semble que la seule différence est que vous le faites via un rappel ? J'ai puisé dans l'argument inutilisé dans le WebGLRenderer.

Désolé de ne pas pouvoir encore s'occuper de ce PR @pailhead. Je suppose que le principal avantage de mon approche est qu'elle n'a nécessité que 3 nouvelles lignes.

Cela dit, je vais maintenant passer un peu de temps à étudier les vôtres et celles de @unconed .

Celui-ci se sent définitivement plus élégant, seulement trois lignes. Je suivais un modèle différent dans l'autre. J'allais dire que c'était peut-être un peu plus verbeux, mais pas si sûr. Je suppose que le seul avantage de l'autre est qu'il était bon d'y aller il y a quelques mois :)

Bon sang, deux fois, avec le même développeur ? Je fais évidemment quelque chose de mal ici, mais quoi?

Désolé pour ça. La seule chose à laquelle je peux penser, c'est que j'ai probablement été submergé. Peut-être une longue discussion ou un PR compliqué qui consommerait trop de mes énergies, alors je décide de le laisser pour plus tard et de passer à des PR plus simples.

Il n'y a pas que vous @pailhead. @bhouston a eu beaucoup de relations publiques comme celle-ci, même

Encore une fois, ce n'est pas que je n'aime pas le PR, c'est que le PR nécessite une certaine attention que je ne peux pas offrir à l'époque. Un bon exemple est le PR d'instance. J'ai réussi à trouver le temps de le lire, j'ai fait mes propres expérimentations et suggéré des simplifications. J'espère pouvoir le revoir bientôt.

Il est difficile de gérer tout cela et d'accorder à chaque RP l'attention qu'il mérite.

L'exemple était-il erroné ? La tête peut rendre ce qui se passe beaucoup plus évident que ma balle à pointes abstraite, mais l'exemple entier fonctionne avec des ombres qui couvrent plus de terrain. C'est lourd pour une raison quelconque, mais je pense que c'est parce que sin/cos sur de nombreux verts.

Maintenant, vous le mentionnez ... Oui, fonctionne à 10 images par seconde sur un nouveau MacBook Pro lors d'un zoom avant 😮

Je pense que ce sont les ombres et le rand, le péché cos et tout, mais oui, il semble que cela prend trop de temps.

Quoi qu'il en soit, je suis surpris que vous ayez tiré cela en trois lignes, j'étais trop concentré sur la façon dont les paramètres matériels sont transformés en uniformes et j'ai suivi ce modèle. La différence est que je fournis une alternative ShaderLib comme un dictionnaire, donc #include <> apporte un code différent. Vous supprimez l'include avant que cela ne se produise et le remplacez par glsl. Je pense que si vous retournez une sorte de wrapper autour du shader, peut-être que cette syntaxe pourrait être plus propre (fournissez simplement le nom du morceau + glsl, au lieu de replace() . Mais cela peut finir par ressembler beaucoup plus à l'autre .

Ce serait vraiment bien d'avoir ça, je n'ai pas travaillé avec autant de moteurs 3D, mais l'unité et le kit de scène semblent avoir quelque chose comme ça.

@unconed

Je suppose que l'un des avantages du onBeforeCompile() est que les propriétés du matériau restent inchangées. Cela ressemble donc plus à une extension des matériaux intégrés.

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

Jouer avec ShaderLib est en effet délicat. L'ajout de hooks pour toujours inchangés devrait cependant être faisable :

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

Le code de remplacement deviendra ceci :

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

Nous supprimerons ensuite tout crochet qui n'a pas été utilisé avant la compilation, bien sûr.

voici à quoi cela ressemble par rapport aux ShaderChunks par instance

//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 identique à l'approche Material.defines :

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)

_Ce n'est qu'un dictionnaire de noms de morceaux, et l'objet uniformes. La manipulation de la chaîne ici ressemble davantage à "je veux réutiliser ce morceau et faire quelque chose par-dessus"_

ce PR :

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
    );

};

Je suppose que l'un des avantages de onBeforeCompile() est que les propriétés du matériau restent inchangées.

Cela fonctionne de la même manière dans les autres relations publiques, donc je pense que la façon dont c'est fait n'a pas d'importance.


Un avantage probablement non pertinent auquel je peux penser est que vous pouvez conserver la logique de manipulation du matériel dans le même bloc et au même niveau. Si vous aviez un morceau de code quelque part qui changeait de couleur et que vous ajoutez plus tard du GLSL personnalisé à ce matériau, vous pouvez ajouter le code pour changer l'uniforme avec la couleur. (sauter le mur de texte)


Si le uv_pars_vertex prête à confusion, je pense qu'une bonne question à poser est :

"où dois-je injecter ma fonction GLSL personnalisée, comme float rand( vec2) ?"

Alors celui-ci pourrait avoir du sens https://github.com/mrdoob/three.js/pull/11050


Un autre argument pourrait être - en supposant que vous connaissiez GLSL, en supposant que vous connaissiez le framework de shader de THREE, vous devez toujours être créatif et réfléchir à l'endroit où vous injectez quoi. J'ai dû réfléchir un peu et parcourir la plupart des shaders pour comprendre que uv_pars_vertex est un endroit pratique pour faire mon extension matérielle.

Il vaudrait mieux passer de quelque chose comme ça vers une abstraction, avoir des crochets lightingPhase , objectTransformation , perspectiveTransformation etc. Ou au moins un morceau factice de guaranteedToBeAboveMainButBelowCommonsAndExtensionCalls inclus dans chaque programme de shader. On dirait que vous allez pour ça avec % vertex % ? Mais je ne connais pas cette syntaxe.

Ce PR, tel quel, semble aller dans la direction opposée. En plus de savoir où vous devez puiser, vous devez également être explicite et manipuler les chaînes, en répétant une partie du travail que le moteur de rendu fait déjà pour vous. Aussi, si vous posez une autre question en plus de la première

Comment utiliser une fonction, déclarée dans le morceau commun, à l'intérieur de la fonction que j'injecte ?

vous pourriez vous retrouver à faire plus de manipulations de cordes que de simplement ajouter les uniformes à l'ensemble du shader.

J'ai dû réfléchir un peu et parcourir la plupart des shaders pour comprendre que uv_pars_vertex est un endroit pratique pour faire mon extension matérielle.

Je ne pense pas qu'il y ait un moyen de contourner cela à moins que nous ne procédions à une sur-ingénierie. L'idée avec % vertex % est d'essayer d'aider un peu en nommant les crochets et plus ou moins vous dire à quel état vous injectez du code, mais il serait difficile de documenter quelles variables sont disponibles à quel moment .

Nous ouvrons une porte pour permettre le piratage de matériaux intégrés, mais je ne pense pas que nous puissions fournir un "support" approprié. L'utilisateur doit être conscient que les choses peuvent se casser.

J'ai essayé de l'aborder dans l'autre PR. J'ai ajouté un crochet factice au moins pour les fonctions, les variantes, les attributs et les uniformes. Une grande partie de la logique GLSL se produit déjà sur les structs, scenekit a documenté chaque variable (mais je ne peux plus trouver la même documentation).

Il aurait probablement besoin d'être remanié au fur et à mesure, mais quelque chose du genre:

#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

Bien sûr, ifdefs et includes ne fonctionneraient probablement pas comme ça. Mais je peux voir que c'est relativement simple avec l'autre approche. Nous pourrions toujours ajouter #include <some_phase> , avec des chaînes vides. Quelque chose pourrait aider à gérer ce qui se déclenche en utilisant #ifdef s. Ainsi, par exemple, si vous fournissez une logique pour la phase "vertex_transformation", le shader définira ce que vous fournirez. Documenter ce que chaque morceau fait avec GLSL serait globalement utile. Sans aller plus loin dans l'abstraction, je pense que ce serait génial d'avoir cette carte de la façon dont les morceaux sont structurés maintenant.

Nous ouvrons une porte pour permettre le piratage de matériaux intégrés, mais je ne pense pas que nous puissions fournir un "support" approprié. L'utilisateur doit être conscient que les choses peuvent se casser.

Nous avons déjà les éléments définis disponibles sur chaque matériau. https://github.com/mrdoob/three.js/issues/10764 a suggéré de modifier THREE.ShaderLib puis de définir une propriété .defines sur une instance de Material . Certes, la propriété n'était pas documentée jusqu'à ce que j'écrive l'entrée (mais vous l'avez approuvée :)), et elle n'est toujours pas définie .

Mais la façon dont cela fonctionne actuellement, et est héritée par chaque matériau, elle est prise en charge - si vous y mettez des éléments, cela affectera le shader de ce matériau . De plus, s'il vous arrive de définir quelque chose qui fait déjà partie de la bibliothèque de shaders, cela peut casser des choses .

N'essayant pas d'être un idiot mais essayant de trouver un exemple. J'ai principalement utilisé des définitions avec ShaderMaterial mais cela affecte tous les matériaux, simplement parce que le fonctionnement de WebGLRenderer . Il n'y a pas de logique qui dit :

si vous êtes beaucoup plus intelligent, surfacez comme une abstraction d'un matériau comme Phong alors ne tenez pas compte des définitions. Parce que trois n'est pas conçu pour que ses morceaux de shader soient remplacés, et le modèle Phong est l'autorité finale sur ce que GLSL doit faire, et sert à cacher cela de l'utilisation, il est logique que les définitions de GLSL n'interfèrent pas avec cela.

Je ne suis pas tout à fait sûr qu'il puisse être cassé, il faudrait que j'essaye.

Cela étant dit, j'aimerais avoir la possibilité de casser des choses, si cela apporte beaucoup de gain. Avec un petit extrait GLSL assez encapsulé, je pourrais facilement changer ma version de trois pour rendre les cartes normales différemment. Prendre une normale et la transformer pour obtenir une autre normale est super simple, à moins que nous commencions à faire de l'infographie d'une manière complètement différente, je ne peux pas voir cela jamais se briser :)

L'instanciation était un autre exemple :

oh j'aimerais utiliser ANGLE_INSTANCED_ARRAYS, trois ne semblent pas le supporter avec ses ombres ou ses matériaux PBR, permettez-moi d'ajouter ces deux lignes et de le faire fonctionner pour moi"

Je suis heureux de voir que cette fonctionnalité arrive enfin sur three.js :). Oui, j'ai également créé un yet another material modifier PR (https://github.com/mrdoob/three.js/pull/7581) Il était si vieux que le code n'est plus pertinent, mais injecte essentiellement des crochets comme @mrdoob propose, puis remplacez-les simplement par votre propre code.
J'aime l'idée des crochets prédéfinis car il est facile de comprendre ce que vous modifiez, car je pense que la plupart des personnes qui souhaitent utiliser cette fonctionnalité souhaitent modifier "légèrement" le matériel par défaut au lieu de le réécrire complètement.

J'aime la simplicité de ce PR, mais si nous recherchons une façon plus structurée/propre de faire les choses, je préférerais avoir déjà un dictionnaire de crochets à remplacer par votre code et un moyen plus simple d'ajouter des uniformes ou du code personnalisé que juste en remplaçant les chaînes.

@fernandojsg si vous avez la possibilité de consulter #10791, donnez votre avis, cela semble très similaire à ce que vous avez fait, sauf que j'ai mis le crochet supplémentaire juste à l'extérieur de main (quelque chose comme votre "pre_vertex") et il y a un peu plus de gestion.

@fernandojsg j'avais oublié ton RP 😚. Je pense que vos relations publiques ressemblent beaucoup à la façon dont je commençais à voir cela fonctionner. Subconscient?

N'essayant pas d'être un idiot mais essayant de trouver un exemple. J'ai principalement utilisé des définitions avec ShaderMaterial mais cela affecte tous les matériaux, simplement parce que le fonctionnement de WebGLRenderer .

Je suis content que la bibliothèque soit piratable de cette manière, mais nous ne devrions pas utiliser les fonctionnalités d'abus en interne pour des choses qui n'étaient pas destinées. Il est facile de se retrouver avec un code fragile de cette façon.

Lire à nouveau #7581... En plus de https://github.com/mrdoob/three.js/commit/e55898c27a843f69a47e602761c60d9bbe91ee35, nous pouvons ajouter le % HOOKS % puis créer une classe comme celle-ci :

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;

};

Ensuite, nous pouvons faire ceci :

```js
var material = new THREE.ExtendedMaterial(
nouveau THREE.MeshBasicMaterial(),
{ vertex: 'transformed.x += sin( position.y ) / 2.0;' }
);

La question est donc : quels crochets devrions-nous avoir ?

VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

FRAGMENT_UNIFORMS
INPUT_FRAGMENT
OUTPUT_FRAGMENT

@mrdoob À titre d'exemple, vous pouvez voir dans cet aperçu où le code doit être injecté dans le vertex shader MeshBasicMaterial pour prendre en charge l'instanciation.

Et voilà pour le reste du matériel :)

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

VERTEX_UNIFORMS semble trop opiniâtre, où devriez-vous ajouter une fonction, un attribut ou une variable ? PRE_VERTEX ou quoi ?

NORMAL_MAP est absolument nécessaire

vert:

PRE_VERTEX
VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

UV
fragment:

PRE_FRAGMENT
INPUT_FRAGMENT
LIGHT
NORMAL

OUTPUT_FRAGMENT

VERTEX_UNIFORMS semble trop opiniâtre, où devriez-vous ajouter une fonction, un attribut ou une variable ? PRE_VERTEX ou quoi ?

Hmm, opiniâtre? Comment?

attribute vec4 aMyAttribute; n'est pas un uniforme :)

float myRand( vec4 foo ) { /*logic*/ return vec4(bar,baz)} n'est pas un uniforme

varying vec3 vMyVarying; n'est pas non plus un uniforme

soit pas au pluriel, soit pas uniformes du tout

Quel est le cas d'utilisation pour « injecter » des attributs ?
Ne devriez-vous pas utiliser geometry.addAttribute( 'aMyAttribute', ... ) place ?

Je ne savais pas que tu n'étais pas obligé de le déclarer. Laisse encore des variantes et des fonctions ?

Laisse encore des variantes et des fonctions ?

Bon point.

vous n'avez vraiment plus à déclarer d'attributs dans GLSL ? Je pensais que cela fonctionnait plus comme des uniformes, vous déclarez en GLSL mais vous n'avez pas à le taper en JS.

Je pense que cette liste peut devenir assez longue assez rapidement, quand il s'agit de certaines choses, le code est assez dispersé. Une façon de faire des cartes normales implique deux attributs supplémentaires, des variables et une logique de transformation à la fois dans vert et frag.

J'aime les derniers changements, c'est toujours simple et élégant. Je suis d'accord avec @pailhead que nous avons besoin d'un endroit pour mettre le code dans la portée globale. Dans https://github.com/mrdoob/three.js/pull/7581, j'avais preMainVertex preMainFragment à injecter globalement afin que vous puissiez les utiliser pour varier et fonctions. Probablement un nom comme globalVertex/Fragment ou similaire pourrait être mieux.

GLOBAL_VERTEX et GLOBAL_FRAGMENT sonnent bien 👌

exactement comment je les ai nommés dans l'autre PR 😆 👍

@pailhead Mais maintenant vous connaissez le secret pour fusionner votre code: ouvrez simplement un PR, laissez-le là un certain temps, puis attendez que @mrdoob pense éventuellement à faire quelque chose de similaire, attendez son PR, puis commentez-le en faisant référence à l'exact de la même manière que vous l'avez fait sur votre RP pour qu'il puisse penser que c'était son idée, il l'ajoutera, fusionnera... profit !

image

Maintenant sérieusement, j'aime aussi ces noms et avec ça je crois que nous sommes prêts à partir, ou est-ce que j'ai raté quelque chose ? Je vais essayer de convertir l'exemple de mon PR vers ce nouveau code ;)

La partie concernant l'entrée du code est bonne, la chronologie est un peu décalée. #7581 a été suggéré il y a presque deux ans. N'a pas été référencé dans https://github.com/mrdoob/three.js/issues/10789 , donc probablement sorti du radar. Je l'aurais cogné ou fourchu.

Même lors de l'importation de modules et de la sélection des trois, les gens ne sont pas très contents d'utiliser des modules de base non officiels. Beaucoup plus facile à dire, "hé, il y a un paquet npm qui gère foo". C'est pourquoi je pense que les PR comme celui-ci sont importants, trois n'est pas assez flexible.

profit!

Quel bénéfice ? ??

La partie concernant l'entrée du code est bonne, la chronologie est un peu décalée. #7581 a été suggéré il y a presque deux ans. N'a pas été référencé dans https://github.com/mrdoob/three.js/issues/10789 , donc probablement sorti du radar. Je l'aurais cogné ou fourchu.

Quoi? Vous n'avez pas vérifié tous les PR ouverts avant d'en créer un nouveau ? #trololol

vrai :)

ok composez la liste et ayons cela dès que possible :D

Est-ce que ce shader (par exemple) :

#include <shadowmap_pars_vertex>

void main() {

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

}

ressemble à ca:

#include <shadowmap_pars_vertex>

void main() {

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

}

Umm, je ne sais pas si les remplacer tous ou simplement injecter le code en laissant une partie du comportement actuel:

#include <shadowmap_pars_vertex>

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

Mon approche initiale consistait à injecter le code supplémentaire en laissant le reste de l'inclusion intact.

Donc % hook % est juste pour les crochets réels, si l'on veut remplacer <begin_vertex> ils doivent toujours le chercher dans une chaîne, c'est-à-dire. il n'y aura pas begin_vertex crochet

Dans mon PR, je voulais juste modifier légèrement le comportement du matériau, généralement effectuer des corrections de couleur après l'exécution complète du fs ou par rapport à des transformations similaires à celle que @mrdoob a montré dans son exemple.
Mais oui, si nous voulons remplacer complètement chacun des inclusions, nous devons utiliser les crochets au lieu des inclusions et ajouter la logique pour injecter l'inclusion elle-même au cas où vous n'auriez pas lié le crochet.

J'aimerais le faire pour les normales (ne pas utiliser de dérivés et utiliser les informations TBN réelles de 3ds max ou de maya ou de n'importe où où la carte normale est générée). Adapter MeshPhongMaterial pour travailler avec des instances nécessitait également de remplacer complètement certains morceaux, mais cela ressemble à un cas limite.

Ce sont les deux auxquels je peux penser, je me demande s'il y en a plus.

Donc % hook % est juste pour les crochets réels, si l'on veut remplacer <begin_vertex> ils doivent toujours le chercher dans une chaîne, c'est-à-dire. il n'y aura pas begin_vertex crochet

Vous pouvez remplacer les deux. Le code actuel vous permet déjà de remplacer n'importe quoi. %HOOK% sont pour plus de commodité.

Comme je l'ai mentionné dans #11562 , qu'en est-il du concept de morceaux de shader remplaçables ?

Exemple:

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};

C'est ainsi que peut être simple le code de transition entre plusieurs textures sur 1 matériau.

Il peut être facilement implémenté et éviter de faire du code inutile.

@sasha240100

https://github.com/mrdoob/three.js/pull/10791 ressemble exactement à celui que vous suggérez, sauf que parts s'appelle shaderChunks et il ne le prend pas comme un argument.
:roll_eyes:

Vous n'avez pas non plus besoin du dictionnaire frag{} , puisque les morceaux sont déjà marqués. Tous ont _fragment et _vertex .

Celui-ci fait la même chose, sauf que vous devez rechercher dans la chaîne pour trouver ce que vous devez remplacer.

@pailhead Oui, laissons tomber le frag: {} . Je l'ai utilisé comme argument car il doit être passé avant que le shader de matériau ne soit concaténé. Dans ce cas, nous pouvons simplement éviter de remplacer des pièces existantes après concaténation.

Nous pouvons en fait ajouter les deux : comme argument et comme propriété. Comme color fait

Où est-ce en ce moment ? Je vois que l'exemple fait le .replace() et je ne vois pas de crochets / morceaux remplaçables? @mrdoob

Je n'ai pas encore pu m'y remettre.

Souhaitez-vous faire un PR pour le truc % hook % ?

Voulez-vous le matériel étendu, à partir du commentaire ci-dessus ?

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;

};

Choisir l'emplacement des crochets et les documenter n'est peut-être pas si trivial ? Je remplacerais peut-être hooks transmis par chunks et chercherais les inclusions, avec l'ajout de global_vert et global_frag pour l'instant ? Ce qui, maintenant que je l'ai écrit, semble que cela ne devrait même pas être la responsabilité de trois, mais d'un autre côté, cela remplacerait les THREE.ExtendedMaterial par des % hook % s à l'avenir ?

Je ne sais pas si vous connaissez l'extension THREE.BAS. Il utilise des espaces réservés dans les matériaux d'origine qui sont accessibles à partir du matériel personnalisé sous forme de tableaux, comme :

  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);'
    ]
  });

J'aime le fait qu'il soit basé sur des conventions simples : vertexInit et fragmentInit seront en haut de la fonction main() par exemple. vertexParameters et fragmentParameters sont en haut du shader, pour définir des uniformes ou des attributs. vertexFunctions et fragmentFunctions sont avant la fonction main(). Et ainsi de suite pour les normales, position etc...

Comme vous pouvez le voir dans la position, vous modifiez la variable transformed pour changer la position finale. Même chose pour les normales ( objectNormal ) ou les couleurs ( diffuseColor ) dans le shader de fragment, par exemple.

Voici à quoi ressemble le matériel Phong, n'ajoute que les espaces réservés : https://github.com/zadvorsky/three.bas/blob/master/src/materials/PhongAnimationMaterial.js

Salut les gars, j'essayais de suivre la conversation mais honnêtement, je suis perdu car je viens de Front end et non de développeur de jeux.

J'essaie de charger un MTL et j'obtiens :

material.onBeforeCompile.toString() n'est pas défini.

Mon fichier MTL pointait à l'origine vers un fichier .psd et je l'ai remplacé par l'un des rares png contenant le même dossier (Normal).

# 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

La sortie de la console est :

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)

et le code que j'utilise est principalement :

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 );

}

Est-ce un problème dans le modèle ? Dans trois? dans le chargeur mtl ? il a une solution ?

Toute aide serait appréciée.
Merci.

Devons-nous faire quelque chose comme material.shader = shader dans material.onBeforeCompile(shader=>...) ?

Je pense que cela semble un peu maladroit, cela pourrait valoir la peine de revoir l'autre suggestion à un moment donné :)

C'est causé par cela, mais qu'est-ce qui fait que le Material n'a pas cette fonction ?
https://github.com/mrdoob/three.js/blob/35a26f178c523514673d992da1aece23c1cfca6e/src/renderers/webgl/WebGLPrograms.js#L244

@mrdoob

Est-il possible que ces shaders étendus ne soient pas correctement mis en cache ? J'essaie d'utiliser deux matériaux différents, l'un a changé dithering_fragment , l'autre non. Lorsque j'ajoute les deux à la scène, il apparaît que seul celui sans le morceau dithering_fragment est utilisé.

Je n'ai pas pu le recréer avec ce violon
https://codepen.io/anon/pen/KQPBjd

Mais avec ce code dans mon exemple

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

}
*/

Mais il n'y a pas d'erreur si j'ajoute un autre matériau à la scène qui partage d'autres inclusions et définitions.

C'est à cause de ça :

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

J'ai la même fonction avec une logique fournie aux deux matériaux, les variables de la logique sont hors de portée du rappel, donc mes deux matériaux obtiennent le même hachage?

array.push( material.onBeforeCompile( obtainShaderSomehow ) )

https://github.com/mrdoob/three.js/pull/10791 l'a résolu comme ceci :

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

Réexamineriez-vous l'autre pull request si je supprime les espaces réservés/crochets global_vertex et global_fragment ? Avec ceux-là ça touchait pas mal de fichiers, sans eux, c'est moins de code. Pas trois lignes, mais il hache correctement :)

Commentez #41 dans ce violon https://codepen.io/anon/pen/KQPBjd
et le matériau de l'autre maillage de la scène changera.

Cela a-t-il été inclus dans une version récente ou est-il encore en développement ?

@mrdoob Je pense qu'il est difficile (ou impossible) de sérialiser des matériaux piratés avec .onBeforeCompile() . Pensez-vous que les utilisateurs devraient utiliser ShaderMaterial s'ils veulent des matériaux intégrés entièrement modifiés (rendu, copie, clonage, sérialisation, etc.) ?

_Veuillez lire ce qui suit de la manière la plus agréable, constructive et non conflictuelle possible_ :)

@takahirox pourquoi cela continue-t-il d'être réclamé?

Je pense qu'il est difficile (ou impossible) de sérialiser les matériaux piratés

Lorsque vous "piratez" des matériaux avec des onBeforeCompile vous ajoutez simplement des uniformes qui sont les mêmes qu'avec n'importe quel autre matériau. Il n'y a pas de différence. Si vous pouvez sérialiser material.color pourquoi ne pouvez-vous pas sérialiser material.userData.myColor ?

Je n'essaie pas de commencer un combat, des murs de texte ou quoi que ce soit. Dans les termes les plus simples et les plus courts possibles, pouvez-vous s'il vous plaît expliquer, ou au moins m'indiquer pourquoi c'est impossible ou difficile ? Je suis ouvert à la possibilité que je rate quelque chose d'évident, mais si c'est le cas, j'aimerais comprendre ce que c'est :)

À partir d'un très petit échantillon, généré par des articles , cette expérience ne semble pas que les gens veuillent utiliser l'approche ShaderMaterial .


C'est juste venu à l'esprit? Existe-t-il une sorte de test qui prouve que c'est difficile ou impossible ? Comme:

runSerializationTest( someMaterialWithOnBeforeCompile ).expect( something )

Comme la méthode onBeforeCompile peut contenir du code très arbitraire, il est certainement impossible de sérialiser de manière robuste ce rappel. Par exemple, la méthode pourrait créer une XMLHttpRequest synchrone et faire quelque chose avec le résultat. ??

Cependant... pour la façon dont il est utilisé pratiquement (pour patcher les shaders), je suis d'accord que ce n'est pas impossible. Mais les API simples (par exemple fn.toString() ) sont bidon, et les API robustes sont plus complexes.

Je ne comprends toujours pas le problème. Et ce point de vue :

Prenez rN qui avait MeshPhongMaterial et n'avait pas MeshStandardMaterial .

Vous pouvez sérialiser MeshPhongMaterial ie. écrivez un JSON comme ceci :

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

Prenons rN+1 qui a les deux matériaux :

Vous pouvez toujours sérialiser les deux de la même manière :

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

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

Nulle part vous n'avez sérialisé le GLSL à partir de MeshStandardMaterial .

De la même manière, en sérialisant tout matériel étendu .

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

Désérialisation :

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 ) 
}

Qu'est-ce que j'oublie ici?


Comme onBeforeCompile ... sérialisez ce rappel.

Pourquoi, pourquoi le considéreriez-vous même :) Je pense que c'est le nœud de ma confusion, pourquoi une réflexion est-elle donnée à ce crochet, cela ne fait pas partie de la définition matérielle, de la description, de quoi que ce soit. Cela a quelque chose à voir avec le moteur, les plugins, les applications, etc., pas les données.

L'exemple que vous avez donné ci-dessus me semble bien, il nécessite juste que le code utilisateur désérialisant le matériel connaisse l'existence de HamburgerPhongMaterial? Je n'ai aucun problème avec cela, cela ne nécessite même pas nécessairement des modifications d'API si vous adoptez une approche similaire à #14099 et remplacez toJSON et fromJSON.

Peut-être avons-nous interprété différemment la question de

... il est difficile (ou impossible) de sérialiser les matériaux piratés avec .onBeforeCompile(). Pensez-vous que les utilisateurs devraient utiliser ShaderMaterial s'ils veulent des matériaux intégrés entièrement fonctionnels (rendu, copie, clonage, sérialisation, etc.) ?

Je vais essayer de séparer ça :

  • si l'utilisateur appelle toJSON() sur un simple MeshStandardMaterial, où le GLSL qui a été modifié par son onBeforeCompile , les modifications apportées au GLSL ne seront pas automatiquement sérialisées.
  • si l'utilisateur sérialise un MeshStandardMaterial avec des propriétés supplémentaires indiquant une modification du GLSL et le charge avec un code personnalisé qui sait quoi faire avec ces propriétés supplémentaires, alors c'est certainement bien.
  • diverses autres options (ShaderMaterial, NodeMaterial, KHR_techniques_webgl glTF, ...) sont disponibles pour sérialiser des matériaux personnalisés arbitraires.

Si le cas dont nous parlons n'en fait pas partie, alors je suppose que je me trompe. S'il s'agit de #14099, je suis (toujours) en faveur de la fusion.

Hmmm, je ne faisais pas vraiment la distinction entre les deux premiers. J'essaie de penser à quelque chose qui ne nécessiterait pas d'uniformes, ce pourrait être un gl_FragColor.xyz /= gl_FragColor.a; par exemple. Mais je ne peux pas imaginer un scénario où vous voudriez sérialiser quelque chose comme ça ( avec le matériel ? ). Il semble que cela entrerait dans le champ d'application d'un moteur de rendu d'effets ou de quelque chose, pas de données matérielles.

materials = loadSerializedMaterials()

effect = new Effect()

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

Le deuxième exemple est ce que j'avais en tête depuis le début. Avec diverses entrées, sérialisez un indice sur ce qu'il faut en faire userData.extension === 'hamburger' .

Je pense que j'ai mal écrit. Ce que je voulais mentionner, c'est

  • Le matériel intégré piraté avec .onBeforeCompile() n'est pas copié, cloné, sérialisé correctement dans la même API que les matériaux intégrés. Il est traité comme du matériel non piraté.
  • Si les utilisateurs veulent un résultat correct, ils ont besoin d'un travail supplémentaire côté utilisateur.
  • Si les utilisateurs veulent le matériel intégré piraté avec l'API principale Three.js sans aucun travail supplémentaire côté utilisateur, ils devraient penser à d'autres options, comme ShaderMaterial .

Ma question concerne uniquement la politique de .onBeforeCompile() plutôt que le cas spécifique. Je voulais clarifier la limitation et ajouter une note dans la doc.

Si j'oublie three.js et tout le système de rendu, je pense que je vois l'intérêt. En tant qu'objet générique, s'il a .clone() vous voulez pouvoir le cloner et avoir des attentes cohérentes. Le problème est que les deux solutions (clonez ceci et non le clonez) semblent avoir un argument valable. Vous ne clonez pas les auditeurs, mais vous vous attendez aussi à ce que quelque chose qui influence le dessin soit cohérent.

Je pense toujours que ma proposition ci-dessus est un moyen valable de le faire, car j'ai rencontré le même problème dans d'autres domaines (comme le partage de tampons de profondeur entre les cibles peut également être résolu avec ownThing || outsideThing .

Au lieu de mettre en cache la fonction, qui ne fonctionne pas, vous mettriez essentiellement en cache userData mais un sous-ensemble de celle-ci. Cela n'a aucun sens de mettre en cache userData.hamburger: 'rare' mais cela fait userData.effectOn: true". The ownInput | propresChunks | ownWhatever` résoudrait ce problème.

Il résout également le problème de :

Comme la méthode onBeforeCompile peut contenir du code très arbitraire, il est certainement impossible de sérialiser de manière robuste ce rappel.

Cela fait longtemps, les gens ont utilisé onBeforeCompile nous devrions maintenant savoir quel type de code arbitraire peut être trouvé là-dedans. Je pense qu'il ne fonctionne que sur l'objet shader qui est passé. Vous faites muter cet objet, mais seules les mutations qui ont du sens avec ShaderMaterial auraient un effet de toute façon. Définir vous-même des éléments de gl serait probablement remplacé, et c'est à peu près la seule chose qui me vient à l'esprit.

Il s'agit essentiellement d'une étape pre three parse , où vous avez la possibilité d'analyser le shader vous-même avec l'interface obligatoire uniforms:{} , puisque vous ne l'avez pas disponible sur Material ( defines:{} d'un autre côté, et les deux sont frères et sœurs dans ShaderMaterial ).

Comment vous le faites, c'est-à-dire quel que soit le code arbitraire, n'a pas d'importance. Il ne renvoie rien, mais il mute shader:{} et le moteur de rendu l'utilise ensuite.

Approches que je propose :

  1. Ajoutez ownThing à divers cours. THREE.WebGLRenderTarget pourrait avoir .ownStencilDepthBuffer . Ici, comme je l'ai suggéré ci-dessus, il s'agirait d'une version de ownGLSL:{} ownInput | ownUniforms . Vous supprimez le besoin de code arbitraire pour muter l'objet shader:{} :

Étant donné une entrée comme celle-ci qui provient de l'intérieur du WebGLRenderer (super privé):

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

Nuke ceci :

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)

Utilisez ceci:

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. Faites en sorte que WebGLRenderer ne sache rien d'autre que ShaderMaterial .

Partout où vous auriez

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

Ont

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

const mat = chunkMaterial.compile()

Le tl: dr des choses ci - dessus est l' utilisateur ne se temps où « la in: shaderString, out: shaderString . Je pense que la raison pour laquelle cela doit être fait à ce stade particulier du pipeline mérite d'être étudiée et comprise. Il semble gêner les choses plus qu'il ne l'aide.

@mrdoob Je pense qu'il est difficile (ou impossible) de sérialiser des matériaux piratés avec .onBeforeCompile() . Pensez-vous que les utilisateurs devraient utiliser ShaderMaterial s'ils veulent des matériaux intégrés entièrement modifiés (rendu, copie, clonage, sérialisation, etc.) ?

Je ne m'attendais pas (considéré) à ce que l'utilisation de onBeforeCompile() soit sérialisée...

Je viens de me faire mordre durement en utilisant onBeforeCompile. J'ai une classe d'aide au shader qui me permet d'apporter des modifications aux shaders intégrés d'une manière définie. J'utilise la méthode onBeforeCompile pour ce faire. Apparemment, comme indiqué dans le premier commentaire, WebGLProgram utilise onBeforeCompile.toString() comme hachage. Mais comme ma fonction est générique (elle remplace des parties des shaders de vertex et de fragments par des variables), onBeforeCompile.toString() ne semble pas différent pour les différents shaders. Cela signifiait que tous mes différents shaders étaient mis en cache en tant que même programme. Un appel eval et un uuid et maintenant mes fonctions sont toutes différentes. Il a fallu une éternité pour comprendre cela.

@donaldr Voir https://github.com/mrdoob/three.js/issues/13192. Je pense qu'il serait bon de documenter ces limitations.

pourriez-vous partager comment vous utilisez l'eval ? J'y ai pensé mais ça me paraissait trop sale. En gros, vous pourriez ajouter const fdscxnmdrek435rkjl en haut du corps, puis eval ?

Cela signifiait que tous mes différents shaders étaient mis en cache en tant que même programme. Un appel eval et un uuid et maintenant mes fonctions sont toutes différentes. Il a fallu une éternité pour comprendre cela.

Cela semble très frustrant, désolé. 😞 Si les modifications que vous deviez apporter semblent avoir du sens dans la bibliothèque elle-même, n'hésitez pas à consulter les numéros ouverts également.

s'il vous plaît n'hésitez pas à ouvrir les problèmes, aussi.

Envisageriez-vous de rouvrir ceux qui existent déjà ? #13192 semble être le même mais il a été fermé :crying_cat_face: Ce serait vraiment bien si une solution de contournement y était publiée si elle était considérée comme une fonctionnalité.

Hmmm...

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

^Est-ce que ça marcherait ? Je n'ai jamais utilisé eval .

L'introduction de Material.customProgramCacheKey() via #17567 garantit que les développeurs ont désormais la possibilité que les programmes de shader ne soient pas partagés pour les matériaux modifiés via onBeforeCompile() avec des instructions conditionnelles.

Veuillez discuter d'autres problèmes ou améliorations existants dans le contexte de onBeforeCompile() dans les nouveaux fils de discussion (comme le n°13446).

Cette page vous a été utile?
0 / 5 - 0 notes