WebGL三角形无法正确呈现

时间:2018-11-06 23:17:40

标签: javascript opengl-es webgl

我正在尝试制作一个webGL应用程序以渲染随机生成的地形。地形的渲染工作正常(几乎),但是当我尝试渲染简单的四边形以模拟水时,水的三角形不在正确的位置。

enter image description here

图像中的红色部分是混乱的三角形,只能是两个三角形,形成与地形一样大的正方形。我发现,如果地形尺寸为33x33点(如图中所示),则水缓冲区的尺寸将由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)的索引数组。

另一个问题是,如果我在terrain.setup_buffer()之后调用water.setup_buffer(),则两个渲染调用(地形和水)都抛出与上述相同的错误(“ GL_INVALID_OPERATION”)。

如果有帮助,我正在使用谷歌浏览器和Windows 10,但在ms边缘上会发生相同的错误。

1 个答案:

答案 0 :(得分:0)

除非使用顶点数组对象(属于WebGL2的一部分,但仅在WebGL1中作为扩展名是可选的),否则顶点属性状态为 IS GLOBAL STATE 。除非您使用的不是顶点数组对象,否则gl.vertexAttribPointergl.enableVertexAttribArraygl.vertexAttribXXX设置的状态都是全局状态

这意味着当您致电

water.setup_buffer();

已设置全局属性状态。然后,您致电

terrain.setup_buffer();

这将覆盖以前的 global 属性状态。

以下是描述属性状态的一些答案

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

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

您应该

(a)使用“顶点数组对象”(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