【问题标题】:WebGL 2.0 Error : glDrawArrays: attempt to access out of range vertices in attribute 0WebGL 2.0 错误:glDrawArrays:尝试访问属性 0 中超出范围的顶点
【发布时间】:2018-10-28 03:50:02
【问题描述】:

我正在学习 WebGL 技术。不幸的是我遇到了一些错误。我想为直升机绘制二维两个形状。我在下面写了代码。正是我想做的就是在一个叫做主体的形状上制作一个螺旋桨。

请注意,我尝试创建一个新缓冲区并绑定它,但仍然失败。我觉得stride还没搞懂,但我想学习如何解决它,即使它很简单。

// Vertex shader
var _vertexShader = `
    attribute vec4 _position;
    void main() {
        gl_Position = _position;
    }
`

// Fragment shader
var _fragmentShader = `
    precision mediump float;

    void main() {
        gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
    }
`

// Create shader
function createShader(gl, type, source) {
    var shader = gl.createShader(type)
    gl.shaderSource(shader, source)
    gl.compileShader(shader)
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
    if (success) {
        return shader
    } else {
        console.log(gl.getShaderInfoLog(shader))
        gl.deleteShader(shader)
        // return false
    }
}

// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram()
    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)
    gl.linkProgram(program)
    var success = gl.getProgramParameter(program, gl.LINK_STATUS)
    if (success) {
        return program
    } else {
        console.log(gl.getProgramInfoLog(program))
        gl.deleteProgram(program)
        // return false
    }
}

function main() {
    // Get a WebGL context.
    var canvas = document.getElementById('canvas')
    var gl = canvas.getContext('webgl2')
    if (!gl) {
        alert('Not support.')
        return false
    }

    // Get the strings for our GLSL shaders.
    var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
    var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
    // Link the two shaders into a program.
    var program = createProgram(gl, vertexShader, fragmentShader)
    // Look up where the vertex data need to go.
    var positionLoc = gl.getAttribLocation(program, '_position')
    // Create a buffer and put the three 2d clip space points in it.
    var body_positionBuf = gl.createBuffer()
    // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = body_positionBuf).
    gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
    var body_positions = new Float32Array(
        [
            // Body triangle (Right)
            -0.25,  0,
            0.25,  0,
            0, 0.5,
            // Body triangle (Left) 
            -0.15, 0,
            0.15, 0,
            0.15, -0.25,
            // Body square
            -0.15, 0,
            -0.15, -0.25,
            0.15, -0.25,
        ]   
    )

    var roter_positions = new Float32Array([
        0.15, 0.1,
        0.15, -0.1,
        -0.15, 0.1,

        -0.15, -0.1,
        -0.15, 0.1,
        0.15, 0.1,
    ])

    gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)
    // Code above this line is initialization code.
    // Code below this line is rendering code.

    // webglUtils.resizeCanvasToDisplaySize(gl.canvas)
    // Tell WebGL how to convert from clip space to pixels.
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)

    // Clear the canvas.
    gl.clearColor(0, 0, 0, 0)
    gl.clear(gl.COLOR_BUFFER_BIT)
    
    // Tell it to use our program (pair of shaders).
    gl.useProgram(program)
    // Turn on the attribute.
    gl.enableVertexAttribArray(positionLoc)
    // Bind the position buffer.
    gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)

    // Tell the attribute how to get data out of body_positionBuf (ARRAY_BUFFER).
    var size = 2
    var type = gl.FLOAT
    var normalize = false
    var stride = 0
    var offset = 0 
    gl.vertexAttribPointer(positionLoc, size, type, normalize, stride, offset)
    // Draw.
    var drawType = gl.TRIANGLES
    var count = body_positions.length / 2
    gl.drawArrays(drawType, offset, count)
}

main()
<canvas id="canvas" width="500" height="250"></canvas>

我该怎么办?我不知道..请帮助我,谢谢。

【问题讨论】:

  • 您必须创建第二个缓冲区。当你想绘图时,你必须绑定缓冲区gl.bindBuffer,定义顶点属性gl.vertexAttribPointer,最后是进行绘图调用gl.drawArrays。这 3 个步骤您必须对这两个对象执行。
  • 如果您只想使用 1 个缓冲区,那么您已经定义了一个足够大的数据存储,可以容纳两个数组 gl.bufferData。通过gl.bufferSubData将两个数组的数据添加到缓冲区中。
  • @Rabbid76 谢谢。我有一些问题。顶点着色器代码相同..?对不起..我不明白..
  • @Rabbid76 我可以看看一些代码的例子吗?

标签: javascript html graphics webgl


【解决方案1】:

body_positions中指定的所有顶点坐标中的第一个覆盖roter_positions中指定的顶点。
改变顶点坐标:

var roter_positions = new Float32Array([
    0.15, 0.6,
    0.15, 0.4,
    -0.15, 0.6,

    -0.15, 0.4,
    -0.15, 0.6,
    0.15, 0.6,
])

注意,我建议使用模型矩阵来分别定义每个对象的位置和方向。但请先解决您的问题,然后再进行下一步。

如果要使用 2 个缓冲区,则必须创建 2 个缓冲区对象,并且必须定义两个对象的数据存储:

var body_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)

var roter_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, roter_positions, gl.STATIC_DRAW)  

最后,您可以通过 2 个单独的绘制调用在 2 个单独的缓冲区中绘制网格。您必须在每次绘制调用之前定义通用顶点属性数据数组:

gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)       
gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)

gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)       
gl.drawArrays(gl.TRIANGLES, 0, roter_positions.length / 2)

查看示例,我将更改应用于您的原始代码:

// Vertex shader
var _vertexShader = `
    attribute vec4 _position;
    void main() {
        gl_Position = _position;
    }
`

// Fragment shader
var _fragmentShader = `
    precision mediump float;

    void main() {
        gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
    }
`

// Create shader
function createShader(gl, type, source) {
    var shader = gl.createShader(type)
    gl.shaderSource(shader, source)
    gl.compileShader(shader)
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
    if (success) {
        return shader
    } else {
        console.log(gl.getShaderInfoLog(shader))
        gl.deleteShader(shader)
        // return false
    }
}

// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram()
    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)
    gl.linkProgram(program)
    var success = gl.getProgramParameter(program, gl.LINK_STATUS)
    if (success) {
        return program
    } else {
        console.log(gl.getProgramInfoLog(program))
        gl.deleteProgram(program)
        // return false
    }
}

function main() {
    // Get a WebGL context.
    var canvas = document.getElementById('canvas')
    var gl = canvas.getContext('webgl2')
    if (!gl) {
        alert('Not support.')
        return false
    }

    // Get the strings for our GLSL shaders.
    var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
    var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
    // Link the two shaders into a program.
    var program = createProgram(gl, vertexShader, fragmentShader)
    // Look up where the vertex data need to go.
    var positionLoc = gl.getAttribLocation(program, '_position')
    // Create a buffer and put the three 2d clip space points in it.
    
    var body_positions = new Float32Array(
        [
            // Body triangle (Right)
            -0.25,  0,
            0.25,  0,
            0, 0.5,
            // Body triangle (Left) 
            -0.15, 0,
            0.15, 0,
            0.15, -0.25,
            // Body square
            -0.15, 0,
            -0.15, -0.25,
            0.15, -0.25,
        ]   
    )

    var roter_positions = new Float32Array([
        0.15, 0.6,
        0.15, 0.4,
        -0.15, 0.6,

        -0.15, 0.4,
        -0.15, 0.6,
        0.15, 0.6,
    ])

    var body_positionBuf = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
    gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)

    var roter_positionBuf = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
    gl.bufferData(gl.ARRAY_BUFFER, roter_positions, gl.STATIC_DRAW)
    
    // webglUtils.resizeCanvasToDisplaySize(gl.canvas)
    // Tell WebGL how to convert from clip space to pixels.
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)

    // Clear the canvas.
    gl.clearColor(0, 0, 0, 0)
    gl.clear(gl.COLOR_BUFFER_BIT)
    
    // Tell it to use our program (pair of shaders).
    gl.useProgram(program)
    // Turn on the attribute.
    gl.enableVertexAttribArray(positionLoc)
    // Bind the position buffer.
    
    gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
    gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)       
    gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)

    gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
    gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)       
    gl.drawArrays(gl.TRIANGLES, 0, roter_positions.length / 2)
}

main();
<canvas id="canvas" width="500" height="250"></canvas>

当然,您可以将 2 个数组添加到 1 个缓冲区。您必须创建一个带有数据存储的缓冲区,该缓冲区对于两个数组来说都足够大。通过gl.bufferData将数据添加到缓冲区:

var body_bytes = body_positions.length * 4;
var roter_bytes = roter_positions.length * 4;

var common_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_bytes + roter_bytes, gl.STATIC_DRAW); 
gl.bufferSubData(gl.ARRAY_BUFFER, 0, body_positions);
gl.bufferSubData(gl.ARRAY_BUFFER, body_bytes, roter_positions);

网格可以通过一个单独的绘制调用来绘制:

gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0) 
var no_of_vertices = (body_positions.length + roter_positions.length) / 2      
gl.drawArrays(gl.TRIANGLES, 0, no_of_vertices)

如果必须在绘制第二个网格之前更改某些程序资源(例如模型矩阵统一),那么这也可以通过 2 个绘制调用来完成:

gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)

// change resources here ...

gl.drawArrays(gl.TRIANGLES, body_positions.length / 2, roter_positions.length / 2)

根据问题中的原始代码查看示例:

// Vertex shader
var _vertexShader = `
    attribute vec4 _position;
    void main() {
        gl_Position = _position;
    }
`

// Fragment shader
var _fragmentShader = `
    precision mediump float;

    void main() {
        gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
    }
`

// Create shader
function createShader(gl, type, source) {
    var shader = gl.createShader(type)
    gl.shaderSource(shader, source)
    gl.compileShader(shader)
    var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
    if (success) {
        return shader
    } else {
        console.log(gl.getShaderInfoLog(shader))
        gl.deleteShader(shader)
        // return false
    }
}

// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
    var program = gl.createProgram()
    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)
    gl.linkProgram(program)
    var success = gl.getProgramParameter(program, gl.LINK_STATUS)
    if (success) {
        return program
    } else {
        console.log(gl.getProgramInfoLog(program))
        gl.deleteProgram(program)
        // return false
    }
}

function main() {
    // Get a WebGL context.
    var canvas = document.getElementById('canvas')
    var gl = canvas.getContext('webgl2')
    if (!gl) {
        alert('Not support.')
        return false
    }

    // Get the strings for our GLSL shaders.
    var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
    var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
    // Link the two shaders into a program.
    var program = createProgram(gl, vertexShader, fragmentShader)
    // Look up where the vertex data need to go.
    var positionLoc = gl.getAttribLocation(program, '_position')
    // Create a buffer and put the three 2d clip space points in it.
    
    var body_positions = new Float32Array(
        [
            // Body triangle (Right)
            -0.25,  0,
            0.25,  0,
            0, 0.5,
            // Body triangle (Left) 
            -0.15, 0,
            0.15, 0,
            0.15, -0.25,
            // Body square
            -0.15, 0,
            -0.15, -0.25,
            0.15, -0.25,
        ]   
    )

    var roter_positions = new Float32Array([
        0.15, 0.6,
        0.15, 0.4,
        -0.15, 0.6,

        -0.15, 0.4,
        -0.15, 0.6,
        0.15, 0.6,
    ])

    var body_bytes = body_positions.length * 4;
    var roter_bytes = roter_positions.length * 4;

    var common_positionBuf = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
    gl.bufferData(gl.ARRAY_BUFFER, body_bytes + roter_bytes, gl.STATIC_DRAW); 
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, body_positions);
    gl.bufferSubData(gl.ARRAY_BUFFER, body_bytes, roter_positions);

    // webglUtils.resizeCanvasToDisplaySize(gl.canvas)
    // Tell WebGL how to convert from clip space to pixels.
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)

    // Clear the canvas.
    gl.clearColor(0, 0, 0, 0)
    gl.clear(gl.COLOR_BUFFER_BIT)
    
    // Tell it to use our program (pair of shaders).
    gl.useProgram(program)
    // Turn on the attribute.
    gl.enableVertexAttribArray(positionLoc)
    // Bind the position buffer.
    
    gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
    gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0) 
    var no_of_vertices = (body_positions.length + roter_positions.length) / 2      
    gl.drawArrays(gl.TRIANGLES, 0, no_of_vertices)
}

main();
<canvas id="canvas" width="500" height="250"></canvas>

【讨论】:

  • 非常感谢!现在我对 WebGL 有了一些了解。还有一个问题。那么,当你在 WebGL 中绘制形状时,你会画出这样的形状吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-07
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
  • 1970-01-01
  • 2017-04-09
相关资源
最近更新 更多