Unity使用LineRender实现签名效果
本文为大家分享了unity制作签名功能的具体代码,供大家参考,具体内容如下
前言:项目中需要做一个签名的功能,同时需要两个两个屏幕进行显示,但是都是在ui上,从网上查了大量资料。
找到两种方法:
1、修改图片像素点 但是是马赛克效果,不满足需求
2、使用linerenderer 的3d签名制作出2d效果
改像素点:
先上代码
using system.collections; using system.collections.generic; using unityengine; using unityengine.ui; public class test : objbase { public gameobject m_obj; private texture2d m_tex; public color m_color; public int size = 3; private color[] m_texturecolorsstart; public rawimage showimg; void start() { if (display.displays.length > 1) display.displays[1].activate(); if (display.displays.length > 2) display.displays[2].activate(); m_tex = m_obj.getcomponent<meshrenderer>().material.maintexture as texture2d; //从纹理中获取像素颜色 m_texturecolorsstart = m_tex.getpixels(); debug.log(m_tex.name); } void update() { //vector3 oldpos=vector3.zero; //oldpos = input.mouseposition; //ray ray = uicam.screenpointtoray(input.mouseposition); ray ray = camera.main.screenpointtoray(input.mouseposition); raycasthit hit; if (input.getmousebutton(0)) { // float m_magnitude = (input.mouseposition - oldpos).magnitude; // vector3 dir = input.mouseposition - oldpos; if (physics.raycast(ray, out hit)) { //在碰撞位置处的uv纹理坐标。 vector2 pixeluv = hit.texturecoord; //以像素为单位的纹理宽度 pixeluv.x *= m_tex.width; pixeluv.y *= m_tex.height; //贴图uv坐标以右上角为原点 for (float i = pixeluv.x - 1; i < pixeluv.x + size; i++) { for (float j = pixeluv.y - 1; j < pixeluv.y + size; j++) { m_tex.setpixel((int)i, (int)j, m_color); } } debug.log(pixeluv); m_tex.apply(); showimg.texture = m_tex; } } if (input.getkeydown(keycode.space)) { //还原 m_tex.setpixels(m_texturecolorsstart); m_tex.apply(); } //在处理鼠标按下的记录下位置,抬起的时候记录下位置,取2个位置中间的位置发射射线 //if (input.getmousebuttondown(0)) //{ //} //if (input.getmousebuttonup(0)) //{ //} } public void onclick() { showimg.texture = m_tex; } }
using system.collections; using system.collections.generic; using unityengine; public class objbase : monobehaviour { public bool isshow { get { return gameobject.activeself; } } // use this for initialization void start() { } /// <summary> /// 显示 /// </summary> /// <param name="parameter"></param> public virtual void show(object parameter = null) { gameobject.setactive(true); } /// <summary> /// 隐藏 /// </summary> /// <param name="parameter"></param> public virtual void hide(object parameter = null) { gameobject.setactive(false); } }
test脚本是用来修改像素点的,objbase只是一个根父类,控制显示和隐藏。
测试场景用的quad,通过读取他的maintexture对应的像素,进行修改,ui中的话就是将一张图片转成texture2d形式,通过读取像素点,进行修改即可,同时还可以实现同步效果。
项目中的hierarchy窗口设置:
项目需求:使用了两个画布,maincamera照射quad,两个ui相机分别照射两个画布,画布的render mode设置为screen space -camera格式。gameobject挂载脚本,quad用来修改其上的图片的像素点。
效果图:
使用linerenderer 3d划线方法实现2d签名效果:
先上代码:
using system.collections; using system.collections.generic; using unityengine; using unityengine.ui; using system.text; using system.io; using unityengine.eventsystems; public class test5 : monobehaviour { public gameobject drawobj; private bool begindraw; private gameobject obj; public transform parent; public rawimage rawimg; public camera uicam; public camera main;//主相机和ui相机共同照射到的地方进行截图 color[] colors; texture2d mytexture2d; public rawimage photo; public rawimage showimg; [serializefield] private string _name; public recttransform canvas1; public void savefile() { camera maincam; gameobject cam = camera.main.gameobject; if (cam) { maincam = cam.getcomponent<camera>(); } else { return; } rendertexture rendertex; rendertex = new rendertexture(screen.width, screen.height, 24); maincam.targettexture = rendertex; maincam.render(); mytexture2d = new texture2d(rendertex.width, rendertex.height); rendertexture.active = rendertex; mytexture2d.readpixels(new rect(0, 0, rendertex.width, rendertex.height), 0, 0); mytexture2d.apply(); byte[] bytes = mytexture2d.encodetojpg(); mytexture2d.compress(true); mytexture2d.apply(); rendertexture.active = null; file.writeallbytes(application.datapath + "/streamingassets/texturetemp.png", bytes); maincam.targettexture = null; gameobject.destroy(rendertex); } public void onclick() { main.rect = new rect(0, 0, 1, 1); capturecamera( main,new rect(screen.width * 0f, screen.height * 0f, screen.width * 1f, screen.height * 1f)); } /// <summary> /// 对相机截图。 /// </summary> /// <returns>the screenshot2.</returns> /// <param name="camera">camera.要被截屏的相机</param> /// <param name="rect">rect.截屏的区域</param> texture2d capturecamera(camera camera,rect rect) { // 创建一个rendertexture对象 rendertexture rt = new rendertexture((int)rect.width, (int)rect.height, 0); // 临时设置相关相机的targettexture为rt, 并手动渲染相关相机 camera.targettexture = rt; camera.render(); //ps: --- 如果这样加上第二个相机,可以实现只截图某几个指定的相机一起看到的图像。 //camera2.targettexture = rt; // camera2.render(); //ps: ------------------------------------------------------------------- // 激活这个rt, 并从中中读取像素。 rendertexture.active = rt; texture2d screenshot = new texture2d((int)rect.width, (int)rect.height, textureformat.rgb24, false); screenshot.readpixels(rect, 0, 0);// 注:这个时候,它是从rendertexture.active中读取像素 screenshot.apply(); // 重置相关参数,以使用camera继续在屏幕上显示 camera.targettexture = null; // camera2.targettexture = null; //ps: camera2.targettexture = null; rendertexture.active = null; // jc: added to avoid errors gameobject.destroy(rt); // 最后将这些纹理数据,成一个png图片文件 byte[] bytes = screenshot.encodetopng(); string filename = application.datapath + string.format("/screenshot_{0}.png", _name); system.io.file.writeallbytes(filename, bytes); debug.log(string.format("截屏了一张照片: {0}", filename)); showimg.texture = screenshot; main.rect = new rect(0.25f, 0.35f, 0.5f, 0.5f); return screenshot; } void start () { if (display.displays.length > 1) display.displays[1].activate(); if (display.displays.length > 2) display.displays[2].activate(); } // update is called once per frame void update () { if (input.getmousebuttondown(0)) { begindraw = true; obj = instantiate(drawobj) as gameobject; obj.transform.parent = parent; } if (input.getmousebuttonup(0)) { begindraw = false; } if (begindraw) { vector3 position = new vector3(input.mouseposition.x, input.mouseposition.y, 10f); position = camera.main.screentoworldpoint(position); //vector3 localpoint; //if(recttransformutility.screenpointtoworldpointinrectangle(canvas1, position, null, out localpoint)) //{ // position = localpoint; //} drawtext dt = obj.getcomponent<drawtext>(); dt.points.add(position); dt.draw(); dt.line.startcolor = color.yellow; dt.line.endcolor = color.yellow; dt.line.startwidth = 0.03f; dt.line.endwidth = 0.03f; } } }
test5是划线和截取签名的操作,绑定在空物体上,onclick函数绑定在按钮上
line:制作签名预制体
using system.collections; using system.collections.generic; using unityengine; public class drawtext : monobehaviour { public list<vector3> points = new list<vector3>(); public linerenderer line; private void awake() { line = getcomponent<linerenderer>(); } public void draw() { line.positioncount = points.count; for (int i = 0; i < points.count; i++) { line.setposition(i, points[i]); line.startwidth =2f; line.endwidth =2f; } } // use this for initialization void start () { } // update is called once per frame void update () { } }
draw text脚本挂在预制体line上,line 添加linerenderer组件,同时material中加入自己创建的材质球
项目需求:hierarchy窗口设置
和上面一种方法一样,也是两个画布,两个ui相机,同时需要一个maincamera
parent为空物体,用来作为根节点,将签名时实时生成的预制体放在其下面,作为子节点,方便后面进行销毁,重新签名。
重点:
第二种方法使用的是特定相机照射画面进行截图,test5中的capturecamera方法就是截取主相机照射到的画面。由于签名不能进行全屏进行截图,只能部分截图,类似相面的画面
下面会有一些常规的功能按钮,重新签名,保存签名等等操作,这些操作就是在ui上进行签名。
所以,通过修改maincamera的viewport rect窗口来进行截图,同时能够实现正常的签名操作。
maincamera的viewport rect设置:
运行刚开始:
通过设置这个属性,可以使签名界面呈现上一个图的效果,前面是ui层,后面是3d层。
然而在截屏图的时候如果始终保持viewport rect是上面的设置,则截图的时候仍把周围的黑色部分也截取出来,刚开始以为特定相机照射截图只截取viewport rect中的图像,后来测试是周围的所有黑色部分也截取了,这样就不满足要求。
所以,在代码中签字 的时候保持上面的设置,截图之前main.rect = new rect(0, 0, 1, 1);设置成全屏,截好之后重新回复成原来的设置 main.rect = new rect(0.25f, 0.35f, 0.5f, 0.5f);,截图完成之后将签名图片赋值给第二个屏幕画布中的rawimage进行展示。
达到效果。结合ui实际签名过程中
中间的白色部分,通过设置maincamera中的camera组件中的background(设置为白色)以及天空盒(windows->lighting->settings->scene->skybox material设置为空),设置为需要的颜色。ui制作的时候需要签名的部分制作成透明的即可。
效果图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。