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
相关问题