Позиция z шейдера WebGL не используется в расчетах глубины

Я пробовал некоторые WebGL, но есть ошибка, которую я не могу найти, как исправить.

В настоящее время у меня есть следующая установка: у меня есть около 100 треугольников, все из которых имеют позицию и рисуются одной функцией gl.drawArrays. Чтобы отрисовать их в правильном порядке, я использовал gl.enable(gl.DEPTH_TEST);, что дало правильный результат.

Проблема, с которой я столкнулся сейчас, заключается в том, что если я обновлю gl_Position треугольников в вершинном шейдере, обновленное значение Z не будет использоваться в тесте глубины. В результате треугольник с gl_Position.z из 1 можно нарисовать поверх треугольника с gl_Position.z из 10, а это не совсем то, что мне нужно.

Что я пробовал?

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.GEQUAL);

с

gl.clear(gl.DEPTH_BUFFER_BIT);
gl.clearDepth(0);
gl.drawArrays(gl.TRIANGLES, 0, verticesCount);

в функции рендеринга.

Для создания буфера используется следующий код:

gl.bindBuffer(gl.ARRAY_BUFFER, dataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positionBufferData, gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, false, 0, 0);

Треугольники с более высоким значением z намного больше по размеру (из-за перспективы), но маленькие треугольники все еще появляются над ними (из-за порядка рендеринга).

В фрагментном шейдере я использовал gl_fragCoord.z, чтобы убедиться, что это правильно, и меньшие треугольники (дальше) получили более высокую альфу, чем большие (ближе).

Что может быть причиной странного поведения при рисовании?


person Alter User    schedule 08.04.2017    source источник
comment
Почему треугольник с z=1 нельзя нарисовать над треугольником с z=10? 1 IS меньше или равно 10.   -  person BDL    schedule 08.04.2017
comment
Я хочу, чтобы более высокое значение Z было сверху, что на самом деле должно быть gl.GEQUAL тогда, хороший момент. Тест глубины по-прежнему не работает на моем обновленном gl_Position.   -  person Alter User    schedule 08.04.2017
comment
Пожалуйста, опубликуйте рабочий фрагмент в вопросе. SO требует, чтобы: Вопросы, запрашивающие помощь по отладке (почему этот код не работает?), должны включать желаемое поведение, конкретную проблему или ошибку и кратчайший код, необходимый для их воспроизведения, в самом вопросе.   -  person gman    schedule 11.04.2017


Ответы (1)


Глубина в клипспейсе изменяется от -1 до 1. Глубина, записанная в буфер глубины, изменяется от 0 до 1. Вы очищаете до 1. Нет значения глубины > 1, поэтому единственное, что вы должны увидеть, это gl_Position.z = 1. Все, что меньше 1, не пройдет тест gl.depthFunc(gl.GEQUAL);. Все, что > 1, будет обрезано. Только 1 находится в диапазоне глубин и больше или равно 1.

В приведенном ниже примере рисуются прямоугольники от меньшего к большему с разными значениями z. Красный — стандартный gl.depthFunc(gl.LESS) с глубиной, очищенной до 1. Зеленый — gl.depthFunc(gl.GEQUAL) с глубиной, очищенной до 0. Синий — gl.depthFunc(gl.GEQUAL) с глубиной, очищенной до 1. Обратите внимание, что синий рисует только один прямоугольник на gl_Position.z = 1, потому что все остальные прямоугольники не проходят тест, поскольку они при Z ‹ 1.

const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
attribute vec4 position;
varying vec4 v_position;
uniform mat4 matrix;
void main() {
   gl_Position = matrix * position;
   v_position = abs(position);
}
`;
const fs = `
precision mediump float;
varying vec4 v_position;
uniform vec4 color;
void main() {
  gl_FragColor = vec4(1. - v_position.xxx, 1) * color;
}
`;
// compiles shaders, links program, looks up attributes
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBindbuffer, gl.bufferData for each array
const z0To1BufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: [ 
     ...makeQuad( .2, 0.00),
     ...makeQuad( .4,  .25),
     ...makeQuad( .6,  .50),
     ...makeQuad( .8,  .75),
     ...makeQuad(1.0, 1.00),
  ],
});
const z1To0BufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: [ 
     ...makeQuad(.2, 1.00),
     ...makeQuad(.4,  .75),
     ...makeQuad(.6,  .50),
     ...makeQuad(.8,  .25),
     ...makeQuad(1., 0.00),
  ],
});

function makeQuad(xy, z) {
  return [
     -xy, -xy, z,
      xy, -xy, z,
     -xy,  xy, z,
     -xy,  xy, z,
      xy, -xy, z,
      xy,  xy, z,
  ];
}

gl.useProgram(programInfo.program);

gl.enable(gl.DEPTH_TEST);

gl.clearDepth(1);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.LESS);
drawRects(-0.66, z0To1BufferInfo, [1, 0, 0, 1]);

gl.clearDepth(0);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.GEQUAL);
drawRects(0, z1To0BufferInfo, [0, 1, 0, 1]);

gl.clearDepth(1);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.depthFunc(gl.GEQUAL);
drawRects(0.66, z1To0BufferInfo, [0, 0, 1, 1]);


function drawRects(xoffset, bufferInfo, color) {
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  let mat = m4.translation([xoffset, 0, 0]);
  mat = m4.scale(mat, [.3, .5, 1]);

  // calls gl.uniformXXX
  twgl.setUniforms(programInfo, {
    color: color,
    matrix: mat,
  });

  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
}
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
<pre>
red  : depthFunc: LESS,   clearDepth: 1
green: depthFunc: GEQUAL, clearDepth: 0
blue : depthFunc: GEQUAL, clearDepth: 1
</pre>

person gman    schedule 09.04.2017
comment
Я протестировал различные комбинации и обнаружил, что depthFunc GEQUAL и clearDepth of 1 все еще работают, это будет означать, что каждый треугольник имеет z-значение, равное 1. Однако проблема заключается в том, что все мои треугольники рисуются в одной функции drawArrays, а их Z определяется в вершинном шейдере. Как мне заставить это работать? В настоящее время с вашим решением порядок все еще не правильный. - person Alter User; 09.04.2017
comment
В моем примере порядок именно такой, каким я его ожидаю. Я рисую красный цвет с z = 0,1 -> 1,0, используя LESS, где 0,1 — маленькое, а 1,0 — большое, и я вижу, что большое изображение отрисовывается за маленьким, что означает, что буфер глубины работал, как и ожидалось. Зеленый — это z = от 1,0 до 0,1, где 1,0 — маленький, а 0,1 — большой. На этот раз я использую GEQUAL. Опять же, поскольку я нарисовал 0,1 последним, и он самый большой, что показывает, что он работает, как и ожидалось. Он не перезаписывает мелочь на z = 1.0, так как буфер глубины работает и был очищен до 0. - person gman; 09.04.2017
comment
Последний (синий) показывает, что если буфер глубины очищен до 1, то работают только вещи со значением 1.0 (первый маленький прямоугольник). Все остальные не работают, потому что они меньше 1.0, а тест GEQUAL. В вашем примере как минимум вам нужно очистить до 0 вместо 1, иначе ничего, кроме z = 1, не может быть отрисовано. - person gman; 09.04.2017
comment
Я понимаю, как значения z влияют на тестирование глубины на данный момент, я пробовал все разные комбинации, но, похоже, ничто не меняет порядок их рисования. .drawArrays() просто рисует каждый треугольник поверх следующего, может быть это потому, что у меня есть 1 вызов отрисовки с разными треугольниками вместо того, чтобы у вас было 3 разных? - person Alter User; 10.04.2017
comment
Нет, я обновил образец, чтобы отрисовать все прямоугольники сразу. Я получаю тот же результат. Вы должны опубликовать больше кода, хотя вы все еще не признали, что ваш текущий код НЕ МОЖЕТ РАБОТАТЬ, потому что он очищает глубину до 1, поэтому единственное значение GEQUAL для 1 равно 1. Все, что больше этого, не будет нарисовано, так как оно обрезано. Из-за этого, если все ваши объекты имеют Z=1, то все они будут GEQUAL, поэтому все они будут отрисованы в порядке отрисовки. В противном случае вы рисуете прямо на холст или рисуете в фреймбуфер? Если фреймбуфер, вы создали вложение глубины? - person gman; 10.04.2017
comment
Обновил код, как вы просили, я признаю, что мой старый код с 1 и gl.GEQUAL не мог работать, обновлен сейчас. - person Alter User; 10.04.2017