2014-04-26 29 views
0

以下代碼是在瀏覽器中使用編程語言J(未顯示)生成烏龜圖形的應用程序的一部分。代碼有效,但沒有動畫。相反,海龜的複雜路徑只能一次顯示在最終狀態。所以我想使用setInterval或setTimeout來產生一個動畫效果,但我看不到如何。將setInterval應用於哪個函數

我嘗試過像setInterval(drawPrimitive, 1000/2);這樣的命令,但結果沒有任何變化。

更新0

我做的,當我在setInterval()調用提供一些虛假的論點得到一點反應。例如,當我輸入setInterval(function(){drawPrimitive(gl.LINES, linecolors[0], moves[0]);}, 3000);,然後在發出turtle命令後,立即執行該命令,然後在多達3秒後 - 但通常更少 - 畫布變白並保持白色,直到我發出另一個命令。但有仍然在繪圖/繪畫中沒有動畫。這有什麼建議嗎?

更新0

感謝的想法。

順便說一句,如果你想看看我在說什麼,你可以看看about 1:34 in this video或只是在視頻的原始閃屏。

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',因爲優化。請參閱:https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame和http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/瞭解如何使用requestAnimationFrame '。 –

+0

我已經讀了一些關於requestAnimationFrame()和它的優點(以及它還沒有普遍適用於所有的瀏覽器),但我想我現在要從setInterval()開始,但甚至無法使setInterval()工作。所以我可以先使用setInterval()的一些幫助。 – zerowords

回答

1

老實說,好像你必須要真正重新構造的代碼,使其逐步

但運行,至少有一點,如果你想要這個工作,你必須改變,你需要創建畫布preserveDrawingBuffer: true

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

這是因爲默認情況下WebGL的清除在每個事件後在畫布上。或者說它標誌着它在下一次平局之前被清除。因此,那裏的

drawThing1(); 
drawThing2(); 

會同時顯示thing1和thing2,

drawThing1(); 
setTimeout(drawThing2, 100); 

只會顯示thing2(當然,你可能會看到thing1了一下),但在執行超時時,畫布會清除,然後thing2將被繪製到清除的畫布上。要防止清除,您需要設置preserveDrawingBuffer: true

此「功能」特別針對移動設備。爲了讓WebGL在屏幕上以正確的瀏覽器行爲繪製和顯示內容,它必須進行雙緩衝。當你繪製你正在繪製到屏幕外的緩衝區。當您的事件退出時,屏幕外緩衝區是交換複製 [1]。交換速度更快,但是您將交換的緩衝區內容未定義,因此WebGL將清除它。如果您想要更慢的複製行爲,請設置preserveDrawingBuffer: false。在這種情況下,你總是渲染到相同的屏幕外緩衝區,所以沒有理由需要清除它。但是,現在必須在當前事件退出時進行復制。

[1]從技術上講,它不會在事件退出時被複制或交換,而是被標記爲 以被複制或交換。當複製或交換髮生時,除了在瀏覽器下次將頁面合成到屏幕時發生的情況之外,沒有明確定義。

同時preserveDrawingBuffer: true幾乎需要複製,將其設置爲false不保證交換。它只是意味着瀏覽器可以交換,如果它認爲這是最好的事情。無論它是交換還是複製,如果preserveDrawingbufferfalse它將清除緩衝區,以便行爲一致。

至於重組,只是改變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實際上會每1/2秒調用一次正確的參數,還有其他的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調用的狀態,以便讓它按照你的要求做。換句話說,只需在setTimeout中調用drawPrimitive或setInterval 將不起作用

這就是爲什麼我說這將需要一些重大的重組。無論是或者你需要捕捉所有的電話,然後回放。 This code attempts to do that。一旦你獲得了所有的通話記錄,你就可以以更低的速度回放。儘管如此,你仍然必須編寫該代碼。


從評論我的觀點這是行不通的,因爲是drawPrimitives依賴於國家被錯過了。也許這個例子將有助於說清楚。假設你有一個使用canvas 2d的程序,繪製兩個不同顏色的矩形。

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

現在讓我們假設你想顯示它的每一步1/2第二分開,所以你寫一個類似deferredDrawPrimitivedeferredFillRect。然後,您可以像這樣更改代碼。

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. 您的事件退出(可能是窗口的Load事件)
  6. 1/2秒通過
  7. fillRect與10,10,20,20一起被調用。它吸取藍色但你想要紅色。
  8. 1/2秒通過
  9. fillRect與15,15,20,20一起被調用。它汲取紅色

你看到在步驟#7的問題?繪製的顏色設置在開始運行的代碼中,但狀態已丟失。當fillRect終於在1/2秒後被調用時,它將用繪製錯誤的顏色

在你的例子中發生了同樣的事情。 gl調用的設置爲drawPrimitive的狀態爲10s或100s。它緩衝去哪些屬性,其着色器程序是最新的,其紋理的約束,這些值是在制服,模式是什麼摻混物,等等等等等等。所有這些是WRONGdrawPrimitive最終從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); 

在canvas 2d中還有很多其他狀態。目前的transform,globalCompositingOperation,strokeStyle,font等等等等。所有這些都需要移動到每一步,否則當它運行時它將使用任何狀態從其他步驟遺留下來。

在你的例子中,你還必須擺脫所有的循環,或者讓它們生成一些能夠逐步完成所有任務的函數,併爲每個循環設置所有必需的狀態。將它逐步運行並不會是一個小小的改變。換句話說,你就需要調用之間每gl通話包括在您的setInterval/setTimeout的功能drawPrimitive爲好。

有很多方法可以使它工作,但它需要代碼的大規模重寫,否則需要記錄gl調用並稍後播放它們的內容。我鏈接上面的一些代碼記錄所有gl。調用。您可以將其用作以較慢的速率播放gl來電的基礎。但它仍然不會很簡單。一些gl調用需要立即發生(如getUniformLocation,createTexture和類似的功能,而其他需要稍後發生)。所以不管有多少工作需要完成。

+0

我一直在尋找更多的requestAnimationFrame(RAF),並且懷疑它畢竟可能適用於我。雖然我敢打賭你的回答是正確的,但我不明白我的代碼的哪一部分需要重組。因爲drawPrimitive()是在一個循環內調用的,所以我只需要將rAF()放入drawPrimitive()中就可以完成重構了嗎?或者這是另一種重組?另外,如果我使用帶有drawPrimitive的rAF(已經有3個參數),我可以通過將它添加到現有的3個drawPrimitive參數列表中來添加rAF所需的時間戳參數,從而生成4個參數?謝謝。 – zerowords

+0

第一個代碼缺少一個結束},但即使如此,當我在for循環中將其添加到drawLines()中時,我仍然無法獲得結果 。相反,我嘗試了makeFunctionToCallDrawPrimitive與您的修改過的drawline,但沒有看到延遲,也沒有看到線條,但看到了最終的烏龜。在那個實驗中,我使用setInterval將setTimeout替換爲相同的結果。然後我嘗試了'var count = 0'的版本,但沒有看到線條和龜。這就像我已經得到的那樣。感謝您的所有想法。我真的不知道我是否會跟隨後面的那些,並且可能弄亂了我報道的那些。 – zerowords

+0

我沒有說我的更改會起作用。你原來的問題是關於如何調用drawPrimitive。但正如我所指出的,drawPrimitive是依賴於其他設置。因此,稍後在setTimeout或setInterval中調用它將不起作用,除非您還保存所有其他狀態,並將它們放在相應的setTimeout中。我會添加更多的答案。 – gman

-1

嘗試setInterval(function(){drawPrimitive();},500),應該工作。

+0

我在init()中添加了結果沒有變化。是否還需要對setInterval()函數進行一些初始化操作? – zerowords

+0

不,沒有更多的事情...嘗試增加間隔讓可以說3000或更大,也許它太快。 – Makerimages

+0

這也沒有區別。是否有一個問題,函數drawPrimitive()包含兩個不在setInterval中提供的參數?例如,當我輸入'setInterval(function(){drawPrimitive(gl.LINES,linecolors [0],moves [0]);},3000);'然後我發出一個turtle命令,命令立即執行並且然後經過多達3秒 - 但通常更少 - 畫布變白並保持白色,直到我發出另一個命令。 – zerowords