Javascript-форум (https://javascript.ru/forum/)
-   Node.JS (https://javascript.ru/forum/node-js-io-js/)
-   -   Загрузка текстур "useTexture" react-three/drei (https://javascript.ru/forum/node-js-io-js/85239-zagruzka-tekstur-usetexture-react-three-drei.html)

Raadsert 23.05.2023 18:41

Загрузка текстур "useTexture" react-three/drei
 
Здравствуйте.
Никак не могу понять в чём разница между функцией "useTexture" из "react-three/drei", и стандартного "TextureLoader" из "Three".

Пробовал их обоих в деле, но "TextureLoader" по какой-то причине не может сразу вернуть данные в параметре data. В то же время "useTexture" сразу выдаёт все данные корректно, хотя тоже работает на основе "TextureLoader".

К примеру если попробовать вот этот код:
const usetex = useTexture(url)
const texload = new THREE.TextureLoader().load(url)

console.log(usetex.source.data); // Тут всё будет в порядке и вернётся текстура
console.log(texload.source.data); // А тут вернёт null


Подскажите пожалуйста, в чём между ними разница?

Aetae 23.05.2023 23:23

Не "сразу".
Загрузка текстур - асинхронная операция. Она начинается сейчас - заканчивается когда-нибудь потом.
const usetex = useTexture(url) - запускает загрузку при первом обращении и дальше использует механизмы react для сохранения и обновления состояния по мере загрузки.
const texload = new THREE.TextureLoader().load(url) - при каждом обновлении компонента заново запускает загрузку текстуры, но никаким не дожидается результата.

Raadsert 24.05.2023 00:28

Цитата:

Сообщение от Aetae (Сообщение 552033)
Не "сразу".
Загрузка текстур - асинхронная операция. Она начинается сейчас - заканчивается когда-нибудь потом.
const usetex = useTexture(url) - запускает загрузку при первом обращении и дальше использует механизмы react для сохранения и обновления состояния по мере загрузки.
const texload = new THREE.TextureLoader().load(url) - при каждом обновлении компонента заново запускает загрузку текстуры, но никаким не дожидается результата.

А возможно ли как то исправить этот недостаток "TextureLoader" не используя "react"?

Aetae 24.05.2023 00:37

Зайти на сайт Three.js и почитать документацию в которой написано как надо использовать TextureLoader.
Тыкнуть тут вверху в Учебник и почитать как работать с асинхронностью.

Raadsert 24.05.2023 12:39

Цитата:

Сообщение от Aetae (Сообщение 552035)
Зайти на сайт Three.js и почитать документацию в которой написано как надо использовать TextureLoader.
Тыкнуть тут вверху в Учебник и почитать как работать с асинхронностью.

Асинхронность не даёт возможности имитировать такую-же работу как "useTexture". Если с начала нужно загрузить текстуры и только после этого выполнять весь код, то что, весь этот код в колбек промиса запихивать?

Aetae 24.05.2023 18:05

Raadsert,

Короткий ответ: да.
Классический ответ: вынести функцию отдельно, и передавать её в коллбэк, чтоб не было лесенки.
Современный ответ: использовать async await чтобы скрыть за ними сложность и получить "плоский" код.


useTexture использует механизмы React-фреймворка. Она не "останавливает" код и ничего не ждёт. Она после того как асинхронная загрузка выполнится внутри себя заставляет компонент отрисоваться повторно, при этом возвращая результат.
Условно:
1ый рендер компонента:
const texture = useTexture(url); // texture === null;

2ый рендер компонента триггернутый из useTexture после загрузки:
const texture = useTexture(url); // texture === текстура;

3ий и далее рендер компонента триггернутый разными причинами:
const texture = useTexture(url); // texture === текстура;

Так работает React.

Raadsert 24.05.2023 18:07

Цитата:

Сообщение от Aetae (Сообщение 552042)
Raadsert,

Короткий ответ: да.
Классический ответ: вынести функцию отдельно, и передавать её в коллбэк, чтоб не было лесенки.
Современный ответ: использовать async await чтобы скрыть за ними сложность и получить "плоский" код.

Но ведь... В случае с реактом ничего подобного не практикуется...

Aetae 24.05.2023 18:22

Raadsert, дополнил ответ. Магии не существует. Синхронный код не может быть асинхронным.

Raadsert 24.05.2023 18:50

Цитата:

Сообщение от Aetae (Сообщение 552044)
Raadsert, дополнил ответ. Магии не существует. Синхронный код не может быть асинхронным.

А что вы подразумеваете под отрисовкой? Повторную загрузку текстур?

Aetae 24.05.2023 18:56

Raadsert, нет, повторную отрисовку реакт-компонента. Сам почитай про реакт и как он работает, мне неохота тут переписывать документацию реакта или устраивать лекцию.

Raadsert 27.05.2023 18:33

Цитата:

Сообщение от Aetae (Сообщение 552050)
Raadsert, нет, повторную отрисовку реакт-компонента. Сам почитай про реакт и как он работает, мне неохота тут переписывать документацию реакта или устраивать лекцию.

Но если в "useTexture" происходит асинхронная загрузка то каким образом у него получается вернуть хоть какие-то значения? Ведь асинхронная функция всегда будет возвращать промис.
Кроме того, не могу понять логику того как у "useTexture" получается делать то, чего не получается у "TextureLoader", он ведь основывается на нём. Хотелось бы разобраться, каким способом реакту, получается устранить недостатки "TextureLoader", но к сожалению не нашёл ни одной статьи на эту тему.

Aetae 27.05.2023 19:15

А ты ищи статьи по реакту в целом, а не по твоей херне.
В рекате функция useTexture вызывается стопицот раз. В первый она нихрена не возвращает, только запускает внутри где-то загрузку и ставит флаг что загрузка пошла. Пока загрузка идёт - она всё ещё нихрена не возвращает. Когда загрузка закончилась, она снова внутри ставит флаг что загрузка завершена и следующий вызов уже возвращает загруженное.

Вот тебе утрированный код как это работает:
<script>
const условныеПотрохаРеактКомпонета = {};

function условныйРеакт(компонент) {
  // вместо статического интервала тут реакции на события, но не суть
  setInterval(() => { 
    document.body.innerHTML = компонент();
  }, 1000);
}

function загрузитьАсинхронно(url, колбэк){
  setTimeout(() => колбэк('текстура, ёба'), 5000);
}



function useTexture(url) {
  if(условныеПотрохаРеактКомпонета.загружено)
    return условныеПотрохаРеактКомпонета.текстура;
  if(условныеПотрохаРеактКомпонета.грузится)
    return 'нихуя, жди';

  условныеПотрохаРеактКомпонета.грузится = true;
  
  загрузитьАсинхронно(url, (result) => {
    условныеПотрохаРеактКомпонета.грузится = false;
    условныеПотрохаРеактКомпонета.загружено = true;
    условныеПотрохаРеактКомпонета.текстура = result;
  });
  
  return 'нихуя, жди'
}

let i = 0;

function нарисоватьХуйнюНаЭкранеКомпонент() {
  const texture = useTexture('http://хуй.пизда');
  return '<b>' + texture + '</b> (нарисовано раз ' + ++i + ')';
}


условныйРеакт(нарисоватьХуйнюНаЭкранеКомпонент);
</script>

Если даже это не поможет, то медицина тут бессильна.

Raadsert 28.05.2023 18:02

Цитата:

Сообщение от Aetae (Сообщение 552105)
А ты ищи статьи по реакту в целом, а не по твоей херне.
В рекате функция useTexture вызывается стопицот раз. В первый она нихрена не возвращает, только запускает внутри где-то загрузку и ставит флаг что загрузка пошла. Пока загрузка идёт - она всё ещё нихрена не возвращает. Когда загрузка закончилась, она снова внутри ставит флаг что загрузка завершена и следующий вызов уже возвращает загруженное.

Вот тебе утрированный код как это работает:
<script>
const условныеПотрохаРеактКомпонета = {};

function условныйРеакт(компонент) {
  // вместо статического интервала тут реакции на события, но не суть
  setInterval(() => { 
    document.body.innerHTML = компонент();
  }, 1000);
}

function загрузитьАсинхронно(url, колбэк){
  setTimeout(() => колбэк('текстура, ёба'), 5000);
}



function useTexture(url) {
  if(условныеПотрохаРеактКомпонета.загружено)
    return условныеПотрохаРеактКомпонета.текстура;
  if(условныеПотрохаРеактКомпонета.грузится)
    return 'нихуя, жди';

  условныеПотрохаРеактКомпонета.грузится = true;
  
  загрузитьАсинхронно(url, (result) => {
    условныеПотрохаРеактКомпонета.грузится = false;
    условныеПотрохаРеактКомпонета.загружено = true;
    условныеПотрохаРеактКомпонета.текстура = result;
  });
  
  return 'нихуя, жди'
}

let i = 0;

function нарисоватьХуйнюНаЭкранеКомпонент() {
  const texture = useTexture('http://хуй.пизда');
  return '<b>' + texture + '</b> (нарисовано раз ' + ++i + ')';
}


условныйРеакт(нарисоватьХуйнюНаЭкранеКомпонент);
</script>

Если даже это не поможет, то медицина тут бессильна.

Сколько не читал статей, все пишут о том как использовать функции а не как они работают.

Получается, реакт создаёт некие события, которые повторно проходятся по функции и обновляют переменные? А почему тогда при первом обходе кода, приложением, оно не выдаёт ошибки на пустые или неподходящие переменные?

Aetae 28.05.2023 19:05

Raadsert, надо смотреть конкретный код, очевидно. Может там есть какие-то проверки на такой случай, может по умолчанию оно возвращает не пустоту, а какую-нить однопиксельную пустую текстуру...

Raadsert 29.05.2023 15:39

Цитата:

Сообщение от Aetae (Сообщение 552117)
Raadsert, надо смотреть конкретный код, очевидно. Может там есть какие-то проверки на такой случай, может по умолчанию оно возвращает не пустоту, а какую-нить однопиксельную пустую текстуру...

Понял. Спасибо большое за помощь.

Raadsert 30.05.2023 19:25

Цитата:

Сообщение от Aetae (Сообщение 552117)
Raadsert, надо смотреть конкретный код, очевидно. Может там есть какие-то проверки на такой случай, может по умолчанию оно возвращает не пустоту, а какую-нить однопиксельную пустую текстуру...

Ещё вопрос. А каким образом реакт создаёт новое событие которое отлавливает состояние загрузки из useTexture?

Aetae 30.05.2023 20:08

Raadsert, для хранения состояния там используется useState, который возвращает значение и функцию для изменения этого значения.
const [value, setValue] = useState(...);
и тут всё просто: функция setValue помимо того что устанавливает новое значение, заодно ещё и триггерит обновление компонента после этого.(опять же - условно, там всё чуть сложней для оптимизации, но суть именно такая)

Это setValue всегда одно для каждого useState(генерится один раз при первом обращении) и может быть вызвано когда угодно откуда угодно.

Raadsert 31.05.2023 16:39

Цитата:

Сообщение от Aetae (Сообщение 552190)
Raadsert, для хранения состояния там используется useState, который возвращает значение и функцию для изменения этого значения.
const [value, setValue] = useState(...);
и тут всё просто: функция setValue помимо того что устанавливает новое значение, заодно ещё и триггерит обновление компонента после этого.(опять же - условно, там всё чуть сложней для оптимизации, но суть именно такая)

Это setValue всегда одно для каждого useState(генерится один раз при первом обращении) и может быть вызвано когда угодно откуда угодно.

А вы не знаете, можно ли как-то посмотреть код функций "useLoader, useThree" из "@react-three/fiber"? Заходил на гитхаб но файлов и функций с подобными именами там нет.

Nexus 31.05.2023 17:11

Цитата:

Сообщение от Raadsert
Заходил на гитхаб но файлов и функций с подобными именами там нет.

Плохо смотрели: useThree, useLoader чуть ниже.


Часовой пояс GMT +3, время: 16:20.