Three.js: Bahan: Ditambahkan onBeforeCompile()

Dibuat pada 9 Jun 2017  ·  75Komentar  ·  Sumber: mrdoob/three.js

Selama bertahun-tahun, permintaan fitur yang umum adalah kemampuan untuk memodifikasi materi bawaan. Hari ini saya menyadari bahwa itu dapat diimplementasikan dengan cara yang mirip dengan Object3D 's onBeforeRender() .

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

WebGLPrograms menambahkan onBeforeCompile.toString() ke hash program jadi ini tidak akan mempengaruhi bahan bawaan lain yang digunakan dalam adegan.

Berikut ini contoh fitur yang sedang beraksi:

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;

};

Tidak hanya kita dapat mengacaukan kode shader, tetapi kita dapat menambahkan seragam khusus.

if ( materialShader ) {

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

}

Apakah itu terlalu hacky?

/cc @WestLangley @bhouston @tschw

Enhancement

Komentar yang paling membantu

@mrdoob Saya pikir sulit (atau tidak mungkin) untuk membuat cerita bersambung Material yang diretas dengan .onBeforeCompile() . Apakah menurut Anda pengguna harus menggunakan ShaderMaterial jika mereka ingin material bawaan yang dimodifikasi secara fungsional (render, copy, clone, serialize, dan sebagainya)?

Saya tidak berharap (dianggap) penggunaan onBeforeCompile() untuk diserialisasi...

Semua 75 komentar

Tampaknya agak sulit untuk memerlukan manipulasi string khusus ketika WebGLProgram.js sudah melakukan banyak pemrosesan. Ada kasus penggunaan terkait, yaitu menambahkan #include baru daripada menimpa yang sudah ada. Anda dapat melakukan ini hari ini dengan memodifikasi THREE.ShaderLib saat runtime, tetapi rasanya sama kotornya.

Mungkin solusi yang lebih baik untuk keduanya adalah mengizinkan Anda menyediakan ShaderLib khusus untuk materi, yang akan menimpa/menambah potongan bawaan tanpa manipulasi string manual dan tanpa merusaknya secara permanen. Itu tidak menghalangi pengait onBeforeCompile untuk kegunaan lain, tetapi tampaknya lebih sesuai dengan apa yang coba dilakukan oleh contoh Anda.

Konon, komposisi shader berdasarkan bahan baku selalu akan sangat rapuh dari satu versi ke versi berikutnya. Jika Anda tidak keberatan memasukkan semua kode kerangka (seperti meshphong_vert.glsl ), Anda sudah dapat memperluas materi bawaan dengan sesuatu seperti ini:

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

Dalam semua kasus, Anda harus mengandalkan organisasi internal shader bawaan untuk tidak terlalu banyak berubah dari satu rilis ke rilis berikutnya. Setiap penyertaan sudah merupakan fungsi yang disamarkan, dengan tanda tangan yang tidak ditentukan dengan jelas. Saya menduga itu akan jauh lebih bersih dan dapat dipelihara di kedua sisi untuk memberikan tingkat fungsi daripada penggantian tingkat-termasuk.

Hari ini aku berpikir..

Bagus! Saya juga berpikir demikian pada 10 Februari ketika saya membuat permintaan tarik ini:

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

Tampaknya satu-satunya perbedaan adalah Anda melakukannya melalui panggilan balik? Saya memanfaatkan argumen yang tidak digunakan di WebGLRenderer.

Saya suka menggunakan ini misalnya untuk membuat threejs berfungsi dengan peta normal, tetapi saya telah menautkan beberapa masalah yang tampaknya sepele untuk diselesaikan dengan ini.

Anda dapat melihat cabang saya dan mengambil ini untuk berputar atau melihat contoh super sederhana ini (hanya menguji deformasi khusus dengan pencahayaan/bayangan dll, dan menambahkan beberapa seragam).

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

Mungkin solusi yang lebih baik untuk keduanya adalah mengizinkan Anda menyediakan ShaderLib khusus untuk materi, yang akan menimpa/menambah potongan bawaan tanpa manipulasi string manual dan tanpa merusaknya secara permanen.

Persis meringkas apa yang terjadi di: https://github.com/mrdoob/three.js/pull/10791 Saya berharap saya bisa menulis deskripsi yang lebih baik :)


Saya harus mengakui bahwa saya tidak tahu apa yang sedang terjadi di sini. Saya pikir saya mengenal seseorang yang menyerah berkontribusi pada tiga, tidak memahaminya saat itu, tetapi sekarang saya merasa agak frustasi.

Frustrasi sebagian besar karena saya mengalami deja vu ketika saya membaca paragraf pertama:

... cara yang mirip dengan onBeforeRender() Object3D.

Hal yang sama persis ini terjadi dengan onBeforeRender() https://github.com/mrdoob/three.js/pull/9738 , ketika butuh satu tahun untuk meyakinkan fitur tersebut.

Astaga, dua kali, dengan dev yang sama? Saya jelas melakukan sesuatu yang salah di sini, tapi apa? Pertama kali adalah file build sedang diperiksa, saya tidak tahu harus berbuat apa, dan itu dilupakan begitu saja. Tapi kali ini saya berada di atas permintaan tarik ini, ada konflik sekarang tetapi mudah diselesaikan, tidak ada konflik selama berbulan-bulan.

Jangan salah paham, saya tidak berpikir itu kesombongan dalam bekerja di sini, saya pikir saya hanya ingin menjalankan ide orang dan mendapatkan umpan balik. @unconed jelas bahwa Anda sudah familiar dengan masalah dan Anda mungkin sudah mencoba hal yang berbeda. Apa yang mungkin menyebabkan pengguna seperti Anda melewatkan PR lainnya?

Saya merasa cukup baik tentang shaderlib per materi, tetapi saya menemukan satu fungsi di penyaji yang melihat/membandingkan seluruh shader sebagai string untuk mengetahui caching, tidak merasa terlalu hebat tentang itu. Berharap ada beberapa umpan balik yang diberikan ...

Apakah contoh itu salah? Kepala mungkin membuatnya jauh lebih jelas apa yang terjadi daripada bola lonjakan abstrak saya, tetapi seluruh contoh bekerja dengan bayangan yang menutupi lebih banyak tanah. Ini berat untuk beberapa alasan, tetapi saya pikir itu karena sin/cos pada banyak vert.

Saya kira jika Anda memulai dari awal... Meskipun perlengkapan adegan benar-benar mengerikan, api ini sangat bagus:

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

Meskipun didokumentasikan dengan buruk, saya tidak dapat menemukan bagian yang lebih menarik, tetapi pada dasarnya mereka telah mengabstraksikan kait di berbagai tahap shader.

Awal tahun ini saya membuat permintaan tarik yang tampaknya melakukan hal serupa jika bukan hal yang sama persis:

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

Tampaknya satu-satunya perbedaan adalah Anda melakukannya melalui panggilan balik? Saya memanfaatkan argumen yang tidak digunakan di WebGLRenderer.

Maaf belum bisa ngurus PR itu @pailhead. Saya kira keuntungan utama dari pendekatan saya adalah hanya membutuhkan 3 baris baru.

Karena itu, sekarang saya akan meluangkan waktu untuk mempelajari saran Anda dan @unconed .

Yang satu ini pasti terasa lebih elegan, hanya tiga baris. Saya mengikuti pola yang berbeda di yang lain. Akan mengatakan bahwa itu mungkin sedikit lebih bertele-tele, tetapi tidak begitu yakin. Saya kira satu-satunya keuntungan dari yang lain adalah bagus untuk pergi beberapa bulan yang lalu :)

Astaga, dua kali, dengan dev yang sama? Saya jelas melakukan sesuatu yang salah di sini, tapi apa?

Maaf tentang itu. Satu-satunya hal yang dapat saya pikirkan adalah bahwa mungkin saya kewalahan. Mungkin diskusi panjang atau PR rumit yang akan menghabiskan terlalu banyak energi saya, jadi saya memutuskan untuk meninggalkannya nanti dan beralih ke PR yang lebih sederhana.

Bukan hanya kamu @pailhead. @bhouston punya banyak PR seperti ini, bahkan @WestLangley dan @Mugen87.

Sekali lagi, bukannya saya tidak suka PR, tapi PR membutuhkan perhatian yang tidak bisa saya berikan saat itu. Contoh yang baik adalah Instance PR. Saya berhasil menemukan waktu untuk membacanya, melakukan eksperimen sendiri dan menyarankan penyederhanaan. Semoga saya bisa segera mengunjunginya kembali.

Sulit untuk mengelola semua ini dan memberikan setiap PR perhatian yang layak mereka dapatkan.

Apakah contoh itu salah? Kepala mungkin membuatnya jauh lebih jelas apa yang terjadi daripada bola lonjakan abstrak saya, tetapi seluruh contoh bekerja dengan bayangan yang menutupi lebih banyak tanah. Ini berat untuk beberapa alasan, tetapi saya pikir itu karena sin/cos pada banyak vert.

Sekarang Anda menyebutkannya... Ya, berjalan pada 10fps di MacBook Pro baru saat memperbesar

Saya pikir itu adalah bayangan dan rand, sin cos dan sebagainya, tapi ya sepertinya terlalu banyak pukulan.

Either way, saya terkejut Anda melakukan ini dalam tiga baris, saya terlalu fokus pada bagaimana params material diubah menjadi seragam dan mengikuti pola itu. Perbedaannya adalah saya memberikan alternatif ShaderLib seperti kamus, jadi #include <> membawa kode yang berbeda. Anda menghapus penyertaan sebelum ini terjadi, dan menggantinya dengan glsl. Saya pikir jika Anda mengembalikan semacam pembungkus di sekitar shader, mungkin sintaks ini bisa lebih bersih (cukup berikan nama chunk + glsl, alih-alih replace() . Tapi mungkin akan terlihat lebih mirip yang lain .

Akan sangat menyenangkan memiliki ini, saya belum pernah bekerja dengan banyak mesin 3d, tetapi unity dan scene kit tampaknya memiliki sesuatu seperti ini.

@unconed

Saya kira satu manfaat dari onBeforeCompile() adalah bahwa sifat material tetap tidak berubah. Jadi rasanya lebih seperti memperluas bahan bawaan.

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

Bermain-main dengan ShaderLib memang rumit. Menambahkan kait yang tidak berubah selamanya harus bisa dilakukan:

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

Kode ganti akan menjadi ini:

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

Kami kemudian akan menghapus semua pengait yang belum pernah digunakan sebelum kompilasi, tentu saja.

inilah tampilannya dibandingkan dengan ShaderChunks per 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 sama dengan pendekatan 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)

_Ini hanya kamus nama chunk, dan objek uniforms. Manipulasi string di sini lebih seperti "saya ingin menggunakan kembali potongan ini, dan melakukan sesuatu di atasnya"_

PR ini:

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

};

Saya kira satu manfaat dari onBeforeCompile() adalah bahwa properti material tetap tidak berubah.

Ia bekerja sama di PR lain, jadi saya pikir tidak masalah bagaimana hal itu dilakukan.


Salah satu manfaat yang mungkin tidak relevan yang dapat saya pikirkan adalah Anda dapat menyimpan logika untuk memanipulasi materi di blok yang sama dan pada level yang sama. Jika Anda memiliki sepotong kode di suatu tempat yang berubah warna, dan kemudian Anda menambahkan GLSL khusus ke materi itu, Anda dapat menambahkan kode untuk mengubah seragam dengan warnanya. (lewati dinding teks)


Jika uv_pars_vertex membingungkan, saya pikir pertanyaan yang bagus untuk ditanyakan adalah:

"di mana saya menyuntikkan fungsi GLSL khusus saya, seperti float rand( vec2) ?"

Maka yang ini mungkin masuk akal https://github.com/mrdoob/three.js/pull/11050


Argumen lain bisa jadi - dengan asumsi Anda tahu GLSL, dengan asumsi Anda tahu kerangka shader TIGA, Anda masih harus kreatif dan memikirkan di mana Anda menyuntikkan apa. Saya harus berpikir sedikit dan melihat melalui sebagian besar shader untuk mengetahui bahwa uv_pars_vertex adalah tempat yang nyaman untuk melakukan ekstensi materi saya.

Akan lebih baik untuk beralih dari sesuatu seperti ini menuju abstraksi, memiliki lightingPhase , objectTransformation , perspectiveTransformation dll. Atau setidaknya potongan dummy guaranteedToBeAboveMainButBelowCommonsAndExtensionCalls yang disertakan dalam setiap program shader. Sepertinya Anda akan melakukannya dengan % vertex % ? Tapi saya tidak terbiasa dengan sintaks itu.

PR ini, seolah-olah, berjalan ke arah yang berlawanan. Selain mengetahui di mana Anda perlu masuk, Anda juga harus eksplisit dan memanipulasi string, mengulangi beberapa pekerjaan yang sudah dilakukan perender untuk Anda. Juga, jika Anda mengajukan pertanyaan lain selain yang pertama

Bagaimana cara saya menggunakan suatu fungsi, yang dideklarasikan dalam potongan commons, di dalam fungsi yang saya suntikkan?

Anda mungkin mendapati diri Anda melakukan lebih banyak manipulasi string daripada hanya menambahkan seragam ke seluruh shader.

Saya harus berpikir sedikit dan melihat melalui sebagian besar shader untuk mengetahui bahwa uv_pars_vertex adalah tempat yang nyaman untuk melakukan ekstensi materi saya.

Saya tidak berpikir ada jalan lain kecuali kita merekayasa secara berlebihan. Gagasan dengan % vertex % adalah mencoba membantu sedikit dengan memberi nama kait dan kurang lebih memberi tahu Anda pada status apa Anda menyuntikkan kode, tetapi akan sulit untuk mendokumentasikan variabel apa yang tersedia pada titik apa .

Kami membuka pintu untuk memungkinkan peretasan materi bawaan, tetapi saya rasa kami tidak dapat memberikan "dukungan" yang tepat. Pengguna harus menyadari bahwa kemungkinan hal-hal dapat rusak.

Saya mencoba mengatasinya di PR lainnya. Saya menambahkan dummy hook setidaknya untuk fungsi, variasi, atribut, dan seragam. Banyak logika GLSL sudah terjadi pada struct, scenekit mendokumentasikan setiap variabel (meskipun tidak dapat menemukan dokumentasi yang sama lagi).

Mungkin perlu difaktorkan ulang saat kita melanjutkan tetapi sesuatu di sepanjang baris ini:

#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

Tentu saja ifdefs dan termasuk mungkin tidak akan berfungsi seperti ini. Tapi saya bisa melihatnya menjadi relatif lurus ke depan dengan pendekatan lain. Kita bisa selalu menambahkan #include <some_phase> , dengan string kosong. Sesuatu dapat membantu mengelola apa yang dipicu dengan menggunakan #ifdef s. Jadi misalnya, jika Anda memberikan logika untuk fase "vertex_transformation", shader akan menentukan apa pun yang Anda berikan. Mendokumentasikan apa yang dilakukan setiap chunk dengan GLSL akan sangat membantu. Tanpa melangkah lebih jauh ke abstraksi, saya pikir akan sangat bagus untuk memiliki peta ini tentang bagaimana potongan terstruktur sekarang.

Kami membuka pintu untuk memungkinkan peretasan materi bawaan, tetapi saya rasa kami tidak dapat memberikan "dukungan" yang tepat. Pengguna harus menyadari bahwa kemungkinan hal-hal dapat rusak.

Kami sudah memiliki hal-hal yang menentukan yang tersedia di setiap materi. https://github.com/mrdoob/three.js/issues/10764 menyarankan untuk memodifikasi THREE.ShaderLib dan kemudian mendefinisikan properti .defines pada instance dari Material . Memang, properti itu tidak berdokumen sampai saya menulis entri (tetapi Anda menyetujuinya :)), dan masih belum ditentukan .

Tetapi cara ini bekerja saat ini, dan diwarisi oleh setiap bahan, itu didukung - jika Anda memasukkan barang ke dalamnya, itu akan mempengaruhi shader untuk bahan itu . Selain itu, jika Anda mendefinisikan sesuatu yang sudah menjadi bagian dari pustaka shader, hal itu dapat merusak sesuatu .

Tidak mencoba menjadi orang yang pintar tetapi mencoba memberikan contoh. Saya kebanyakan menggunakan definisi dengan ShaderMaterial tetapi itu mempengaruhi semua materi, hanya karena cara kerja WebGLRenderer . Tidak ada logika yang mengatakan:

jika Anda jauh lebih pintar, permukaan seperti abstraksi dari materi seperti Phong maka jangan memperhitungkan definisi. Karena tiga tidak dirancang untuk menimpa potongan shadernya, dan template Phong adalah otoritas terakhir tentang apa yang perlu dilakukan GLSL, dan berfungsi untuk menyembunyikan ini dari penggunaan, masuk akal untuk tidak memiliki definisi GLSL yang mengganggu ini.

Saya tidak sepenuhnya yakin apakah itu bisa rusak, saya harus mencoba.

Dengan itu, saya ingin opsi untuk memecahkan sesuatu, jika itu memberikan banyak keuntungan. Dengan cuplikan GLSL yang cukup enkapsulasi dan kecil, saya dapat dengan mudah mengubah versi tiga saya untuk membuat peta normal secara berbeda. Mengambil normal, dan mengubahnya menjadi normal lain sangat mudah, kecuali kita mulai melakukan grafik komputer dengan cara yang sama sekali berbeda, saya tidak dapat melihat ini pernah melanggar :)

Instance adalah contoh lain:

oh saya ingin menggunakan ANGLE_INSTANCED_ARRAYS, tiga sepertinya tidak mendukungnya dengan bayangan atau bahan PBR, biarkan saya menambahkan dua baris ini, dan membuatnya bekerja untuk saya"

Saya senang melihat bahwa fitur ini akhirnya hadir di three.js :). Ya, saya juga membuat yet another material modifier PR (https://github.com/mrdoob/three.js/pull/7581) Itu sudah sangat tua sehingga kode tidak relevan lagi, tetapi pada dasarnya menyuntikkan kait seperti @mrdoob mengusulkan dan kemudian hanya menggantinya dengan kode Anda sendiri.
Saya suka ide kait yang telah ditentukan karena mudah untuk memahami apa yang Anda modifikasi karena saya yakin sebagian besar orang yang ingin menggunakan fitur ini ingin memodifikasi "sedikit" materi default alih-alih menulis ulang sepenuhnya.

Saya suka kesederhanaan PR ini tetapi jika kita mencari cara yang lebih terstruktur/bersih untuk melakukan hal-hal, saya lebih suka memiliki kamus kait untuk diganti dengan kode Anda dan cara yang lebih sederhana untuk menambahkan seragam atau kode khusus daripada hanya mengganti string.

@fernandojsg setiap kali Anda dapat memeriksa #10791, berikan umpan balik, tampaknya sangat mirip dengan apa yang Anda lakukan kecuali saya telah meletakkan kait tambahan di luar main (sesuatu seperti "pre_vertex" Anda) dan ada sedikit lebih banyak manajemen.

@fernandojsg saya sudah lupa tentang PR Anda . Saya pikir PR Anda terlihat cukup banyak tentang bagaimana saya mulai melihat ini bekerja. Alam bawah sadar?

Tidak mencoba menjadi orang yang pintar tetapi mencoba memberikan contoh. Saya kebanyakan menggunakan definisi dengan ShaderMaterial tetapi itu mempengaruhi semua materi, hanya karena cara kerja WebGLRenderer .

Saya senang perpustakaan dapat diretas dengan cara itu, tetapi kita tidak boleh menggunakan fitur penyalahgunaan secara internal untuk hal-hal yang tidak dimaksudkan. Sangat mudah untuk berakhir dengan kode rapuh seperti itu.

Membaca #7581 lagi... Di atas https://github.com/mrdoob/three.js/commit/e55898c27a843f69a47e602761c60d9bbe91ee35 kita dapat menambahkan % HOOKS % dan kemudian membuat kelas seperti ini:

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;

};

Kemudian kita bisa melakukan ini:

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

Jadi pertanyaannya adalah, kait apa yang harus kita miliki?

VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

FRAGMENT_UNIFORMS
INPUT_FRAGMENT
OUTPUT_FRAGMENT

@mrdoob Sebagai contoh, Anda dapat melihat di ini di mana kode perlu disuntikkan ke shader vertex MeshBasicMaterial untuk mendukung pembuatan instance.

Dan di sini untuk sisa bahan :)

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

VERTEX_UNIFORMS terdengar terlalu keras kepala, di mana Anda harus menambahkan fungsi, atribut, atau variasi? PRE_VERTEX atau apa?

NORMAL_MAP pasti dibutuhkan

vert:

PRE_VERTEX
VERTEX
TRANSFORMED_VERTEX
PROJECTED_VERTEX

NORMAL
TRANSFORMED_NORMAL

UV
fragment:

PRE_FRAGMENT
INPUT_FRAGMENT
LIGHT
NORMAL

OUTPUT_FRAGMENT

VERTEX_UNIFORMS terdengar terlalu keras kepala, di mana Anda harus menambahkan fungsi, atribut, atau variasi? PRE_VERTEX atau apa?

Hmm, berpendapat? Bagaimana?

attribute vec4 aMyAttribute; bukan seragam :)

float myRand( vec4 foo ) { /*logic*/ return vec4(bar,baz)} bukan seragam

varying vec3 vMyVarying; juga bukan seragam

baik tidak jamak, atau tidak seragam sama sekali

Apa kasus penggunaan untuk atribut "menyuntikkan"?
Bukankah seharusnya Anda menggunakan geometry.addAttribute( 'aMyAttribute', ... ) sebagai gantinya?

Saya tidak tahu Anda tidak perlu menyatakannya. Masih menyisakan variasi dan fungsi?

Masih menyisakan variasi dan fungsi?

Poin bagus.

Anda benar-benar tidak perlu mendeklarasikan atribut sama sekali lagi di GLSL? Saya pikir itu bekerja lebih seperti uniforms , Anda mendeklarasikan dalam GLSL tetapi tidak harus mengetiknya di JS.

Saya pikir daftar ini bisa tumbuh cukup besar cukup cepat, ketika datang ke hal-hal tertentu kode menjadi sangat tersebar. Salah satu cara melakukan peta normal melibatkan dua atribut tambahan, variasi dan logika transformasi di kedua vert dan frag.

Saya suka perubahan terbaru itu masih sederhana dan elegan. Saya setuju dengan @pailhead bahwa kita membutuhkan tempat untuk meletakkan kode dalam lingkup global. Di https://github.com/mrdoob/three.js/pull/7581 Saya memiliki preMainVertex preMainFragment untuk disuntikkan secara global sehingga Anda dapat menggunakannya untuk memvariasikan dan berfungsi. Mungkin nama seperti globalVertex/Fragment atau serupa bisa lebih baik.

GLOBAL_VERTEX dan GLOBAL_FRAGMENT terdengar bagus

persis bagaimana saya menamai mereka di PR lain 👍

@pailhead Tapi sekarang Anda tahu rahasia untuk menggabungkan kode Anda: Buka saja PR, biarkan di sana beberapa saat, lalu tunggu @mrdoob akhirnya berpikir untuk melakukan sesuatu yang serupa, tunggu PR-nya lalu beri komentar di sana dengan referensi yang tepat cara yang sama seperti yang Anda lakukan pada PR Anda sehingga dia bisa berpikir itu adalah idenya, dia akan menambahkannya, gabungkan ... untung!

image

Sekarang serius saya suka juga nama-nama itu dan dengan itu saya yakin kita siap untuk pergi, atau apakah saya melewatkan sesuatu? Saya akan mencoba mengonversi contoh dari PR saya ke kode baru ini;)

Bagian tentang kode yang masuk baik-baik saja, garis waktunya agak tidak aktif. #7581 disarankan hampir dua tahun lalu. Tidak dirujuk di https://github.com/mrdoob/three.js/issues/10789 , jadi mungkin hilang dari radar. Saya akan menabraknya atau memotongnya.

Bahkan ketika mengimpor modul dan memilih tiga buah ceri Anda sendiri, orang tidak terlalu senang menggunakan modul inti non-resmi. Jauh lebih mudah untuk mengatakan, "hei, ada paket npm yang menangani foo". Makanya saya rasa PR seperti ini penting, tiga saja tidak cukup fleksibel.

laba!

Keuntungan apa? 😆

Bagian tentang kode yang masuk baik-baik saja, garis waktunya agak tidak aktif. #7581 disarankan hampir dua tahun lalu. Tidak dirujuk di https://github.com/mrdoob/three.js/issues/10789 , jadi mungkin hilang dari radar. Saya akan menabraknya atau memotongnya.

Apa? Anda tidak memeriksa semua PR terbuka sebelum membuat yang baru? #trololol

benar :)

ok hubungi dalam daftar dan mari kita dapatkan ini secepatnya :D

Akankah shader ini (misalnya):

#include <shadowmap_pars_vertex>

void main() {

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

}

terlihat seperti ini:

#include <shadowmap_pars_vertex>

void main() {

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

}

Umm saya tidak yakin apakah mengganti semuanya atau hanya menyuntikkan kode di sekitar meninggalkan beberapa perilaku saat ini:

#include <shadowmap_pars_vertex>

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

Pendekatan awal saya adalah untuk menyuntikkan kode tambahan meninggalkan sisa termasuk tidak tersentuh.

Jadi % hook % hanya untuk kait yang sebenarnya, jika seseorang ingin mengganti <begin_vertex> mereka masih harus mencarinya dalam sebuah string, mis. tidak akan ada pengait begin_vertex ?

Dalam PR saya, saya hanya ingin sedikit memodifikasi perilaku material, biasanya melakukan beberapa koreksi warna setelah fs dieksekusi sepenuhnya atau transformasi vs yang serupa dengan yang ditunjukkan @mrdoob dalam contohnya.
Tapi ya, jika kita ingin sepenuhnya mengganti setiap penyertaan, kita harus menggunakan pengait alih-alih penyertaan dan menambahkan logika untuk menyuntikkan penyertaan itu sendiri jika Anda tidak mengikat pengait.

Saya ingin melakukan itu untuk normal (tidak menggunakan turunan, dan menggunakan informasi TBN aktual dari 3ds max atau maya atau di mana pun di mana peta normal dihasilkan). Mengadaptasi MeshPhongMaterial untuk bekerja dengan instans juga memerlukan penggantian beberapa bongkahan sekaligus, tetapi ini terasa seperti kasus tepi.

Itu adalah dua yang dapat saya pikirkan, saya ingin tahu apakah ada lagi.

Jadi % hook % hanya untuk kait yang sebenarnya, jika seseorang ingin mengganti <begin_vertex> mereka masih harus mencarinya dalam sebuah string, mis. tidak akan ada pengait begin_vertex ?

Anda bisa mengganti keduanya. Kode saat ini sudah memungkinkan Anda mengganti apa pun. %HOOK% yang untuk kenyamanan.

Seperti yang saya sebutkan di #11562 , bagaimana dengan konsep potongan shader yang dapat diganti?

Contoh:

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

Ini adalah bagaimana sederhana dapat kode transisi antara beberapa tekstur pada 1 bahan.

Itu dapat dengan mudah diimplementasikan dan menghindari pembuatan kode yang tidak perlu.

@sasha240100

https://github.com/mrdoob/three.js/pull/10791 terlihat persis seperti yang Anda sarankan, kecuali parts disebut shaderChunks dan tidak menganggapnya sebagai argumen.
:roll_eyes:

Anda juga tidak memerlukan kamus frag{} , karena potongannya sudah ditandai. Semuanya memiliki _fragment dan _vertex .

Yang ini melakukan hal yang sama kecuali Anda harus mencari melalui string untuk menemukan apa yang perlu Anda ganti.

@pailhead Ya, mari kita lepaskan frag: {} . Saya menggunakannya sebagai argumen karena itu harus diteruskan sebelum shader material digabungkan. Dalam hal ini kita hanya dapat menghindari mengganti bagian yang ada setelah penggabungan.

Kami sebenarnya dapat menambahkan keduanya: sebagai argumen dan sebagai properti. Seperti yang dilakukan color

Di mana ini sekarang? Saya melihat contohnya melakukan .replace() dan saya tidak melihat kait/potongan yang dapat diganti? @mrdoob

Belum bisa kembali ke ini.

Apakah Anda ingin melakukan PR untuk hal % hook % ?

Apakah Anda ingin materi diperpanjang, dari komentar di atas?

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;

};

Memilih tempat untuk kait dan mendokumentasikannya mungkin sebenarnya tidak sepele? Saya mungkin akan mengganti hooks diteruskan dengan chunks dan mencari yang disertakan, dengan tambahan global_vert dan global_frag untuk saat ini? Yang sekarang saya tulis, sepertinya itu bukan tanggung jawab tiga orang, tetapi di sisi lain itu akan mematikan THREE.ExtendedMaterial untuk % hook % di masa depan?

Saya tidak tahu apakah Anda mengetahui ekstensi THREE.BAS. Ini menggunakan placeholder dalam materi asli yang dapat diakses dari materi yang disesuaikan sebagai array, seperti:

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

Saya suka fakta bahwa ini didasarkan pada konvensi sederhana: vertexInit dan fragmentInit akan berada di atas fungsi main() misalnya. vertexParameters dan fragmentParameters berada di atas shader, untuk mendefinisikan uniform atau atribut. vertexFunctions dan fragmentFunctions berada sebelum fungsi main(). Dan seterusnya untuk normal, posisi dll...

Seperti yang Anda lihat di posisi, Anda mengubah variabel transformed untuk mengubah posisi akhir. Hal serupa untuk normal ( objectNormal ) atau warna ( diffuseColor ) di shader fragmen, misalnya.

Beginilah tampilan materi Phong, hanya menambahkan placeholder: https://github.com/zadvorsky/three.bas/blob/master/src/materials/PhongAnimationMaterial.js

Hai teman-teman, saya mencoba mengikuti obrolan tetapi jujur ​​​​saya tersesat karena saya berasal dari Front end dan bukan pengembang game.

Saya mencoba memuat MTL dan saya mendapatkan:

material.onBeforeCompile.toString() tidak terdefinisi.

File MTL saya awalnya menunjuk ke file .psd dan saya mengubahnya ke salah satu dari beberapa png yang berisi folder yang sama (Normal) Saya kira tidak mungkin untuk memuat psd, tetapi bagaimanapun juga dengan psd gagal.

# 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

Keluaran konsol adalah:

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)

dan kode yang saya gunakan terutama:

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

}

Apakah ini masalah dalam model? di Tiga? di mtl loader? itu punya solusi?

Bantuan apa pun akan dihargai.
Terima kasih.

Apakah kita harus melakukan sesuatu seperti material.shader = shader di material.onBeforeCompile(shader=>...) ?

Saya pikir itu terasa agak kikuk, mungkin perlu meninjau kembali saran lain di beberapa titik :)

@mrdoob

Apakah mungkin shader yang diperluas ini tidak di-cache dengan benar? Saya mencoba menggunakan dua bahan berbeda, yang satu dithering_fragment berubah, yang lain tidak. Ketika saya menambahkan keduanya ke adegan, tampaknya hanya satu tanpa potongan dithering_fragment yang digunakan.

Saya tidak dapat membuatnya kembali dengan biola ini
https://codepen.io/anon/pen/KQPBjd

Tetapi dengan kode ini dalam contoh saya

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

}
*/

Tapi tidak salah jika saya menambahkan materi lain ke adegan yang berbagi termasuk dan mendefinisikan lainnya.

Hal ini karena ini:

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

Saya memiliki fungsi yang sama dengan beberapa logika yang disediakan untuk kedua materi, variabel untuk logika berada di luar cakupan panggilan balik, sehingga kedua materi saya mendapatkan hash yang sama?

array.push( material.onBeforeCompile( obtainShaderSomehow ) )

https://github.com/mrdoob/three.js/pull/10791 menyelesaikannya seperti ini:

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

Apakah Anda akan mempertimbangkan kembali permintaan tarik lainnya jika saya menghapus placeholder/kait global_vertex dan global_fragment ? Dengan itu menyentuh banyak file, tanpa mereka, itu lebih sedikit kode. Bukan tiga baris, tetapi hash dengan benar :)

Komentar #41 di biola ini https://codepen.io/anon/pen/KQPBjd
dan bahan mesh lain dalam adegan akan berubah.

Apakah ini sudah termasuk dalam rilis terbaru, atau semuanya masih dalam pengembangan?

@mrdoob Saya pikir sulit (atau tidak mungkin) untuk membuat cerita bersambung Material yang diretas dengan .onBeforeCompile() . Apakah menurut Anda pengguna harus menggunakan ShaderMaterial jika mereka ingin material bawaan yang dimodifikasi secara fungsional (render, copy, clone, serialize, dan sebagainya)?

_Silakan baca yang berikut ini dengan cara yang paling baik, konstruktif, dan tidak konfrontatif_ :)

@takahirox mengapa ini terus diklaim?

Saya pikir sulit (atau tidak mungkin) untuk membuat cerita bersambung Materi yang diretas

Bila Anda "hack" bahan dengan onBeforeCompile Anda hanya menambahkan seragam yang sama dengan bahan lainnya. Tidak ada perbedaan. Jika Anda dapat membuat serial material.color mengapa Anda tidak dapat membuat serial material.userData.myColor ?

Saya tidak mencoba untuk memulai perkelahian, dinding teks atau apa pun. Dalam istilah sesingkat mungkin yang paling sederhana, dapatkah Anda menjelaskan, atau setidaknya mengarahkan saya ke arah yang benar mengapa itu tidak mungkin atau sulit? Saya terbuka untuk kemungkinan bahwa saya kehilangan sesuatu yang jelas, tetapi jika saya ingin memahami apa itu :)

Dari sampel yang sangat kecil, yang dihasilkan oleh artikel , eksperimen ini sepertinya tidak ingin menggunakan pendekatan ShaderMaterial .


Ini baru terpikirkan? Apakah ada semacam tes yang membuktikan bahwa ini sulit atau tidak mungkin? Suka:

runSerializationTest( someMaterialWithOnBeforeCompile ).expect( something )

Karena metode onBeforeCompile dapat berisi kode yang sangat arbitrer, tentu saja tidak mungkin untuk membuat serial callback itu dengan kuat. Misalnya, metode ini dapat membuat XMLHttpRequest sinkron dan melakukan sesuatu dengan hasilnya. 😅

Namun... untuk cara pemakaiannya secara praktis (untuk menambal shader) saya setuju bukan tidak mungkin. Tetapi API yang mudah (misalnya fn.toString() ) dapat diretas, dan API yang kuat lebih kompleks.

Saya masih tidak mengerti masalahnya. Bagaimana dengan sudut pandang ini:

Ambil rN yang memiliki MeshPhongMaterial dan tidak memiliki MeshStandardMaterial .

Anda dapat membuat serial MeshPhongMaterial yaitu. tulis JSON seperti ini:

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

Ambil rN+1 yang memiliki kedua bahan:

Anda masih dapat membuat serial keduanya dengan cara yang sama:

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

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

Tidak ada tempat Anda membuat serial GLSL dari MeshStandardMaterial .

Dengan cara yang sama, membuat serialisasi materi yang diperluas .

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

Deserialisasi:

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

Apa yang kulewatkan di sini?


Seperti onBeforeCompile ... buat cerita bersambung panggilan balik itu.

Mengapa, mengapa Anda mempertimbangkannya :) Saya pikir ini adalah inti dari kebingungan saya, mengapa ada pemikiran yang diberikan pada kait ini, itu bukan bagian dari definisi material, deskripsi, apa pun. Ini ada hubungannya dengan mesin, plugin, aplikasi, dll, bukan datanya.

Contoh yang Anda berikan di atas terlihat bagus bagi saya, itu hanya mengharuskan kode pengguna yang menghapus serial materi tahu tentang keberadaan HamburgerPhongMaterial? Saya tidak punya masalah dengan itu, itu bahkan tidak memerlukan perubahan API jika Anda mengambil pendekatan yang mirip dengan #14099 dan menimpa keJSON dan dariJSON.

Mungkin kita telah menafsirkan pertanyaan @takahirox secara berbeda di sini?

...sulit (atau tidak mungkin) untuk membuat serialisasi Materi yang diretas dengan .onBeforeCompile(). Apakah menurut Anda pengguna harus menggunakan ShaderMaterial jika mereka ingin material bawaan yang dimodifikasi secara fungsional (render, copy, clone, serialize, dan sebagainya)?

Saya akan mencoba memisahkannya:

  • jika pengguna memanggil toJSON() pada MeshStandardMaterial biasa, di mana GLSL yang telah dimodifikasi onBeforeCompile , perubahan pada GLSL tidak akan secara otomatis diserialisasi.
  • jika pengguna membuat serial MeshStandardMaterial dengan beberapa properti tambahan yang menunjukkan perubahan pada GLSL, dan memuatnya dengan kode khusus yang tahu apa yang harus dilakukan dengan properti tambahan itu, maka tentu saja tidak masalah.
  • berbagai pilihan lain (ShaderMaterial, NodeMaterial, glTF's KHR_techniques_webgl , ...) tersedia untuk membuat serialisasi materi kustom arbitrer.

Jika kasus yang kita bicarakan bukan salah satunya, maka saya kira saya salah paham di sini. Jika ini tentang #14099, saya (masih) mendukung penggabungan itu.

Hmmm, saya sebenarnya tidak membedakan antara dua yang pertama. Saya mencoba memikirkan sesuatu yang tidak memerlukan seragam, bisa jadi gl_FragColor.xyz /= gl_FragColor.a; misalnya. Tetapi saya tidak dapat membayangkan skenario di mana Anda ingin membuat cerita bersambung seperti ini ( bersama dengan materinya? ). Sepertinya itu akan termasuk dalam lingkup beberapa penyaji efek atau sesuatu, bukan data material.

materials = loadSerializedMaterials()

effect = new Effect()

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

Contoh kedua adalah apa yang ada dalam pikiran saya selama ini. Bersama dengan berbagai masukan, buatlah petunjuk bersambung tentang apa yang harus dilakukan dengan masukan tersebut userData.extension === 'hamburger' .

Saya pikir saya menulis dengan buruk. Yang ingin saya sebutkan adalah

  • Materi Bawaan yang diretas dengan .onBeforeCompile() tidak disalin, dikloning, diserialisasikan dengan benar di API yang sama dengan materi bawaan'. Ini ditangani sebagai materi yang tidak diretas.
  • Jika pengguna menginginkan hasil yang benar, mereka membutuhkan pekerjaan ekstra di sisi pengguna.
  • Jika pengguna menginginkan materi bawaan yang diretas dengan API inti Three.js tanpa kerja ekstra dari sisi pengguna, mereka harus memikirkan opsi lain, seperti ShaderMaterial .

Pertanyaan saya hanya untuk kebijakan .onBeforeCompile() daripada untuk kasus tertentu. Saya ingin mengklarifikasi batasan dan menambahkan catatan di dokumen.

Jika saya lupa tentang three.js dan seluruh sistem rendering, saya pikir saya mengerti maksudnya. Sebagai objek generik, jika memiliki .clone() Anda ingin dapat mengkloning dan memiliki harapan yang konsisten. Masalahnya adalah kedua solusi (klon ini bukan mengkloningnya) tampaknya memiliki argumen yang valid. Anda tidak mengkloning pendengar, tetapi Anda juga agak mengharapkan sesuatu yang memengaruhi gambar menjadi konsisten.

Saya masih berpikir bahwa proposal saya dari atas adalah cara yang valid untuk melakukan ini, karena saya mengalami masalah yang sama di area lain (seperti berbagi buffer kedalaman antar target juga dapat diselesaikan dengan ownThing || outsideThing .

Alih-alih men-cache fungsi, yang tidak berfungsi, pada dasarnya Anda akan men-cache userData tetapi sebagian darinya. Tidak masuk akal untuk men-cache userData.hamburger: 'rare' tetapi melakukan userData.effectOn: true". The ownInput | sendiriChunks | ownWhatever` akan menyelesaikan masalah ini.

Ini juga memecahkan masalah:

Karena metode onBeforeCompile dapat berisi kode yang sangat arbitrer, tentu saja tidak mungkin untuk membuat serial callback itu dengan kuat.

Sudah lama, orang telah menggunakan onBeforeCompile kita harus tahu sekarang jenis kode arbitrer apa yang dapat ditemukan di sana. Saya pikir itu hanya beroperasi pada objek shader yang dilewatkan. Anda mengubah objek itu, tetapi hanya mutasi yang masuk akal dengan ShaderMaterial akan berpengaruh. Mengatur beberapa barang gl sendiri mungkin akan ditimpa, dan itu adalah satu-satunya hal yang muncul di pikiran saya.

Ini pada dasarnya adalah langkah pre three parse , di mana Anda mendapatkan kesempatan untuk mengurai shader sendiri bersama dengan antarmuka uniforms:{} wajib, karena Anda tidak memilikinya di Material ( defines:{} di sisi lain yang Anda lakukan, dan keduanya adalah saudara kandung di ShaderMaterial ).

Bagaimana Anda melakukannya, yaitu apa pun kode arbitrernya, tidak masalah. Itu tidak mengembalikan apa pun, tetapi memutasi shader:{} dan penyaji kemudian menggunakannya.

Pendekatan yang saya sarankan:

  1. Tambahkan ownThing ke berbagai kelas. THREE.WebGLRenderTarget dapat memiliki .ownStencilDepthBuffer . Di sini, seperti yang saya sarankan di atas, itu akan menjadi beberapa versi ownGLSL:{} ownInput | ownUniforms . Anda menghapus kebutuhan akan kode arbitrer untuk mengubah objek shader:{} :

Diberikan input seperti ini yang berasal dari dalam WebGLRenderer (super private):

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

nuklir ini:

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)

Gunakan ini:

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. Buat WebGLRenderer tidak tahu apa-apa selain ShaderMaterial .

Di mana pun Anda akan memiliki

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

Memiliki

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

const mat = chunkMaterial.compile()

tl:dr dari hal-hal di atas adalah pengguna tidak peduli dengan titik waktu tertentu ketika "kompilasi" (parse) terjadi . Saya pikir mereka lebih peduli tentang apa yang terjadi, dan apa yang terjadi dapat diisolasi ke segelintir jika tidak satu kasus penggunaan in: shaderString, out: shaderString . Alasan mengapa itu harus dilakukan pada titik tertentu dalam pipa yang menurut saya layak untuk diselidiki dan dipahami. Tampaknya menghalangi banyak hal daripada membantu.

@mrdoob Saya pikir sulit (atau tidak mungkin) untuk membuat cerita bersambung Material yang diretas dengan .onBeforeCompile() . Apakah menurut Anda pengguna harus menggunakan ShaderMaterial jika mereka ingin material bawaan yang dimodifikasi secara fungsional (render, copy, clone, serialize, dan sebagainya)?

Saya tidak berharap (dianggap) penggunaan onBeforeCompile() untuk diserialisasi...

Saya baru saja digigit dengan menggunakan onBeforeCompile. Saya memiliki kelas pembantu shader yang memungkinkan saya membuat modifikasi pada shader bawaan dengan cara yang ditentukan. Saya menggunakan metode onBeforeCompile untuk melakukan ini. Rupanya, seperti yang dinyatakan dalam komentar pertama, WebGLProgram menggunakan onBeforeCompile.toString() sebagai hash. Tapi karena fungsi saya generik (mengganti bagian dari vertex dan fragment shader dengan variabel) onBeforeCompile.toString() tidak terlihat berbeda untuk shader yang berbeda. Ini berarti semua shader saya yang berbeda di-cache sebagai program yang sama. Panggilan eval dan uuid dan sekarang semua fungsi saya terlihat berbeda. Butuh selamanya untuk mencari tahu ini.

@donaldr Lihat https://github.com/mrdoob/three.js/issues/13192. Saya pikir akan baik untuk mendokumentasikan keterbatasan ini.

dapatkah Anda membagikan bagaimana Anda menggunakan eval? Aku sudah memikirkannya tapi sepertinya terlalu kotor. Anda pada dasarnya dapat menambahkan const fdscxnmdrek435rkjl ke bagian atas badan dan kemudian eval?

Ini berarti semua shader saya yang berbeda di-cache sebagai program yang sama. Panggilan eval dan uuid dan sekarang semua fungsi saya terlihat berbeda. Butuh selamanya untuk mencari tahu ini.

Kedengarannya sangat membuat frustrasi, maaf. Jika modifikasi yang Anda perlukan terlihat masuk akal di perpustakaan itu sendiri, silakan buka edisi juga.

silahkan merasa dipersilakan untuk membuka masalah, juga.

Apakah Anda mempertimbangkan untuk membuka kembali yang sudah ada? #13192 tampaknya sama tetapi ditutup :crying_cat_face: Akan sangat menyenangkan jika pekerjaan di sekitar diposting di sana jika itu dianggap sebagai fitur.

Hmmm...

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

^ Apakah ini akan berhasil? Saya tidak pernah menggunakan eval .

Pengenalan Material.customProgramCacheKey() melalui #17567 memastikan bahwa pengembang sekarang memiliki kemungkinan bahwa program shader tidak dibagikan untuk materi yang dimodifikasi melalui onBeforeCompile() dengan pernyataan bersyarat.

Harap diskusikan masalah atau peningkatan lain yang ada dalam konteks onBeforeCompile() di utas baru (seperti #13446).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat