我正在尝试制作一个webGL应用程序以渲染随机生成的地形。地形的渲染工作正常(几乎),但是当我尝试渲染简单的四边形以模拟水时,水的三角形不在正确的位置。
图像中的红色部分是混乱的三角形,只能是两个三角形,形成与地形一样大的正方形。我发现,如果地形尺寸为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边缘上会发生相同的错误。
答案 0 :(得分:0)
除非使用顶点数组对象(属于WebGL2的一部分,但仅在WebGL1中作为扩展名是可选的),否则顶点属性状态为 IS GLOBAL STATE 。除非您使用的不是顶点数组对象,否则gl.vertexAttribPointer
,gl.enableVertexAttribArray
,gl.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