SDL2中纹理旋转的小困惑
这几天使用SDL2练手一个小应用,其中有个需求是期望对纹理进行旋转,结果发现:不旋转时显示正常,一旦旋转之后输出的图形就会发生很严重的扭曲。 之所以产生这个现象,是因为我创建的这个纹理并不是正方形,而是一个长和宽不相同的矩形(长方形)。现在虽然找到了原因,也通过临时创建了一个正方形来暂且解决了问题,但心中总难免觉得“膈应”。还是期望能够有更正确的解决方案,以正确实现这个需求。 在写完上面的“备忘”之后,又经过了一天的时间,现在已经对长方形的旋转进行了两次改良,感觉应该是已经基本正确、完美的实现了对长方形纹理的旋转。以下是这篇文章的思想脉络: 以下是详细的记录: 一、无法正确完成长方形纹理旋转的现象 在SDL2中,创建了一个32*16的纹理,暂且称这个纹理为“一杆长枪”吧。这个“一杆长枪”的纹理本来是水平渲染到屏幕上的,看上去就是一把图像准确的“长枪”。 我期望是使用键盘的M按键控制纹理旋转,我的旋转策略非常简单,每次按下M按键,只会顺时针产生一次90度的旋转,因而长枪或者水平出现在屏幕上、或者就是竖直的出现在屏幕上。这个需求并不复杂,也没有必要考虑其他刁钻的角度,感觉应该很容易实现。 然而当我完成了代码的撰写之后,长枪水平出现在屏幕上时,的确是正确的。可一旦旋转了90度之后,长枪并没有如预期的竖直的出现在屏幕上,而是竖直、却被压扁了的出现在了屏幕上——渲染结果是竖直的“手枪”出现在了屏幕上。 在反复的胡乱修改代码过程中,还有一些时候会出现如下各种更加离谱的情况: 当然以上更加离谱的情形实际上是我的代码完全不正确导致的。实际上代码如果正确,只可能出现一种情况:水平时是长枪、竖直时是短枪。 二、导致问题出现的原因 经过将近一天的排查,最终才搞清楚原因:SDL_RenderCopyEx()方法虽然的确可以完成旋转,但是它在不指定原纹理、新纹理的尺寸时,是会自动进行一定的缩放的——它会依渲染目标区域先对图像进行缩放、然后才进行旋转操作。 网上之所以没有这个问题的讨论,是因为大家或者没有我这样的需求、或者就是有我这种需求但实现的都是正确的,没有人入坑、自然也就不会有人讨论。 我的需求有一个“坑”:纹理并不是直接绘制在“屏幕渲染区域上”,而是纹理渲染在“与纹理相同的渲染区域上”。这个细微的差异引起了我所遇到的问题。 如果纹理是被渲染在屏幕上,屏幕一般都是在640×480以上,显然的:对于一杆32×16的长方形“长枪”,无论怎样旋转,都会在640×480的内部展现出来,不会发生扭曲。 然而如果期望的是:将32×16的长枪,渲染到16×32的渲染区中,就会出现各种各样的问题了: 起初:是将32×16的长枪,渲染到32×16的渲染区中,这个时候长枪不用进行旋转,显示正常:水平的长枪; 然后按下了键盘上的M按键,程序开始对长枪进行旋转处理:我的程序逻辑是:创建一个新的16×32的渲染区域,然后将32×16的长枪通过SDL_RenderCopyEx()复制进入16×32的渲染区域、并同时进行90度的旋转。此时,SDL_RenderCopyEx()内部的操作很可能是:先将32×16的纹理资源贴入16×32的渲染区域中,因为没有指定缩放,所以它自行进行缩放,将长枪从32×16缩放成了16×8,然后再进行旋转,最终得到了一个8×16的竖直图像并呈现在16×32的渲染区域中。 上述描述不够直观、但我也不想额外画图说明这个事情了,总之大概意思就是这样——旋转之后的图案总是会出现各种各样的扭曲情形。 三、粗糙的、简易的解决方案 知道了原因,解决起来就比较容易了,但实际上也并不是一帆风顺的。我起初是退而求其次的放弃了32×16这样的“长方形”,而是将软件中用到的所有图形,都做成了32×32的图形,这样无论怎样旋转(只要不出现30度、45度等非直角角度),一定可以正常显示。 然而这种退而求其次的方法并不可行——毕竟软件后面还有很多功能要基于正确的资源尺寸进行进一步的开发。 所以第二次调整改成了更复杂的一个方案:长枪还是32×16的纹理资源,但是每次想进行重新旋转的时候,渲染显示区域都先创建一个32×32的显示区、然后完成旋转之后放在这个32×32的显示区域中。再重新创建一个正确的16×32的显示区,将32×32显示区中的竖直长枪复制出来、写入到16×32的显示区中…… 这虽然是可行的,但是浪费内存、CPU资源不说,更令程序变得十分复杂。要额外的记录很多临时数据、还要计算数值长枪在32×32区域中的正确位置才能准确裁切出来。 以上两个方案都是“自然而然”想到的方案,虽然“思路清晰”、但是“实现复杂”并且总是感觉存在着诸多的隐患。 四、更正确、完美的解决方案 今天我才想明白:其实没有必要让SDL2去进行纹理的旋转,SDL2的SDL_RenderCopyEx()进行纹理旋转有它巨大的优势——对于各种角度的旋转都可以轻松胜任。然而我也用不到这些角度呀,我只需要进行90度、180度、270度的旋转,这几个最基本的旋转就是最基本的矩阵90度旋转。…