以下代码是在浏览器中使用编程语言J(未显示)生成海龟图形的应用程序的一部分。代码有效,但没有动画。相反,海龟的复杂路径只是在最终状态下显示出来。所以我想使用setInterval或setTimeout来产生动画效果,但我看不出如何。
我尝试了setInterval(drawPrimitive, 1000/2);
之类的命令,但结果没有变化。
更新0
当我在setInterval()调用中提供一些虚构的参数时,我确实得到了一些反应。例如,当我输入setInterval(function(){drawPrimitive( gl.LINES, linecolors[0], moves[0]);}, 3000);
然后在我发出乌龟命令之后,该命令立即执行,然后在多达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 :(得分:1)
老实说,您似乎必须真正重新构建代码才能使其按步骤运行
但是,如果你想要这个,你必须改变至少一件事,你需要使用preserveDrawingBuffer: true
创建画布,如
gl = canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true });
这是因为默认情况下,WebGL会在每个事件后清除画布。或者更确切地说,它标志着它在下一次抽签之前被清除。所以,在哪里
drawThing1();
drawThing2();
会显示thing1和thing2,
drawThing1();
setTimeout(drawThing2, 100);
只显示thing2(好吧,你可能会看到thing1片刻)但是当执行超时时,画布将被清除,然后thing2将被绘制到该清除的画布上。为防止清除,您需要设置preserveDrawingBuffer: true
。
这个&#34;功能&#34;专门针对移动设备。为了使WebGL能够在屏幕上绘制和显示具有正确浏览器行为的内容,必须对其进行双重缓冲。当您绘制时,您正在绘制到屏幕外缓冲区。当您的活动退出时,屏幕外缓冲区已交换或已复制 [1]。交换速度更快但是你要交换的缓冲区中的内容是未定义的,因此WebGL会清除它。如果您想要较慢的复制行为,请设置preserveDrawingBuffer: false
。在这种情况下,您总是渲染到相同的屏幕外缓冲区,因此没有理由需要清除它。但是,现在必须在当前事件退出的任何时候复制它。
[1]从技术上讲,当事件退出时它不会被复制或交换,而是被标记 被复制或交换。除非在下次浏览器将页面合成到屏幕时发生,否则没有明确定义复制或交换发生的时间。
虽然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
实际上每隔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或setInterval 中调用drawPrimitive
就行了。
这就是我说它需要进行一些重大改组的原因。或者你需要捕获所有对gl的调用然后再播放它们。 This code attempts to do that。一旦你捕获了所有的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);
那么当我们运行它时会发生什么?
fillStyle
设置为&#34;红色&#34; fillRect
10,10,20,20
fillStyle
设置为&#34;蓝色&#34; fillRect
15,15,20,20
调用fillRect。它吸引了 BLUE ,但你想要红色。10,10,20,20
调用fillRect。它用红色绘制你在第7步看到了问题吗?要绘制的颜色在开头运行的代码中设置,但状态丢失。当fillRect最终被调用1/2秒后,它将使用错误的颜色进行绘制。
在你的例子中也发生了同样的事情。有{10}或100个15,15,20,20
次调用为gl
设置状态。哪些缓冲区取决于哪些属性,哪个着色器程序是最新的,哪些纹理绑定,哪些值是哪些制服,混合模式是什么等等等等。所有这些都是错误时{{最后从setTimeout / setInterval
如果您希望它能够工作,您必须将所有状态移动到设定的间隔。由于WebGL示例太复杂,我将展示画布2d示例。为了使它工作,你必须做类似
的事情drawPrimitive
画布2d还有很多其他状态。当前drawPrimitive
,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);
,transform
,globalCompositingOperation
等等所有这些都需要移动到每个步骤,否则当它运行时它将使用无论状态从其他步骤中遗留下来。
在您的示例中,您还必须摆脱所有循环,或者让它们生成功能,逐步完成所有操作并为每个循环设置所需的所有状态。它不会是一个小小的变化,让它在步骤中运行。换句话说,您还需要在setInterval / setTimeout函数中调用strokeStyle
之间包含每个font
调用。
有很多方法可以使它工作,但它需要大量的重写代码,否则它需要记录gl
调用的东西并稍后播放它们。我链接到上面的一些代码记录了所有drawPrimitive
。调用。您可以将其用作以较慢速率播放gl
电话的基础。尽管如此,它仍然不会简单。有些gl
次调用需要立即发生(例如gl
,gl
和类似的功能,而其他需要稍后发生。)因此,无论需要做多少工作,都需要做。
答案 1 :(得分:-1)
尝试setInterval(function(){drawPrimitive();},500)
,应该可以。