【游戏开发创新】Unity+人工智能,让小朋友的画成真,六一儿童节一起来画猫猫吧(Unity

您所在的位置:网站首页 游戏的画图 【游戏开发创新】Unity+人工智能,让小朋友的画成真,六一儿童节一起来画猫猫吧(Unity

【游戏开发创新】Unity+人工智能,让小朋友的画成真,六一儿童节一起来画猫猫吧(Unity

2024-07-08 13:00:10| 来源: 网络整理| 查看: 265

文章目录 一、前言二、一起来画猫猫三、爬虫,无穷只猫四、猫猫作品展五、Unity制作讲解1、界面素材2、UGUI制作界面3、分辨率适配4、如何检测鼠标事件5、世界坐标转局部坐标6、画线原理7、橡皮擦原理8、重置图片9、训练模型下载10、通过生成器得到图像 六、结束语七、附录,《睡吧孩子》画画过程记录1、线稿过程2、成稿

一、前言

点关注不迷路,持续输出Unity干货文章。 嗨,大家好,我是新发。 马上要六一儿童节了,我这个老人家又可以假装6岁了。 小时候我喜欢画画,那时候流行七龙珠、宠物小精灵、数码宝贝,这些我都画过。 二年级的时候老师带我去报名了校级的画画比赛,让我比赛的时候画五星红旗,我说这个太简单了,不过老师还是坚持让我画五星红旗。比赛时我很快就画好了,坐在位子上开始偷看四周人都在画什么,我转头看到后座的小朋友画的是一艘华丽的游轮… … 比赛结束当场就宣部了结果,我没有得奖;老师为了安慰我,说要带我去她家里炸虾丸给我吃;我坐在凳子上等了很久很久,尿急得不得了,但又不好意思说,差点就晕过去… … 长大后,不怎么画画了,兴趣点来了就画一幅,下面这张图是我用iPad画的,场景是我现在租的真实的小窝(30平的小单间),蹲在我身上的是我养的猫——皮皮(它现在3周岁了),这幅画我命名为《睡吧孩子》, 在这里插入图片描述

注:本图的线稿作画过程和最终成图见文章末尾。

我把自己画成一个孩子,我希望每个人心中都有一个小孩子,不忘本心,永远清澈明朗,无忧亦无惧。 好了,扯远了,回归本文主题,六一儿童节,我们一起来当小朋友吧。

二、一起来画猫猫

小孩子一般都喜欢画画,我用Unity做了一个画猫猫的Demo,效果如下,AI会根据你画的线稿自动识别生成对应的猫图。 操作说明: 鼠标左键是画线,右键是橡皮擦(或者按Shift+鼠标左键也是橡皮擦),按C是清空画布。 在这里插入图片描述 想玩的同学可以直接下载我打好的包, Windows版本: 网盘地址:https://pan.baidu.com/s/1RrpmwVhwEe0hKv06RCvCbQ 提取码:3yyq

另,本Demo源码工程已上传到CodeChina,感兴趣的同学可自行下载学习。 地址:https://codechina.csdn.net/linxinfa/UnityDrawCatAI 注:我使用的Unity版本:Unity 2020.2.7f1c1 (64-bit)。

下文我会讲解Demo的制作细节。

三、爬虫,无穷只猫

画不好,没关系,多看些猫图,给你准备了爬虫,无穷只猫~ 在这里插入图片描述 爬虫python代码

import requests import os import urllib # 百度图片爬虫 class Spider_baidu_image(): def __init__(self): self.url = 'http://image.baidu.com/search/acjson?' self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.\ 3497.81 Safari/537.36'} self.headers_image = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.\ 3497.81 Safari/537.36','Referer':'http://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1557124645631_R&pv=&ic=&nc=1&z=&hd=1&latest=0©right=0&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&sid=&word=%E8%83%A1%E6%AD%8C'} # 构造参数数组 def get_param(self): keyword = urllib.parse.quote(self.keyword) params = [] for i in range(1,self.paginator+1): params.append('tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord={}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=1&latest=0©right=0&word={}&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&cg=star&pn={}&rn=30&gsm=78&1557125391211='.format(keyword,keyword,30*i)) return params # 构造url数组 def get_urls(self, params): urls = [] for i in params: urls.append(self.url+i) return urls # 遍历请求url并下载图片 def get_image(self, urls): cwd = os.getcwd() file_name = os.path.join(cwd,self.keyword) if not os.path.exists(self.keyword): os.mkdir(file_name) index = 0 for url in urls: json_data = requests.get(url,headers = self.headers).json() json_data = json_data.get('data') for i in json_data: if i: image_url = i.get('thumbURL') index += 1 with open(file_name+'\\{}.jpg'.format(index),'wb') as f: f.write(requests.get(image_url, headers = self.headers_image).content) print('下载: ' + image_url) def __call__(self, *args, **kwargs): # 构造参数 params = self.get_param() # 构造url数组 urls = self.get_urls(params) # 请求 self.get_image(urls) if __name__ == '__main__': spider = Spider_baidu_image() # 关键字 spider.keyword = '可爱的猫' # 页数,每页30张图 spider.paginator = 100 # 开始执行 spider() 四、猫猫作品展

给大家看看我画的几幅,大家可以大胆发挥想象力,考验一下人工智能~ 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

五、Unity制作讲解

又到了技术讲解环节了,下面我来讲下本Demo的制作过程。 原理图如下: 在这里插入图片描述

1、界面素材

首先,在网上找一些素材图, 请添加图片描述请添加图片描述

请添加图片描述请添加图片描述 接着,把素材图导入Unity工程中, 在这里插入图片描述 图片的格式都设置成Sprite (2D and UI), 在这里插入图片描述

2、UGUI制作界面

使用UGUI制作界面,主要使用到的UI组件是: 图片:Image组件,文字:Text组件,文字的描边使用了Outline组件, 在这里插入图片描述 其中绘图的UI是RawImage, 在这里插入图片描述

3、分辨率适配

做UI一定要注意分辨率适配,比如后面的天空底图,将Anchor设置为top - stretch 在这里插入图片描述 这样在不同分辨率下都可以适配铺满屏幕上方,其他UI对象根据具体情况设置Anchor。 在这里插入图片描述

4、如何检测鼠标事件

如下,我们怎么捕获鼠标的事件? 在这里插入图片描述 这里是通过Event Trigger组件来实现监听的, 在这里插入图片描述 在UI对象上挂Event Trigger组件,点击Add New EventType即可添加对应的事件监听。因为我们是要在图上画画,所以我们要监听Drag事件。 在这里插入图片描述 设置响应函数为SketchPad脚本的OnDrag函数。 在这里插入图片描述 这样我们就可以在SketchPad脚本的OnDrag函数中去实现相关的逻辑了。

// 拖拽事件响应函数 public void OnDrag(BaseEventData baseData) { PointerEventData data = (PointerEventData)baseData; // 鼠标当前的坐标 Vector2 pos = data.position; // 拖拽的变化向量 Vector2 delta = data.delta; // ... }

其中通过传递过来的参数,我们可以知道鼠标点击的坐标position和拖拽的变化向量delta。 在这里插入图片描述

5、世界坐标转局部坐标

上面的OnDrag函数,我们得到的position是世界坐标,我们需要转换为图片的局部坐标,这样我们才方便在图片的这个位置画线,通过Transform的InverseTransformPoint方法即可将世界坐标转为以该Transform为父节点的局部坐标。

var area = data.pointerDrag.GetComponent(); // 起始点局部坐标 var p0 = area.InverseTransformPoint(data.position - data.delta); // 终止点局部坐标 var p1 = area.InverseTransformPoint(data.position);

InverseTransformPoint函数模型如下: 在这里插入图片描述 如果是要将局部坐标转世界坐标,则是使用TransformPoint函数,对应的函数模型如下: 在这里插入图片描述

6、画线原理

我们在图上画画,本质上是画一段一段的小线段。可以近距离观察, 在这里插入图片描述 利用的是RenderTexture.active,当我们给RenderTexture.active赋值的时候,所有的渲染操作会进入这个激活的RenderTexture对象,

// 备份 var prevRT = RenderTexture.active; // 把画图的的RenderTexture对象_sourceTexture赋值给active RenderTexture.active = _sourceTexture; // 执行绘图,所有的绘图会渲染到_sourceTexture对象上 // ... // 还原 RenderTexture.active = prevRT;

执行绘图的逻辑,用的是Graphics类,它有大量的绘图操作函数,我们用的是DrawMeshNow函数。 在这里插入图片描述 可以看到DrawMeshNow需要一个Mesh参数,所以我们需要构造一个Mesh,我们构造一个线段的Mesh,

Mesh _lineMesh = new Mesh(); _lineMesh.MarkDynamic(); _lineMesh.vertices = new Vector3[2]; _lineMesh.SetIndices(new[] { 0, 1 }, MeshTopology.Lines, 0);

再给这个Mesh塞入两个顶点,

List _vertexList = new List(4); _vertexList.Clear(); _vertexList.Add(p0); _vertexList.Add(p1); _lineMesh.SetVertices(_vertexList);

不过光有Mesh还不够,Mesh只是网格,它只定义了形状,还欠一个材质,所以我们再弄一个材质球,

Material _lineMaterial = new Material(_drawShader); // 黑色 _lineMaterial.color = Color.black;

最后,调用Graphics.DrawMeshNow进行绘图,

_lineMaterial.SetPass(0); Graphics.DrawMeshNow(_lineMesh, Matrix4x4.identity);

这样子,我们的线段Mesh就画到了_sourceTexture对象上了。

7、橡皮擦原理

画的过程中,想用使用橡皮擦,使用鼠标右键或者按住Shift+鼠标左键即可。 在这里插入图片描述 橡皮擦的原理和画线的原理是一样的,只不过橡皮擦的Mesh不是线段,而是正方形(两个三角形构成)。

其中SetIndices参数数组是顶点的序号,每三个序号为一组组成一个三角形,这里的顺序决定了Mesh的法线方向,也即决定了正面,默认情况下shader只会处理正面的渲染。 可以使用右手来判断法线的方向,四指按序号绕着旋转的方向,拇指指向的就是法线方向,下图的这个序号顺序,法线是指向屏幕外,也就是正面是超向屏幕外的。 在这里插入图片描述

Mesh _eraserMesh = new Mesh(); _eraserMesh.MarkDynamic(); _eraserMesh.vertices = new Vector3[4]; _eraserMesh.SetIndices(new[] { 0, 1, 2, 1, 3, 2 }, MeshTopology.Triangles, 0);

橡皮擦的材质球颜色为白色,

Material _eraserMaterial = new Material(_drawShader); // 白色 _eraserMaterial.color = Color.white;

绘制的时候,需要设置4个顶点的坐标,

// 半边长 const float d = 0.05f; _vertexList.Clear(); _vertexList.Add(p0 + new Vector3(-d, -d, 0)); _vertexList.Add(p0 + new Vector3(+d, -d, 0)); _vertexList.Add(p0 + new Vector3(-d, +d, 0)); _vertexList.Add(p0 + new Vector3(+d, +d, 0)); _eraserMesh.SetVertices(_vertexList);

检测是否橡皮擦的逻辑:

public void OnDrag(BaseEventData baseData) { var data = (PointerEventData)baseData; // ... bool eraser = (data.button == PointerEventData.InputButton.Right); eraser |= Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); // ... } 8、重置图片

画的过程中,想要重新画,按C键即可。 在这里插入图片描述 重置图片用的是Graphics的Blit方法。 在这里插入图片描述 这个方法的作用就是使用着色器将源纹理复制到目标渲染纹理上。 重置逻辑如下:

if (Input.GetKeyDown(KeyCode.C)) { Graphics.Blit(_defaultTexture, _sourceTexture); Graphics.Blit(_defaultTexture, _resultTexture); } 9、训练模型下载

需要先下载训练模型,放到StreamingAssets目录中, 在这里插入图片描述 训练模型下载地址: https://raw.githubusercontent.com/affinelayer/pix2pix-tensorflow-models/master/edges2cats_AtoB.pict 初始化的时候,会去读取这个训练模型数据,

var filePath = Path.Combine(Application.streamingAssetsPath, "edges2cats_AtoB.pict"); Dictionary _weightTable = Pix2Pix.WeightReader.ReadFromFile(filePath); Pix2Pix.Generator _generator = new Pix2Pix.Generator(_weightTable);

通过训练模型我们构造了生成器_generator。

10、通过生成器得到图像

通过Start方法传入我们画的图像,通过GetResult方法得到生成的图像。

// Generator.cs 生成器 public void Start(Texture input) { Image.ConvertToTensor(input, _temp1); _progress = 0; } public void GetResult(RenderTexture output) { Image.ConvertFromTensor(_temp1, output); }

如果继续往Image里面走,就是GpuBackend模块, 在这里插入图片描述 对pix2pix感兴趣的同学,可以访问它的开源项目: 地址:https://github.com/affinelayer/pix2pix-tensorflow

六、结束语

就先写这么多吧~ 喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~

推荐阅读: 《[Unity 3D] 权游红袍女在火中看到了什么,我看到了…(粒子系统 | 火焰特效 | ParticleSystem | 手把手制作)》 《[Unity 2D] 重温红白机经典FC游戏,顺便教你快速搭建2D游戏关卡(Tilemap | 场景 | 地图)》 《520程序员的浪漫,给CSDN近两万的粉丝比心心(python爬虫 | Unity循环复用列表 | 头像加载与缓存)》 《ShaderGraph使用教程与各种特效案例:Unity2020(持续更新)》 《Unity使用ShaderGraph配合粒子系统,制作子弹拖尾特效(Fate/stay night金闪闪的大招效果)》 《使用Unity ShaderGraph实现在模型上涂鸦的效果,那么,纹个手吧》 《学Unity的猫——第十五章:Unity粒子系统ParticleSystem,下雪啦下雪啦》 《Unity实现水果忍者切水果的刀痕效果教程(两种实现方式:TrailRenderer、LineRenderer)》 《Unity流体模拟,支持粒子系统,支持流体碰撞交互(Obi Fluid插件使用教程)》 《玩转贝塞尔曲线,教你在Unity中画Bezier贝塞尔曲线(二阶、三阶),手把手教你推导公式》 《Unity UGUI制作雷达图/天赋图/属性图/能力图,因为太怕痛就全点了防御力》 《使用Unity ShaderGraph实现刮刮乐的刮卡剔除效果,感受一下刮中500万的时刻》 《Unity后处理(图像优化特效技术),实现影视级别的镜头效果,辅助标签:PostProcessing》

七、附录,《睡吧孩子》画画过程记录 1、线稿过程

在这里插入图片描述

2、成稿

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭