Не смешивайте ломаную линию, которая пересекла линии с собой в webgl - PullRequest
0 голосов
/ 30 сентября 2019

Я рисую две полилинии (которые являются линиями в образце) в webgl с включенным смешиванием.

gl.uniform4f(colorUniformLocation, 0, 0, 0, 0.3);

gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.2, -1, 0.2, 1,]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 2);

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, -1, 0, 1,0, -1, 0, 1]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 4);

Здесь - образец кода.

Левая линия пересекается сама с собой, и кажется, что она сливается с собой, поэтому в результате она становится темнее.

Я бы хотел, чтобы смесь работала между этими полилиниями, но не хочу, чтобы полилиния смешивалась сама с собой. Есть ли способ сделать это?

1 Ответ

1 голос
/ 30 сентября 2019

Одним из способов будет использование теста трафарета. Вы задали бы webgl так, чтобы при рисовании пикселя трафарет сохранял определенное значение, а тест трафарета устанавливался таким образом, чтобы он не проходил, если он видит это значение.

Сначала приведем пример, в котором 2 набораперекрывающиеся треугольники с наложением. Пары темнеют там, где они перекрываются

function main() {
  const m4 = twgl.m4;
  const gl = document
      .querySelector('canvas')
      .getContext('webgl');

  const vs = `
  attribute vec4 position;
  uniform mat4 matrix;
  void main() {
    gl_Position = matrix * position;
  }
  `;
  
  const fs = `
  precision mediump float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
  `;
  
  // compile shader, link program, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
  gl.useProgram(programInfo.program);
  
  // create a buffer and put data in it
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: {
      numComponents: 2,
      data: [
        -0.5, -0.2,
         0.5, -0.2,
         0.5,  0.2,
         
        -0.2, -0.5,
        -0.2,  0.5,
         0.2,  0.5,
      ],
    },
  });
  
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.uniform??
  twgl.setUniforms(programInfo, {
    color: [0.5, 0, 0, 0.5],
    matrix: m4.identity(),
  });
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
  
  twgl.setUniforms(programInfo, {
    color: [0, 0, 0.5, 0.5],
    matrix: m4.rotateZ(
        m4.translation([-0.1, 0.2, 0]),
        Math.PI * 1.2),
  });
  twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

Затем тот же пример с тестом трафарета на

Сначала нам нужно запросить буфер трафарета

  const gl = someCanvas.getContext('webgl2', {stencil: true});

Затем мы включаем тест трафарета

  gl.enable(gl.STENCIL_TEST);

Настройте тест так, чтобы он рисовал, только если буфер трафарета равен нулю

  gl.stencilFunc(
     gl.EQUAL,   // the test
     0,          // reference value
     0xFF,       // mask
  );

И установите операцию, чтобы мы увеличивали трафарет, когдамы рисуем, чтобы они больше не были равны нулю и поэтому проваливаем тест

  gl.stencilOp(
     gl.KEEP,  // what to do if the stencil test fails
     gl.KEEP,  // what to do if the depth test fails
     gl.INCR,  // what to do if both tests pass
  );

Между первым тиражом и вторым очищаем буфер трафарета

gl.clear(gl.STENCIL_BUFFER_BIT);

Пример

function main() {
  const m4 = twgl.m4;
  const gl = document
      .querySelector('canvas')
      .getContext('webgl', {stencil: true});

  const vs = `
  attribute vec4 position;
  uniform mat4 matrix;
  void main() {
    gl_Position = matrix * position;
  }
  `;
  
  const fs = `
  precision mediump float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
  `;
  
  // compile shader, link program, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
  gl.useProgram(programInfo.program);
  
  // create a buffer and put data in it
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: {
      numComponents: 2,
      data: [
        -0.5, -0.2,
         0.5, -0.2,
         0.5,  0.2,
         
        -0.2, -0.5,
        -0.2,  0.5,
         0.2,  0.5,
      ],
    },
  });
  
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  
  gl.enable(gl.STENCIL_TEST);
  gl.stencilFunc(
     gl.EQUAL,  // the test
     0,         // reference value
     0xFF,      // mask
  );
  gl.stencilOp(
     gl.KEEP,    // what to do if the stencil test fails
     gl.KEEP,    // what to do if the depth test fails
     gl.INCR,    // what to do if both tests pass
  );
  
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.uniform??
  twgl.setUniforms(programInfo, {
    color: [0.5, 0, 0, 0.5],
    matrix: m4.identity(),
  });
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
  
  gl.clear(gl.STENCIL_BUFFER_BIT);  
  
  twgl.setUniforms(programInfo, {
    color: [0, 0, 0.5, 0.5],
    matrix: m4.rotateZ(
        m4.translation([-0.1, 0.2, 0]),
        Math.PI * 1.2),
  });
  twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

Другое решение, вы также можете использовать тест глубины, если вы рисуете 2D-вещи. Тест глубины по умолчанию отрисовывается, только если глубина gl.LESS текущей глубины, поэтому включение теста глубины и установка другой глубины между отрисовками также будет работать, если глубина треугольников одинакова. Вы можете вычислить различное значение глубины для каждой нарисованной вещи, вам нужно найти разрешение в битах буфера глубины. Или вы можете использовать gl.polygonOffset

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.POLYGON_OFFSET_FILL); 

... then ...

for (let i = 0; i < numThingsToDraw; ++i) {
  gl.polygonOffset(0, -i);  // each thing 1 depth unit less
  draw2DThing(things[i]);
}

пример

function main() {
  const m4 = twgl.m4;
  const gl = document
      .querySelector('canvas')
      .getContext('webgl');
  
  const vs = `
  attribute vec4 position;
  uniform mat4 matrix;
  void main() {
    gl_Position = matrix * position;
  }
  `;
  
  const fs = `
  precision mediump float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
  `;
  
  // compile shader, link program, look up locations
  const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
  gl.useProgram(programInfo.program);
  
  // create a buffer and put data in it
  const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
    position: {
      numComponents: 2,
      data: [
        -0.5, -0.2,
         0.5, -0.2,
         0.5,  0.2,
         
        -0.2, -0.5,
        -0.2,  0.5,
         0.2,  0.5,
      ],
    },
  });
  
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.POLYGON_OFFSET_FILL);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.uniform??
  twgl.setUniforms(programInfo, {
    color: [0.5, 0, 0, 0.5],
    matrix: m4.identity(),
  });
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);

  gl.polygonOffset(0, -1);
  
  twgl.setUniforms(programInfo, {
    color: [0, 0, 0.5, 0.5],
    matrix: m4.rotateZ(
        m4.translation([-0.1, 0.2, 0.0]),
        Math.PI * 1.2),
  });
  twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
...