我正在将我的精灵绘制功能从canvas 2d转换到webgl。
由于我是webgl(和openGL也是)的新手,我从这个教程中学习http://games.greggman.com/game/webgl-image-processing/,我确实从中复制了许多行,以及我发现的其他一些行。
最后我让它工作,但有一些问题。由于某些原因,有些图像永远不会被绘制出来,而另一些则会,然后我在屏幕上看到随机的大黑色方块,最后它使firefox崩溃…
我绞尽脑汁想解决这些问题,但我就是迷路了…我得找人帮忙。请有人看看我的代码,告诉我,如果你看到我在哪里犯了错误。
顶点着色器和片段着色器:
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
uniform vec2 u_translation;
uniform vec2 u_rotation;
varying vec2 v_texCoord;
void main()
{
// Rotate the position
vec2 rotatedPosition = vec2(
a_position.x * u_rotation.y + a_position.y * u_rotation.x,
a_position.y * u_rotation.y - a_position.x * u_rotation.x);
// Add in the translation.
vec2 position = rotatedPosition + u_translation;
// 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 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()
{
// Look up a color from the texture.
gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>
我使用几个分层画布,以避免浪费资源重画大背景和前景在每一帧,而他们从来没有改变。所以我的画布在liste_canvas[]中,上下文在liste_ctx[]中,c是id("背景"/"游戏"/"前景"/"信息")。下面是它们的创建代码:
// Get A WebGL context
liste_canvas[c] = document.createElement("canvas") ;
document.getElementById('game_div').appendChild(liste_canvas[c]);
liste_ctx[c] = liste_canvas[c].getContext('webgl',{premultipliedAlpha:false}) || liste_canvas[c].getContext('experimental-webgl',{premultipliedAlpha:false});
liste_ctx[c].viewport(0, 0, game.res_w, game.res_h);
// setup a GLSL program
liste_ctx[c].vertexShader = createShaderFromScriptElement(liste_ctx[c], "2d-vertex-shader");
liste_ctx[c].fragmentShader = createShaderFromScriptElement(liste_ctx[c], "2d-fragment-shader");
liste_ctx[c].program = createProgram(liste_ctx[c], [liste_ctx[c].vertexShader, liste_ctx[c].fragmentShader]);
liste_ctx[c].useProgram(liste_ctx[c].program);
这是我的精灵绘制功能。我的图像也存储在一个列表中,sprites[],以字符串名称作为id。它们存储原点(不一定是真正的中心)为。orgn_x和。orgn_y。
function draw_sprite( id_canvas , d_sprite , d_x , d_y , d_rotation , d_scale , d_opacity )
{
if( id_canvas=="" ){ id_canvas = "game" ; }
if( !d_scale ){ d_scale = 1 ; }
if( !d_rotation ){ d_rotation = 0 ; }
if( render_mode == "webgl" )
{
c = id_canvas ;
// look up where the vertex data needs to go.
var positionLocation = liste_ctx[c].getAttribLocation(liste_ctx[c].program, "a_position");
var texCoordLocation = liste_ctx[c].getAttribLocation(liste_ctx[c].program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = liste_ctx[c].createBuffer();
liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, texCoordBuffer);
liste_ctx[c].bufferData(liste_ctx[c].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]), liste_ctx[c].STATIC_DRAW);
liste_ctx[c].enableVertexAttribArray(texCoordLocation);
liste_ctx[c].vertexAttribPointer(texCoordLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);
// Create a texture.
var texture = liste_ctx[c].createTexture();
liste_ctx[c].bindTexture(liste_ctx[c].TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_WRAP_S, liste_ctx[c].CLAMP_TO_EDGE);
liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_WRAP_T, liste_ctx[c].CLAMP_TO_EDGE);
liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_MIN_FILTER, liste_ctx[c].LINEAR);
liste_ctx[c].texParameteri(liste_ctx[c].TEXTURE_2D, liste_ctx[c].TEXTURE_MAG_FILTER, liste_ctx[c].LINEAR);
// Upload the image into the texture.
liste_ctx[c].texImage2D(liste_ctx[c].TEXTURE_2D, 0, liste_ctx[c].RGBA, liste_ctx[c].RGBA, liste_ctx[c].UNSIGNED_BYTE, sprites[d_sprite] );
// set the resolution
var resolutionLocation = liste_ctx[c].getUniformLocation(liste_ctx[c].program, "u_resolution");
liste_ctx[c].uniform2f(resolutionLocation, liste_canvas[c].width, liste_canvas[c].height);
// Create a buffer and put a single clipspace rectangle in it (2 triangles)
var buffer = liste_ctx[c].createBuffer();
liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, buffer);
liste_ctx[c].enableVertexAttribArray(positionLocation);
liste_ctx[c].vertexAttribPointer(positionLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);
// then I calculate the coordinates of the four points of the rectangle
// taking their origin and scale into account
// I cut this part as it is large and has no importance here
// and at last, we draw
liste_ctx[c].bufferData(liste_ctx[c].ARRAY_BUFFER, new Float32Array([
topleft_x , topleft_y ,
topright_x , topright_y ,
bottomleft_x , bottomleft_y ,
bottomleft_x , bottomleft_y ,
topright_x , topright_y ,
bottomright_x , bottomright_y ]), liste_ctx[c].STATIC_DRAW);
// draw
liste_ctx[c].drawArrays(liste_ctx[c].TRIANGLES, 0, 6);
}
}
我没有找到任何方法来移植ctx。顺便说一下,webgl的globalAlpha。如果有人知道我如何在我的代码中添加它,我也会很感激的。
请帮助。谢谢。
我不知道为什么事情会崩溃,只是一些随机的评论。
-
只创建缓冲区和纹理一次。
当前代码正在创建缓冲区和纹理每次你调用
draw_sprite
。相反,你应该在初始化时创建它们,然后在稍后使用创建的缓冲区和纹理。类似地,您应该在初始化时查找属性和统一位置,然后在绘制时使用它们。每次调用
draw_sprite
时,你都在创建新的缓冲区和新的纹理,这可能会导致firefox崩溃,因为它内存不足。 我认为更常见的是用一个单位正方形制作一个缓冲区,然后使用矩阵数学将该正方形移动到你想要的地方。有关矩阵数学的一些帮助,请参阅http://games.greggman.com/game/webgl-2d-matrices/。
如果你走那条路,那么你只需要调用一次所有与缓冲区相关的东西。
即使你不使用矩阵数学,你仍然可以添加平移和缩放到你的着色器,然后只用一个单位矩形的缓冲(如
)gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW)
然后把它平移到你想要的位置然后缩放到你想要的大小
事实上,如果你走矩阵路线,它将很容易模拟2d上下文的矩阵函数
ctx.translate
,ctx.rotate
,ctx.scale
等…如果将上下文放到一个局部变量中,代码可能更容易理解和输入。
而不是像
liste_ctx[c].bindBuffer(liste_ctx[c].ARRAY_BUFFER, buffer); liste_ctx[c].enableVertexAttribArray(positionLocation); liste_ctx[c].vertexAttribPointer(positionLocation, 2, liste_ctx[c].FLOAT, false, 0, 0);
你可以这样做
var gl = liste_ctx[c]; gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.enableVertexAttribArray(positionLocation); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
在上下文中存储东西会很棘手
这段代码liste_ctx[c].vertexShader = createShaderFromScriptElement(liste_ctx[c], "2d-vertex-shader"); liste_ctx[c].fragmentShader = createShaderFromScriptElement(liste_ctx[c], "2d-fragment-shader"); liste_ctx[c].program = createProgram(liste_ctx[c], [liste_ctx[c].vertexShader, liste_ctx[c].fragmentShader]);
使它看起来像你将只有一个单一的顶点着色器,一个单一的片段着色器和单一的程序。也许你是,但在WebGL中有几个着色器和程序是很常见的。
对于globalAlpha,首先你需要打开混合。
gl.enable(gl.BLEND);
你需要告诉它如何混合。要与画布2d上下文相同需要使用预乘法alpha math
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
然后你需要将着色器绘制的颜色乘以alpha值。例如
<script id="2d-fragment-shader" type="x-shader/x-fragment"> precision mediump float; // our texture uniform sampler2D u_image; // global alpha uniform float u_globalAlpha; // the texCoords passed in from the vertex shader. varying vec2 v_texCoord; void main() { // Look up a color from the texture. vec4 color = texture2D(u_image, v_texCoord); // Multiply the color by u_globalAlpha gl_FragColor = color * u_globalAlpha; } </script>
那么你需要设置
u_globalAlpha
。在init时查找它的位置var globalAlphaLocation = gl.getUniformLocation(program, "u_globalAlpha");
在绘制时设置它
gl.uniform1f(globalAlphaLocation, someValueFrom0to1);
我个人通常使用
vec4
并将其称为u_colorMult
<script id="2d-fragment-shader" type="x-shader/x-fragment"> precision mediump float; // our texture uniform sampler2D u_image; // colorMult uniform float u_colorMult; // the texCoords passed in from the vertex shader. varying vec2 v_texCoord; void main() { // Look up a color from the texture. gl_FragColor = texture2D(u_image, v_texCoord) * u_colorMult; } </script>
然后我可以给精灵着色例如,我可以用
把精灵画成红色glUniform4fv(colorMultLocation, [1, 0, 0, 1]);
这也意味着我可以很容易地画纯色。创建一个1x1像素的纯白色纹理。当我想绘制纯色时,我只需要绑定纹理并将
u_colorMult
设置为我想要绘制的颜色。