书接上回,在游戏开发中,资料片动画是非常重要的,因为它们可以增加游戏的吸引力和品牌价值。类比炉石传说的资料片,我们可以看到以下几个方面的重要性:
- 增加可玩性
资料片动画可以向游戏中添加新的角色、场景和故事情节,这些元素可以增加游戏的可玩性。例如,在《炉石传说》的资料片中,新的卡牌和英雄角色背景内容,使得玩家可以体验到更多的游戏乐趣。
- 增加品牌价值
资料片动画可以增加游戏品牌的价值和认可度。这些动画可以成为游戏品牌的标志性元素,例如《炉石传说》的资料片动画已经成为该游戏品牌的重要组成部分之一。这些动画可以向玩家和非玩家展示游戏品牌的风格、主题和价值观,从而增加品牌的认知度和忠诚度。
总之,资料片动画在游戏开发中非常重要,它们可以增加游戏的可玩性、吸引力和品牌价值。所以,我们使用AI创作开发游戏,也可以尝试制作静态图动效的资料片.
AI切深度图
在游戏开发中,制作上述动画效果的主要方法有三种:播放视频动画、播放gif动态图片以及使用shader动态渲染静态图片。在移动平台或Web平台游戏中,资源大小和包体大小是非常重要的因素。因此,使用第三种方式可以大大减小资源大小,提高游戏的加载速度,并减少玩家等待时间。
使用shader动态渲染静态图片是一种常用的方法,它可以通过改变纹理坐标、颜色、透明度等属性来实现动画效果。这种方法可以在运行时动态生成动画效果,而不需要使用额外的视频或图像资源。这样可以减少游戏的资源占用量,降低包体大小,提高游戏的性能和加载速度。
如果要使用shader动态渲染一张静态图片,首先需要有一张图片的深度图。在本期的主题中,涉及到AI创作与游戏开发,可以采用一些AI工具来进行图像切割,以获取深度图。其中,可以使用leiapix来获取深度图,并对其进行微调,以获得更细致的分层动画效果。
相比传统的人工抠图方式,使用AI工具可以更快速、准确地获取深度图,同时也可以减轻人工操作的工作量。这样可以大大提高生产效率,同时也可以使得游戏开发者专注于游戏的核心设计和开发工作。
在游戏开发中,使用shader动态渲染静态图片可以实现各种动画效果,例如旋转、平移、缩放、淡入淡出等。通过使用深度图,可以将图片分成多层,然后使用shader来对每一层进行动态渲染,从而实现更加逼真和生动的动画效果。
下面的深度图就是通过工具获取到的.
shader效果实现
用于根据一张原图和一张黑白深度图创建分层动画的镜头特效。这个着色器程序包括一个顶点着色器和一个片段着色器。顶点着色器将顶点从2D空间转换为3D空间,并将它们传递给片段着色器。片段着色器从原图和深度图中获取像素的颜色和深度值,并使用它们来计算每个像素的深度和偏移因子。然后,它使用深度值和偏移因子来控制像素的偏移程度,并混合原始颜色和偏移颜色。最后,我们在每帧更新动画,并更新焦点距离、焦距变化范围和深度图像素。
顶点着色器
attribute vec4 a_Position;
attribute vec2 a_Texcoord;
varying vec2 v_Texcoord;
varying vec4 v_Position;
uniform mat4 u_MvpMatrix;
uniform mat4 u_WorldMat;
void main() {
gl_Position = u_MvpMatrix * a_Position;
v_Texcoord = a_Texcoord;
v_Position = u_WorldMat * a_Position;
}
片段着色器
precision highp float;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform sampler2D displacementMap;
uniform sampler2D uSampler;
uniform vec4 dimensions;
uniform vec2 mapDimensions;
uniform float scale;
uniform vec3 offset;
uniform float focus;
uniform float enlarge;
float aspect = dimensions.x / dimensions.y;
// mono version of perspective shader
vec3 perspective(vec2 uv,
float horizontal_parallax, // 0 - no parallax
float vertical_parallax, // same
float perspective_factor, // 0 - no perspective
float h_convergence, // 0.0 - near, 0.5 - center, 1.0 - far
float v_convergence // same
) {
const float sensitivity = 15.0; // aligns animation with the previous version where it was multiplied by 15
horizontal_parallax *= sensitivity;
vertical_parallax *= sensitivity;
vec3 ray_origin = vec3(uv.x - 0.5, uv.y - 0.5, +1.0);
vec3 ray_direction = vec3(uv.x - 0.5, uv.y - 0.5, -1.0);
ray_direction.xy *= perspective_factor;
ray_origin.xy /= 1.0 + perspective_factor;
ray_direction.x += horizontal_parallax;
ray_direction.y += vertical_parallax;
ray_origin.x -= h_convergence * horizontal_parallax;
ray_origin.y -= v_convergence * vertical_parallax;
const int step_count = 45; // affects quality and processing time
const float hit_threshold = 0.01;
ray_direction /= float(step_count);
for (int i = 0; i < step_count; i++) {
ray_origin += ray_direction;
vec2 vFlipUV = (ray_origin.xy + 0.5) * vec2(1, -1) + vec2(0, (dimensions.y - 1.0) / dimensions.y);
float scene_z = texture2D(displacementMap, vFlipUV).x;
if (ray_origin.z < scene_z) {
if (scene_z - ray_origin.z < hit_threshold) return texture2D(uSampler, ray_origin.xy + 0.5).rgb;
ray_origin -= ray_direction; // step back
ray_direction /= 2.0; // decrease ray step to approach surface with greater precision
}
}
return texture2D(uSampler, ray_origin.xy + 0.5).rgb;
}
void main(void) {
vec2 uv = (vTextureCoord - vec2(0.5)) / vec2(enlarge) + vec2(0.5);
float gain = scale * 0.075;
float persp_factor = scale * 3.0 * offset.z;
gl_FragColor = vec4(perspective(uv, -gain * offset.x, gain * offset.y * aspect, persp_factor, 1.0 - focus, 1.0 - focus), 1.0) * vColor;
}