我使用Canvas元素制作了一个游戏,并且每帧都会绘制300多张图像,我不得不让它以<20 fps的速度运行,需要60 +%CPU:
所以我想我最好的选择是切换到WebGL。我一直在搜索网页,寻找教程和示例。这是一个复杂的东西,但我设法破解了一个工作源。
<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL - 2D Image</title>
<link type="text/css" href="http://games.greggman.com/downloads/examples/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
<script type="text/javascript" src="http://games.greggman.com/downloads/examples/webgl/resources/webgl-utils.js"></script>
<script>
window.onload = main;
function main() {
image = new Image();
image.src = "http://games.greggman.com/downloads/examples/webgl/resources/leaves.jpg"; // MUST BE SAME DOMAIN!!!
image.onload = function() {
setUp();
setInterval(function(){
x1 += 1;
y1 += 1
for (i = 0; i < 30; i++) {
for (e = 0; e < 10; e++) {
//render(image, x1+i*5, y1+e*5);
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set a rectangle the same size as the image.
setRectangle(gl, x1+i*5, y1+e*5, image.width, image.height);
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}
},1000/60);
}
}
var x1 = 0;
var y1 = 0;
var image;
var canvas;
var gl;
var texture;
function setUp() {
canvas = document.getElementById("canvas");
gl = getWebGLContext(canvas);
// setup GLSL program
vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
// Create a texture.
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// Create a buffer for the position of the rectangle corners.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
}
function render(image, x, y) {
}
function randomInt(range) {
return Math.floor(Math.random() * range);
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
</script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script></script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
</body>
</html>
它绘制了300张图像,但只占用普通画布的CPU的1/2。现在这不是太糟糕但它仍然不够好。 我做错了什么或不必要的?有没有办法通过在GPU中进行计算或缓存来缓解CPU? 我已经在着色器中进行了研究,是否可以使用着色器存储纹理和坐标,只需在着色器中输入一个数组来设置我需要绘制的每个图像的位置点?
谢谢, 我希望这种疯狂很容易解决。
答案 0 :(得分:2)
你真的需要画300(不同)图像吗?或者只有300个具有相同纹理的矩形? (这是你目前正在做的事情)
一般来说,由于多种原因,您的代码非常无效:
setRectangle()
中)glBindTexture
)。最好设置一次纹理,然后绘制一批具有相同纹理的对象您的代码的更优化版本如下所示:
// called once
setup() {
// load and setup texture
// create vertex buffer object for your vertex data
// setup shader
}
// called every frame
display() {
gl.useProgram(..) // set shader
gl.bindTexture(..) // set texture
gl.bindBuffer(..) // set buffer
gl.drawArrays(0, 1800) // draw all you object in one step
}
对于你真的想用另一个纹理绘制每个对象的情况,它有点复杂。但是,您还有多种选择可以优化: