从零开始编写软光栅(3):位图Bitmap的表示

​这节我们创建一个Bitmap(位图)类表示一幅图像的内容。

我们先来思考一下一幅图像该如何表示,一幅图像是由若干个颜色像素点组成的,这在计算机中可以表示为一个数组。Java中图像的一个像素的格式是ARGB。一种直观的做法是创建一个像素数组,大小为高*宽,然后每个像素中保存了ARGB这三个分量。

从零开始编写软光栅(3):位图Bitmap的表示_第1张图片

但是本系列教程将使用一种比较不直观的方式,在数组中直接保存各个颜色分量的值。所以数组[0]表示第一个像素的颜色分量A,数组[1]表示第一个像素的颜色分量R,后面依次是第一个像素的G、B,数组[4]表示第二个像素的颜色分量A……如此类推。这样做的目的是很多时候我们需要统一处理某个颜色分量,如果使用第一种比较直观的方式,需要不停地拆分像素颜色分量,再修改值,再打包成像素。为了减少后面大量繁琐的工作,我们采取第二种比较不直观的方式。

从零开始编写软光栅(3):位图Bitmap的表示_第2张图片

我们创建一个Java类:

public class Bitmap {
    private final int m_width;
    private final int m_height;
    private final byte m_components[];
}

该类保存了Bitmap的宽度和高度,以及表示该图像的数组。注意该数组的元素不是一个单一的像素,而是像素中的一个颜色分量。

在构造函数中,指定宽度和高度后,创建该图像数组。之所以要乘以4,就是因为一个像素中包含四个颜色分量。

public Bitmap(int width, int height)
{
    m_width = width;
    m_height = height;
    m_components = new byte[width * height * 4];
}

接下来再创建一个Clear函数,该函数要做的事情就是将图像清空,传入一个char值,将数组所有元素设为该值。如果传入的是最小值0,那么该图像就是变成黑色,传入最大值255,该图像就是变成白色。传入的是0~255之间的值则是灰色。

public void Clear(char shade){ Arrays.fill(m_components, shade); }

接下来编写一个画像素点的函数。只有简单的数学运算,只要理解了该Bitmap的储存结构,也不难理解该函数了。这里我们不用a、r、g、b的顺序,而改为a、b、g、r的顺序,只是为了方便Java读取。

public void DrawPixel(int x, int y, byte a, byte b, byte g, byte r)
{
    int index = (x + y * m_width) * 4;
    m_components[index] = a;
    m_components[index+1] = b;
    m_components[index+2] = g;
    m_components[index+3] = r;
}

最后,我们编写一个输出只有b、g、r颜色的byte数组(利用参数作为输出)。

public void CopyToByteArray(byte[] dest)
{
    for(int i = 0; i < m_width * m_height; i++)
    {
        dest[i*3] = m_components[i*4+1];
        dest[i*3+1] = m_components[i*4+2];
        dest[i*3+2] = m_components[i*4+3];
    }
}

该系列教程中其实也只有上面这个函数会使用这么多繁琐的位操作,所以不用担心。

Bitmap类的编写就完成了。

我们使用Bitmap能方便地表示图像缓冲区或者一张纹理,这也是计算机图形学中最常用、最基本的概念。通过这节课的学习,你就能理解到,所谓的图片、纹理、法线贴图之类的东西,都只不过是一个数组(通常是二维数组,当然也会有一维数组和三维数组,套路都是一样的),如果里面存放了ARGB这样的颜色分量的话,那它可以表示一张人眼能够看到的图片。上面我们用一个byte(字节,8位)表示了一个颜色,那么ARGB一共4*8=32位,这就是所谓的32位图。顺便说一下24位图,其实只是把A丢掉了,像上面我们CopyToByteArray做的事情一样。其它的16位图也是类似的原理,只是每个颜色分量的位数不同。

当然,有的人会耍点小聪明,除了放ARGB,那我是不是放其它数据呢?比如模型对应的法线方向?Of Couse,这就是所谓的法线贴图了。因为模型只能表示一系列的顶点数据,无法表示除顶点外的模型表面上的凹凸情况(你说不能增加顶点吗?能增加顶点,那就变成了高模,但是即使是高模也无法表示出表面每一个点的凹凸情况,你想想一个小三角形表面覆盖了多少像素就知道了,而且模型顶点数太多,GPU会吃不消),但是利用法线贴图传入了模型表面上任意点的法线,通过法线,那么我们可以增加光照的细节!(比如,如果是凹下去的表面,应该会更暗)除了法线外,世界上那么多大牛,脑洞肯定会很多,视差贴图(Parallax Mapping)、移位贴图(Displacement Mapping)等等,都是因为Bitmap里存的不是ARGB而已,这个后面在公众号会专门开一篇仔细讲。

从零开始编写软光栅(3):位图Bitmap的表示_第3张图片
最左边是没有法线贴图的效果,最右边是加上法线贴图的效果,模型顶点数都是一样的

下集预告:既然Bitmap类创建出来了,那如何把它显示出来呢?欢迎继续收看。

——————我是优秀的分割线————————

实际上,这是一套本人使用费曼技巧进行学习的教程

该教程并非本人完全原创,原创者为油管Up主thebennybox

在中文优秀资料普遍匮乏的环境下,本人经常会通过借鉴国外的优秀学习资料帮助自己进行学习

在本人制作一系列的中文解说教程和梳理知识的同时,其实也是可以帮助英语能力尚不足的学习者降低学习门槛,希望大家多多支持!

如果您觉得本课程有所帮助的话

请考虑资助一下Up主

Up主有一个德国留学的梦,留学生活费用不低,但目前已经辞职在家专心备考,请有能力的土豪高抬贵手,打赏小弟一两杯咖啡~

关注“游戏开发指南”公众号

回复“软光栅”可以获得本系列课程的打赏及源码下载方式,并随时订阅最新消息
从零开始编写软光栅(3):位图Bitmap的表示_第4张图片

你可能感兴趣的:(2.,从零开始编写软光栅)