我正在努力将单个像素绘制到UIView
以创建分形图像。我的问题是我的渲染速度。我目前正在运行这个循环260,000次,但想渲染更多像素。实际上,在iPad Mini上运行大约需要5秒钟。
之前我使用的是UIBezierPath
,但这甚至有点慢(约7秒)。我一直在查看NSBitMap
内容,但我不确定这是否会加速它或如何实现它。
我还在考虑尝试将环路中的像素存储到数组中,然后在循环后将它们全部绘制在一起。尽管如此,我不太确定存储然后从数组中检索像素的最佳过程是什么。
任何有关加快此过程的帮助都会很棒。
for (int i = 0; i < 260000; i++) {
float RN = drand48();
for (int i = 1; i < numBuckets; i++) {
if (RN < bucket[i]) {
col = i;
CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:(colorSelector[i][0]) green:(colorSelector[i][1]) blue:(colorSelector[i][2]) alpha:(1)].CGColor));
break;
}
}
xT = myTextFieldArray[1][1][col]*x1 + myTextFieldArray[1][2][col]*y1 + myTextFieldArray[1][5][col];
yT = myTextFieldArray[1][3][col]*x1 + myTextFieldArray[1][4][col]*y1 + myTextFieldArray[1][6][col];
x1 = xT;
y1 = yT;
if (i > 10000) {
CGContextFillRect(context, CGRectMake(xOrigin+(xT-xMin)*sizeScalar,yOrigin-(yT-yMin)*sizeScalar,.5,.5));
}
else if (i < 10000) {
if (x1 < xMin) {
xMin = x1;
}
else if (x1 > xMax) {
xMax = x1;
}
if (y1 < yMin) {
yMin = y1;
}
else if (y1 > yMax) {
yMax = y1;
}
}
else if (i == 10000) {
if (xMax - xMin > yMax - yMin) {
sizeScalar = 960/(xMax - xMin);
yOrigin=1000-(1000-sizeScalar*(yMax-yMin))/2;
}
else {
sizeScalar = 960/(yMax - yMin);
xOrigin=(1000-sizeScalar*(xMax-xMin))/2;
}
}
}
修改
我创建了一个多维数组来存储UIColors,所以我可以使用位图来绘制我的图像。它明显更快,但我现在的颜色不合适。
这是我将UIColors存储到数组中的地方:
int xPixel = xOrigin+(xT-xMin)*sizeScalar;
int yPixel = yOrigin-(yT-yMin)*sizeScalar;
pixelArray[1000-yPixel][xPixel] = customColors[col];
这是我的绘画内容:
CGDataProviderRef provider = CGDataProviderCreateWithData(nil, pixelArray, 1000000, nil);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGImageRef image = CGImageCreate(1000,
1000,
8,
32,
4000,
colorSpace,
kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast,
provider,
nil, //No decode
NO, //No interpolation
kCGRenderingIntentDefault); // Default rendering
CGContextDrawImage(context, self.bounds, image);
不仅颜色不是它们应该是的颜色,而且每次渲染图像时,颜色都与前一次完全不同。我一直在用颜色测试不同的东西,但我仍然不知道为什么颜色是错误的,而且我更加困惑他们如何不断变化。
答案 0 :(得分:6)
每像素绘图 - 每个像素的复杂计算,如分形渲染 - 是您可以要求计算机执行的最困难的事情之一。这里的每个其他答案都涉及其困难的一个方面,但这并不是全部。 (幸运的是,这种渲染也是现代硬件优化的东西,如果你知道要问什么。我会做到这一点。)
@jcaron和@JustinMeiners都注意到CoreGraphics中的矢量绘图操作(甚至是矩形填充)会对基于CPU的光栅化造成损失。操纵位图数据的缓冲区会更快,但速度要快得多。
将缓冲区放到屏幕上也需要时间,特别是如果您不得不经历创建位图图像缓冲区然后在CG上下文中绘制它们的过程 - 这会做很多顺序在CPU上绘制工作和大量内存带宽工作来复制该缓冲区。所以@JustinMeiners是正确的,直接访问GPU纹理内存将是一个很大的帮助。
但是,如果您仍在使用CPU代码填充缓冲区,那么您仍会受到两项费用的限制(最好,如果您天真地这样做会更糟):
@ JustinMeiners&#39;答案对他的用例很有用 - 图像序列是预渲染的,因此他确切地知道每个像素将是什么,他只需要将其转换为纹理存储器。但是您的用例需要大量的每像素计算。
幸运的是,每像素计算是GPU的设计目标!欢迎来到像素着色器的世界。对于屏幕上的每个像素,您可以运行一个独立的计算来确定该点与分形集之间的关系,从而确定绘制它的颜色。可以同时为多个像素并行运行该计算,并且输出直接进入屏幕,因此没有内存开销将位图转储到帧缓冲区。
在iOS上使用像素着色器的一种简单方法是SpriteKit - 它可以为您处理大部分必要的OpenGL / Metal设置,因此您需要编写的是GLSL中的每像素算法(实际上,它是在金属支持的设备上自动转换为金属着色器语言的GLSL)。 Here's a good tutorial on that和here's another on OpenGL ES pixel shaders for iOS in general。
答案 1 :(得分:2)
如果你想最大化渲染速度,我建议使用位图渲染。矢量栅格化速度要慢得多,CGContext绘图并不适合高性能实时渲染。
我遇到了类似的技术挑战,发现CVOpenGLESTextureCacheRef
是最快的。纹理缓存允许您将位图直接上载到图形内存中以便快速渲染。渲染使用OpenGL,但因为它只是2D全屏图像 - 你真的不需要了解OpenGL才能使用它。
你可以看到我在这里看到的一个使用纹理缓存的例子:https://github.com/justinmeiners/image-sequence-streaming
我原来的问题与此有关: How to directly update pixels - with CGImage and direct CGDataProvider
我的项目从文件中呈现位图,所以它有点不同,但您可以查看ISSequenceView.m,了解如何使用纹理缓存并设置OpenGL进行此类渲染。
您的渲染程序可能类似于:
1. Draw to buffer (raw bytes)
2. Lock texture cache
3. Copy buffer to texture cache
4. Unlock texture cache.
5. Draw fullscreen quad with texture
答案 2 :(得分:2)
如果你真的想单独更改许多不同的像素,最好的选择可能是分配一块内存(大小为width * height * bytes per pixel
),直接在内存中进行更改,然后将整个内容转换为使用CGBitmapContextCreateWithData
可能有更快的方法(见贾斯汀的答案)。