简体   繁体   English

如何将 WebGL 集成到使用 canvas 和 2D context.drawImage 的现有游戏中?

[英]How to integrate WebGL into existing game which is using canvas and 2D context.drawImage?

I have a game which uses context.drawImage() to draw spritesheets of the game objects into a hidden canvas which is then copied over to another canvas(viewport).我有一个游戏,它使用context.drawImage()将游戏对象的 spritesheet 绘制到隐藏的 canvas 中,然后将其复制到另一个画布(视口)。 The current renderer has a camera which copies a portion of the canvas to the view.当前渲染器有一个相机,可以将 canvas 的一部分复制到视图中。 Each gameobject is added to a layer in the renderer and sorted by some value x each frame before rendering.每个游戏对象都被添加到渲染器中的一个层,并在渲染前按每个帧的某个值 x 排序。 Layers are drawn in a specific order and objects in the layers aswell.图层以特定顺序绘制,图层中的对象也是如此。

So I have following structure:所以我有以下结构:

//Renderer object
var layers = [[object,object,object,object],//objects sorted according to when they should be rendered
              [object,object,object,object],
              [object,object,object]];

//Simplified one frame render function
for(var i = 0; i < layers.length;i++){
    for(var j = 0; j < layers[i].length;j++){
        this.drawImage(layers[i][j]);
    }
}

...
//DrawImage function
this.drawImage = function(object){
    if(typeof object.currentAnimation !== "undefined"){//draw animation
        var tileId = object.currentAnimation.tileIds[object.currentAnimation.currentTile];
        var amountOfBlocksInARow = object.currentAnimation.tilesheet.width/object.currentAnimation.tileSize;
            
        var sourceX = Math.floor(tileId % amountOfBlocksInARow) *object.currentAnimation.tileSize;
        var sourceY = Math.floor(tileId / amountOfBlocksInARow) *object.currentAnimation.tileSize;
        this.canvasContext.drawImage(object.currentAnimation.tilesheet, sourceX, sourceY, object.currentAnimation.tileSize, object.currentAnimation.tileSize, object.x, object.y,object.width,object.height);
        object.currentAnimation.nextFrame();
    }else{//if theres no animation just an image then draw with object properties}

}

...
//Rest of the render function
//snap camera to its target then copy the camera to the view
this.cameraSnapToTarget();
this.viewportContext.drawImage(this.canvas, this.camera.x, this.camera.y, this.camera.width, this.camera.height, 0, 0, this.viewportCanvas.width, this.viewportCanvas.height);

And this html: <canvas id="hiddenCanvas"></canvas> <canvas id="viewportCanvas"></canvas>还有这个 html: <canvas id="hiddenCanvas"></canvas> <canvas id="viewportCanvas"></canvas>

I was wondering if there was a way to implement the WebGL into this so I wouldn't have to change the structure.我想知道是否有一种方法可以将 WebGL 实现到其中,这样我就不必更改结构。 Currently I have tried to initialize webgl, make some buffers and shaders, but I can't seem to draw anything with it.目前我已经尝试初始化 webgl,制作一些缓冲区和着色器,但我似乎无法用它绘制任何东西。 I followed https://webglfundamentals.org/webgl/lessons/webgl-2d-drawimage.html this and don't know how to set up the buffers and shaders to benefit from the original code.我遵循了 https://webglfundamentals.org/webgl/lessons/webgl-2d-drawimage.html这个并且不知道如何设置缓冲区和着色器以从原始代码中受益。 Do I have to change this structure or can it be implemented in this?我必须更改此结构还是可以在其中实施?

Thank you in advance for any help!预先感谢您的任何帮助!

There was a few things I needed to add for my code for this to work First I needed to make textures of the spritesheets I use for the gameobjects:我需要为我的代码添加一些东西才能工作首先我需要制作我用于游戏对象的 spritesheet 的纹理:

//I load all my images into an array I can search from
var imageArray = [];//global variables
var textures = [];//global variables
//and use it after defining onload like this:
imageArray.push(img);

After images have all loaded I initialize the textures for the webgl:图像全部加载后,我为 webgl 初始化纹理:

for(var i = 0; i < imageArray.length; i++){
    initializeTexture(imageArray[i]);
}

function initializeTexture(img){//these images are already loaded into the dom
   var tex = renderer.viewportContext.createTexture();
   renderer.viewportContext.bindTexture(renderer.viewportContext.TEXTURE_2D, tex);
            
   renderer.viewportContext.texParameteri(renderer.viewportContext.TEXTURE_2D, renderer.viewportContext.TEXTURE_WRAP_S, renderer.viewportContext.CLAMP_TO_EDGE);
   renderer.viewportContext.texParameteri(renderer.viewportContext.TEXTURE_2D, renderer.viewportContext.TEXTURE_WRAP_T, renderer.viewportContext.CLAMP_TO_EDGE);
   renderer.viewportContext.texParameteri(renderer.viewportContext.TEXTURE_2D, renderer.viewportContext.TEXTURE_MIN_FILTER, renderer.viewportContext.LINEAR);
        
   var texture = {
       width: img.width,   
       height: img.height,
       texture: tex,
                    
   };
   renderer.viewportContext.bindTexture(renderer.viewportContext.TEXTURE_2D, tex);
   renderer.viewportContext.texImage2D(renderer.viewportContext.TEXTURE_2D, 0, renderer.viewportContext.RGBA, renderer.viewportContext.RGBA, renderer.viewportContext.UNSIGNED_BYTE, img);
   textures.push(texture);//array containing one of each spritesheet texture
}

Then I have Animation object which I use to assign spritesheets to gameobjects:然后我有 Animation object 用于将精灵表分配给游戏对象:

function Animation(spritesheet,tileSize,tileIds,name = "",playOnce = false){
    this.name = name;
    this.spritesheet = spritesheet;
    this.tileSize = tileSize;
    this.tileIds = tileIds;
    this.speed = 1;
    this.currentTile = 0;
    this.playOnce = playOnce;
    this.texture;
    
    this.state = "stopped";
    
    this.setAnimationSpeed = function(speed){
        this.speed = speed;
    }
    
    this.setTextureFromImage = function(img){
        this.texture = textures[imageArray.indexOf(img)];//find the spritesheet from array of images which is indexed same as textures array texture[i] = imageArray[i] etc. 
    }
    
    this.setName = function(name){
        this.name = name;
    }
    
    this.setLoop = function (loop){
        this.loop = loop;
    }
    
    this.setSpritesheet = function (spritesheet){
        this.spritesheet = spritesheet;
    }
    
    this.setState = function(state){
        this.state = state;
    }
    
    this.getState = function(){
        return this.state;
    }
    
    this.play = function(){
        this.state = "playing";
    }
    
    this.stop = function(){
        this.state = "stopped";
    }
    
    this.nextFrame = function(){
        if(this.state == "playing"){
            if(typeof this.tileIds[this.currentTile+1] !== "undefined"){
                this.currentTile++;
            }else{
                if(this.playOnce)
                    this.setState("played");
                else
                    this.currentTile = 0;
            }
        }
    }
    this.setTextureFromImage(this.spritesheet);
}

As optimization I could use the index of imageArray and get the texture from the original texture array when rendering.作为优化,我可以使用 imageArray 的索引并在渲染时从原始纹理数组中获取纹理。 I use the Animation object in all gameobjects like this:我在所有游戏对象中使用 Animation object,如下所示:

this.animations = [
    new Animation(this.spritesheet,32,[0,1],"walkDown"),//walkdown
    new Animation(this.spritesheet,32,[2,3],"walkRight"),//walkright
    new Animation(this.spritesheet,32,[4,5],"walkLeft"),//walkleft
    new Animation(this.spritesheet,32,[6,7],"walkUp"),//walkup
]

Finally I can render them with:最后我可以渲染它们:

//Renderer
this.viewportContext = this.viewportCanvas.getContext("webgl",{ alpha: false ,premultipliedAlpha: false});
    
    
this.render = function(){//render one frame
    this.sortLayers();//can do sorting here
    this.viewportContext.viewport(0,0,this.viewportContext.canvas.width, 
    this.viewportContext.canvas.height);
    this.viewportContext.clear(this.viewportContext.COLOR_BUFFER_BIT);//clear buffers before rendering
    for(var i = 0; i < this.layerArray.length;i++){
        if(this.layerArray[i].constructor === Array){
            for(var j = 0; j < this.layerArray[i].length;j++){
                var object = this.layerArray[i][j];
                if(typeof object.currentAnimation !== "undefined"){
                    var tileId = object.currentAnimation.tileIds[object.currentAnimation.currentTile];
                    var amountOfBlocksInARow = object.currentAnimation.spritesheet.width/object.currentAnimation.tileSize;
                    var sourceX = Math.floor(tileId % amountOfBlocksInARow) *object.currentAnimation.tileSize;
                    var sourceY = Math.floor(tileId / amountOfBlocksInARow) *object.currentAnimation.tileSize;
                    this.drawImage(object.currentAnimation.texture.texture,
                        object.currentAnimation.texture.width,
                        object.currentAnimation.texture.height, 
                        sourceX, sourceY, 
                        object.currentAnimation.tileSize, 
                        object.currentAnimation.tileSize, 
                        object.x,object.y,
                        object.width,object.height);
                    }
                }
            }
        }
    }
}

this.drawImage = function(tex, texWidth, texHeight,srcX, srcY, srcWidth, srcHeight,dstX, dstY, dstWidth, dstHeight){
    if (dstX === undefined) {
        dstX = srcX;
        srcX = 0;
    }
    if (dstY === undefined) {
        dstY = srcY;
        srcY = 0;
    }
    if (srcWidth === undefined) {
        srcWidth = texWidth;
    }
    if (srcHeight === undefined) {
        srcHeight = texHeight;
    }
    if (dstWidth === undefined) {
        dstWidth = srcWidth;
        srcWidth = texWidth;
    }
    if (dstHeight === undefined) {
        dstHeight = srcHeight;
        srcHeight = texHeight;
    }
    
    this.viewportContext.enable(this.viewportContext.BLEND);
    this.viewportContext.blendFunc(this.viewportContext.SRC_ALPHA, this.viewportContext.ONE_MINUS_SRC_ALPHA);
    this.viewportContext.bindTexture(this.viewportContext.TEXTURE_2D, tex);
    
    this.viewportContext.useProgram(program);
    
    this.viewportContext.bindBuffer(this.viewportContext.ARRAY_BUFFER,positionBuffer);
    this.viewportContext.enableVertexAttribArray(positionAttributeLocation);
    this.viewportContext.vertexAttribPointer(positionAttributeLocation, 2, this.viewportContext.FLOAT, false, 0, 0);
    this.viewportContext.bindBuffer(this.viewportContext.ARRAY_BUFFER, textureCoordinateBuffer);
    this.viewportContext.enableVertexAttribArray(textureCoordinateLocation);
    this.viewportContext.vertexAttribPointer(textureCoordinateLocation, 2, this.viewportContext.FLOAT, false, 0, 0);
    
    this.matrix = m4.orthographic(0, this.viewportContext.canvas.width, this.viewportContext.canvas.height, 0, -1, 1);
    
    this.matrix = m4.translate(this.matrix,dstX,dstY,0);
    
    this.matrix = m4.scale(this.matrix, dstWidth, dstHeight, 1);
    
    this.viewportContext.uniformMatrix4fv(matrixLocation,false,this.matrix);
    
    var textureMatrix = m4.scaling(1/ texWidth, 1/texHeight, 1);
    //scaling
    
    textureMatrix = m4.translate(textureMatrix, srcX, srcY, 0);
    textureMatrix = m4.scale(textureMatrix, srcWidth, srcHeight, 1);
    
    this.viewportContext.uniformMatrix4fv(textureMatrixLocation, false, textureMatrix);
    
    this.viewportContext.drawArrays(this.viewportContext.TRIANGLES, 0, 6);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM