WebGL треугольники отображаются неправильно - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь создать приложение webGL для рендеринга случайно сгенерированных ландшафтов. Рендеринг ландшафта работает нормально (почти), но когда я пытаюсь сделать простой квадрат, имитирующий воду, треугольники воды не в нужном месте.

enter image description here

Красные части на изображении - это перепутанные треугольники, которые должны быть только двумя треугольниками, образующими квадрат размером с местность. Я обнаружил, что если размер ландшафта составляет 33х33 точки (как на картинке), размер водяных буферов составляет 1089 треугольников вместо двух, что довольно странно. Тот же принцип применяется для других размеров местности, то есть 65x65, 129x129 и т. Д.

Мой код воды примерно такой: размер 50:

height: 0,
rotation: [0, 0, 0],
scale: [1, 1, 1],
ver: [
    -size,  0,  size,
    -size,  0, -size,
     size,  0, -size,
    -size,  0,  size,
     size,  0, -size,
     size,  0,  size
],
vao: undefined,

setup_buffer: function(){

    this.vao = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vao);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.ver), gl.STATIC_DRAW);

    gl.vertexAttribPointer(
        water_shader.position_attrib_location, // Attribute location
        3, // Number of elements per attribute
        gl.FLOAT, // Type of elements
        gl.FALSE,
        3 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
        0 // Offset from the beginning of a single vertex to this attribute
    );

    gl.bindBuffer(gl.ARRAY_BUFFER, null);
}

Итак, все, что я делаю, - это создаю и связываю буфер, сохраняю в нем 6 вершин и определяю их через vertexAttribPointer перед отсоединением буфера.

Функция terrain.setup_buffer () практически такая же, за исключением того, что она использует буфер индекса и одна вершина содержит 9 координат (положение, цвет, нормаль) вместо 3. Обратите внимание, что генерация ландшафта и переменные ландшафта нет в этом коде, но я могу заверить, что все функции работают и все переменные существуют и инициализируются.

this.vao = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vao);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.ver), gl.STATIC_DRAW);

this.ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.ind), gl.STATIC_DRAW);

gl.vertexAttribPointer(
    terrain_shader.position_attrib_location, // Attribute location
    3, // Number of elements per attribute
    gl.FLOAT, // Type of elements
    gl.FALSE,
    9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
    0 // Offset from the beginning of a single vertex to this attribute
);
gl.vertexAttribPointer(
    terrain_shader.color_attrib_location, // Attribute location
    3, // Number of elements per attribute
    gl.FLOAT, // Type of elements
    gl.FALSE,
    9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
    3 * Float32Array.BYTES_PER_ELEMENT // Offset from the beginning of a single vertex to this attribute
);
gl.vertexAttribPointer(
    terrain_shader.normal_attrib_location, // Attribute location
    3, // Number of elements per attribute
    gl.FLOAT, // Type of elements
    gl.FALSE,
    9 * Float32Array.BYTES_PER_ELEMENT, // Size of an individual vertex
    6 * Float32Array.BYTES_PER_ELEMENT // Offset from the beginning of a single vertex to this attribute
);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

Так что это мой основной цикл со всеми инициализациями.

var canvas = document.getElementById('opengl-surface');
var gl = canvas.getContext('webgl');

if (!gl) {
    console.log('WebGL not supported, falling back on experimental-webgl');
    gl = canvas.getContext('experimental-webgl');
}

if (!gl) {
    alert('Your browser does not support WebGL');
}

gl.clearColor(0.75, 0.85, 0.8, 1.0);
gl.enable(gl.DEPTH_TEST);

//create shader
water_shader.setup_shader();
terrain_shader.setup_shader();

// Create buffers
terrain.generate(5, 0.9, true);

water.setup_buffer();
terrain.setup_buffer();

var projectionMatrix = new Float32Array(16);
mat4.perspective(projectionMatrix, glMatrix.toRadian(45), canvas.width/canvas.height, 0.1, 1000.0);

gl.useProgram(water_shader.program);
gl.uniformMatrix4fv(water_shader.location_projection_matrix, gl.FALSE, projectionMatrix);
gl.uniform4fv(water_shader.location_color, [1, 0, 0, 1]);
gl.useProgram(null);

gl.useProgram(terrain_shader.program);
gl.uniformMatrix4fv(terrain_shader.location_projection_matrix, gl.FALSE, projectionMatrix);
gl.uniform3fv(terrain_shader.location_light_direction, light.direction);
gl.uniform3fv(terrain_shader.location_light_color, light.color);
gl.useProgram(null);

//
// Main render loop
//
var identity = new Float32Array(16);
mat4.identity(identity);

var loop = function(){

    camera.rotate();
    camera.translate();

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    //render terrain
    {
        gl.useProgram(terrain_shader.program);
        gl.uniformMatrix4fv(terrain_shader.location_view_matrix, gl.FALSE, camera.view_matrix());
        gl.uniformMatrix4fv(terrain_shader.location_model_matrix, gl.FALSE, terrain.model_matrix());

        gl.bindBuffer(gl.ARRAY_BUFFER, terrain.vao);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, terrain.ibo);
        gl.enableVertexAttribArray(terrain_shader.position_attrib_location);
        gl.enableVertexAttribArray(terrain_shader.color_attrib_location);
        gl.enableVertexAttribArray(terrain_shader.normal_attrib_location);
        gl.drawElements(gl.TRIANGLES, terrain.ind.length, gl.UNSIGNED_SHORT, 0);
        gl.disableVertexAttribArray(terrain_shader.position_attrib_location);
        gl.disableVertexAttribArray(terrain_shader.color_attrib_location);
        gl.disableVertexAttribArray(terrain_shader.normal_attrib_location);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.useProgram(null);
    }


    //render water_shader
    {
        gl.useProgram(water_shader.program);
        gl.uniformMatrix4fv(water_shader.location_view_matrix, gl.FALSE, camera.view_matrix());
        gl.uniformMatrix4fv(water_shader.location_model_matrix, gl.FALSE, water.model_matrix());

        gl.bindBuffer(gl.ARRAY_BUFFER, water.vao);
        gl.enableVertexAttribArray(water_shader.position_attrib_location);
        gl.drawArrays(gl.TRIANGLES, 0, 1089); //here should be 2 istead of 1089
        gl.disableVertexAttribArray(water_shader.position_attrib_location);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        gl.useProgram(null);
    }
    requestAnimationFrame(loop);
};

requestAnimationFrame(loop);

Шейдеры довольно прямолинейны и не требуют особых объяснений. Для полноты вот мой код шейдера воды

VS:

precision mediump float;

attribute vec3 vertPosition;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

void main()
{
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertPosition, 1.0),
}

FS:

precision mediump float;

uniform vec4 color;

void main()
{
  gl_FragColor = color;
}

Есть и другие проблемы, например, если уменьшить размер рельефа до (2 ^ 3 + 1) x (2 ^ 3 + 1) вершин, я получаю сообщение об ошибке «GL_INVALID_OPERATION: glDrawArrays: попытка получить доступ к вершинам диапазона в атрибуте 0». Этого не должно быть, так как я зарегистрировал массивы и получил массив вершин размером 729 (9x9x9) и индексный массив размером 384 (8x8x2x3).

Другая проблема состоит в том, что если я вызываю water.setup_buffer () после terrain.setup_buffer (), оба вызова рендеринга (terrain и water) выдают ту же ошибку, что и выше ("GL_INVALID_OPERATION").

Если это поможет, я работаю на Google Chrome и Windows 10, но на MS Edge возникают те же ошибки.

1 Ответ

0 голосов
/ 07 ноября 2018

Если вы не используете объекты Vertex Array (которые являются частью WebGL2, но являются необязательными в качестве расширения в WebGL1), состояние атрибута вершины IS GLOBAL STATE . Это состояние, установленное gl.vertexAttribPointer, gl.enableVertexAttribArray, gl.vertexAttribXXX - это все глобальное состояние, если только вы не используете объекты Vertex Array (которые вы не используете)

Это означает, что когда вы звоните

water.setup_buffer();

Установлено состояние атрибута global . Вы тогда звоните

terrain.setup_buffer();

Перезаписывает предыдущее глобальное состояние атрибута.

Вот некоторые ответы, которые описывают состояние атрибута

https://stackoverflow.com/a/27164577/128511

https://stackoverflow.com/a/28641368/128511

Вы должны либо

(a) использовать объекты Vertex Array (VAO), чтобы состояние атрибута соответствовало VAO

или

(b) отдельная настройка буферов (время начала инициализации) и настройка атрибутов (время рендеринга).

Без VAO обычный способ рендеринга -

for each thing you want to draw
   gl.useProgram
   setup attributes for that thing
   bind textures and set uniforms for that thing
   call gl.drawElements or gl.drawArrays
...