Трабл с нанесением дерева в webgl - PullRequest
0 голосов
/ 13 октября 2019

На этот раз снова получаем больше ошибок, за исключением того, что на этот раз он находится на стороне webgl, а не на стороне математического алгоритма.

Мой предыдущий пост был только о рисовании простого 2d-рекурсивного дерева. Теперь я пытаюсь нарисовать дерево в месте щелчка мышью, с красными линиями, если щелкнуть левой кнопкой мыши, и синим, если справа. Я исправил предыдущую проблему и смог отобразить дерево в предыдущей сборке программы. Однако теперь, когда я нажимаю на холст, дерево даже не появляется. Когда я консоль регистрирую массив, где хранятся точки, все точки кажутся. Я думаю, что что-то упустил, но я не знаю достаточно webgl, чтобы знать, что это может быть.

Я создал рабочую программу, которая может рисовать точки разного цвета в зависимости от щелчка мыши и положения мыши, но у меня все еще недостаточно опыта, чтобы понять, что у меня есть в этой программе, которая позволяет ейработать и почему эта текущая программа не может ничего отображать.

моя текущая программа:

// Vertex shader program
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +
    'void main() {\n' +
    '  gl_Position = a_Position;\n' +
    '}\n';

// Fragment shader program
var FSHADER_SOURCE =
    'precision mediump float;\n' +
    'uniform vec4 u_FragColor;\n' +
    'void main() {\n' +
    '  gl_FragColor = u_FragColor;\n' +
    '}\n';

var m = 0;

function main() {
    var canvas = document.getElementById('webgl');
    // Get the rendering context for WebGL
    var uniform1i = 0;

    var gl = getWebGLContext(canvas);
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    // Initialize shaders
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    // // Get the storage location of a_Position
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return;
    }

    // Get the storage location of u_FragColor
    var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
    if (!u_FragColor) {
        console.log('Failed to get the storage location of u_FragColor');
        return;
    }

    // Register function (event handler) to be called on a mouse press
    canvas.onmousedown = function (ev) { click(ev, gl, canvas, a_Position, u_FragColor) };

    // Specify the color for clearing <canvas>
    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.LINES, 0, 1, m);
}


function click(ev, gl, canvas, a_Position, u_FragColor) {
    var x = ev.clientX; // x coordinate of a mouse pointer
    var y = ev.clientY; // y coordinate of a mouse pointer
    var rect = ev.target.getBoundingClientRect();

    x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
    y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);


    if (ev.button == 0) {
        var depth = 4;
        gl.uniform4f(u_FragColor, 1.0, 0, 0, 1.0);// Red
        //red tree, 4 steps, length 50, halved each step

        // Write the positions of vertices to a vertex shader
        var n = initVertexBuffers(gl, x, y);
        if (n < 0) {
            console.log('Failed to set the positions of the vertices');
            return;
        }
        m = n;

    }
    if (ev.button == 2) {
        var depth = 6;
        //blue tree, 6 steps, length 40, halved each step
        gl.uniform4f(u_FragColor, 0, 0, 1.0, 1.0);// Blue
        // Write the positions of vertices to a vertex shader
        var n = initVertexBuffers(gl, x, y);
        if (n < 0) {
            console.log('Failed to set the positions of the vertices');
            return;
        }
        m = n;
    }
}


function initVertexBuffers(gl, x, y) {
    let start = [];
    let points = createPoints(x, y, 0.4, 4, Math.PI / 2, start);
    console.log(points);
    var vertices = new Float32Array(points);
    let n = points.length / 2; // The number of vertices
    // Create a buffer object
    var vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
        console.log('Failed to create the buffer object');
        return -1;
    }

    // Bind the buffer object to target
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    // Write date into the buffer object
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }
    // Assign the buffer object to a_Position variable
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    // Enable the assignment to a_Position variable
    gl.enableVertexAttribArray(a_Position);

    return n;
}

//var points = [x, y];
//var angle = 0;
//var prevPoints = [];
//var prevPointIndex = 0;
function createPoints(x, y, length, depth, angle, points) {
    if (depth > 0) {
        //draws line
        let x2 = x + length * Math.cos(angle);
        let y2 = y + length * Math.sin(angle);
        points.push(x, y, x2, y2);

        //draw left branch;
        createPoints(x2, y2, length / 2, depth - 1, angle + Math.PI / 4, points);

        //goes back
        //points.push(x2, y2);

        //draw right branch
        createPoints(x2, y2, length / 2, depth - 1, angle - Math.PI / 4, points);

        //goes back
        //points.push(x2, y2);
        //console.log(points);
        return points;
    }
    return;
}

мои ставки заключаются в том, что я что-то упускаю в функциях main или click, ноЯ публикую все, потому что я не уверен на 100%.

1 Ответ

0 голосов
/ 13 октября 2019

Код, который вы опубликовали, вызывает только gl.drawXXX один раз в основном, поэтому он никогда не будет ничего рисовать.

У вас есть настройки, поэтому при нажатии мыши будет вызываться click, ноclick никогда не вызывает gl.drawXXX

Далее, каждый раз, когда вызывается click, вы создаете новый буфер с новым набором точек. Это не обычный способ использования WebGL. Обычный способ - настроить точки один раз (один раз для каждой вещи, которую вы хотите нарисовать), а затем использовать матрицы для изменения положения, ориентации и масштаба.

Я предлагаю вам прочитать некоторые другие учебные пособия по WebGL. Этот тип делает то, что вы делаете сейчас, но он описывает, как изменить положение , ориентация и масштаб ,затем как это сделать с матрицами для большей гибкости . Он также охватывает рисование нескольких вещей

В любом случае исправьте свой код так, как вам нужно рисовать после изменения вершин

// Vertex shader program
var VSHADER_SOURCE = 
    'attribute vec4 a_Position;\n' +
    'void main() {\n' +
    '  gl_Position = a_Position;\n' +
    '}\n';

// Fragment shader program
var FSHADER_SOURCE =
    'precision mediump float;\n' +
    'uniform vec4 u_FragColor;\n' +
    'void main() {\n' +
    '  gl_FragColor = u_FragColor;\n' +
    '}\n';

var m = 0;

function main() {
    var canvas = document.getElementById('webgl');
    // Get the rendering context for WebGL
    var uniform1i = 0;

    var gl = canvas.getContext('webgl');
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    // Initialize shaders
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    // // Get the storage location of a_Position
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return;
    }

    // Get the storage location of u_FragColor
    var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
    if (!u_FragColor) {
        console.log('Failed to get the storage location of u_FragColor');
        return;
    }

    // Register function (event handler) to be called on a mouse press
    canvas.onmousedown = function (ev) { click(ev, gl, canvas, a_Position, u_FragColor) };

}


function click(ev, gl, canvas, a_Position, u_FragColor) {
    var x = ev.clientX; // x coordinate of a mouse pointer
    var y = ev.clientY; // y coordinate of a mouse pointer
    var rect = ev.target.getBoundingClientRect();

    x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
    y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);

    if (ev.button == 0) {
        var depth = 4;
        gl.uniform4f(u_FragColor, 1.0, 0, 0, 1.0);// Red
        //red tree, 4 steps, length 50, halved each step

        // Write the positions of vertices to a vertex shader
        var n = initVertexBuffers(gl, x, y);
        if (n < 0) {
            console.log('Failed to set the positions of the vertices');
            return;
        }
        m = n;

    }
    if (ev.button == 2) {
        var depth = 6;
        //blue tree, 6 steps, length 40, halved each step
        gl.uniform4f(u_FragColor, 0, 0, 1.0, 1.0);// Blue
        // Write the positions of vertices to a vertex shader
        var n = initVertexBuffers(gl, x, y);
        if (n < 0) {
            console.log('Failed to set the positions of the vertices');
            return;
        }
        m = n;
    }
    
    // Specify the color for clearing <canvas>
    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.LINES, 0, m);
    
}


function initVertexBuffers(gl, x, y) {
    let start = [];
    let points = createPoints(x, y, 0.4, 4, Math.PI / 2, start);
    console.log(points);
    var vertices = new Float32Array(points);
    let n = points.length / 2; // The number of vertices
    // Create a buffer object
    var vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
        console.log('Failed to create the buffer object');
        return -1;
    }

    // Bind the buffer object to target
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    // Write date into the buffer object
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }
    // Assign the buffer object to a_Position variable
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    // Enable the assignment to a_Position variable
    gl.enableVertexAttribArray(a_Position);

    return n;
}

//var points = [x, y];
//var angle = 0;
//var prevPoints = [];
//var prevPointIndex = 0;
function createPoints(x, y, length, depth, angle, points) {
    if (depth > 0) {
        //draws line
        let x2 = x + length * Math.cos(angle);
        let y2 = y + length * Math.sin(angle);
        points.push(x, y, x2, y2);

        //draw left branch;
        createPoints(x2, y2, length / 2, depth - 1, angle + Math.PI / 4, points);

        //goes back
        //points.push(x2, y2);

        //draw right branch
        createPoints(x2, y2, length / 2, depth - 1, angle - Math.PI / 4, points);

        //goes back
        //points.push(x2, y2);
        //console.log(points);
        return points;
    }
    return;
}
main();

//----
function initShaders(gl, vSrc, fSrc) {
  const program = twgl.createProgram(gl, [vSrc, fSrc]);
  gl.program = program; // THIS IS EXTREMELY BAD AND WRONG CODE!!!
  gl.useProgram(program); // THIS IS ALSO WRONG!
  return program;
}
canvas { border: 1px solid black; }
<canvas id="webgl"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

Есть много проблем с кодом

  1. gl.program это не вещь. Какую бы книгу или учебник вы ни изучали, это ужасно, плохо и неправильно. В программах WebGL обычно есть несколько шейдерных программ, поэтому функция, подобная initShaders, должна возвращать созданную программу, чтобы вы могли вызывать ее несколько раз с различным источником вершинных шейдеров и источником фрагментных шейдеров для создания нескольких программ. Он не должен взламывать одну созданную программу в контексте webgl, где она не принадлежит.

    Примечание: даже если я реализовал initShaders, так как вы не предоставили его, ясно из вашего кода, что он делал gl.program = gl.createProgram. Это бессмысленный код. Распространено иметь несколько программ.

  2. вызов gl.useProgram в initShaders также, возможно, неправильно. (Опять же, ясно, поскольку в вашем коде нигде в вашем коде не указано, что ваша версия initShaders делает это. Опять же, это не имеет смысла, поскольку на обычной странице WebGL у вас будет несколько шейдеров, поэтому вам нужно будет вызвать gl.useProgramнесколько раз.

  3. Учебное пособие, которое вы читаете, выглядит устаревшим, поскольку оно объединяет строки для GLSL. Самый простой способ сделать GLSL - это использовать многострочные литералы шаблона

    var VSHADER_SOURCE = `
        attribute vec4 a_Position;
        void main() {
          gl_Position = a_Position;
        }
    `;
    

    намного проще

  4. Вызывает какую-то функцию getWebGLContext. Ничего такого не нужно. Просто используйте canvas.getContext('webgl')

  5. вызов gl.drawArrays имеет m в неправильном месте. Правильный способ вызова gl.drawArrays - gl.drawArrays(primitiveType, offset, vertexCount). Смещение почти равно 0. Нет 4-го параметра.

Пожалуйста, ознакомьтесь с некоторыми лучшими учебниками, подобными тем, которые приведены выше.

...