以前,要绘制一副图片,尤其是带部分透明度的大幅图像,要花上10ms级的时间(我是指在我的烂机器上,嗯),这在游戏这样要求高速图像更新的应用里根本是不能容忍的。
为什么这么慢?
一般计算机数字图像的绘制其实是图像从一块存储区域转移到系统显存(VRAM)中某个位置的过程,在图像转移完成后再由显卡负责整个屏幕信号的转换和输出,然后经过显示器将模拟或数字信号转换成人眼能分辨的图像。只有第一个阶段是被应用程序控制的,图像转移过程的好坏决定了整个图像的渲染速度。
一般,图像的转移分为以下几种:
内存到显存的匹配格式图片转移;
显存到显存的图片转移;
内存到显存的不匹配格式图片转移;
图像转移的速度,取决于两个因素:格式和位置;
所谓位置,便是指图像的源和目的,一般从内存到显存比较从显存到显存慢。要提高渲染速度,除尽量把图片搁显存里以外,还要尽量减少其中传输次数和总的传输数据量;
所谓格式,是指源和目的的图像格式是否匹配,如果不匹配,会造成转换时间等损失。诸位有没有看过《东方快车谋杀案》?东方快车入俄罗斯边境后要停车一晚上……换轮子,因为此国的铁路宽度与领国不同……这个道理是一样的,使用匹配格式绘图,能大大增加渲染速度。
小弟我根据实验,得出了三种转移方式的比较:
内存到显存的不匹配格式图片转移,慢~到~爆~;
内存到显存的匹配格式图片转移,较快;
显存到显存的图片转移,贝戒~快~口阿~;
下面讲讲实现上述三者的方法:
先讲慢到爆的那个。如果不用任何加速手段,仅采用往awt 组件上drawImage(),结果就是慢到爆,因为格式不匹配,属于第三种。既然已经这么慢了,于是这种渲染方法又附带一个缺点:闪烁;
接下来,一般最常用的手段是用Swing来绘图,因为它内建了自动显存缓冲,因此渲染速度大大增加而且不用担心闪烁问题,转移方式属于第一种,且是懒人的首选。当然,我们也可以在AWT里手动实现显存内缓冲,毕竟Swing就是构造在AWT之上的;
最后一种最最王道的方法,需要手动把图片转移进显存
咱们分成3步走:
首先,下面这个巨长的语句可以申请一块与显存图像格式相容的内存,
它可以用来1离屏缓存和2存储图像:
Image buffer =
GraphicsEnvironment
.getLocalGraphicsEnvironment ()
.getDefaultScreenDevice ()
.getDefaultConfiguration ()
.createCompatibleImage (w, h, Transparency.BITMASK); // 最后一个参数指定透明方式
类似的,加上几个字母后这个语句能够制造一块可以用的显存
Image buffer =
GraphicsEnvironment
.getLocalGraphicsEnvironment ()
.getDefaultScreenDevice ()
.getDefaultConfiguration ()
.createCompatibleVolatileImage (w, h);
(对了,上述方法可以用Component的createImage和createVolatileImage代替,效果一样,缺点是不能手动指定透明方式而且代码不够华丽,不能用来吓唬小朋友)
接着用ImageIO载入图像文件
Graphics2D g = buffer.createGraphics();
g.drawImage (ImageIO.read ("c:/pic.png") , null, 0, 0);
g.dispose ();
这样一次性地把内存图像以匹配格式的形式转移入内存/显存
那么接下来使用缓存的过程就非常简单了,只要将欲绘制的图像画在离屏缓冲里,然后这么覆盖Component的绘制方法:
public void update (Graphics g)
{
paint (g);
}
public void paint (Graphics g)
{
if (offScreenBuffer == null) return;
g.drawImage (offScreenBuffer, 0, 0, null); //显存到显存的Blitting
}
这样你就可以用几乎接近C++的速度来绘制图像啦
说到这里VolatileImage是千好万好,但是它其实有两个致命的缺点:
第一,
api doc里这么说:
The drawing surface of an image (the memory where the image contents actually reside) can be lost or invalidated, causing the contents of that memory to go away.
(大意:绘制使用的显存表面会丢失,然后图像也丢失了)
我先按照上述办法,把所有图片载入显存,再在显存开一个离屏缓冲,运行一个awt窗口,用渲染线程不断刷图片,然后我打开了《禽兽争霸3》爽了一盘亡灵,退出游戏后一看,Java窗口黑了~~
原来DirectX程序禽兽争霸独占显存后,把原本在屏外缓冲区的VolatileImage的显存给抢了,这也就是为什么它会命名为VolatileImage的原因,除运行独占显存的程序,其它情况如更改分辨率和色深等也会造成显存内容丢失,但是如果你只用VI做离屏缓冲而把图片存在内存里的话是没有问题的;
第二,显存中的VolatileImage对透明度支持不佳
实际应用中,所有载入显存的PNG图像会莫名奇妙地变成无透明图像
这个问题,呃,暂时还没有解决,那么要么用慢一点渲染方法,要么就把游戏里所有的人物都弄成方块状,名为《方块世界》好
为什么这么慢?
一般计算机数字图像的绘制其实是图像从一块存储区域转移到系统显存(VRAM)中某个位置的过程,在图像转移完成后再由显卡负责整个屏幕信号的转换和输出,然后经过显示器将模拟或数字信号转换成人眼能分辨的图像。只有第一个阶段是被应用程序控制的,图像转移过程的好坏决定了整个图像的渲染速度。
一般,图像的转移分为以下几种:
内存到显存的匹配格式图片转移;
显存到显存的图片转移;
内存到显存的不匹配格式图片转移;
图像转移的速度,取决于两个因素:格式和位置;
所谓位置,便是指图像的源和目的,一般从内存到显存比较从显存到显存慢。要提高渲染速度,除尽量把图片搁显存里以外,还要尽量减少其中传输次数和总的传输数据量;
所谓格式,是指源和目的的图像格式是否匹配,如果不匹配,会造成转换时间等损失。诸位有没有看过《东方快车谋杀案》?东方快车入俄罗斯边境后要停车一晚上……换轮子,因为此国的铁路宽度与领国不同……这个道理是一样的,使用匹配格式绘图,能大大增加渲染速度。
小弟我根据实验,得出了三种转移方式的比较:
内存到显存的不匹配格式图片转移,慢~到~爆~;
内存到显存的匹配格式图片转移,较快;
显存到显存的图片转移,贝戒~快~口阿~;
下面讲讲实现上述三者的方法:
先讲慢到爆的那个。如果不用任何加速手段,仅采用往awt 组件上drawImage(),结果就是慢到爆,因为格式不匹配,属于第三种。既然已经这么慢了,于是这种渲染方法又附带一个缺点:闪烁;
接下来,一般最常用的手段是用Swing来绘图,因为它内建了自动显存缓冲,因此渲染速度大大增加而且不用担心闪烁问题,转移方式属于第一种,且是懒人的首选。当然,我们也可以在AWT里手动实现显存内缓冲,毕竟Swing就是构造在AWT之上的;
最后一种最最王道的方法,需要手动把图片转移进显存
咱们分成3步走:
首先,下面这个巨长的语句可以申请一块与显存图像格式相容的内存,
它可以用来1离屏缓存和2存储图像:
Image buffer =
GraphicsEnvironment
.getLocalGraphicsEnvironment ()
.getDefaultScreenDevice ()
.getDefaultConfiguration ()
.createCompatibleImage (w, h, Transparency.BITMASK); // 最后一个参数指定透明方式
类似的,加上几个字母后这个语句能够制造一块可以用的显存
Image buffer =
GraphicsEnvironment
.getLocalGraphicsEnvironment ()
.getDefaultScreenDevice ()
.getDefaultConfiguration ()
.createCompatibleVolatileImage (w, h);
(对了,上述方法可以用Component的createImage和createVolatileImage代替,效果一样,缺点是不能手动指定透明方式而且代码不够华丽,不能用来吓唬小朋友)
接着用ImageIO载入图像文件
Graphics2D g = buffer.createGraphics();
g.drawImage (ImageIO.read ("c:/pic.png") , null, 0, 0);
g.dispose ();
这样一次性地把内存图像以匹配格式的形式转移入内存/显存
那么接下来使用缓存的过程就非常简单了,只要将欲绘制的图像画在离屏缓冲里,然后这么覆盖Component的绘制方法:
public void update (Graphics g)
{
paint (g);
}
public void paint (Graphics g)
{
if (offScreenBuffer == null) return;
g.drawImage (offScreenBuffer, 0, 0, null); //显存到显存的Blitting
}
这样你就可以用几乎接近C++的速度来绘制图像啦
说到这里VolatileImage是千好万好,但是它其实有两个致命的缺点:
第一,
api doc里这么说:
The drawing surface of an image (the memory where the image contents actually reside) can be lost or invalidated, causing the contents of that memory to go away.
(大意:绘制使用的显存表面会丢失,然后图像也丢失了)
我先按照上述办法,把所有图片载入显存,再在显存开一个离屏缓冲,运行一个awt窗口,用渲染线程不断刷图片,然后我打开了《禽兽争霸3》爽了一盘亡灵,退出游戏后一看,Java窗口黑了~~
原来DirectX程序禽兽争霸独占显存后,把原本在屏外缓冲区的VolatileImage的显存给抢了,这也就是为什么它会命名为VolatileImage的原因,除运行独占显存的程序,其它情况如更改分辨率和色深等也会造成显存内容丢失,但是如果你只用VI做离屏缓冲而把图片存在内存里的话是没有问题的;
第二,显存中的VolatileImage对透明度支持不佳
实际应用中,所有载入显存的PNG图像会莫名奇妙地变成无透明图像
这个问题,呃,暂时还没有解决,那么要么用慢一点渲染方法,要么就把游戏里所有的人物都弄成方块状,名为《方块世界》好
本文探讨了计算机数字图像渲染过程中存在的问题,并介绍了如何通过优化图像格式和位置转移来提高图像渲染速度的方法。文中详细讲解了内存到显存及显存到显存的不同图像转移方式及其性能表现。

3562

被折叠的 条评论
为什么被折叠?



