分享到新浪微博 分享到QQ空间 打印

[原创改版教程] nds 3D游戏hack入门(3) 多边形、顶点与光照

nds 3D游戏hack入门(3) 多边形、顶点与光照

接下来讲解多边形,多边形在3D模型中类似于骨架的作用,搭好了骨架,才能把纹理包裹上去
NDS的3D引擎支持2种多边形的绘制,这2种多边形又有2种绘制模式,绘制多边形的方式是通过依次设置顶点坐标完成
关于顶点坐标的设置顺序,不同的模式有不同的顺序,下面也将介绍
此外为了更逼真地模拟出3D模型,NDS的3D引擎支持顶点着色,顶点着色一般分2步骤,第一步是设置顶点颜色,第二步是设置光照参数,第二步是可以省略的
接下来的颜色混合将由3D引擎完成,本文也会介绍光照对于顶点着色的实际影响,此外,顶点颜色对于纹理混合的影响,没意外的话,将在纹理贴图这一章介绍

多边形参数设置

引用
复制内容到剪贴板
代码:
40004A4h - Cmd 29h - POLYGON_ATTR - 设置多边形参数 (W)
  0-3   分别对应4道光线的启用flag (0为禁用,1为启用)
  4-5   多边形模式(0=Modulation,1=Decal,2=Toon/Highlight Shading,3=Shadow)
  6     多边形背面是否渲染(0为隐藏,1为渲染) //线段总是被渲染(因为没有正面/背面)
  7     多边形正面是否渲染(0为隐藏,1为渲染)
  8-10  无用
  11    Depth-value for Translucent Polygons  (0=Keep Old, 1=Set New Depth)
  12    远截面横穿多边形时是否渲染       (0=隐藏, 1=渲染/裁剪)
  13    1-Dot polygons的深度比深度阈值大时是否隐藏 (0=隐藏, 1=依旧渲染)
  14    深度测试,跟之前绘制的多边形采用何种方式比较 (0=小于, 1=等于) (经常用0)
  15    雾化启用                            (0=禁用, 1=启用)
  16-20 Alpha通道(0=线框,1..30=透明,31=非透明、固实)
  21-23 无用
  24-29 多边形ID(00h..3Fh,用于透明/阴影/勾边)
  30-31 无用
参数一个个分析过去
首先是0-3的灯光控制flag,1bit控制一个
用这个可以控制多边形接受到的光线的数量,关于4道光线的具体控制以及对顶点着色的影响将在下文提到
4-5的多边形模式,这4个模式影响的是纹理混合,在纹理篇中介绍
6 7的话,则是控制多边形正面跟背面是否渲染,关于正面跟背面的判定,我也不清楚,desmume里找不到相关的代码
11的话,我没有翻译,毕竟3D方面我也是半吊子,呵呵,这个flag,我的理解是对于透明状态的多边形是否要重定义Depth-value
12的话,远截面在上一章有介绍,就不多说了,如何判定是否横穿,可以通过判断多边形跟远截面是否有相交的部分
13的话,先解释一下1-Dot polygons,1-Dot polygons就是非常小或者距离非常远的多边形,在屏幕上经常以一个点出现
NDS的3D引擎提供一个I/O寄存器,允许你设置深度阈值,1-Dot polygons的深度大于这个值(即意离camera更远),将自动隐藏
设置深度阈值的I/O寄存器是0x4000610(DISP_1DOT_DEPTH),bit13的作用就是选择是否开启这个功能
14的话,是深度测试,在opengl里又叫DepthFunc,深度测试的作用就是与之前绘制的多边形的深度进行比较,大于则隐藏,这里提供2种比较判定,一种小于一种是等于
GBATEK提到,设置为等于,可能会出现数值误差导致的绘图错误,存在不可知的结果,所以一般设为0
15的话,雾化启用,由于雾化属于3D特效,这个系列的教程就不讲解了,其实GBATEK这部分写得很少,想研究也比较费力
16-20的话,阿尔法通道,阿尔法通道是用来表示多边形的透明程度的
24-29的话,各种3D特效内用来表示优先级的东西吧,不讲解
此外,关于多边形属性,NO$GBA debugger的3D查看器有提供完整的细节信息显示


注意polygon_attr,后面包括了多边形属性的细节,这里表示灯光0开启,多边形模式为Modulation,正面渲染,开启雾化效果,阿尔法的值为0x1f(不透明)

SDK内相关的API为
复制内容到剪贴板
代码:
void G3_PolygonAttr(int light,GXPolygonMode polyMode,GXCull cullMode,int polygonID,int alpha,int misc);
cullMode就是上文提到的正背面是否渲染的设置参数,0同时不显示,1显示正面,2显示背面,3全部显示
misc是个5bit的flags,对应多边形属性的bit11到bit15

设置顶点颜色

为多边形的顶点上色,如果有开启光照特效的话,顶点的颜色将受光照特效的影响而改变,具体见下文
复制内容到剪贴板
代码:
4000480h - Cmd 20h - COLOR - Directly Set Vertex Color (W)
  Parameter 1, Bit 0-4    Red
  Parameter 1, Bit 5-9    Green
  Parameter 1, Bit 10-14  Blue
  Parameter 1, Bit 15-31  Not used
这个不用我翻译了吧,这个寄存器允许你直接设置顶点颜色的RGB值,注意,如果RGB555要转换成RGB666的话,必须乘以2然后加1,不能只是乘以2
这一小节的内容可能太短,我补个简单的hack实例,rom是空之探险队汉化版,目标是修改开始菜单对话框的顶点颜色
先跟踪顶点颜色,发现,它来自0x22A8A5C,这里存储了顶点颜色RGB的3个值,将其中一个修改成0,我将R修改为0,它变成下面这样


对于纹理混合结果产生了很大的影响
另外,0x22A8A5F存储多边形的阿尔法通道,可以通过修改它将对话框透明化


SDK内对应的API为
复制内容到剪贴板
代码:
void G3_Color(GXRgb color);
绘制3D多边形
上文也说到了,多边形的绘制,依靠按顺序设置顶点坐标完成
DS的3D引擎支持2种多边形绘制,一种是三角形一种是四边形,每种多边形又有2种绘制模式,一种是分开的绘制方式,一种是连接在一起的绘制方式
如果要绘制线段,可以先将绘制模式设置为三角形,让其中2个顶点重合即可,此外要注意,绘制四边形的时候,不能绘制凹多边形
复制内容到剪贴板
代码:
  分开绘制三角形     合并绘制三角形     绘制线段
  v0                 v2___v4____v6
  |\      v3         /|\  |\    /\     v0    v1
  | \     /\      v0( | \ | \  /  \     ------
  |__\   /__\        \|__\|__\/____\         v2
  v1 v2 v4  v5       v1   v3  v5   v7

  分开绘制四边形             合并绘制四边形           凹多边形
    v0__v3                 v0__v2____v4     v10__    v0__v3     v4
     /  \   v4____v7        /  \     |\ _____ / /v11   \/       |\
    /    \   |    \        /    \    | |v6 v8| /       /\     v5| \
   /______\  |_____\      /______\___|_|_____|/       /__\     /___\
   v1    v2  v5    v6     v1    v3  v5 v7   v9       v2   v1   v6   v7
绘制的顺序也体现在这里了

绘制多边形之前,得先设置多边形的绘制模式
引用
复制内容到剪贴板
代码:
4000500h - Cmd 40h - BEGIN_VTXS - 开始设置顶点 (W)
  Parameter 1, Bit 0-1    Primitive Type (0..3见下)
  Parameter 1, Bit 2-31   无用
参数是0到3,对应的就是分开绘制三角形 分开绘制四边形 合并绘制三角形 合并绘制四边形

SDK内对应的API
复制内容到剪贴板
代码:
void G3_Begin(GXBegin primitive);
设定顶点坐标的I/O寄存器
复制内容到剪贴板
代码:
400048Ch - Cmd 23h - VTX_16 - Set Vertex XYZ Coordinates (W)
4000490h - Cmd 24h - VTX_10 - Set Vertex XYZ Coordinates (W)
4000494h - Cmd 25h - VTX_XY - Set Vertex XY Coordinates (W)
4000498h - Cmd 26h - VTX_XZ - Set Vertex XZ Coordinates (W)
400049Ch - Cmd 27h - VTX_YZ - Set Vertex YZ Coordinates (W)
参数的设定如下,
VTX_16接收2个32bit的参数,其中,第一字节的bit0-bit15存储X坐标,bit16-bit31存储Y坐标,第二字节的bit0-bit15存储Z坐标
它们都是有符号的带12位小数的定点数

VTX_10接收1个32bit的参数,X Y Z各占10bit,分别为有符号的带6为小数的定点数
VTX_XY VTX_XZ VTX_YZ都接收一个32bit参数,X Y Z的数字格式同VTX_16
但只能设置X Y Z中的其中2个,另外一个将沿用最后一次被设置的值,为绘制某些图提供了方便

SDK内对应的API
复制内容到剪贴板
代码:
void G3_Vtx(fx16 x, fx16 y, fx16 z);
void G3_Vtx10(fx16 x, fx16 y, fx16 z);
void G3_VtxXY(fx16 x, fx16 y);
void G3_VtxXZ(fx16 x, fx16 z);
void G3_VtxYZ(fx16 y, fx16 z);
还有一个设置相对顶点坐标的I/O寄存器
复制内容到剪贴板
代码:
40004A0h - Cmd 28h - VTX_DIFF - Set Relative Vertex Coordinates (W)
接收一个32bit的参数,坐标的偏移量X Y Z分别以10bit存储,均为有符号带9位小数的定点数
将把上一次设置的顶点坐标的X Y Z与偏移量的X Y Z相加,得出新的顶点坐标,注意,因为这里设置的顶点坐标最终的结果是有符号带12位小数的定点数
所以3D引擎在计算的时候,会把小数的最后3位补0后再计算,因此,在设置参数的时候,可以先除以8后(即逻辑右移3位)再设置

SDK内对应的API为
复制内容到剪贴板
代码:
void G3_VtxDiff(fx16 x, fx16 y, fx16 z);
最后一个是顶点坐标绘制结束的I/O寄存器
复制内容到剪贴板
代码:
4000504h - Cmd 41h - END_VTXS - End of Vertex List (W)
根据GBATEK的说法,这个接口其实无用,DS硬件跟NO$GBA上这个操作无意义,但在官方的模拟器上似乎有用?(gbatek在这里也打了个问号,汗)
本来嘛,顶点坐标设置好以后,就可以交给3D引擎绘制了,不需要它也可以

SDK内对应的API为
复制内容到剪贴板
代码:
void G3_End();
看到这里,我想有的读者应该有疑问了,这里的顶点坐标的取值范围异常的小(最大的都不超过10),稍微大一点的3D模型都很难绘制
其实不用担心这个问题,除了将大的3D模型分割以外,还有一个方法
因为顶点坐标会乘以当前的position矩阵,换算到世界坐标系中,所以可以不断地修改position矩阵,以适应3D模型的绘制需要

再来个hack实例,通过修改顶点坐标调整空之探险队的对话框
通过跟踪可以发现对话框的顶点坐标都是通过计算间接生成的(也没必要一个个独立设置,因为对话框本来就是比较简单的图形)
所以要修改的话,只需要一步步逆回去,找到影响计算结果的数值即可(这里可能是对话框的宽高)
之前的hack实例没有说得很详细,一些略掉了,这里稍微详细点,也算填充下篇幅
在0x400048C(VTX_16)上下断点,发现它会断在函数sub_1FF8728,没猜错的话,这是对话框绘制函数
在函数一开始的地方改写为bx r14(让它不执行这个函数),发现对话框完全不显示了,判断正确


接下来分析这个函数的参数,这个函数只接收一个参数,是指向一个结构体的指针
在分析的时候,要一步步往C或者C++靠拢,因为基于C或者C++编写的游戏,必定有大量相关的特征
然后观察这个参数的变化,0x224D260 0x224D294 0x224D2C8 0x224D2FC...以一个线性的方式增长,每次加0x34,可以肯定这里存放了结构体数组
也可以初步断定,结构体的大小是0x34,并且这个结构体内定义了顶点坐标
因为我们只是需要修改顶点坐标调整对话框,所以结构体并不需要完全分析出来,当然,如果能完全分析,那可以做更多的hack
如何确认结构体里的顶点坐标呢,很简单,通过分析sub_1FF8728这个函数就行
在函数的开始,可以看到r0的值被赋值到r10上,后面的代码对于结构体的访问都是ldr* r* [r10,*]/str* r* [r10,*]这样的形式
利用这个特点可以很容易分析出结构体的具体结构
先运行到写入顶点坐标的代码


写入第一个顶点坐标的代码在0x1FF88FC上,接下来看一下R0是从哪来的,唔,跟R0相关的运算好多
最靠近的一条是mov r0,r0,r1,lsr 10h,这里把r1跟r0进行了合并,通过0x400048C的定义可以得知,这里的r0是Y坐标,r1是X坐标
先确认R0,盯着R0一步步往上确认,可以看到R5这个寄存器是R0的一部分,接下来看一下R5的来历,这个很好确认
在0x1FF87B0这里,读者要是觉得眼花,不妨把这段代码dump出成文本,用文本工具搜索R5
发现它是来自r2,往上确认r2的来源,是从r10+0x18这个位置上读取的,所以可以判定结构体的0x18处存储一个点的顶点坐标的Y(16位有符号变量)
观察可知结构体的0x16是同一个点的顶点坐标的X,0x1A后面的12字节存储另外3个顶点坐标,接下来要看一下这几个顶点坐标是怎样生成的
下断点然后分析,这个过程我就不细写了
最后会来到0x22A8950,分别存储对话框的坐标与大小,修改后的效果图
嘛,这里插一句,这个对话框的坐标与大小跟显示在屏幕上一致,因此直接搜索也可以,哦呵呵呵呵~

从CVV转换到屏幕坐标
这段差点漏掉,现在我们来考虑一个问题,当一个点的坐标从模型坐标系变换到世界坐标系,再变换到camera坐标系,最后转到长宽高各为2的CVV空间里
首先这个变换过程,利用上篇文章提到的裁剪矩阵来说明就是
(x,y,z,1)*裁剪矩阵
因为裁剪矩阵本身就是position矩阵*投影矩阵的结果
如果记(xx,yy,zz,ww)=(x,y,z,1)*裁剪矩阵
则屏幕上的坐标为
复制内容到剪贴板
代码:
  screen_x = (xx+ww)*viewport_width / (2*ww) + viewport_x1
  screen_y = (yy+ww)*viewport_height / (2*ww) + viewport_y1
其中
  viewport_width=viewport_x2-viewport_x1
  viewport_height=viewport_y2-viewport_y1
关于x1 y1 x2 y2,直观点说,就是2个坐标,把ds的屏幕左下角作为原点形成的二维坐标系中的坐标
可以在no$gba debugger的I/O窗口的LCD 3D标签页确认到,老实说我找老半天才找到这个属性


这个公式的推导顺便解说一下
利用比例跟相似多边形的概念,很容易就能推导出来
首先得把坐标的ww转成1,也就是所有的分量除以ww,即(xx/ww,yy/ww,zz/ww,1)
根据比例跟相似多边形的概念,有如下关系式
复制内容到剪贴板
代码:
((xx/ww)-(-1))/2 = (screen_x-viewport_x1)/viewport_width
((yy/ww)-(-1))/2 = (screen_y-viewport_y1)/viewport_height
接着求出screen_x跟screen_y的值就行

[ 本帖最后由 enler 于 2013-3-23 20:34 编辑 ]
附件: 您所在的用户组无法下载或查看附件

TOP

多边形光照的参数设置
接下来讲解多边形的光照以及相关的向量,由于我没有完全理解光照特效
本部分仅解释相关的I/O寄存器,对gbatek进行适当的翻译,并结合desmume的源代码解释具体的工作方式
要实现多边形的光照效果,有2个步骤,第一步设置光照的相关参数,第二步是设置法向量(Normal Vector)

首先要在多边形参数中设置好灯光,用几个开几个

设置光照参数的I/O寄存器
复制内容到剪贴板
代码:
40004C8h - Cmd 32h - LIGHT_VECTOR - 设置光线的向量(W)
40004CCh - Cmd 33h - LIGHT_COLOR - 设置光线的颜色(W)
40004C0h - Cmd 30h - DIF_AMB - MaterialColor0 - 设置漫反射与环境反射的颜色(W)
40004C4h - Cmd 31h - SPE_EMI - MaterialColor1 - 设置镜面反射与自发光的颜色(W)
40004D0h - Cmd 34h - SHININESS - 设置漫反射的Shininess Table(W)
LIGHT_VECTOR的参数
复制内容到剪贴板
代码:
  bit
  0-9   向量的X分量 (1bit符号,9bit小数)
  10-19 向量的Y分量 (1bit符号,9bit小数)
  20-29 向量的Z分量 (1bit符号,9bit小数)
  30-31 指示要设置光线的ID (0..3)
这个命令将传进来的向量与directional矩阵相乘,算出最终的光线向量,同时算出半角向量(HalfVector),然后正规化半角向量(每个分量除以向量的长度)
半角向量的求法很简单,光线向量与视角向量(LineOfSightVector)相加即可,NDS的视角向量固定为(0,0,-1)
每个光线向量都有一个对应的半角向量,NDS有4个光线向量,同样的,也有4个半角向量

LIGHT_COLOR的参数
复制内容到剪贴板
代码:
  bit
  0-4   Red          (0..1Fh)
  5-9   Green        (0..1Fh)
  10-14 Blue         (0..1Fh)
  15-29 无用
  30-31 指示要设置光线的ID (0..3)
nds采用的颜色格式通常为RGB5,这个没什么好说的

DIF_AMB的参数
复制内容到剪贴板
代码:
  bit
  0-4   漫反射的Red分量
  5-9   漫反射的Green分量
  10-14 漫反射的Blue分量
  15    是否将漫反射颜色设置为顶点颜色
  16-20 环境反射的Red分量
  21-25 环境反射的Green分量
  26-30 环境反射的Blue分量
  31    无用
先来说说漫反射
假设你在小黑屋里用一个手电筒照射茶壶吧,你想让茶壶的某一角度看上去最亮,那么把手电筒正对向那个角度就行
在3D引擎内就是把法向量与光线向量的方向设置成互为相反即可

再来说说环境反射,一个物体不单单直接反射光线,它也间接反射来自其他物体的反光
比如房间里有面镜子,不直接照射物体,而是先照射镜子,让它反射到物体上,这时候物体反射来自镜子的反光,这就是环境反射了

SPE_EMI的参数
复制内容到剪贴板
代码:
  bit
  0-4   镜面反射的Red分量
  5-9   镜面反射的Green分量
  10-14 镜面反射的Blue分量
  15    是否启用镜面反射Shininess Table (0=禁用, 1=启用)
  16-20 自发光的Red分量
  21-25 自发光的Green分量
  26-30 自发光的Blue分量
  31    无用
在自然界经常有表面相当光滑的物体,比如金属片,从观察者的角度来看,不同的视角,它反射的光的强度也不尽相同
如果想要让视角观察到最强烈的光线,则让法向量平分视角与入射的光线向量
Shininess Table的话,还是放在下文的顶点着色的实际运算过程里说明吧

自发光就是自己在发光,不受制于外部光源影响

DIF_AMB与SPE_EMI这2个I/O寄存器设置的4种颜色,可以理解为颜色的修正量,具体的计算将在下文列出

接下来是Shininess Table的设置
复制内容到剪贴板
代码:
40004D0h - Cmd 34h - SHININESS - 设置镜面反射的Shininess Table (W)
一共32个参数,每个参数4字节,每字节记录一个Shininess(我的翻译是“镜面反射度”,下面也这样描述),从低字节到高字节记录

  0-7   Shininess 0 (无符号带8bit的小数的定点数)
  8-15  Shininess 1 (同上)
  16-23 Shininess 2 (同上)
  24-31 Shininess 3 (同上)
这个表是否使用取决于SPE_EMI的参数的bit15

SDK内对应的API
复制内容到剪贴板
代码:
void G3_LightVector(GXLightId lightID, fx16 x, fx16 y, fx16 z);
void G3_LightColor(GXLightId lightID, GXRgb rgb);
void G3_MaterialColorDiffAmb(RGB diffuse, GXRgb ambient, BOOL IsSetVtxColor);
void G3_MaterialColorSpecEmi(GXRgb specular, GXRgb emission, BOOL IsShininess);
void G3_Shininess(const u32* table);
法向量设置
上面的那些仅仅只是设置光照参数而已,如果没有设置顶点法向量的话,是不会对顶点着色产生影响的
我们来看一下设置法向量的I/O寄存器以及参数
引用
复制内容到剪贴板
代码:
4000484h - Cmd 21h - NORMAL - 设置法向量(W)
  0-9   X分量(有符号带9bit的小数)
  10-19 Y分量(有符号带9bit的小数)
  20-29 Z分量(有符号带9bit的小数)
  30-31 无用
SDK内对应的API
复制内容到剪贴板
代码:
void G3_Normal(fx16 x, fx16 y, fx16 z);
设置好之后,它会把传进来的参数乘以directional矩阵,让它转成真正的法向量
当然,实际的影响不止这些,一旦设置好法向量,3D引擎将会进行顶点着色运算,见下文

顶点着色运算过程(*选读)
这部分特别参考了desmume项目中的gfx3d.cpp里的gfx3d_glNormal函数,有兴趣的可以去下载desmume的源代码看看
先贴出GBATEK中执行法向量命令时的伪代码
复制内容到剪贴板
代码:
  IF TexCoordTransformMode=2 THEN TexCoord=NormalVector*Matrix (see TexCoord)
  NormalVector=NormalVector*DirectionalMatrix
  VertexColor = EmissionColor
  FOR i=0 to 3
   IF PolygonAttrLight[i]=enabled THEN
    DiffuseLevel = max(0,-(LightVector[i]*NormalVector))
    ShininessLevel = max(0,2*((-HalfVector[i])*(NormalVector))^2 - 1)
    IF TableEnabled THEN ShininessLevel = ShininessTable[ShininessLevel]
    ;注: 下面的代码要遍历RGB分量...
    VertexColor = VertexColor + SpecularColor*LightColor[j]*ShininessLevel
    VertexColor = VertexColor + DiffuseColor*LightColor[j]*DiffuseLevel
    VertexColor = VertexColor + AmbientColor*LightColor[j]
   ENDIF
  NEXT i
原版的伪代码有误,我按照desmume的说法进行修正了
step1
它先检测纹理坐标的转换方式,如果是根据法向量进行转换,则转换纹理坐标,但这个内容跟顶点着色无关
这里不介绍,在下篇的纹理贴图里介绍纹理坐标时会提到它的

step2
计算出法向量,并且顶点颜色设置成自发光的颜色

step3
接下来要遍历4道光线(如果没有启用则continue),并计算漫反射度跟镜面反射度
漫反射度,先算出光线向量与法向量夹角的余弦,然后变更符号,如果小于0,则取0
(这里插一句,根据向量的定理,2个向量相乘的结果叫点积,等于2个向量的模相乘并乘以夹角的余弦,如果模都等于1
则直接等于夹角的余弦,一般在设置光线向量跟法向量的时候,就要把它设置为模为1的单位向量了)

镜面反射度,先记半角向量的负向量与法向量的点积为cosθ,需要计算出cos2θ,desmume的开发者在这个地方吐槽了GBATEK,说求cos2θ的过程错掉了,汗
实际的求法是cos2θ = 2*(cosθ)^2-1,跟漫反射度一样,如果小于0,则取0
不过desmume加了一个判断,如果大于等于1,则取0xfff,这里是有符号12位小数的定点数,表示0.999755859375,就是不能让它超过1

接下来,如果启用镜面反射度表的话,还需要从表中读取,将镜面反射度右移5位,转成整型后作为索引值在表中定位,然后取用之

step4
接下来就是真正的顶点着色了
按RGB逐个计算过去,先计算漫反射部分,就是光线颜色*漫反射颜色*漫反射度
镜面反射部分就是光线颜色*镜面反射颜色*镜面反射度
环境反射部分就是光线颜色*环境反射颜色
接着全部与顶点颜色相加即可完成顶点着色

镜面反射的注意事项
因为镜面反射度跟半角向量有关,而半角向量跟视线向量有关,但是nds的3D引擎中,视线向量是不会随着camera的旋转而改变
这就意味着,如果camera旋转了,则镜面反射将发生严重的误差

[ 本帖最后由 enler 于 2013-3-23 20:33 编辑 ]

TOP

引用:
原帖由 喝神 于 2013-6-19 15:37 发表
= =那DS里的3d引擎中的向量很复杂,其中的原理估计得学了专业知识才能了解
看的不明不白的,果然改版的都是神人么= =
顺便来句迟来的话:PTB新版总算有人发技术帖了
我觉得矩阵篇比这篇写得好,矩阵篇的hack实例都比较好
这篇的文字太多,向量部分又比较抽象,找不到比较好的hack实例,所以我就直接按gbatek跟desmume写了

TOP

引用:
原帖由 逆风DE影 于 2013-6-20 08:34 发表
DS里面的素材是怎么取。。。不是有人直接拿出来,然后放GBA改版
取素材不难的,除了A5I3 A3I5 4x4_texel,ct2都是直接支持的,这3个都是有透明通道的材质,4x4那个还是高清材质,具体见本系列教程的第四章纹理贴图篇

TOP

回复 11楼 喝神 的帖子

我默认看这系列教程的人,了解线性代数、空间几何、c语言、汇编语言等相关知识

TOP