
#include <dos.h>
#include <math.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#define PI 3.1415926
/*定义按键*/
#define ESC 0x11b
/*以下4个键,依次是上 下 左 右*/
#define X_axis_clkwise0x4800
#define X_axis_Cntclkwise 0x5000
#define Y_axis_clkwise0x4b00
#define Y_axis_Cntclkwise 0x4d00
/*以下2个键,依次是A, D*/
#define Z_axis_clkwise0x1e61
#define Z_axis_Cntclkwise 0x2064
#define Distance_forward 0x1177
#define Distance_Backward 0x1f73
/*以下6个键,依次是U, J, I, K, O, L*/
#define X_Delta_Plus 0x1675
#define X_Delta_Minus 0x246a
#define Y_Delta_Plus 0x1769
#define Y_Delta_Minus 0x256b
#define Z_Delta_Plus 0x186f
#define Z_Delta_Minus 0x266c
/*绕X轴旋转矩阵*/
float X_Rotate_Matrix[4][4] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
/*绕Y轴旋转矩阵*/
float Y_Rotate_Matrix[4][4] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
/*绕Z轴旋转矩阵*/
float Z_Rotate_Matrix[4][4] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
/*平移矩阵*/
float Transist_Matrix[4][4] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 }
/*透视投影变换矩阵*/
float Perspective_Projection[4][4] = { 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 1 }
int num
float *Matrix_Mul(float *pMatrix1, int Num_Row_Matrix1, int Num_Column_Matrix1,
float *pMatrix2, int Num_Row_Matrix2, int Num_Column_Matrix2 ) {
/*实现两个矩阵:
输入参数: *pMatrix1: 指向第一个矩阵
Num_Row_Matrix1: 第一个矩阵的行数
Num_Column_Matrix1: 第一个矩阵的列数
余下三个参数类推
return 指向运算结果的float类型指针.*/
int i, j, m, n
float *pNewMatrix1, *pNewMatrix2, Sum
if( Num_Column_Matrix1 != Num_Row_Matrix2) {
printf("Invalid Matrixs!\n")
return 0
}
pNewMatrix1 = malloc(Num_Row_Matrix1 * Num_Column_Matrix2 * 4)
/*申请内存空间, Size(/bytes) = 第一个矩阵的行数 * 第二个矩阵的列数 * 4(= sizeof(float))*/
pNewMatrix2 = pNewMatrix1
/*具体算法详见如下代码*/
for( i = 0i <Num_Row_Matrix1i++) {
for( n = 0n <Num_Column_Matrix2n++) {
Sum = 0
for( j = 0j <Num_Column_Matrix1j++)
Sum += (*(pMatrix1+i*Num_Column_Matrix1+j)) * (*(pMatrix2+j*Num_Column_Matrix2+n))
*(pNewMatrix1++) = Sum
}
}
return pNewMatrix2
}
/*转换成齐次坐标矩阵*/
void Matrix_Convertion(float *pMatrix, int Num_Row) {
int i, j
for(i = 0i <Num_Rowi++) {
if((*(pMatrix+i*4+3)) != 0) {
*(pMatrix+i*4) = (*(pMatrix+i*4)) / (*(pMatrix+i*4+3))
*(pMatrix+i*4+1) = (*(pMatrix+i*4+1)) / (*(pMatrix+i*4+3))
*(pMatrix+i*4+2) = (*(pMatrix+i*4+2)) / (*(pMatrix+i*4+3))
}
}
}
/*取得投影坐标*/
float *Get_X_Y(float *pMatrix, int Num_Row) {
int i, j, Num
float *pNewMatrix
Num = 0
for(i = 0i <Num_Rowi++) {
if((*(pMatrix+i*4+3)) != 0)
Num++
}
pNewMatrix = malloc(Num * 2 * 4)
/*存放格式,{(x1, y1),(x2, y2), ... ,(xn, yn)}*/
for(i = 0i <Numi++) {
if((*(pMatrix+i*4+3)) != 0) {
*(pNewMatrix+i*2) = (*(pMatrix+i*4))+300 /*显示在屏幕中心, x = 300*/
*(pNewMatrix+i*2+1) = (*(pMatrix+i*4+1))+200 /*显示在屏幕中心, y = 200*/
}
}
return pNewMatrix
}
/*设置旋转矩阵, Rotate around aixs labled with X or Y or Z*/
void SetMatrix_X(float X_Angle) {
float CosX, SinX
SinX = sin(X_Angle * PI /128)
CosX = cos(X_Angle * PI /128)
X_Rotate_Matrix[1][1] = CosX
X_Rotate_Matrix[1][2] = SinX
X_Rotate_Matrix[2][1] = -1 * SinX
X_Rotate_Matrix[2][2] = CosX
}
void SetMatrix_Y(float Y_Angle) {
float CosY, SinY
SinY = sin(Y_Angle * PI /128)
CosY = cos(Y_Angle * PI /128)
Y_Rotate_Matrix[0][0] = CosY
Y_Rotate_Matrix[0][2] = -1 * SinY
Y_Rotate_Matrix[2][0] = SinY
Y_Rotate_Matrix[2][2] = CosY
}
void SetMatrix_Z(float Z_Angle) {
float CosZ, SinZ
SinZ = sin(Z_Angle * PI /128)
CosZ = cos(Z_Angle * PI /128)
Z_Rotate_Matrix[0][0] = CosZ
Z_Rotate_Matrix[0][1] = SinZ
Z_Rotate_Matrix[1][0] = -1 * SinZ
Z_Rotate_Matrix[1][1] = CosZ
}
/*类同*/
void Set_Transist_Matrix(float X, float Y,float Z) {
Transist_Matrix[3][0] = X
Transist_Matrix[3][1] = Y
Transist_Matrix[3][2] = Z
}
/*类同*/
void Set_Perspective_Projection(float k) {
Perspective_Projection[2][3] = -1/k
}
/*初始化图形驱动*/
void InitGraph(void) {
int gd=DETECT,gm
initgraph(&gd,&gm,"E:\\TC")
}
/*生成立方体*/
float *Cube(void) {
int i, j, k
float *pPoints1, *pPoints2
num = 0
for( i = -50i <= 50i += 20)
for( j = -50j <= 50j += 20)
for( k = -50k <= 50k += 20)
num++
pPoints1 = malloc( num * 4 * 4 )
pPoints2 = pPoints1
for( i = -50i <= 50i += 20)
for( j = -50j <= 50j += 20)
for( k = -50k <= 50k += 20) {
*(pPoints1++) = i
*(pPoints1++) = j
*(pPoints1++) = k
*(pPoints1++) = 1
}
return pPoints2
}
/*Functions used for drawing &Clearing*/
void Plot_NewPoints(float *pPoints) {
int i
for(i=0i<numi++)
putpixel( (int) (*(pPoints+i*2)), (int) (*(pPoints+i*2+1)), 7)
}
void Clear_OldPoints(float *pPoints) {
int i
for(i=0i<numi++)
putpixel( (int) (*(pPoints+i*2)), (int) (*(pPoints+i*2+1)), 0)
}
/*Function used for controlling*/
void Operate(int Switch, float *Ang_Rot_X, float *Ang_Rot_Y, float *Ang_Rot_Z,
float *X_Delta, float *Y_Delta, float *Z_Delta,float *Distance) {
switch(Switch) {
case X_axis_clkwise: (*Ang_Rot_X)--break
case X_axis_Cntclkwise: (*Ang_Rot_X)++break
case Y_axis_clkwise: (*Ang_Rot_Y)--break
case Y_axis_Cntclkwise: (*Ang_Rot_Y)++break
case Z_axis_clkwise: (*Ang_Rot_Z)--break
case Z_axis_Cntclkwise: (*Ang_Rot_Z)++break
case X_Delta_Plus: (*X_Delta)--break
case X_Delta_Minus: (*X_Delta)++break
case Y_Delta_Plus: (*Y_Delta)--break
case Y_Delta_Minus: (*Y_Delta)++break
case Z_Delta_Plus: (*Z_Delta)++break
case Z_Delta_Minus: (*Z_Delta)--break
case Distance_forward: (*Distance)++break
case Distance_Backward: (*Distance)-- break
default: (*Ang_Rot_Y)++break
}
}
int main() {
int i, j, Key
float *pMatrix1, *pMatrix2
float *pBasePoints
float *pPerspectivePoints
float Ang_Rot_Xaxis, Ang_Rot_Yaxis, Ang_Rot_Zaxis
float X_Delta, Y_Delta, Z_Delta
float Distance
clrscr()
InitGraph()
/*Varieties initialized*/
pBasePoints = Cube()
Ang_Rot_Xaxis = 0
Ang_Rot_Yaxis = 0
Ang_Rot_Zaxis = 0
X_Delta = 0
Y_Delta = 0
Z_Delta = 0
Distance = 200
Key = 0
while(Key != ESC) {
if( bioskey(1) )
Key = bioskey(0)
Operate(Key, &Ang_Rot_Xaxis, &Ang_Rot_Yaxis, &Ang_Rot_Zaxis,
&X_Delta, &Y_Delta, &Z_Delta, &Distance)
SetMatrix_X(Ang_Rot_Xaxis)
SetMatrix_Y(Ang_Rot_Yaxis)
SetMatrix_Z(Ang_Rot_Zaxis)
Set_Transist_Matrix(X_Delta, Y_Delta, Z_Delta)
Set_Perspective_Projection(Distance)
/*The following may be known by you
pay your attention specially to the pair of malloc &free */
pMatrix1 = Matrix_Mul( (float*)X_Rotate_Matrix, 4, 4, (float*)Y_Rotate_Matrix, 4, 4)
pMatrix2 = Matrix_Mul( pMatrix1, 4, 4, (float*)Z_Rotate_Matrix, 4, 4)
free(pMatrix1)
pMatrix1 = Matrix_Mul( pMatrix2, 4, 4, (float*)Transist_Matrix, 4, 4)
free(pMatrix2)
pMatrix2 = Matrix_Mul( pMatrix1, 4, 4, (float*)Perspective_Projection, 4, 4)
free(pMatrix1)
pMatrix1 = Matrix_Mul( pBasePoints, num, 4, pMatrix2, 4, 4)
free(pMatrix2)
Matrix_Convertion( pMatrix1, num)
pPerspectivePoints = Get_X_Y(pMatrix1, num)
Plot_NewPoints(pPerspectivePoints)
delay(5000)
Clear_OldPoints(pPerspectivePoints)
free(pPerspectivePoints)
free(pMatrix1)
}
free(pBasePoints)
closegraph()
return 0
}
时间:2021-5月 修改:2021-7月知识储备:日式动漫,卡通,漫画风......【引用GGX团队GDC演讲原话(原版PDF最后一段话):NPR(Non Photo Realistic)为了在这个领域发展,需要有能力和有知识力对日本动画的风格进行理论分析。并且理解团队擅长的领域,和过往的经验(团队以前全是做日本动漫经验),不仅需要开发新技术(3D图形技术),还要提高这些3D新技术的应用能力。】
NPR系列笔记共三篇,本篇为系列第一篇: GUILTY GEAR Xrd -SIGN- 渲染【罪恶装备渲染】
崩3效果学习引用:它是基于罪恶装备的卡通渲染,在此之上做自己创新。(重申:资源来源于网络,此处仅当学习使用,非商业使用!)下面崩3效果已知存在的问题:资源问题,模型顶点色丢失、模型顶点法线(修正后的)丢失,渲染存在瑕疵(:)暂不纠结小问题,先实现大体效果,再深入去除瑕疵。所以在做渲染时资源要做好!否则渲染效果会差上太多)
基于GGX渲染的崩3效果
目录 罪恶装备格斗游戏项目相关 引擎的选择 图形标准 照明与阴影--基本 照明与阴影--高光 描边 画面的提升处理 经典的Cel动漫风处理 让3D看起来更像2D 后处理
罪恶装备格斗游戏项目相关 启始:采用 *** 纵杆方向输入和按下按钮的命令系统的即时格斗游戏风格,面对面的角色在侧面进行战斗view visual是卡普空的《街头霸王》,他是始祖。3D格斗游戏的最初存在就是世嘉的《虚拟战士》。有趣的是,并非所有格斗游戏都已转向 3D 图形。传统的以“像素化”为基础的游戏被赋予了新的名字“2D格斗游戏”并幸存下来。 3D格斗:图形更逼真,动作更逼真,效果更炫,与GPU的进化相一致。 2D格斗:是良性发展的,2D格斗游戏的画面终究会是一一“绘制人物的运动模式的作品”,即使GPU进化,能得到的好处也没有太大。现在基本上没有创新 2D 格斗游戏的图形。在用2D图形绘制角色的同时,用3D图形表达背景。 3D2D融合格斗:《街头霸王 IV》打造了不破坏 90 年代初大片《街头霸王 II》像素化味道氛围的 3D 画面。图形是 3D,而游戏系统是 2D。 GGX:分辨率1920 x 1080,PS4版,全高清 https://www.youtube.com/watch?v=QafDmE2EGYs 看起来像“手绘效果”。但是 100% 由 3D 图形组成。在视觉效果中,镜头大胆移动,动态展现人物动作,这正是3D图形的好处。
引擎的选择:使用 Unreal Engine 3 作为游戏引擎 1、有自研引擎,但无力改进这个引擎,并投入生产。 2、UE4的推出推动了UE3的折扣,变成了我们这样的中小型工作室也很容易拿到的价格。这一点可以说是导致采用的主要因素之一。 3、《罪恶装备Xrd -SIGN-》Demo得到Epic Games赞誉,高度评价“发挥了UE3的新潜力”,之后得到多方面相关支持和帮助,团队敲定--使用UE3。 4、开源,并非常稳定。5、美术师的易用性。6、有3D项目技术沉淀,所以从2D转到3D项目
图形标准 硬件:世嘉的“RINGEDGE 2”(* 规格未公开)系统板。 系统:Windows XP Embedded 的 32 位版本,执行二进制文件为 32 位。 渲染分辨率(街机版):1280 x 720 ,720p,帧率为 60fps。 抗锯齿:后处理 FXAA(快速近似抗锯齿)代替标准的 MSAA(多采样抗锯齿)。 模型面数:单场景的总数约800,000面。细分:两主角约25万,加背景约55万,参考值。单角色大约 40,000面。 优化:使用 GPU 裁剪。 头部:战斗场景和过场动画,分别建模。 总体:任何场景中的多边形数量都没有太大的变化。不同状态处使用不同的LOD层级。 角色不同部位:制作对应的LOD层级。右图,战斗场景的3D模型中,进行了故意放大眼睛等变化。 骨骼数量:主角约为 460。最大约为 600。四肢内置的骨骼结构,包括四肢,基本是一样的。头部大约 有 170 块骨头,其中大约 70 块用于面部表情。对面部表情特别讲究的原因! 渲染方式:采用Forward。Deferred 在处理许多光源时是一种有效的技术,但对于类似动漫的“2D 风 格”来说可能没有必要。然而,似乎Z-buffer渲染(* Z-Prepass.深度值先出)是为了早期剔除而执行 的。这意味着对所有 3D 模型至少进行了两次几何渲染。 贴图容量:每个场景的总纹理容量约为 160MB 或更少。 Shader标准:每个场景的着色器程序总数大约是 60 到 70 个顶点着色器和大约 70 到 80 个像素着色器。 Shader编写:本村(他是美术:))团队负责角色着色器,每个美术负责人负责相关效果着色器,几乎没有程序单独开发着色器的情况。本村全名:Junya Christopher Motomura
Milia 的过场动画 3D 模型(图上)和战斗场景的 3D 模型(图下)之间的差异。特别是眼睛的大小差异显着。</figcaption>
照明与阴影--基本
功能亮点:没有物理精度。 项目追求:“视觉的最终目标”是cel动画风格。艺术正确而不是物理上正确。 角色投影:太阳平行光投影到场景。 其他阴影:静态背景物体的阴影,由顶点颜色和纹理产生的老方法阴影,可以是烘焙,也可以是手绘。
着色器混合。将上述 5 个元素组合到最终渲染结果。</figcaption>
角色自身阴影:每个角色单独照明。Cel 动画的艺术效果就是注重艺术外观【艺术至上】。 避免瑕疵:使用场景统一光照看起来很逼真。但是,例如,当角色来到某个位置时,可能会在脸上投下阴影并变得 漆黑,或者阴影可能会消失并变得平坦。 优化效果:每个角色的光照着色器中都有一个光源的图像。每个角色都有自己的向量参数作为光源。逐帧调整光源的位置和方向。
纹理 + 正常着色(左)和Cel着色(右)
阴影调整前(左)和阴影调整后(右
划分明暗区域的示例(左)。右图为进行编辑和外观校正后的示例(产品级示例)。
基本着色: 二阶卡通着色器机制,当超过某个阈值“明亮”,低于某个阈值“阴影”。 原理: 当光照结果可以取0到255之间的值时,它是一个处理系统,其中将128或更多设置为“明亮”,并将127设置为“阴影”。 问题: 如果明暗结果的值在接近预设阈值的区域内,则明暗区域很可能因人物或相机的轻微移动而发生反转,或者明暗区域出现瑕疵。可能会出现斑驳的形状,看起来很糟糕。 解决: 顶点颜色中添加“权重AO”,放在 R 通道。是自身AO,并手动绘制修正。注意看下面角色,它在下巴下面和脖子周围。下巴下方和颈部周围会出现头部的自阴影和,而这种“权重参数”对此有很大贡献。
阴影的权重参数AO,AO外加了手绘修改
无阴影修复(左)和有(右)的区别
这个“权重AO”也设置在光照控制,纹理的G(绿色)通道“ilm纹理”,也对应的阴影纹理。例如,下巴的背面和脖子和下巴之间的关节周围的区域,总是被阴影化,以这种方式进行阴影化。
ilm 纹理(左),它的 G 通道(中),(右)是G通道中裙子处阴影
凯的影子。左边是阴影编辑前,右边是编辑后
仅施加光的状态(左)和加权重进行阴影控制的状态(右)
顶点法线修正 目的:像cel动画一样的明暗着色,继续修正文中前面顶点AO和lightMap G没做好的瑕疵。 原理:通过改变顶点法线“vertex NormalDir”来调整阴影的外观。 问题:带动作的角色资源,需要在引擎中确认导入正确,有无丢失修正后的顶点法线。
上排为前效果,下排为后效果
前效果(左)和后效果(右)
法线的传递示例
头发没有阴影调整,头发带有阴影调整
标准阴影,修正后阴影,头发顶点法线调整目标
照明与阴影--高光
左ilm 贴图,中ilm贴图B通道,右ilm 纹理 R 通道
镜面高光被禁用(左)、启用(中)和在不同角度启用(右)。
胸部、头发、额头下缘、脸颊、嘴唇等高光部分几乎固定,但厚度随角度而变化,在极端角度时消失。 为了强调肌肉,纹理被设置为即使从衣服的顶部也能突出肌肉。
皮肤和服装,即使它们彼此接近,当材料不同时,亮点不会相互粘连,或者身体上。沿着不均匀的边界和物质边界放置了亮点。
金属光泽在固定时感觉很奇怪,因此会根据角度而变化。相机和灯光之间的角度(猜测是halfVector)始终将高光保持在最佳位置,使其更具说服力。
ilm 纹理(左)和将其依次分解为 A、R、G 和 B 通道。
镜面高光: R通道:是一个“镜面高光强度”参数,金属和光滑材质的部分设置的更大一些。 B通道:用于照明控制。最大值为高光,反之,值越小高光越淡。
具体表现(颜色混合)
环境光和光源颜色的颜色调整,阴影变化
丰富颜色:由于toon shader,只明暗调色便有些单调。在材质的质感上缺乏说服力和丰富性。只针对暗部,且不同的区域做更祥细的调色。 解决方法:通过将 SSS 纹理的值乘以“环境光颜色”获得的着色颜色。如果是向阳面(非阴影)的光照结果,则忽略 SSS 纹理。只加在暗部。 贴图制作:中灰的明度,颜色饱和度跟随设计。
NPR shader里SSS贴图不完全模拟次表面散射。半透明薄纸上的阴影颜色很淡,不是吗?它是模拟这样一个图像的纹理。
没有轮廓(左)和有(右)轮廓的比较
描边
1、基础描边实现方法: 基础实现:标准Backface Culling技术。Z-buffer是先进的,所以在第一阶段,可以获得近乎完美的轮廓线。
膨胀后倒背剔除绘制的轮廓绘制
图来自育碧 2004 年,它使用了Backface Culling。
2、虚实描边控制: A、模型加线;B、并在顶点颜色中“控制”,由此可以增加描边的虚实关系。
轮廓粗细调整结果,调整轮廓线的粗细(顶点颜色A通道显示)
3、模型顶点颜色的分配: R : 对阴影判断阈值的偏移。(见前面着色部分,顶点AO+手绘修正) G : 轮廓线根据与相机的距离扩大多少的系数 B : 等高线 Z 轴偏移值 A : 轮廓厚度系数。0.5为标准,1为最大厚度,0为无等高线
没有轮廓强度控制(左)和有(右)。注意鼻子、脸颊和下巴区域。
比较从肩膀到手臂的轮廓线调整的图像
G 和 A 是与Outline控制相关的参数。 R 是前面着色部分,顶点AO+手绘修正,在光照和着色步骤(1)中提到过。 B 是相对于视点在深度方向(Z方向)上偏移(偏移)多少的系数。控制Outline线显示(见下面头发Outline)。
有了上述第二点的这种顶点颜色绘制机制,可以在美术制作3D模型的阶段,就可以调整实时查看效果,从而可以同时制作模型和轮廓。
实际形状(左)、未进行轮廓线 Z 偏移的状态(中心)和进行了 Z 偏移的状态(右)。
4、后期效果Outline的方法: 已知问题:难以控制线条粗细的强度。 实现方法:通过检测渲染结果的深度值中的步长,或者通过检测视线(viewDir)与方向(??未实现过本人也不太明白)的点积值中的步长来检测轮廓。以像素为单位的表面( 法线向量)。确定线像素。 哪种情况下使用:在Backface Culling方法确定几何负载过高时使用,最近的《Gravity Rush 2》被用于专用于场景3D的描线绘制方法。 https://www.youtube.com/watch?v=ywWof1b8L7Y&t=11s
5、本村线Outline或者内描边 得到问题:接缝和肌肉脊线描边,凹槽的零件,不能用Backface Culling绘制轮廓线。如果用贴图的话,在相机放大的时候会出现锯齿,贴图法和漂亮的Outline线差别会很明显。 分析问题:纹理映射表现出锯齿状是什么情况?研究如何创建“不依赖于纹理分辨率的美丽线段”。 解决问题:本村线技术
左图,普通贴图结果,锯齿明显。右图,本村线法,在相同的分辨率下,线条也很清晰。
本村线原理: 横向或纵向排列纹素,再拉UV,修改UV。 A、贴图像素横平竖直画 B、模型卡线 C、模型UV修改
本村线附近(线框展示)
贴图画垂直线,再在 UV 中扭曲UV,得到右图完美的结果,这就是本村线原理
本村线延伸使用,布线示例。注意布线是根据你要的效果布线设置的
左图锯齿特别明显,本村线(右图)。
没有双线性设置(左)和有(右)
例如:由于纹理映射应用了双线性过滤,因此这种曲线或对角线被赋予了适度的渐变插值。而这正是正确的抗锯齿效果(抗锯齿原理)。 如何调整本村线条的虚实强度关系?配合贴图,拉UV调节UV。
画面的提升处理
看起来像 cel 动画,不仅仅是因为它们采用了卡通着色器。在其他部分的独特匠心也发挥着重要作用。
通过附加部件实现
对于头发变形的Milia,准备了额外的零件,最好更换它
每个角色的动作都经过独特控制,因此它们可以以动画和动态的方式表达。不同于一般的3D游戏画面。 角色的眼睛、鼻子和嘴巴在脸上有“大动作”。可以膨胀和收缩,放大和相反地收缩.
从任何方向看到的面部表情(左图)都看起来都很正确,通常无法将其准确表达为初始的夸张设计(中图)。因此,通过充分利用装备,调整眼、鼻、嘴(右)的位置和角度,只追求“从镜头里看到的画面”,实现了角色的有效制作。
骨骼:标准配置。 动漫夸张表现普通:眼球可以自由地移动,嘴巴可以变形和开合,使脸部的体积发生变化。四肢可以自由伸缩。 动漫夸张表现增加:通过更换零件、添加零件或表现纹理来处理更夸张的动漫表现。
普通骨骼应用于三个轴来缩放。如果用这个,就可以把它表现得像一个变形的双头人物。
可能需要一套自定义DCC工具面板
特效的3D 建模
为了符合项目的艺术方式,帧动画制作工艺复现
烟雾效果(左)。它是3D建模的,外观会发生变化(右)。
跳跃过程中产生的烟雾的线框显示
3D模型融化效果
经典的 cel 动漫风处理
限定动画帧 经过动画与电影的对比,根据项目的不同设定和产品对象受众习惯(硬核漫画爱好者,更喜欢低帧动画),采用了每秒60帧动画的3D图形系统,并且敢于采用绘图动画式的帧分割。
过场动画( 事件场景)基于每秒 15 帧,略高于 cel 动画的每秒 8 到 12 帧。由于战斗场景中的动作与技能表现直接相关,因此脚本中根据规范为每一帧单独指定了“一个姿势显示多少帧”。每个pose的显示帧时间不是固定的,感觉像是“2F、3F、3F、1F、1F、2F、2F、3F、4F”(*注:1F≒16.67ms)。
在动画行业中,像迪斯尼这样的每秒绘制和动画全部为24帧的动画被称为“全动画”,连续显示同一帧并以每秒8到12帧左右移动的动画被称为“有限动画”不过《GGX》的动画故意分为粗略的帧,可以说是一个有限的动画系统。
有限动画的制作风格与普通 3D 游戏图形中的动画略有不同。一般 3D 游戏图形中的角色动画是在角色模型内创建骨骼“轨迹”的图像。使用高阶曲线函数(F 曲线)来创建轨迹,或者基于运动捕捉获取的数据。有限动画是一点一点地移动角色,为每一帧创建一个姿势。这就像粘土动画。一开始尝试了一种制作方法,即使用 F 曲线以每秒 60 帧的全帧移动角色,然后将 60 帧的动画进行细化。然而,当我这样做时,它看起来就像是“有掉帧的 3D 图形画面”(是不好的)。
完整动画和有限动画的区别 https://www.youtube.com/watch?v=xZ3npkMNj0s 在实际制作中,首先制作故事板。在这个故事板中,例如,如果是特殊动作,则有格斗游戏的规格,例如“整体由60帧组成”和“第30帧出拳,攻击判断从这个时间”。它被设置。然后,获得这个故事板的动画师决定“每一帧角色模型的姿势”。
关于梅的必杀技的故事板。还写了54帧20张等信息 20帧幻灯片 https://www.youtube.com/watch?v=32nxg1jqe54 虽说是限定动画,但游戏规范显示每秒60帧是什么意思?意思是“每秒60帧作为视频规范”,作为动画的帧更新率是“内置显示帧”如上所述。它是“间隔”。比如一个pose设计成2F时间,在60帧/秒(=大约16.67ms/帧)的显示系统中,同样 的pose会显示33.33ms(=16.67ms x 2F),就是这样。另一方面,抛物线轨迹的运动和角色跳跃时发生的导d运动以 60 fps 的速度更新。另外,我认为不言而喻的是,玩家命令输入的时间以 1 / 60 秒的间隔被接受。 在为每一帧摆姿势以使其在过场动画中看起来不错时,角色模型的每个部分的位置和该角色专用的光源的位置都是逐帧调整的。在相机环绕角色的动画的情况下,鼻子的位置会针对每一帧进行调整。GGX独特的图形表达,不仅 没有先进的技术,也没有这种熟练的动漫艺术感的积累。 必杀技确认的限定动画 https://www.youtube.com/watch?v=PK3_liNvGZQ
让3D看起来更像2D(一)- 碰撞检测设置和类似 3D 的外观吸收 采用了球体、立方体、胶囊形状等3D碰撞检测机制,跟一般的3D游戏一样,因为画面是3D的,但是玩法是2D格斗游戏本身。熟悉碰撞检测区域的设置,特别是被攻击区域(=受到攻击时受到伤害的区域)和攻击区域(接触敌人时受到伤害的区域)。 通过有限动画系统创建的角色运动中的每一帧都被视为2D图形,并且使用内部工具将碰撞判断区域作为2D用于该帧。 3D 图形也证实了画面左右边缘的人物看起来有点胖,来的时候往往会显得有点瘦到中心。就是这样。如果无人看管,上述“设置为二维数据的碰撞检测区域”将根据角色在屏幕上的位置而移动。为了消除这种偏差,碰撞检测区域根据角色在屏 幕上的位置进行了适当的调整,或者抑制了屏幕上角色所在位置造成的“胖瘦”。必须选择始终与碰撞确定区域匹配的任何一个。在2D格斗游戏来说,这是一个很自然的选择,因为格斗角色位于屏幕的左侧、右侧或中心以及外观大 小的变化都没有关系。作为3D图形基础2D格斗游戏的先驱者街头霸王IV系列也选择了与GGX相同的方法。实际的“胖瘦”调整是通过设计在 2D 屏幕上绘制 3D 模型时执行的“3D → 2D 投影”方法进行的。 有两种类型的 3D → 2D 投影:近处物体看起来大而远处物体看起来很小的“透视投影”和不发生这种差异的“平行投影”(正射投影)。在 GGX透视投影采用了“混合投影”,将强度约30%的平行投影和强度约70%的平行投影结合在一起。 结果,可以抑制角色靠近左右边缘时变胖的情况和靠近中心的体重减轻的情况,并且可以使屏幕上任何位置的尺寸几乎恒定。 同样的混合投影应该在垂直方向上应用,但没有水平方向那么致命(即使不应用),测试玩家也不会感到不舒服。因为它是关于那个,我们没有介绍它。 在点图(sprite)中,从上方绘制的角色看起来像是覆盖了下方绘制的角色。在 3D 图形中,每个角色都有一个 3D 尺寸,因此当两个角色之间的距离越来越近时,一个角色伸出的手臂可能会卡在另一个角色中。有必要考虑采取措施避免这种情况。 最后,在攻击方绘制角色时,仅将深度 (=Z) 值在 3D 空间中向视点侧偏移约 1 米,以防止这种重叠。简而言之,攻击角色的绘制使其始终通过深度 (=Z) 测试,而忽略了攻击角色的 3D 上下文。
深度调整前(左)及其 Z 缓冲区(右)。两个身体在中心重叠
深度调整后(左)及其 Z 缓冲区(右)
两个互相打架的角色,但在3D坐标处理上,他们在同一Z轴上面对面。它是一种仅在绘图时进行干预的设备。在双臂抓住对手并投掷等技术的情况下,还进行调整,例如切割该偏移以使投掷侧在投掷侧的双臂内。 有一些部分被特效类似假手段遮挡。 在GUILTY GEAR Xrd -SIGN-的战斗场景中旋转镜头时 https://www.youtube.com/watch?v=cKJ2imcEp5c 通过实施这个处理系统,在大多数情况下不自然会消失。 让3D看起来更像2D(2)-反转角色时的计量器绘制和处理 画面的创建着重于作为 2D 格斗游戏的可玩性,还绘制了体力等各种计量器。仪表在纹理上渲染并粘贴在板多边形上,但该板多边形放置在 Z 位置,以便它位于战斗角色的后面。 因此,当角色跳高并与仪表重叠时,仪表会被绘制为位于角色后面。但是,有一个例外是只有在摄像机角度发生变化时才会在前面绘制仪表,例如在空中发起的攻击动作开始时。 摄像机角度发生剧烈变化时,例如当视点环绕时,会导致仪表陷入背景的干扰是预见到的,因此导致仪表落后于角色的特殊处理被禁用。放在角色的前面。 这是基于这样的判断:“当包含这个镜头角度效果时,游戏本身会暂时中断,因此即使将 UI 放在前面,也不会对玩家造成压力。”
对称问题解决:如果你只想左右轴对称地反转角色模型,就将3D模型相对于垂直轴(Y轴)轴对称反转(=“构成3D模型的顶点的X” ”。您所要做的就是替换“坐标值”的正负)。当然,为了反转光照效果,需要根据3D模型的反转来 反转上面提到的角色专属光源。这时候的问题是一些人物服装上绘制的人物。例如,Sol 的腰带上刻着“FREE”二字,但如果只是将角色的 3D 模型倒置,则字符串将是镜像书写。在实际游戏画面中,这种情况是可以适当避免的,但 这是因为在倒置时,切换了“绘制人物部分纹理贴图的UV贴图”,而人物始终是正方向的纹理贴图。这是处理它的结果。即使3D模型倒置,也只是将字符纹理适当地重新粘贴在向前的方向上,使其不会
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)