Python,Pygame,图像处理:重新提取加载的png,作为等轴测Ti的纹理

2024-06-28 14:25:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我是一个17岁的程序员,试图用pygame用python编写一个等距游戏。在完成了一个瓷砖引擎,使用了不好看的,gimp绘制的PNG,我想知道,是否可以通过纹理渲染一些瓷砖。我希望我提供了所有需要理解的东西,我的问题是什么,请原谅我的英语不够好。在

简单地说,我要做的就是生成一个128×128像素宽的等距瓷砖图像,使用下面的图片作为块的所有三个边的纹理:

texture

(这里有链接,因为这是我的第一篇文章,所以还不能放图片)

为了更好地解释我所要做的,我画了一张小图: desired output

我已经在互联网上搜索了大约2个小时,没有找到解决方案,除了图块的顶部,以下是我在代码中已经得到的:

这是图像处理模块,transformToRightPart()是我需要帮助的方法:

import pygame

class Image(object):
    '''
    Use this Module to create Tiles by Texture to use them later in the Tileengine.
    It is important to run pygame.init() before creating objects of this class!
    Contains unfinished Elements!
    '''
    def __init__(self, path):
        self.loadFromPath(path)

    def getIMG(self):
        assert self.originalIMG is not None, "No picture to return"
        if not self.IMG == None:
            return self.IMG
        else:
            return self.originalIMG

    def loadFromPath(self, path):
        '''
        Don't do convert() or convert_alpha() here,
        as Objects of this class are created during the loading process,
        with no pygame.display() created.
        '''
        self.originalIMG = pygame.image.load(path)
        self.IMG = None

    def transformToTopPart(self):
        '''
        Transforms the loaded Image to the Top Part of an Isometric Tile, with the Dimensions 2:1,
        said in Pixels: 128 px Width by 64 px Height.
        '''
        self.IMG = pygame.transform.rotate(self.originalIMG, 45)
        self.IMG = pygame.transform.scale(self.IMG, (128, 64))

    def transformToRightPart(self):
        '''
        TODO!! Don't ask how (X.X)
        Transforms the loaded Image to the right Part of an Isometric Tile.
        '''
        assert False, "This method isn't finished, try something different ;)"

    def transformToLeftPart(self):
        '''
        Transforms the loaded Image to the left Part of an Isometric Tile.
        Due to the nice geometric fact, that the shape of the left part,
        is just the flipped right part shape and we don't lose quality by flipping,
        we do this little trick, to enshorten the code.
        '''
        self.originalIMG = pygame.transform.flip(self.originalIMG, True, False)
        self.transformToRightPart()
        self.IMG = pygame.transform.flip(self.IMG, True, False)
        self.originalIMG = pygame.transform.flip(self.originalIMG, True, False)

这是一个模块,它创建一个窗口,其中包含要渲染的平铺:

^{pr2}$

代码的输出如下所示:

my output

我想实现的是:

desired output


Tags: ofthetopathimageselffalseimg
2条回答

非常感谢Spektre为帮助我所做的一切努力,但总而言之,经过两天的反复思考和错误修复,我自己想出了一个解决方案。它可能没有像Spektre在他的c++例子中那样直接在数组中定位像素那么快或有效,但是这是一种方式,你只依赖于pygame,而且很容易理解。在

我做了什么?-我写了两个函数,第一个函数得到一个只包含另一个曲面的一个列的曲面,它有一个索引,表示该列的x位置。 第二步,计算一个系数,每行向下移动多远,如果最后一行向下移动一定数量的像素,然后返回一个带有偏移图像的表面。在

这是神奇的密码:

import pygame

from pygame.locals import *
from pygame import Surface

def getColumn(surface, index):
    assert index <= surface.get_width(), "index can't be bigger, than surface width"
    height = surface.get_height()
    subsurf = Surface((1,height)) # Create Surface 1 px by picture-height high, to store the output in
    subsurf.blit(surface.subsurface(pygame.Rect( (index,0),(1,height) )),(0,0)) # Blit a one pixel width subsurface with x Position at index of the image to subsurf
    return subsurf

def shiftRightDown(surface, pixels):
    size = surface.get_size()
    newSize = (size[0], size[1]+pixels)
    coeff = pixels / size[0]
    returnSurface = Surface(newSize)
    for i in range(size[1]): # here happens the magic
        returnSurface.blit(getColumn(surface, i), (i,0+int(i*coeff)))
    return returnSurface

毕竟,我非常尊重Spektres的编码技巧,尽管我很难理解c plus示例中的任何东西,因为我是一个完全的初学者。在

好吧,我只是简单地复制纹理像素到精灵使用平面投影(每边的基本向量)+一些重新缩放,因为纹理不符合你的精灵分辨率。我在<强> C++ +>强>中做了,所以这里我的注释代码(你可以从中提取方程):

// [constants]
const int sxs=128;              // target sprite resolution [pixels]
const int sys=128;
const int height=32;            // height/thickness of your tile [pixels]
const DWORD cback=0x00FFFFFF;   // background color (or invisible for the sprite)

// [variables]
DWORD **ptxr,**pspr;            // direct pixel access pointers (any 32bit variable type)
Graphics::TBitmap *txr,*spr;    // VCL bitmaps
int txs,tys,x,y,x0,y0,xx,yy,th;

// [init]
// create VCL bitmaps (can ignore this)
txr=new Graphics::TBitmap; // input texture
spr=new Graphics::TBitmap; // output sprite
// load texture
txr->LoadFromFile("texture.bmp");
txs=txr->Width;
tys=txr->Height;

// prepare sprite resolution
spr->SetSize(sxs,sys);
// allow direct pixel access
txr->HandleType=bmDIB; txr->PixelFormat=pf32bit; ptxr=new DWORD*[tys]; for (y=0;y<tys;y++) ptxr[y]=(DWORD*)txr->ScanLine[y];
spr->HandleType=bmDIB; spr->PixelFormat=pf32bit; pspr=new DWORD*[sys]; for (y=0;y<sys;y++) pspr[y]=(DWORD*)spr->ScanLine[y];

// [render sprite]
th=height*(txs-1)/(sxs-1);  // height of tile in texture [pixels]
// clear
for (y=0;y<sys;y++)
 for (x=0;x<sxs;x++)
  pspr[y][x]=cback;
// top side
x0=0; y0=(sys*3/4)-height;
for (y=0;y<tys;y++)
 for (x=0;x<txs;x++)
    {
    // isometric projection of top side
    xx=x0+(x+y)*(sxs-1)/((txs-1)*2);
    yy=y0+(x-y)*(sxs-1)/((txs-1)*4);
    // copy pixel from texture to sorite
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
     pspr[yy][xx]=ptxr[y][x];
    }
// left side
x0=0; y0=(sys*3/4)-height;
for (y=0;(y<tys)&&(y<th);y++)
 for (x=0;x<txs;x++)
    {
    // isometric projection of top side
    xx=x0+(x      )*(sxs-1)/((txs-1)*2);
    yy=y0+(x+(4*y))*(sxs-1)/((txs-1)*4);
    // copy pixel from texture to sorite
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
     pspr[yy][xx]=ptxr[y][x];
    }
// right side
x0=sxs/2; y0=sys-height-1;
for (y=0;(y<txs)&&(y<th);y++) // x,y are swapped to avoid connection seems
 for (x=0;x<tys;x++)
    {
    // isometric projection of top side
    xx=x0+(+x      )*(sxs-1)/((txs-1)*2);
    yy=y0+(-x+(4*y))*(sxs-1)/((txs-1)*4);
    // copy pixel from texture to sorite
    if ((xx>=0)&&(xx<sxs)&&(yy>=0)&&(yy<sys))
     pspr[yy][xx]=ptxr[x][y];
    }

// here do your stuff with your sprite spr I render source and resulting images into bitmap to show on screen
// you can ignoe this
bmp->SetSize(txs+5+sxs,max(tys,sys));
bmp->Canvas->Brush->Color=clBtnFace;
bmp->Canvas->FillRect(TRect(0,0,bmp->Width,bmp->Height));
bmp->Canvas->Draw(0,0,txr);
bmp->Canvas->Draw(txs+5,0,spr);

// [exit]
// release memory
delete[] ptxr;
delete[] pspr;
if (txr) delete txr; txr=NULL;
if (spr) delete spr; spr=NULL;

纹理必须是方形的否则右侧渲染将有访问违规问题,更不用说可见的接缝了。。。在

下面输出sprite示例代码:

rendered sprite

现在它是如何工作的:

overview

忽略VCLinit/load/exit处理图像的东西,因为重要的只是渲染。在

每个部分包括设置起点(红色正方形)并将纹理x,y坐标转换为平面投影基向量(黑色箭头)中该起点的偏移量。在

偏移量还乘以纹理和精灵之间的分辨率来处理它们不同的大小。在

请看这里了解我使用的直接像素访问:

PS

你可以添加灯光来增强3D效果。。。这是当顶部为100%时,左侧为75%,右侧为50%强度时的外观:

lighting

模拟从左上方射来的光

相关问题 更多 >