WebGL ничего не рендерит, каковы подходящие матрицы? - PullRequest
1 голос
/ 04 июня 2019

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

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

У меня есть объект, установленный в пространстве в позиции 0,0,0 с позициями вершин =

    [
        -0.5, -0.5, 0,
        0.5, -0.5, 0,
        0.5, 0.5, 0
    ]

У моей камеры установлен угол обзора 60 градусов, аспект canvas.width / canvas.height, ближняя плоскость 1/1000 и дальняя плоскость 50. И расположен на (0,0,-10), смотрящем на мой объект.

Во время рендеринга я поставляю свой вершинный шейдер:

Unifrom Matrix4 u_model

[1 0 0 0
 0 1 0 0
 0 0 1 0
 0 0 0 1]

так в основном тождественная матрица

Uniform Matrix4 u_view

[-1  0  0  0
  0  1  0  0
  0  0 -1 -10
  0  0  0  1]

и Uniform Matrix4 u_projection

 0.0003282401348280833 0                      -0.3129605393123332  0
 0                     0.0003282401348280833  -0.3129605393123332  0
 0                     0                      -1.0000400008000159 -0.002000040000800016
 0                     0                      -1                   0

Моя модель Matrix

[n11, n12, n13, n14
 n21, n22, n23, n24,
 n31, n32, n33, n34,
 n41, n42, n43, n44 ]

моя перспективная матрица расчета:

static perspective(fov, aspect, near, far) {
    const r = fov * aspect;
    const l = -4;
    const t = r;
    const b = l;
    const matrix = new Matrix4();
    matrix.n11 = (2 * near) / (r - l);
    matrix.n12 = 0;
    matrix.n13 = (r+l)/(r-l);
    matrix.n14 = 0;
    matrix.n21 = 0;
    matrix.n22 = (2 * near) / (t - b);
    matrix.n23 = (t+b)/(t-b);
    matrix.n24 = 0;
    matrix.n31 = 0;
    matrix.n32 = 0;
    matrix.n33 = (near + far) / (near - far);
    matrix.n34 = (2 * near * far) / (near - far);
    matrix.n41 = 0;
    matrix.n42 = 0;
    matrix.n43 = -1;
    matrix.n44 = 0;
    return matrix;
}

мой вершинный шейдер

 this.vertexShaderScript =
        '\r\n' +
        'precision highp float;\r\n' +
        'uniform mat4 u_model;\r\n' +
        'uniform mat4 u_view;\r\n' +
        'uniform mat4 u_projection;\r\n' +
        'attribute vec3 a_position;\r\n' +
        'attribute vec4 a_color;\r\n' +
        'varying vec4 v_color;\r\n' +
        'void main(void) {\r\n' +
        '    v_color = a_color;\r\n' +
        '    gl_Position = u_projection * u_view *  u_model * vec4(a_position, 1.0);\r\n' +
        '}\r\n';

и фрагмент шейдера

 this.fragmentShaderScript = '\r\n' +
        'precision highp float;\r\n' +
        'varying vec4 v_color;\r\n' +
        'void main(void) {\r\n' +
        '    gl_FragColor = v_color;\r\n' +
        '}\r\n';

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

Какая из матриц неправильная?

Остальной код можно найти на моем github: https://github.com/barteq100/webgl

1 Ответ

1 голос
/ 05 июня 2019

Чего вы пытаетесь достичь?

Ваша функция перспективы не имеет смысла для меня.Похоже, что она основана на функции glFrustum из давно устаревшего OpenGL 2.1

Вы заметите, что функция принимает 6 аргументов: влево, вправо, внизу, сверху, близко к дальнему.Ваш занимает всего 4, а цифры, которые вы вводите, кажутся бессмыслицей.Почему l означает левый код с -4?Как вы думаете, почему r должно быть fov * aspect?

Тогда вы не показали код, который устанавливает ваши матрицы, поэтому мы не знаем, как вы его передаете.Матрицы WebGL (и OpenGL), как ожидается, будут основными строками.Или, другими словами, матрица перевода будет указана следующим образом в JavaScript

const translationMatrix = [
  1, 0, 0, 0,
  0, 1, 0, 0,
  0, 1, 1, 0,
  x, y, z, 1,
];

Спецификация OpenGL называет каждую строку этой матрицы столбцом, но по стандартам компьютерного языка они являются строками.См. https://webglfundamentals.org/webgl/lessons/webgl-matrix-vs-math.html

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

В любом случае, вот ваша перспективная функция.Если я переместлю камеру, то найду куб, который рисую в начале координат с какой-то очень странной перспективой

'use strict';

/* global twgl, m4, requestAnimationFrame, document */

class Matrix4 { }

function perspective(fov, aspect, near, far) {
  const r = fov * aspect;
  const l = -4;
  const t = r;
  const b = l;
  const matrix = new Matrix4();
  matrix.n11 = (2 * near) / (r - l);
  matrix.n12 = 0;
  matrix.n13 = (r+l)/(r-l);
  matrix.n14 = 0;
  matrix.n21 = 0;
  matrix.n22 = (2 * near) / (t - b);
  matrix.n23 = (t+b)/(t-b);
  matrix.n24 = 0;
  matrix.n31 = 0;
  matrix.n32 = 0;
  matrix.n33 = (near + far) / (near - far);
  matrix.n34 = (2 * near * far) / (near - far);
  matrix.n41 = 0;
  matrix.n42 = 0;
  matrix.n43 = -1;
  matrix.n44 = 0;
  return matrix;
}

function toMat(m) {
  return [
    m.n11, m.n21, m.n31, m.n41,
    m.n12, m.n22, m.n32, m.n42,
    m.n13, m.n23, m.n33, m.n43,
    m.n14, m.n24, m.n34, m.n44,
  ];
}

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

const vs = `
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main() {
  gl_Position = projection * modelView * position;
  v_normal = mat3(modelView) * normal;
  v_texcoord = texcoord;
}
`;

const fs = `
precision highp float;

varying vec3 v_normal;
varying vec2 v_texcoord;
varying float v_modelId;

void main() {
  vec3 lightDirection = normalize(vec3(1, 2, -3));  // arbitrary light direction
  
  float l = dot(lightDirection, normalize(v_normal)) * .5 + .5;
  gl_FragColor = vec4(vec3(0,1,0) * l, 1);
}
`;

// compile shader, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make some vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);

function render(time) {
  time *= 0.001;  // seconds
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);

  const fov = Math.PI * 0.25;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const near = 0.1;
  const far = 100;
  const projection = toMat(perspective(fov, aspect, near, far));
  
  const camera = m4.translation([0, 0, 1]);
  const view = m4.inverse(camera);
  let modelView = m4.rotateY(view, time);

  gl.useProgram(programInfo.program);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
  twgl.setUniforms(programInfo, {
    projection,
    modelView,
  });  
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

Если я изменю функцию перспективы на что-то более традиционное, то после перемещения камеры я получу нечто более нормальное

'use strict';

/* global twgl, m4, requestAnimationFrame, document */

class Matrix4 { }

function perspective(fov, aspect, near, far) {

  const f = Math.tan(Math.PI * 0.5 - 0.5 * fov);
  const rangeInv = 1.0 / (near - far);

  const matrix = new Matrix4();
  matrix.n11 = f / aspect;
  matrix.n12 = 0;
  matrix.n13 = 0;
  matrix.n14 = 0;
  matrix.n21 = 0;
  matrix.n22 = f;
  matrix.n23 = 0;
  matrix.n24 = 0;
  matrix.n31 = 0;
  matrix.n32 = 0;
  matrix.n33 = (near + far) * rangeInv;
  matrix.n34 = near * far * rangeInv * 2;
  matrix.n41 = 0;
  matrix.n42 = 0;
  matrix.n43 = -1;
  matrix.n44 = 0;
  return matrix;
}

function toMat(m) {
  return [
    m.n11, m.n21, m.n31, m.n41,
    m.n12, m.n22, m.n32, m.n42,
    m.n13, m.n23, m.n33, m.n43,
    m.n14, m.n24, m.n34, m.n44,
  ];
}

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

const vs = `
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main() {
  gl_Position = projection * modelView * position;
  v_normal = mat3(modelView) * normal;
  v_texcoord = texcoord;
}
`;

const fs = `
precision highp float;

varying vec3 v_normal;
varying vec2 v_texcoord;
varying float v_modelId;

void main() {
  vec3 lightDirection = normalize(vec3(1, 2, -3));  // arbitrary light direction
  
  float l = dot(lightDirection, normalize(v_normal)) * .5 + .5;
  gl_FragColor = vec4(vec3(0,1,0) * l, 1);
}
`;

// compile shader, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make some vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);

function render(time) {
  time *= 0.001;  // seconds
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);

  const fov = Math.PI * 0.25;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const near = 0.1;
  const far = 100;
  const projection = toMat(perspective(fov, aspect, near, far));
  
  const camera = m4.translation([0, 0, 3]);
  const view = m4.inverse(camera);
  let modelView = m4.rotateY(view, time);

  gl.useProgram(programInfo.program);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
  twgl.setUniforms(programInfo, {
    projection,
    modelView,
  });  
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

Если вы хотите сохранить ту же перспективную математику (т. Е. Использовать матрицу из glFrustum, указанную выше), то это нужные вам значениядля l, r, t, b

  const t = near * Math.tan(0.5 * fov);
  const b = -t;
  const r = t * aspect;
  const l = -r;

'use strict';

/* global twgl, m4, requestAnimationFrame, document */

class Matrix4 { }

function perspective(fov, aspect, near, far) {

  const t = near * Math.tan(0.5 * fov);
  const b = -t;
  const r = t * aspect;
  const l = -r;
  
  const matrix = new Matrix4();
  matrix.n11 = (2 * near) / (r - l);
  matrix.n12 = 0;
  matrix.n13 = (r+l)/(r-l);
  matrix.n14 = 0;
  matrix.n21 = 0;
  matrix.n22 = (2 * near) / (t - b);
  matrix.n23 = (t+b)/(t-b);
  matrix.n24 = 0;
  matrix.n31 = 0;
  matrix.n32 = 0;
  matrix.n33 = (near + far) / (near - far);
  matrix.n34 = (2 * near * far) / (near - far);
  matrix.n41 = 0;
  matrix.n42 = 0;
  matrix.n43 = -1;
  matrix.n44 = 0;
  return matrix;
}

function toMat(m) {
  return [
    m.n11, m.n21, m.n31, m.n41,
    m.n12, m.n22, m.n32, m.n42,
    m.n13, m.n23, m.n33, m.n43,
    m.n14, m.n24, m.n34, m.n44,
  ];
}

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

const vs = `
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main() {
  gl_Position = projection * modelView * position;
  v_normal = mat3(modelView) * normal;
  v_texcoord = texcoord;
}
`;

const fs = `
precision highp float;

varying vec3 v_normal;
varying vec2 v_texcoord;
varying float v_modelId;

void main() {
  vec3 lightDirection = normalize(vec3(1, 2, -3));  // arbitrary light direction
  
  float l = dot(lightDirection, normalize(v_normal)) * .5 + .5;
  gl_FragColor = vec4(vec3(0,1,0) * l, 1);
}
`;

// compile shader, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make some vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);

function render(time) {
  time *= 0.001;  // seconds
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);

  const fov = Math.PI * 0.25;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const near = 0.1;
  const far = 100;
  const projection = toMat(perspective(fov, aspect, near, far));
  
  const camera = m4.translation([0, 0, 3]);
  const view = m4.inverse(camera);
  let modelView = m4.rotateY(view, time);

  gl.useProgram(programInfo.program);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
  twgl.setUniforms(programInfo, {
    projection,
    modelView,
  });  
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
...