Как визуализировать отдельные пиксели для одного слоя 3D-текстуры в фреймбуфере?

У меня есть 3D-текстура 4x4x4, которую я инициализирую и показываю правильно, чтобы раскрасить мою сетку вершин 4x4x4 (см. прикрепленную красную сетку с одним белым пикселем - 0,0,0).

Как визуализировать отдельные пиксели для одного слоя 3D-текстуры в фреймбуфере?

Однако, когда я визуализирую 4 слоя в буфере кадра (все четыре одновременно, используя gl.COLOR_ATTACHMENT0 --> gl.COLOR_ATTACHMENT3, только четыре из шестнадцати пикселей на слое успешно визуализируются моим фрагментным шейдером (чтобы они стали зелеными).

Как визуализировать отдельные пиксели для одного слоя 3D-текстуры в фреймбуфере?

Когда я делаю только один слой с gl.COLOR_ATTACHMENT0, те же 4 пикселя отображаются правильно измененными для 1 слоя, а остальные 3 слоя остаются с исходным цветом без изменений. Когда я изменяю gl.viewport(0, 0, size, size) (size = 4 в этом примере) на что-то другое, например, на весь экран, или на размеры, отличные от 4, тогда записываются разные пиксели, но не более 4 , Моя цель состоит в том, чтобы индивидуально точно указать все 16 пикселей каждого слоя. Пока я использую цвета в качестве учебного опыта, но текстура на самом деле предназначена для информации о положении и скорости каждой вершины для моделирования физики. Я предполагаю (ошибочное предположение?) с 64 точками/вершинами, что я запускаю вершинный шейдер и фрагментный шейдер по 64 раза каждый, окрашивая один пиксель при каждом вызове.

Я удалил из шейдеров весь код, кроме жизненно важного. Я оставил javascript без изменений. Я подозреваю, что моя проблема заключается в неправильной инициализации и передаче массива позиций вершин.

//Set x,y position coordinates to be used to extract data from one plane of our data cube
//remember, z we handle as a 1 layer of our cube which is composed of a stack of x-y planes. 
const oneLayerVertices = new Float32Array(size * size * 2);
count = 0;  
for (var j = 0; j < (size); j++) {
    for (var i = 0; i < (size); i++) {
        oneLayerVertices[count] = i;
        count++;

        oneLayerVertices[count] = j;
        count++;

        //oneLayerVertices[count] = 0;
        //count++;

        //oneLayerVertices[count] = 0;
        //count++;

    }
}

const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
   position: {
      numComponents: 2,
      data: oneLayerVertices,
   },
});

И затем я использую bufferInfo следующим образом:

gl.useProgram(computeProgramInfo.program);
   twgl.setBuffersAndAttributes(gl, computeProgramInfo, bufferInfo);

   gl.viewport(0, 0, size, size); //remember size = 4

   outFramebuffers.forEach((fb, ndx) => {
      gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
      gl.drawBuffers([
         gl.COLOR_ATTACHMENT0,
         gl.COLOR_ATTACHMENT1,
         gl.COLOR_ATTACHMENT2,
         gl.COLOR_ATTACHMENT3
      ]);

      const baseLayerTexCoord = (ndx * numLayersPerFramebuffer);
      console.info("My baseLayerTexCoord is "+baseLayerTexCoord);
      twgl.setUniforms(computeProgramInfo, {
         baseLayerTexCoord,
         u_kernel: [
             0, 0, 0,
             0, 0, 0,
             0, 0, 0,

             0, 0, 1,
             0, 0, 0,
             0, 0, 0,

             0, 0, 0,
             0, 0, 0,
             0, 0, 0,
         ],
         u_position: inPos,      
         u_velocity: inVel,      
         loopCounter: loopCounter,   

         numLayersPerFramebuffer: numLayersPerFramebuffer
      });
      gl.drawArrays(gl.POINTS, 0, (16));
   });

ВЕРШИННЫЙ ШЕЙДЕР: calc_vertex:

const compute_vs = `#version 300 es
  precision highp float;
  in vec4 position;
  void main() {
    gl_Position = position;
  }
`;

ФРАГМЕНТ ШЕЙДЕР: calc_fragment:

const compute_fs = `#version 300 es
precision highp float;

out vec4 ourOutput[4];

void main() {
   ourOutput[0] = vec4(0,1,0,1);
   ourOutput[1] = vec4(0,1,0,1);
   ourOutput[2] = vec4(0,1,0,1);
   ourOutput[3] = vec4(0,1,0,1);
}
`;
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
165
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я не уверен, что вы пытаетесь сделать и что, по вашему мнению, будут делать позиции.

У вас есть 2 варианта моделирования графического процессора в WebGL2.

  1. используйте трансформирующую обратную связь.

    В этом случае вы передаете атрибуты и генерируете данные в буферах. По сути, у вас есть входные и выходные атрибуты, и обычно вы запускаете только вершинный шейдер. Другими словами, ваши варианты, выходные данные вашего вершинного шейдера, записываются в буфер. Итак, у вас есть как минимум 2 набора буферов, currentState и nextState, и ваш вершинный шейдер считывает атрибуты из currentState и записывает их в nextState.

    Существует пример записи в буферы с помощью обратной связи преобразования здесь, хотя в этом примере обратная связь преобразования используется только в начале для однократного заполнения буферов.

  2. использовать текстуры, прикрепленные к фреймбуферам

    в этом случае аналогично у вас есть 2 текстуры, currentState и nextState. Вы устанавливаете nextState в качестве цели рендеринга и считываете из currentState для создания следующего состояния.

    сложность в том, что рендерить в текстуры можно только путем вывода примитивов в вершинном шейдере. Если currentState и nextState являются 2D-текстурами, это тривиально. Просто выведите четырехугольник от -1,0 до +1,0 из вершинного шейдера, и все пиксели в nextState будут визуализированы.

    Если вы используете 3D-текстуру, то то же самое, за исключением того, что вы можете отображать только 4 слоя одновременно (ну, gl.getParameter(gl.MAX_DRAW_BUFFERS)). так что вам придется сделать что-то вроде

    for(let layer = 0; layer < numLayers; layer += 4) {
       // setup framebuffer to use these 4 layers
       gl.drawXXX(...) // draw to 4 layers)
    }
    

    или лучше

    // at init time
    const fbs = [];
    for(let layer = 0; layer < numLayers; layer += 4) {
       fbs.push(createFramebufferForThese4Layers(layer);
    }
    
    // at draw time
    fbs.forEach((fb, ndx) => {;
       gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
       gl.drawXXX(...) // draw to 4 layers)
    });
    

    Я предполагаю, что несколько вызовов отрисовки медленнее, чем один вызов отрисовки, поэтому другое решение состоит в том, чтобы вместо этого рассматривать 2D-текстуру как 3D-массив и соответствующим образом вычислять координаты текстуры.

Я не знаю, что лучше. Если вы моделируете частицы, и им нужно только смотреть на свое текущее состояние, то преобразование обратной связи проще. Если вам нужно, чтобы каждая частица могла смотреть на состояние других частиц, другими словами, вам нужен произвольный доступ ко всем данным, тогда ваш единственный вариант — хранить данные в текстурах.

Что касается позиций, я не понимаю ваш код. Позиции определяют примитивы POINTS, LINES или TRIANGLES, так как же передача целых значений X, Y в наш вершинный шейдер поможет вам определить POINTS, LINES или TRIANGLES?

Похоже, вы пытаетесь использовать POINTS, и в этом случае вам нужно установить gl_PointSize на размер точки, которую вы хотите нарисовать (1.0), и вам нужно преобразовать эти позиции в пространство клипа.

gl_Position = vec4((position.xy + 0.5) / resolution, 0, 1);

где resolution — размер текстуры.

Но делать это таким образом будет медленно. Гораздо лучше просто нарисовать полноразмерный (от -1 до +1) четырехугольник клипа. Для каждого пикселя в месте назначения будет вызываться фрагментный шейдер. gl_FragCoord.xy будет расположением центра отображаемого в данный момент пикселя, поэтому первый пиксель в левом нижнем углу gl_FragCoord.xy будет (0,5, 0,5). Пиксель справа от этого будет (1,5, 0,5). Пиксель справа от этого будет (2,5, 0,5). Вы можете использовать это значение для расчета доступа к currentState. Предполагая, что отображение 1x1 самым простым способом будет

int n = numberOfLayerThatsAttachedToCOLOR_ATTACHMENT0;
vec4 currentStateValueForLayerN = texelFetch(
    currentStateTexture, ivec3(gl_FragCoord.xy, n + 0), 0);
vec4 currentStateValueForLayerNPlus1 = texelFetch(
    currentStateTexture, ivec3(gl_FragCoord.xy, n + 1), 0);
vec4 currentStateValueForLayerNPlus2 = texelFetch(
    currentStateTexture, ivec3(gl_FragCoord.xy, n + 2), 0);
...

vec4 nextStateForLayerN = computeNextStateFromCurrentState(currentStateValueForLayerN);
vec4 nextStateForLayerNPlus1 = computeNextStateFromCurrentState(currentStateValueForLayerNPlus1);
vec4 nextStateForLayerNPlus2 = computeNextStateFromCurrentState(currentStateValueForLayerNPlus2);
...

outColor[0] = nextStateForLayerN;
outColor[1] = nextStateForLayerNPlus1;
outColor[2] = nextStateForLayerNPlus1;
...

Я не знаю, нужно ли вам это, но просто для проверки вот простой пример, который отображает разные цвета для каждого пикселя текстуры 4x4x4, а затем отображает их.

const pointVS = `
#version 300 es

uniform int size;
uniform highp sampler3D tex;
out vec4 v_color;

void main() {
  int x = gl_VertexID % size;
  int y = (gl_VertexID / size) % size;
  int z = gl_VertexID / (size * size);
  
  v_color = texelFetch(tex, ivec3(x, y, z), 0);
  
  gl_PointSize = 8.0;
  
  vec3 normPos = vec3(x, y, z) / float(size); 
  gl_Position = vec4(
     mix(-0.9, 0.6, normPos.x) + mix(0.0,  0.3, normPos.y),
     mix(-0.6, 0.9, normPos.z) + mix(0.0, -0.3, normPos.y),
     0,
     1);
}
`;

const pointFS = `
#version 300 es
precision highp float;

in vec4 v_color;
out vec4 outColor;

void main() {
  outColor = v_color;
}
`;

const rtVS = `
#version 300 es
in vec4 position;
void main() {
  gl_Position = position;
}
`;

const rtFS = `
#version 300 es
precision highp float;

uniform vec2 resolution;
out vec4 outColor[4];

void main() {
  vec2 xy = gl_FragCoord.xy / resolution;
  outColor[0] = vec4(1, 0, xy.x, 1);
  outColor[1] = vec4(0.5, xy.yx, 1);
  outColor[2] = vec4(xy, 0, 1);
  outColor[3] = vec4(1, vec2(1) - xy, 1);
}
`;

function main() {
  const gl = document.querySelector('canvas').getContext('webgl2');
  if (!gl) {
    return alert('need webgl2');
  }
  
  const pointProgramInfo = twgl.createProgramInfo(gl, [pointVS, pointFS]);
  const rtProgramInfo = twgl.createProgramInfo(gl, [rtVS, rtFS]);
  
  const size = 4;
  const numPoints = size * size * size;
  const tex = twgl.createTexture(gl, {
    target: gl.TEXTURE_3D,
    width: size,
    height: size,
    depth: size,
  });
  
  const clipspaceFullSizeQuadBufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: {
      data: [
        -1, -1,
         1, -1,
        -1,  1,
        
        -1,  1,
         1, -1,
         1,  1,
      ],
      numComponents: 2,
    },
  });
  
  const fb = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (let i = 0; i < 4; ++i) {
    gl.framebufferTextureLayer(
        gl.FRAMEBUFFER,
        gl.COLOR_ATTACHMENT0 + i,
        tex,
        0, // mip level
        i, // layer
    );
  }
  
  gl.drawBuffers([
     gl.COLOR_ATTACHMENT0,
     gl.COLOR_ATTACHMENT1,
     gl.COLOR_ATTACHMENT2,
     gl.COLOR_ATTACHMENT3,
  ]);

  gl.viewport(0, 0, size, size);
  gl.useProgram(rtProgramInfo.program);
  twgl.setBuffersAndAttributes(
      gl,
      rtProgramInfo,
      clipspaceFullSizeQuadBufferInfo);
  twgl.setUniforms(rtProgramInfo, {
    resolution: [size, size],
  });
  twgl.drawBufferInfo(gl, clipspaceFullSizeQuadBufferInfo);
  
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.drawBuffers([
     gl.BACK,
  ]);
  
  gl.useProgram(pointProgramInfo.program);
  twgl.setUniforms(pointProgramInfo, {
    tex,
    size,
  });
  gl.drawArrays(gl.POINTS, 0, numPoints);
}
main();
<canvas></canvas>
<script src = "https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

Он рисует шейдер с полным пространством клипа (rtVS/rtFS), поэтому да, все пиксели в цели рендеринга будут отображаться независимо от размера, и да, ваши значения для gl_FragCoord.xy будут for(y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { gl_FragCoord.xy = vec2(x + 0.5, y + 0.5); runFragmentShader(); } }

gman 26.06.2019 05:58

Теперь я могу использовать gl_FragCoord плюс мой слой, чтобы установить значения в любой из 64 точек. Я вижу цвета, которые я установил на экране. Однако, когда я запрашиваю с помощью texelFetch «vec4 mypixel = texelFetch (currentStateTexture, ivec3 (gl_FragCoord.xy, 1), 0)», а затем пытаюсь выполнить сравнение возвращаемого значения «if (mypixel.r == 0.0) .. «Я никогда не получаю спичек. Не могли бы вы подсказать, что я могу делать неправильно? Я устанавливаю цвета, используя «particlePosition[layer] = vec4(0.0, 1.0, 0.0, 1.0);» а затем нашВывод[0] =ParticlePosition[0]. Все цвета и альфа-каналы, которые я устанавливаю, равны 0,0 или 1,0.

billvh 26.06.2019 06:25

Задайте еще вопрос, где вы можете разместить свой новый код?

gman 26.06.2019 06:47

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