Применить setInterval к какой функции

0

Следующий код является частью приложения для создания графики черепахи в браузере [с использованием языка программирования J (не показан)]. Код работает, но анимации нет. Вместо этого сложные пути черепах просто отображаются в их конечном состоянии, все сразу. Поэтому я хотел бы использовать setInterval или setTimeout для создания эффекта анимации, но я не вижу, как это сделать.

Я пробовал команды вроде setInterval(drawPrimitive, 1000/2); но без изменений результатов.

Обновление 0

Я получаю немного реакции, когда я поставлю некоторые фиктивные аргументы в вызове setInterval(). Например, когда я вхожу в setInterval(function(){drawPrimitive( gl.LINES, linecolors[0], moves[0]);}, 3000); то после того, как я выдаю команду черепахи, команда выполняется немедленно, а затем после 3 секунд - но часто меньше - холст становится белым и остается белым до тех пор, пока я не выдаст еще одну команду. Но в рисовании/рисовании анимации пока нет. Означает ли это что-нибудь?

Обновление 0

Спасибо за идеи.

Кстати, если вы хотите посмотреть, о чем я говорю, вы можете посмотреть примерно 1:34 в этом видео или просто на оригинальном заставке видео.

drawTurtles(linecolors,moves,leftColors,rightColors,backColors,bottoms,lefts,rights,backs,bottomNs,leftNs,rightNs,backNs);

    function drawTurtles(linecolors,moves,leftColor,rightColor,backColor,bottom,left,right,back,bottomNs,leftNs,rightNs,backNs){
    gl.uniform1i( uLit, 0 );
    drawLines(linecolors,moves)
    bottomColor = [ 1,1,1,0]; 
    gl.uniform1i( uLit, 1 );
    for(var i=0;i<leftColor.length;i++)
    {
    gl.uniform3f( uNormal, leftNs[i][0],leftNs[i][1],leftNs[i][2]);
    drawPrimitive( gl.TRIANGLES, leftColor[i], left[i]);
    gl.uniform3f( uNormal, rightNs[i][0],rightNs[i][1],rightNs[i][2]);
    drawPrimitive( gl.TRIANGLES, rightColor[i], right[i]);
    gl.uniform3f( uNormal, backNs[i][0],backNs[i][1],backNs[i][2]);
    drawPrimitive( gl.TRIANGLES, backColor[i], back[i]);
    gl.uniform3f( uNormal, -bottomNs[i][0],-bottomNs[i][1],-bottomNs[i][2]);
    drawPrimitive( gl.TRIANGLES, bottomColor, bottom[i]);
    }
}

function drawLines(linecolors,moves) { 
setInterval(drawPrimitive, 1000/2);
    gl.lineWidth(2);
    gl.uniform1i( uLit, 0 );
    for(var i=0;i<linecolors.length;i++)
    {
    drawPrimitive( gl.LINES, linecolors[i], moves[i]);
    }
    gl.lineWidth(1);
}


function drawPrimitive( primitiveType, color, vertices ) {
    gl.enableVertexAttribArray(aCoords);
    gl.bindBuffer(gl.ARRAY_BUFFER,aCoordsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STREAM_DRAW);
    gl.uniform4fv(uColor, color);
    gl.vertexAttribPointer(aCoords, 3, gl.FLOAT, false, 0, 0);
    gl.drawArrays(primitiveType, 0, vertices.length/3);
}

function init() {
    var canvas = document.getElementById("glcanvas");
    var vertexShaderSource = getTextContent("vshader"); 
    var fragmentShaderSource = getTextContent("fshader");
    var prog = createProgram(gl,vertexShaderSource,fragmentShaderSource);
    linecolors = [];
    moves = [];
    gl.useProgram(prog);
    aCoords =  gl.getAttribLocation(prog, "coords");
    uModelview = gl.getUniformLocation(prog, "modelview");
    uProjection = gl.getUniformLocation(prog, "projection");
    uColor =  gl.getUniformLocation(prog, "color");
    uLit =  gl.getUniformLocation(prog, "lit");
    uNormal =  gl.getUniformLocation(prog, "normal");
    uNormalMatrix =  gl.getUniformLocation(prog, "normalMatrix");
    aCoordsBuffer = gl.createBuffer();
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);
}
  • 0
    В самом деле, не используйте setInterval и setTimeout из-за оптимизации. См: developer.mozilla.org/en/docs/Web/API/... и paulirish.com/2011/requestanimationframe-for-smart-animating для того, как использовать requestAnimationFrame .
  • 0
    Я немного читал о requestAnimationFrame () и его преимуществах (а также о том, что он пока недоступен для всех браузеров), но я подумал, что сейчас начну с setInterval (), но не могу даже заставить setInterval () работать. Поэтому я мог бы сначала использовать некоторую помощь с setInterval (), пожалуйста.
Теги:
webgl
animation

2 ответа

1
Лучший ответ

Честно говоря, похоже, вам нужно будет реально перестроить код, чтобы он выполнялся пошагово

Но, по крайней мере, одно дело, которое вы должны изменить, если хотите, чтобы это сработало, вам нужно создать свой холст с помощью preserveDrawingBuffer: true как в

gl = canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true });

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

drawThing1();
drawThing2();

Покажу как вещь1, так и вещь2,

drawThing1();
setTimeout(drawThing2, 100);

Покажет только вещь2 (ну, вы можете увидеть вещь1 на мгновение), но когда тайм-аут будет выполнен, холст будет очищен, а затем предмет2 будет нарисован на этот очищенный холст. Чтобы предотвратить очистку, вам необходимо установить preserveDrawingBuffer: true.

Эта "особенность" специально предназначена для мобильных устройств. Для того, чтобы WebGL рисовал и показывал материал на экране с правильным поведением браузера, он должен быть дважды буферизирован. Когда вы рисуете, вы рисуете в буфер вне экрана. Когда ваше событие выйдет из того, что внеэкранный буфер обменивается или копируется [1]. Обмен происходит быстрее, но то, что в буфере, с которым вы будете обмениваться, не определено, поэтому WebGL очищает его. Если вы хотите более медленное копирование, вы устанавливаете preserveDrawingBuffer: false. В этом случае вы всегда показываете один и тот же буфер вне экрана, поэтому нет необходимости его очищать. Но теперь его нужно копировать в любое время, когда текущее событие завершается.

[1] Технически он не копируется или не заменяется при выходе из события, а скорее становится помеченным для копирования или замены. Когда именно происходит копирование или своп, четко не определено иначе, чем это произойдет в следующий раз, когда браузер будет компоновать страницу на экране.

Также while preserveDrawingBuffer: true значительной степени требует копирования, установка его в false не гарантирует обмен. Это просто означает, что браузер может меняться, если он считает, что лучше всего делать. Независимо от того, будет ли он меняться или копироваться, если preserveDrawingbuffer false он очистит буфер, чтобы поведение было согласованным.

Что касается реструктуризации, то просто изменить drawPrimitive будет недостаточно.

Сначала setInterval(drawPrimitive, 1000/2) просто вызовет drawPrimitive без аргументов. Но, как вы видите, drawPrimitive требует 3 аргумента. Чтобы предоставить аргументы, вы можете назвать это так

setInterval(function(i) {
  return function() {
    drawPrimitive(gl.LINES, linecolors[i], moves[i]);
  }(i),
  1000/2);}

Чтобы разделить это, у нас есть функция, которая создает функцию для вызова drawPrimitives. Более общая версия может выглядеть так:

function makeFunctionToCallDrawPrimitive(arg1, arg2, arg2) {
  return function() {
    drawPrimitive(arg1, arg2, arg3);
  }
}

Теперь вы можете так называть

function drawLines(linecolors,moves) { 
  gl.lineWidth(2);
  gl.uniform1i( uLit, 0 );
  for(var i=0;i<linecolors.length;i++)
  {
    var func = makeFunctionToCallDrawPrimitive(gl.LINES, linecolors[i], moves[i]);
    setTimeout(func, i * 500);
  }
  gl.lineWidth(1);
}

Мы могли бы даже создать функцию для установки таймаута, а также

var count = 0;
function deferredDrawPrimitive(arg1, arg2, arg3) {
    var func = makeFunctionToCallDrawPrimitive(arg1, arg2, arg3);
    setTimeout(func, ++count * 500);
}

А затем измените каждый вызов на drawPrimtive на то, чтобы вместо этого был вызов deferredDrawPrimitive но этого все еще недостаточно для общего случая.

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

...
gl.uniform3f( uNormal, leftNs[i][0],leftNs[i][1],leftNs[i][2]);
drawPrimitive( gl.TRIANGLES, leftColor[i], left[i]);
gl.uniform3f( uNormal, rightNs[i][0],rightNs[i][1],rightNs[i][2]);
drawPrimitive( gl.TRIANGLES, rightColor[i], right[i]);
...

Вы можете изменить это на это

...
gl.uniform3f( uNormal, leftNs[i][0],leftNs[i][1],leftNs[i][2]);
deferredDrawPrimitive( gl.TRIANGLES, leftColor[i], left[i]);
gl.uniform3f( uNormal, rightNs[i][0],rightNs[i][1],rightNs[i][2]);
deferredDrawPrimitive( gl.TRIANGLES, rightColor[i], right[i]);
...

Но строки gl.uniform3f влияют на то, как drawPrimtive будет функционировать. Вы должны каким-то образом сохранить состояние этих и всех других вызовов gl чтобы заставить его делать то, что вы хотите. Другими словами, просто вызов drawPrimitive в setTimeout или setInterval НЕ РАБОТАЕТ.

Вот почему я сказал, что это потребует серьезной реструктуризации. Либо это, либо вам нужно записать все вызовы на gl, а затем воспроизвести их. Этот код пытается это сделать. После того, как вы заработаете все вызовы gl, вы сможете воспроизводить их на более медленной скорости. Однако вам придется писать этот код.


Из комментариев моя точка зрения, что ЭТО НЕ РАБОТАЕТ, потому что drawPrimitives зависит от состояния, было пропущено. Возможно, этот пример поможет сделать это понятным. Предположим, что у вас была программа, использующая canvas 2d, которая рисует два прямоугольника разных цветов, подобных этому.

ctx.fillStyle = "red";
ctx.fillRect(10, 10, 20, 20);
ctx.fillStyle = "blue";
ctx.fillRect(15, 15, 20, 20);

Теперь предположим, что вы хотите, чтобы он показывал каждый шаг 1/2 секунды, так что вы пишете deferredFillRect подобное deferredDrawPrimitive. Затем вы меняете код следующим образом.

ctx.fillStyle = "red";
deferredFillRect(10, 10, 20, 20);
ctx.fillStyle = "blue";
deferredFillRect(15, 15, 20, 20);

Итак, что происходит, когда мы запускаем это?

  1. fillStyle установлен на "красный"
  2. setTimeout - это установка для вызова fillRect 1/2 секунды с 10,10,20,20
  3. fillStyle установлен на "синий"
  4. setTimeout - это установка для вызова fillRect 1 секунду с 15,15,20,20
  5. ваше событие выйдет (возможно, событие загрузки окна)
  6. проходит 1/2 секунды
  7. fillRect вызывается с 10,10,20,20. Он рисует синим цветом, но вам нужен красный.
  8. проходит 1/2 секунды
  9. fillRect вызывается с 15,15,20,20. Он рисует красным

Вы видите проблему на шаге 7? Цвет рисунка устанавливается в коде, который запускается в начале, но это состояние теряется. Когда fillRect, наконец, будет вызван через 1/2 секунды позже, он будет рисовать с неправильным цветом.

То же самое происходит и в вашем примере. Там 10 или 100 drawPrimitive gl вызывает это состояние для drawPrimitive. Какие буферы идут, какие атрибуты, какая программа шейдера текущая, какие текстуры связаны, какие значения находятся в форме, какой режим drawPrimitive т.д. И т.д. И т.д. Все это НЕПРАВИЛЬНО, когда drawPrimitive наконец вызывается из setTimeout/setInterval

Если вы хотите, чтобы он работал, вам пришлось бы переместить все это состояние на заданный интервал. Поскольку пример WebGL слишком сложный, я покажу пример canvas 2d. Чтобы заставить его работать, вам нужно будет сделать что-то вроде

function step1() {
  ctx.fillStyle = "blue";
  ctx.fillRect(10,10,20,20);
}
function step2() {
  ctx.fillStyle = "red";
  ctx.fillRect(15,15,20,20);
}
setTimeout(step1, 500);
setTimeout(step2, 1000);

Там много других состояний в холсте 2d. В настоящее время transform, то globalCompositingOperation, то strokeStyle, то font и т.д. и т.д. и т.д. Все, что нужно будет перенести на каждый шаг в противном случае, когда это работает он будет использовать все состояние, оставшийся от других шагов.

В вашем примере вам также придется избавиться от всех циклов, иначе они будут генерировать функции, которые делают все по шагам и устанавливают все необходимое для каждого из них. Это не будет небольшим изменением, чтобы заставить его работать пошагово. Другими словами, вам нужно включить каждый вызов gl между вызовами drawPrimitive в ваших drawPrimitive setInterval/setTimeout.

Там много способов заставить его работать, но для этого потребуется МАССИВНОЕ ЗАПИСЬ кода, иначе ему потребуется что-то, что записывает вызовы gl и воспроизводит их позже. Я связан с некоторым кодом выше, который записывает все gl. звонки. Вы можете использовать это в качестве основы для повторного воспроизведения вызовов gl более медленными темпами. Тем не менее, это было бы непросто. Некоторые вызовы gl должны выполняться немедленно (например, getUniformLocation, createTexture и аналогичные функции, тогда как другие должны произойти позже). Поэтому, несмотря на то, что нужно много работы.

  • 0
    Я больше смотрю на requestAnimationFrame (rAF) и подозреваю, что это может сработать для меня в конце концов. Держу пари, что ваш ответ правильный, но я не понимаю, какая часть моего кода требует реструктуризации. Требуется ли реструктуризация, потому что drawPrimitive () вызывается внутри цикла, поэтому мне просто нужно поместить rAF () в drawPrimitive ()? Или это другой вид реструктуризации? Кроме того, если я использую rAF с drawPrimitive, у которого уже есть 3 аргумента, могу ли я добавить аргумент метки времени, требуемый для rAF, просто добавив его в префикс к списку из существующих 3 аргументов drawPrimitive, создав 4 аргумента? Благодарю.
  • 0
    В первом коде отсутствует окончание}, но даже когда я добавил его в цикл for в drawLines (), я не смог получить никаких результатов. Поэтому вместо этого я попытался сделать makeFunctionToCallDrawPrimitive с вашими пересмотренными линиями рисования, но не увидел никаких задержек, а также не увидел никаких линий, но увидел окончательную черепаху. В этом эксперименте я заменил setTimeout на setInterval с тем же результатом. Затем я попробовал версию с var count = 0 но не увидел ни строк, ни черепах. Это насколько я получил. Спасибо за все ваши идеи. Я действительно не знаю, следую ли я последним, и, возможно, испортил те, о которых я сообщил.
Показать ещё 3 комментария
-2

Попробуйте setInterval(function(){drawPrimitive();},500), должен работать.

  • 0
    Я добавил это в init () без изменений в результатах. Есть ли какая-то инициализация для функции setInterval (), которую мне нужно сделать?
  • 0
    Нет, больше ничего нет ... попробуйте увеличить интервал, скажем, 3000 или больше, возможно, он слишком быстрый.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню