Несколько текстур вывода из одной программы

Я пытаюсь научиться делать несколько выходов из одной и той же программы в WebGL2, используя возможности gl.drawBuffer().

Я просмотрел книгу "Руководство по программированию OpenGL ES 3.0", глава 11, где перечислено, что необходимо для того, чтобы иметь место множественный вывод. Однако пример источника шейдера очень тривиален и выводит только постоянные значения.

Хотелось бы узнать, есть ли у кого-нибудь лучший пример? или можно ли объяснить, что случилось с изменением координат текстуры? В обычном коде шейдера я бы использовал это, чтобы найти значения данных из моих входных данных и записать их. Теперь, несмотря на наличие нескольких макетов, как меняющиеся координаты текстуры будут соответствовать каждому макету? Что происходит с размерами моего viewPort? какой выходной текстуре соответствует это?

Вот несколько шагов в моем понимании:

  1. Создайте массив присоединения Color GL_COLOR_ATTACHMENT0, ...
  2. Создайте объект фреймбуфера для каждого вывода
  3. Создание текстур вывода
  4. Для каждого FB:

    • BindFramebuffer
    • BindTexture
    • Свяжите текстуру с FBO: frameBufferTexture2D (..., color_attchment_from_step1)
  5. вызвать drawBuffers, передавая массив прикрепления цветов

Внутри шейдера получаются следующие выходные значения:

layout(location = 0) out vec4 fragData0;

layout(location = 1) out vec4 fragData1;
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
2 155
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужен только один объект фреймбуфера. Вы прикрепляете к нему все текстуры. Итак, ваши шаги будут

  1. Создайте объект фреймбуфера и BindFramebuffer
  2. Создание текстур вывода
  3. Для каждой текстуры:
  4. Свяжите текстуру с FBO: frameBufferTexture2D (...)
  5. Создайте массив присоединения Color GL_COLOR_ATTACHMENT0, ...
  6. вызвать drawBuffers, передавая массив прикрепления цветов

function main() {
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) {
    return alert("need WebGL2");
  }
  const vs = `
  #version 300 es
  void main() {
    gl_PointSize = 300.0;
    gl_Position = vec4(0, 0, 0, 1);
  }
  `;

  const fs = `
  #version 300 es
  precision mediump float;

  layout(location = 0) out vec4 outColor0;
  layout(location = 1) out vec4 outColor1;
  layout(location = 2) out vec4 outColor2;
  layout(location = 3) out vec4 outColor3;

  void main() {
    outColor0 = vec4(1, .5, .3, .7);   // orange
    outColor1 = vec4(.6, .5, .4, .3);  // brown
    outColor2 = vec4(.2, .8, .0,  1);  // green
    outColor3 = vec4(.3, .4, .9, .6);  // blue
  } 
  `

  const program = twgl.createProgram(gl, [vs, fs]);

  const textures = [];
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let i = 0; i < 4; ++i) {
    const tex = gl.createTexture();
    textures.push(tex);
    gl.bindTexture(gl.TEXTURE_2D, tex);
    const width = 1;
    const height = 1;
    const level = 0;
    gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, 
                  gl.RGBA, gl.UNSIGNED_BYTE, null);
    // attach texture to framebuffer
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
                            gl.TEXTURE_2D, tex, level);
  }

  // our framebuffer textures are only 1x1 pixels
  gl.viewport(0, 0, 1, 1);

  // tell it we want to draw to all 4 attachments
  gl.drawBuffers([
    gl.COLOR_ATTACHMENT0,
    gl.COLOR_ATTACHMENT1, 
    gl.COLOR_ATTACHMENT2,
    gl.COLOR_ATTACHMENT3,
  ]);

  // draw a single point
  gl.useProgram(program);
  {
    const offset = 0;
    const count = 1
    gl.drawArrays(gl.POINT, 0, 1);
  }

  // --- below this is not relevant to the question but just so we
  // --- we can see it's working

  // render the 4 textures
  const fs2 = `
  #version 300 es
  precision mediump float;
  uniform sampler2D tex[4];
  out vec4 outColor;
  void main() {
    vec4 colors[4];

    // you can't index textures with non-constant integer expressions
    // in WebGL2 (you can in WebGL1 lol)
    colors[0] = texture(tex[0], vec2(0));
    colors[1] = texture(tex[1], vec2(0));
    colors[2] = texture(tex[2], vec2(0));
    colors[3] = texture(tex[3], vec2(0));
    
    vec4 color = vec4(0);
    for (int i = 0; i < 4; ++i) { 
      float x = gl_PointCoord.x * 4.0;
      float amount = step(float(i), x) * step(x, float(i + 1));
      color = mix(color, colors[i], amount);
    }
    outColor = vec4(color.rgb, 1);
  }
  `;
  const prgInfo2 = twgl.createProgramInfo(gl, [vs, fs2]);
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.useProgram(prgInfo2.program);
  // binds all the textures and set the uniforms
  twgl.setUniforms(prgInfo2, {
    tex: textures,
  });
  gl.drawArrays(gl.POINTS, 0, 1);
}
main();
<script src = "https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

Большое спасибо @gman. У меня все еще есть вопросы: все ли текстуры должны быть одного размера? Похоже, это хорошее решение для вывода разных цветов, но может быть не так полезно для вывода с произвольными размерами текстур. Если бы это было возможно, мне потребовались бы различные входные переменные координат текстуры для передачи в источник моего фрагмента.

Jeff Saremi 11.08.2018 15:47

И, кроме того, как будет работать масштабирование? Я думаю, что в настоящее время я устанавливаю масштаб, устанавливая gl.viewport(0,0,width, height), и именно так я узнаю, что при получении vTexCoords, различающихся в моем фрагментном шейдере, мне пришлось бы умножить s и t на шкалы по X и Y, чтобы получить логические координаты. Итак, если разрешено выводить текстуры разного размера, мне также понадобятся различные коэффициенты масштабирования в качестве входных данных?

Jeff Saremi 11.08.2018 15:47

Текстуры, прикрепленные к одному и тому же фреймбуферу, не могут иметь разного размера. Все они должны быть одного размера. Я также не уверен, какое отношение texcoords имеет к области просмотра. В большинстве шейдеров эти 2 не связаны. Также вы создаете свои собственные атрибуты, так что вы не ограничены одним набором текстурных кордов, и вам даже не нужно иметь координаты текстур.

gman 11.08.2018 16:13

Еще раз спасибо за разъяснения.

Jeff Saremi 11.08.2018 16:26

Я пытаюсь сделать это, но мне нужно прикрепить еще одну текстуру после первого рисования. Я заметил, что вы привязываете фреймбуфер к NULL. Я не могу этого сделать. Однако, если я просто позвоню в gl.framebufferTexture2D(), я получу статус FRAMEBUFFER_INCOMPLETE_DIMENSIONS. Как мне сбросить мой FB после выполнения множественного вывода? Благодарность

Jeff Saremi 20.09.2018 18:18

Это старый, но я понятия не имею, что означает «сбросить мой fb». Вы должны сделать по одному fb для каждой комбинации вложений, которую хотите использовать. Вы не должны изменять fbs

gman 30.05.2020 08:11

Другие вопросы по теме