游戏引擎理论与实现系列03-生成图像

来源:互联网 发布:eplan软件 64位 编辑:程序博客网 时间:2024/05/01 01:16

  • Tick机制
  • Bitmap位图
    • RGB
    • Alpha通道
    • 色彩深度编辑
    • Bitmap Class
  • Screen
    • Drawing a Bitmap
    • Scanning a Bitmap
  • Load Image
  • 主程序
    • 缓冲策略
    • BufferImage and Raster DataBuffer
    • DrawImage
  • 参考资料

Tick机制

public class Ticker {    public void tick() {    }}

游戏服务器程序内部都会统一设计所谓的tick机制,这个机制一般来说有两个用途,一是许多业务模块都会有定时处理的需求,如:游戏中一天时间的轮转;二是游戏服务器内部会有一些日常主动驱动的事件,如:水,火山熔浆动画,这些都在tick中处理。关于tick机制的实现和其中一些问题, 我们将来会详细介绍和探讨,对于现在, 我们只需从整体结构上知道tick机制的存在和重要性。

Bitmap(位图)

又称栅格图(Raster graphics),是使用像素阵列(Pixel-array/Dot-matrix點陣)来表示的图像。

根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。例如,位深度为 1 的像素位图只有两个可能的值(黑色和白色),所以又称为二值位图。位深度为 8 的图像有 28(即 256)个可能的值。位深度为 8 的灰度模式图像有 256 个可能的灰色值。

RGB图像由三个颜色通道组成。8 位/像素的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道 (bpc) 的 RGB 图像称作 24 位图像(8 位 x 3 通道 = 24 位数据/像素)。通常使用24位RGB组合数据位表示的的位图称为真彩色位图。

RGB

位图颜色的一种编码方法,用红(Red)、绿(Green)、蓝(Blue)三原色的光学强度来表示一种颜色。这是最常见的位图编码方法,可以直接用于屏幕显示。

Alpha通道

在原有的图片编码方法基础上,增加像素的透明度信息。图形处理中,通常把RGB三种颜色信息称为红通道、绿通道和蓝通道,相应的把透明度称为Alpha通道。多数使用颜色表的位图格式都支持Alpha通道。

色彩深度[编辑]

色彩深度又叫色彩位数,即位图中要用多少个二进制位来表示每个点的颜色,是分辨率的一个重要指标。常用有1位(单色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增强色),24位和32位(真彩色)等。色深16位以上的位图还可以根据其中分别表示RGB三原色或CMYK四原色(有的还包括Alpha通道)的位数进一步分类,如16位位图图片还可分为R5G6B5,R5G5B5X1(有1位不携带信息),R5G5B5A1,R4G4B4A4等等。

Bitmap Class

本引擎所有的图形操作都是通过Bitmap与Screen或图形缓存来完成。我们会发现, Bitmap其实是非常简单。我们从介绍Bitmap类的定义开始。每个像素Pixel为一个32bit 的整数, 因此可以支持24位RGB组合数据位和Alpha通道。 所有的Pixel保存在一个数组结构Pixels中。

public class Bitmap {    protected int width;    protected int height;    protected int[] pixels;    public Bitmap(int width, int height) {        this.width = width;        this.height = height;        this.pixels = new int[width * height];    }    public int getWidth() {        return width;    }    public int getHeight() {        return height;    }    public int[] getPixels() {        return pixels;    }}

Screen

屏幕实质上就是一大Bitmap.

public class Screen extends Bitmap {    public Screen(int width, int height) {        super(width, height);    }    public void render(Ticker ticker, Bitmap bitMap) {        this.putBitmap(bitMap, 0, 0);    }}

Drawing a Bitmap

以上介绍了Bitmap, 接下我们介绍如何将将一个Bitmap 画到另外一个Bitmap,最常见的,比如,将Bitmap画到屏幕上,对应左上角的位置为(offX, offY)
透明色

    public void putBitmap(Bitmap bitmap, int offX, int offY) {        int w = bitmap.getWidth();        int h = bitmap.getHeight();        int[] p = bitmap.getPixels();        int[] screenPixels = super.pixels;        int posX = 0, posY = 0;        for (int y = 0; y < h; y++) {            posY = offY + y;            if (posY < 0 || posY >= super.height) {                continue;            }            int screenRowOffset = posY * width;            int bitmapRowOffset = y * w;            for (int x = 0; x < w; x++) {                posX = offX + x;                if (posX < 0 || posX >= super.width) {                    continue;                }                screenPixels[screenRowOffset + posX] = p[bitmapRowOffset + x];            }        }    }

Scanning a Bitmap

该函数是putBitmap函数的反向函数,用于获取对应bitmap覆盖的屏幕位置。 注意该返回对象时通过传参数实现的。

    public void getBitMap(BitMap bitmap, int offX, int offY) {        int w = bitmap.getWidth();        int h = bitmap.getHeight();        int[] bitMapPixels = bitmap.getPixels();        int[] screenPixels = super.pixels;        int posX = 0, posY = 0;        int pixel;        for (int y = 0; y < h; y++) {            posY = offY + y;            if (posY < 0 || posY >= super.height) {                continue;            }            int screenRowOffset = posY * width;            int bitmapRowOffset = y * w;            for (int x = 0; x < w; x++) {                posX = offX + x;                if (posX < 0 || posX >= super.width) {                    continue;                }                pixel = screenPixels[screenRowOffset + posX];                if(pixel!=0){                    bitMapPixels[bitmapRowOffset + x] = pixel;                }            }        }    }

Load Image

读取图像文件并保存到Bitmap。

public class ImageLoader {    public static BitMap floors = load("/tex/floors.png");    private static BitMap load(String filename) {        try {            URL url = ImageLoader.class.getResource(filename);            BufferedImage img = ImageIO.read(url);            int w = img.getWidth();            int h = img.getHeight();            BitMap result = new BitMap(w, h);            int[] pixels = result.getPixels();            img.getRGB(0, 0, w, h, pixels, 0, w);            return result;        } catch (IOException e) {            throw new RuntimeException(e);        }    }}

主程序

缓冲策略

public void createBufferStrategy(int numBuffers)

生成多缓冲(Multi-buffering)策略, 多缓冲(Multi-buffering)的方式可以显著地改善渲染性能。它将始终根据该缓冲区数参数来创建 bufferstrategy 。首先尝试翻页的缓冲区交换(page-flipping) 策略,然后使用加速缓冲区尝试blitting 策略,最后使用不加速的blitting 策略。

        BufferStrategy bs = this.getBufferStrategy();        if (bs == null) {            this.createBufferStrategy(3);            return;// skip one render        }

BufferImage and Raster DataBuffer

生成和屏幕一样尺寸的图像缓存BufferedImage。 并用screenPixels指向对应缓存数据的的数组, 以方便程序的操作。

    private BufferedImage screenImage;    private int[] screenPixels;
    this.screenImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);    DataBufferInt buffer = (DataBufferInt) screenImage.getRaster().getDataBuffer();    this.screenPixels = buffer.getData();

DrawImage

        Bitmap floors = ImageLoader.sprites;        this.screen.render(ticker,floors);        for (int i = 0; i < this.screen.getPixels().length; i++) {            this.screenPixels[i] = this.screen.getPixels()[i];        }        Graphics g = bs.getDrawGraphics();        g.drawImage(this.screenImage, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);        g.dispose();        bs.show();
  1. 获取Bitmap对象
  2. 将Bitmap对象画到对应的屏幕对象
  3. 通过循环,将Screen Bitmap 像素设置到 屏幕缓存像素数组
  4. 将屏幕缓存像素数组显示。

参考资料

维基百科-位图

0 0
原创粉丝点击