前言:
首先,我们需要明确:
对于pygame框架,是由SDL写出来的,而SDL是OpenGL一步步写出来的。所以说,在pygame框架里使用live2d就需要pygame的OPENGL模式以及live2d-py库。
所以本篇文章主要介绍:
(1)live2d-py库的使用方法
(2)OpenGL与pygame的交互
(3)部分OpenGL基础知识
目录:
前言:
live2d-py:
①、导入模块
②、初始化
③、实例化模型对象
④、加载模型
⑤、正常pygame循环
⑥、其他常用方法
(1)、Resize,调整live2d大小
(2)、SetScale,设置模型缩放比例
(3)、SetAutoBreathEnable,开启/关闭 自动呼吸
(4)、SetAutoBlinkEnable,开启/关闭 自动眨眼
(5)、SetOffset,设置模型偏移量
(6)、SetExpression,设置live2d表情
(7)、SetRandomExpression,随机设置一个live2d表情
(8)、StartMotion,设置live2d动作
(9)、StartRandomMotion,随机设置一个live2d动作
(10)、IsMotionFinished,判断当前动作是否完成
(11)、ResetExpression,重新设置live2d表情为默认表情
(12)、StopAllMotions,停止所有的动作
⑦、其它注意事项
(1)、禁止/允许日志输出
(2)、与pygame交互
OpenGL & Pygame交互
①、安装OpenGL:
②、渲染图片:
③、渲染文字:
④、注意:
(1)、多放置
(2)、v2模型的特殊性
实例:
后记:
live2d-py:
首先,肯定是需要pip一下:
pip install live2d-py
之后,我们需要知道:对于live2d,有v2与v3之分,(大致)也就是2d与3d的区别。
区别方式就是:
v2里面会有.moc文件;而v3里面的是.moc3文件。
①、导入模块
这时候就要根据我们的模型的版本进行导入。
如果是v2模型:
import live2d.v2 as live2d
import pygame
from pygame.locals import *#这个之后讲,暂时未用到
from OpenGL.GL import *
反之,是v3模型:
import live2d.v3 as live2d
import pygame
from pygame.locals import *#这个之后讲,暂时未用到
from OpenGL.GL import *
不管是什么版本的模型,之后的操作类似(但不完全相同)。
②、初始化
之后就是初始化:
#以pygame的交互为例
pygame.init()
live2d.init()#但如果是对于v3模型,需要加一句
if live2d.LIVE2D_VERSION == 3:#这个函数会激活内部的OpenGL函数#其实,v2模型也能用这个函数,但最好区分一下live2d.glewInit()#这时候用到了pygame的OpenGL模式
pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync = 1)
③、实例化模型对象
之后就是需要实例化模型对象,以此通过一些方法来控制live2d的行为。
model = live2d.LAppModel()
之后,就可以调用这个对象里的方法控制live2d。
④、加载模型
需要让对象知道控制的哪个live2d:
#对于v2模型,一般导入的是后缀名是(.model.json)的json文件路径
#如:
model_json_path: str = "my_v2_model.model.json"#对于v3模型,一般导入的是后缀名是(.model3.json)的json文件路径
#如:
model_json_path: str = "my_v3_model.model3.json"#最后加载live2d
model.LoadModelJson(model_json_path)
⑤、正常pygame循环
之后就可以通过pygame的事件来进行一系列操作了。
一般,调用的顺序就是:clearBuffer -> Update -> Draw -> flip/update
run: bool = Truewhile run:for event in pygame.event.get():match event.type:case pygame.QUIT:run = not runbreakcase _:pass#其他的操作之后就要刷新live2d了...#1、清除缓冲区live2d.clearBuffer()#2、更新live2d到缓冲区model.Update()#3、渲染live2d到屏幕model.Draw()#4、需要用的pygame刷新pygame.display.flip() #或者是pygame.display.update()#之后是其他的操作...#退出之后:
#1、结束live2d
live2d.dispose()
#2、结束pygame
pygame.quit()
#3、结束程序
exit()
这样,就能正常显示live2d了。
但是,可能达不到自己想要的效果,比如:大小、位置、动作、表情之类的还没有设置,那么请看下文所讲述的。
⑥、其他常用方法
这个对象还有好多方法来控制live2d的一些属性。
(1)、Resize,调整live2d大小
window_width: int = 800
window_height: int = 600model.Resize(window_width, window_height)
(2)、SetScale,设置模型缩放比例
live2d_scale: float = 0.8#缩小了20%,也就是变成了原来大小的80%
model.SetScale(live2d_scale)
(3)、SetAutoBreathEnable,开启/关闭 自动呼吸
#开启自动呼吸
model.SetAutoBreathEnable(True)#关闭自动呼吸
model.SetAutoBreathEnable(False)
(4)、SetAutoBlinkEnable,开启/关闭 自动眨眼
#开启自动眨眼
model.SetAutoBlinkEnable(True)#关闭自动眨眼
model.SetAutoBlinkEnable(False)
(5)、SetOffset,设置模型偏移量
offset_x: int = 100
offset_y: int = 100#现在live2d会偏移原点(100, 100)
model.SetOffset(offset_x, offset_y)
(6)、SetExpression,设置live2d表情
#这个函数的第一个参数是:字符串类型的表情ID
#这个ID可以去相应的.model.json里去查看
#进入后找到"expressions"键,里面的"name"都是表情ID#例如:
model.SetExpression("cry")model.SetExpression("default")
(7)、SetRandomExpression,随机设置一个live2d表情
#单纯的随机挑选一个live2d表情
model.SetRandomExpression()
(8)、StartMotion,设置live2d动作
#这个函数有三个主要参数,分别是:
#group: str -> 和SetExpression类似,都是填入.model.json里面的"name"
#no: int -> 默认为0就行
#priority: int -> 代表这个动作的优先级,毕竟动作的发生需要时间,没有特殊需求那就1即可#例如:
model.StartMotion("happy", 0, 1)
(9)、StartRandomMotion,随机设置一个live2d动作
#类似于SetRandonExpression
model.StartRandomMotion()
(10)、IsMotionFinished,判断当前动作是否完成
#若当前动作完成
print(model.IsMotionFinished()) #输出:True#若当前动作未完成
print(model.IsMotionFinished()) #输出:False
(11)、ResetExpression,重新设置live2d表情为默认表情
#那什么是默认的表情呢?
#就是.model.json里面Expression里的"default"表情model.ResetExpression()
(12)、StopAllMotions,停止所有的动作
model.StopAllMotions()
⑦、其它注意事项
(1)、禁止/允许日志输出
在正常情况下,live2d-py这个库会不断地输出日志到缓冲区,可能会扰乱我们的正常工作。那么需要调用下列函数来“禁止日志输出”:
import live2d.v2 as live2d#参数是False则禁止日志输出
#反之,允许日志输出
live2d.setLogEnable(False)
(2)、与pygame交互
因为使用的是pygame的OpenGL模式,所以pygame的图片/文字渲染就行不通了,现在需要使用OpenGL来进行操作了。
OpenGL & Pygame交互
我们需要知道,OpenGL是一个状态机:
就是通过改变全局OpenGL的状态来进行差异化“输出”。
OpenGL主要由三个包构成,分别是:
-OpenGL.GL:是核心API,包含了主要函数
-OpenGL.GLU:包含了许多被包装的实用方法
-OpenGL.GLUT:包含了关于屏幕的创建以及基本事件
而我们在这里只需要使用OpenGL.GL包就满足了。
①、安装OpenGL:
pip install PyOpenGL PyOpenGL-accelerate
②、渲染图片:
我们知道,在pygame里都是以pygame.SurfaceType进行渲染的,而要在OpenGL的话需要将pygame.SurfaceType转换成字节串(bytes)来转换。
需要注意的是,OpenGL的坐标系和pygame的坐标系不一样:
-OpenGL是以中心为原点的权值/±1坐标系;
-pygame是以TopLeft为原点的数值坐标系。
如下图:
具体方法&讲解如下:
#导入OpenGL.GL包
#下面没见过的函数都是来自这里
from OpenGL.GL import *
import pygamesurface = pygame.image.load("image.png").convert_alpha()#1、将surface对象转成成字节串,并保持RGB通道
texture_data: bytes = pygame.image.tostring(surface, "RGBA", 1)#2、获得纹理id,可以看成申请一段空间
id = glGenTextures(1)#3、告诉OpenGL,这段空间的纹理是2D纹理(图片)
glBindTexture(GL_TEXTURE_2D, id)#4、将图片的字节串绑定到这段空间里,下面细讲
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,surface.get_width(),surface.get_height(),0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data
)#5、设置参数:开启模糊但反锯齿模式(可以这么理解)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)#6、为了防止中间可能的其他函数的干扰,需要再绑定一次
glBindTexture(GL_TEXTURE_2D, id)#7、渲染的开始,并且通过“四边形”模式(图片)渲染
glBegin(GL_QUADS)#8、如上图:
glTexCoord2f(0, 0); glVertex3f(x1, y1, z1) #如图,图片左下角,分别对应xyz的权值,z1=0即可
glTexCoord2f(1, 0); glVertex3f(x2, y2, z2) #如图,图片右下角,分别对应xyz的权值,z2=0即可
glTexCoord2f(1, 1); glVertex3f(x3, y3, z3) #如图,图片右上角,分别对应xyz的权值,z3=0即可
glTexCoord2f(0, 1); glVertex3f(x4, y4, z4) #如图,图片左上角,分别对应xyz的权值,z4=0即可#9、结束渲染
glEnd()
而这些函数,需要在pygame基本循环里去使用,就类似于pygame的blit一样。
当然,最好不要直接用,最好把它们封装成函数或类,毕竟没必要一直生成id,直接一个变量保存下来就行。例如:
class Live2D(object):@staticmethoddef render(surface: pygame.SurfaceType) -> object:texture_data: bytes = pygame.image.tostring(surface, "RGBA", 1)id: object = glGenTextures(1)glBindTexture(GL_TEXTURE_2D, id)glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,surface.get_width(),surface.get_height(),0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)return id@staticmethoddef blit(id: object, *args: tuple[tuple[3], tuple[3], tuple[3], tuple[3]]) -> None:glBindTexture(GL_TEXTURE_2D, id)glBegin(GL_QUADS)glTexCoord2f(0, 0); glVertex3f(*args[3])glTexCoord2f(1, 0); glVertex3f(*args[1])glTexCoord2f(1, 1); glVertex3f(*args[2])glTexCoord2f(0, 1); glVertex3f(*args[0])glEnd()
其中,对于glTexImage2D函数(C++解释)(来自微软):
void WINAPI glTexImage2D(GLenum target, #模式,GL_TEXTURE_2DGLint level, #细节级别编号。 级别 0 是基础图像级别。GLint internalformat, #纹理中颜色分量的数量,与tostring的相同,GL_RGBAGLsizei width, #长度GLsizei height, #宽度GLint border, #边框的宽度,必须为 0 或 1,为1就行。GLint format, #和level一样,都选GL_RGBAGLenum type, #字节串类型(bytes)const GLvoid *pixels #纹理数据,就是那个字节串
);
③、渲染文字:
和渲染图片类似,需要注意的是:
back_color = (255, 255, 255)
font_color = (0, 0, 0)text_surface = pygame.font.SysFont("幼圆", 36).render("Hello", True, font_color, back_color)#注意!这里的back_color不能省略了!如果省略,那么文字不能正常显示了!
④、注意:
一般,以上方法都是可以使用的,但真正实践的时候有复杂需求的话会出现诸多问题,有些我能解决但有些解决不了:
(1)、多放置
不允许多放置:放置多个时,放置的不知道为什么全是第一个。(局限在v2模型)
(2)、v2模型的特殊性
(仅仅)在v2模型的展示时,需要在最后加一句(glUseProgram(0))来恢复设置,才能正常的放置图片/文字并显示:
run: bool = Truewhile run:#其他的操作之后就要刷新live2d了...#1、清除缓冲区live2d.clearBuffer()#2、更新live2d到缓冲区model.Update()#3、渲染live2d到屏幕model.Draw()#4、需要用的pygame刷新pygame.display.flip() #或者是pygame.display.update()#之后是其他的操作...#⭐、就是这句话glUseProgram(0)#退出之后:
#1、结束live2d
live2d.dispose()
#2、结束pygame
pygame.quit()
#3、结束程序
exit()
⭐如果大家能解决的话,一定要发出来造福大家啊!
实例:
最后就举个完整的实例来结束文章,下面的全部上面都出现过:
# -*- coding: utf-8 -*-
# -*- file: live2d_test.py -*-import live2d.v2 as live2d
import pygame
from pygame.locals import *
from OpenGL.GL import *class Live2D(object):@staticmethoddef render(surface: pygame.SurfaceType) -> object:texture_data = pygame.image.tostring(surface, "RGBA", 1)id: object = glGenTextures(1)glBindTexture(GL_TEXTURE_2D, id)glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,surface.get_width(),surface.get_height(),0,GL_RGBA,GL_UNSIGNED_BYTE,texture_data)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)return id@staticmethoddef blit(id: object, *args: tuple[tuple[3], tuple[3], tuple[3], tuple[3]]) -> None:glBindTexture(GL_TEXTURE_2D, id)glBegin(GL_QUADS)glTexCoord2f(0, 0); glVertex3f(*args[3])glTexCoord2f(1, 0); glVertex3f(*args[1])glTexCoord2f(1, 1); glVertex3f(*args[2])glTexCoord2f(0, 1); glVertex3f(*args[0])glEnd()live2d.setLogEnable(False)
pygame.init()
live2d.init()if live2d.LIVE2D_VERSION == 3:live2d.glewInit()pygame.display.set_mode((1200, 800), DOUBLEBUF | OPENGL, vsync = 1)model: live2d.LAppModel = live2d.LAppModel()
model.LoadModelJson(r"v2\kasumi2\kasumi2.model.json")
model.Resize(1200, 800)
model.SetAutoBlinkEnable(True)
model.SetAutoBreathEnable(True)
model.StartRandomMotion()back = Live2D.render(pygame.image.load(r"beach1.jpg").convert_alpha())
back_pos = ((-1.0, 1.0, 0), (1.0, -1.0, 0), (1.0, 1.0, 0), (-1.0, -1.0, 0))
run: bool = Truewhile run:for event in pygame.event.get():if event.type == pygame.QUIT:run = not runbreaklive2d.clearBuffer()Live2D.blit(back, *back_pos)model.Update()model.Draw()pygame.display.flip()if live2d.LIVE2D_VERSION == 2:glUseProgram(0)live2d.dispose()
pygame.quit()
exit()
后记:
文章有问题还请大家指正。
live2d-py的作者是国人,他在BiliBili上有号,也发过视频。
kirakiradokidoki⭐~