Unity3d Mobile platform to achieve a large-scale (in fact, compared to the PC or small-scale) animated character rendering scheme---absolute original solution ...

Source: Internet
Author: User
Tags mul

Mobile phone hardware limitations, many PC rendering optimization technology is no way to directly take over the use of. At present, some games in order to achieve the effectiveness of multi-force combat, a variety of reduction in the number of bones, model surface number of the scheme, but can not escape the Bone animation calculation this link.

At the time of the last company, I thought of a plan, did not expect to finally write, did not expect to finally use the ...

First on the picture, there are 100 soldiers and 10 cute baby scenes, each role of the action is separate control, in the Millet 3 can be 60fps frame rate smooth operation, before also tried, more than 300 role-driven painting can also be about 55 fps run

Let's start by explaining the pros and cons of this package:

Advantages: You can not calculate bone animation, and can play the skeleton animation smoothly (in fact, it is not a skeletal animation), each character action can be controlled separately, the CPU is very small consumption;

Cons: There is no transition in the action switch (when the group is fighting, this does not affect the aesthetics); A little consumption of memory (in fact, it's OK)

To explain the idea of the program:

In order to dispense with skeletal animation, the existing skeletal animation system cannot be used. The alternative scenario is only vertex morphing animations, and the traditional vertex animations are either memory-intensive or not, and unity support is not good.

So since vertex animation can only be selected and in order to reduce memory consumption, you can use the GPU to interpolate shader to achieve the effect of vertex animation playing bone animation.

Shader interpolation is really a lot of scenarios, but the combination of concrete implementation, I chose two ways to achieve a relatively simple scenario:

The 1.vertex shader controls the position of vertices by sampling the UV offset sample texture per frame, and when I have implemented it on the PC, I suddenly find an obvious problem: sampling textures in the vertex shader, the PC-side part of the graphics card is supported, but the mobile phone GPU is not thinking about it.

2. So only the second option: or Vextex shader to play the animation, then there is a question, where does vertex data come from? Unity's mesh structure has many channels, vertices, colors, uv,uv2, normals, tangents. In addition to the color (mainly the accuracy of the problem) and UV (or must give texture coordinates a position) outside the channel I can be used to save vertex data, and then by controlling the point of time to combine the vertex interpolation, but there is a problem to solve, this way a mesh can only interpolate 4 key vertices, What about longer animations, which can be switched by generating multiple mesh in advance.

Animation interception can be achieved through Unity's bakemesh function or artwork, following the code for the combined mesh:

Byte[] Make (mesh mesh1, mesh mesh2, mesh mesh3, mesh mesh4, float cliptimelenghts, float frame2pos, float frame3pos)
{
mesh[] Meshs = new mesh[] {mesh1, MESH2, Mesh3, MESH4};
Vertexanimationresmanager.clipmeshdata meshdata = new Vertexanimationresmanager.clipmeshdata ();
Meshdata.submeshcount = Meshs[0].submeshcount;

int count = meshs[0].vertices. Length;
Vertex
if (meshs[0].vertices! = null && meshs[0].vertices. Length > 0)
{
Meshdata.vertexbuffer = new Float[count * 3];

for (int i = 0; i < meshs[0].vertices. Length; i++)
{
Meshdata.vertexbuffer[i * 3] = meshs[0].vertices[i].x;
Meshdata.vertexbuffer[i * 3 + 1] = MESHS[0].VERTICES[I].Y;
Meshdata.vertexbuffer[i * 3 + 2] = meshs[0].vertices[i].z;
}
}
Uv
if (MESHS[0].UV! = null && meshs[0].uv. Length > 0)
{
Meshdata.uvbuffer = new Float[count * 2];

for (int i = 0; i < meshs[0].vertices. Length; i++)
{
Meshdata.uvbuffer[i * 2] = meshs[0].uv[i].x;
Meshdata.uvbuffer[i * 2 + 1] = MESHS[0].UV[I].Y;
}

GCHandle Versrchand = GCHandle.Alloc (Meshs[0].uv, gchandletype.pinned);
Marshal.Copy (Versrchand.addrofpinnedobject (), Meshdata.uvbuffer, 0, meshData.uvBuffer.Length);
Versrchand.free ();
}

Normals are used here to save the vertex information of the second frame of animation
if (meshs[1].vertices! = null && meshs[1].vertices. Length > 0)
{
Meshdata.normalbuffer = new Float[count * 3];

for (int i = 0; i < meshs[0].vertices. Length; i++)
{
Meshdata.normalbuffer[i * 3] = meshs[1].vertices[i].x;
Meshdata.normalbuffer[i * 3 + 1] = MESHS[1].VERTICES[I].Y;
Meshdata.normalbuffer[i * 3 + 2] = meshs[1].vertices[i].z;
}
}

Tangent is used here to save the vertex information of the animated third frame
if (meshs[2].vertices! = null && meshs[2].vertices. Length > 0)
{
Meshdata.tangentbuffer = new Float[count * 4];

for (int i = 0; i < meshs[0].vertices. Length; i++)
{
Meshdata.tangentbuffer[i * 4] = meshs[2].vertices[i].x;
Meshdata.tangentbuffer[i * 4 + 1] = MESHS[2].VERTICES[I].Y;
Meshdata.tangentbuffer[i * 4 + 2] = meshs[2].vertices[i].z;
Meshdata.tangentbuffer[i * 4 + 3] = meshs[3].vertices[i].x;
}
}

UV2 the vertex yz coordinate x coordinate used to save the fourth key frame rate is stored by the W channel of the Tangent
if (meshs[3].vertices! = null && meshs[3].vertices. Length > 0)
{
Meshdata.uv2buffer = new Float[count * 2];

for (int i = 0; i < meshs[0].vertices. Length; i++)
{
Meshdata.uv2buffer[i * 2] = MESHS[3].VERTICES[I].Y;
Meshdata.uv2buffer[i * 2 + 1] = meshs[3].vertices[i].z;
}
}

The color is used to store the 5th vertex information
if (meshs[indexs[4]].vertices! = null && meshs[indexs[4]].vertices. Length > 0)
//{
Color channels appear to have no negative numbers, and range from 0 to 1 all you need to map the model vertices to [0,1] and the mapping range to [ -1,1]

Meshdata.colorbuffer = new Float[count * 4];
for (int i = 0; i < meshs[indexs[4]].vertices. Length; i++)
// {
Meshdata.colorbuffer[i * 4] = (meshs[indexs[4]].vertices[i].x * 0.5f) + 0.5f;
Meshdata.colorbuffer[i * 4 + 1] = (MESHS[INDEXS[4]].VERTICES[I].Y * 0.5f) + 0.5f;
Meshdata.colorbuffer[i * 4 + 2] = (MESHS[INDEXS[4]].VERTICES[I].Z * 0.5f) + 0.5f;
// }
//}


Count = 0;
int len = 0;
Meshdata.submeshtrianglelens = new Int[meshdata.submeshcount];
for (int i = 0; i < Meshdata.submeshcount; i++)
{
Len = meshs[0]. Gettriangles (i). Length;
Count + = Len;
Meshdata.submeshtrianglelens[i] = len;
}

Meshdata.trianglebuffer = new Int[count];

len = 0;
for (int i = 0; i < Meshdata.submeshcount; i++)
{
Meshs[0]. Gettriangles (i). CopyTo (Meshdata.trianglebuffer, Len);
Len + = Meshdata.submeshtrianglelens[i];
}

Bytebuffer bbuffer = new Bytebuffer ();
Bbuffer. Writefloat (cliptimelenghts);
Bbuffer. Writefloat (Frame2pos);
Bbuffer. Writefloat (Frame3pos);
Bbuffer. Writeint (Meshs[0].submeshcount);

for (int i=0;i<meshdata.submeshtrianglelens.length;i++)
{
Bbuffer. Writeint (Meshdata.submeshtrianglelens[i]);
}

Bbuffer. Writeint (meshData.triangleBuffer.Length);
for (int i = 0; i < meshData.triangleBuffer.Length; i++)
{
Bbuffer. Writeint (Meshdata.trianglebuffer[i]);
}

Bbuffer. Writeint (meshData.vertexBuffer.Length);
for (int i = 0; i < meshData.vertexBuffer.Length; i++)
{
Bbuffer. Writefloat (Meshdata.vertexbuffer[i]);
}

Bbuffer. Writeint (meshData.normalBuffer.Length);
for (int i = 0; i < meshData.normalBuffer.Length; i++)
{
Bbuffer. Writefloat (Meshdata.normalbuffer[i]);
}

Bbuffer. Writeint (meshData.tangentBuffer.Length);
for (int i = 0; i < meshData.tangentBuffer.Length; i++)
{
Bbuffer. Writefloat (Meshdata.tangentbuffer[i]);
}

Bbuffer. Writeint (meshData.uvBuffer.Length);
for (int i = 0; i < meshData.uvBuffer.Length; i++)
{
Bbuffer. Writefloat (Meshdata.uvbuffer[i]);
}

Bbuffer. Writeint (meshData.uv2Buffer.Length);
for (int i = 0; i < meshData.uv2Buffer.Length; i++)
{
Bbuffer. Writefloat (Meshdata.uv2buffer[i]);
}
Return Bbuffer. Tobytes ();
}

After interception, save as your own binary files, run the time to load and parse the code as follows:

public void Addanimationinfo (String aniname, byte[] clipdata)
{
Vertexanimationclipinfo clipinfo = null;

Animationclipinfos.trygetvalue (Aniname, out clipinfo);

if (clipinfo!=null)
{
Debug.logerror ("Animation clip has exits! ");
Return
}

Clipinfo = new Vertexanimationclipinfo ();

Bytebuffer bbuffer = new Bytebuffer (clipdata);
int Count = Bbuffer. ReadInt ();

for (int i = 0; i < Count; i++)
{
Clipmeshdata meshdata = Getmeshdata (bbuffer);
Clipinfo.cliptotaltimelen + = Meshdata.timelenth;
CLIPINFO.CLIPLENGHTS.ADD (Meshdata.timelenth);
CLIPINFO.EVERYCLIPFRAMETIMEPOINTS.ADD (New Vector3 (Meshdata.frame2timepoint, meshdata.frame3timepoint)); , Meshdata.frame4timepoint
CLIPINFO.CLIPMESHS.ADD (Meshdata.genmesh ());
}

Bbuffer. Close ();

Animationclipinfos.add (Aniname, clipinfo);
}

Vertexanimationclipinfo is defined as follows:

[Serializable]
public class Vertexanimationclipinfo
{
public float Cliptotaltimelen = 0;
Public list<mesh> CLIPMESHS = new list<mesh> ();
Public list<vector2> everyclipframetimepoints = new list<vector2> ();
Public list<float> cliplenghts = new list<float> ();
}

Clipmeshdata is defined as follows:

public class Clipmeshdata
{
public float Timelenth;

Frame1timepoint =0 frame4timepoint = 1
public float frame2timepoint = 0.333f;
public float frame3timepoint = 0.666f;
public float frame4timepoint = 0.75f;

public int submeshcount;
Public int[] Submeshtrianglelens;
Public int[] Trianglebuffer;
Public float[] VertexBuffer;
Public float[] Normalbuffer;
Public float[] Tangentbuffer;
Public float[] Uvbuffer;
Public float[] Uv2buffer;
Public float[] Colorbuffer;

Public Mesh Genmesh ()
{
Mesh mesh = new mesh ();

int vertexcount = VERTEXBUFFER.LENGTH/3;

Mesh.submeshcount = Submeshcount;
Vertex
vector3[] Vertexs = new Vector3[vertexcount];
for (int i = 0; i < Vertexcount; i++)
{
Vertexs[i] = new Vector3 (Vertexbuffer[i * 3], Vertexbuffer[i * 3 + 1], Vertexbuffer[i * 3 + 2]);
}
Mesh.vertices = Vertexs;
Uv
vector2[] UV = new Vector2[vertexcount];
for (int i = 0; i < Uvs. Length; i++)
{
Uv[i] = new Vector2 (Uvbuffer[i * 2], Uvbuffer[i * 2 + 1]);
}
MESH.UV = UV;
Uv2
vector2[] Uv2 = new Vector2[vertexcount];
for (int i = 0; i < Uvs. Length; i++)
{
Uv2[i] = new Vector2 (Uv2buffer[i * 2], Uv2buffer[i * 2 + 1]);
}
Mesh.uv2 = Uv2;

Normal
vector3[] normals = new Vector3[vertexcount];
for (int i = 0; i < normals. Length; i++)
{
Normals[i] = new Vector3 (Normalbuffer[i * 3], Normalbuffer[i * 3 + 1], Normalbuffer[i * 3 + 2]);
}
Mesh.normals = normals;

Tangent
var tangents = new Vector4[vertexcount];
for (int i = 0; i < tangents. Length; i++)
{
Tangents[i] = new Vector4 (Tangentbuffer[i * 4], Tangentbuffer[i * 4 + 1], Tangentbuffer[i * 4 + 2], Tangentbuffer[i * 4 + 3]);
}
Mesh.tangents = tangents;
Color
color[] colors = new COLOR[COLORBUFFER.LENGTH/4];
for (int i = 0; i < colors. Length; i++)
//{
Colors[i] = new Vector4 (Colorbuffer[i * 4], Colorbuffer[i * 4 + 1], Colorbuffer[i * 4 + 2], 1);
//}
Mesh.colors = colors;

Triangle
int startIndex = 0;
int bufferlen = 0;

for (int i = 0; i < Submeshcount; i++)
{
Bufferlen = Submeshtrianglelens[i];
if (Bufferlen <= 0) continue;
var triindexbuffer = new Int[bufferlen];
Array.copy (Trianglebuffer, StartIndex, Triindexbuffer, 0, Bufferlen);
Mesh. Settriangles (Triindexbuffer, i);
StartIndex + = Bufferlen;
}
return mesh;
}
}

Play the animation to toggle the corresponding mesh, the following is the GPU interpolation used by the shader code:


Shader "Lxz_test/vertexanimation-nocolorbuf" {
Properties {
_maintex ("Base (RGB)", 2D) = "White" {}
_curtime ("Time", Float) = 0
_frame2time ("Frame2time", Float) = 0.333
_frame3time ("Frame3time", Float) = 0.666
_color ("Maincolor", Color) = (1,1,1,1)
}
Subshader {
Tags {"QUEUE" = "Geometry" "rendertype" = "Opaque"}

Pass {
Blend Srcalpha Oneminussrcalpha
Cgprogram
#pragma vertex vert
#pragma fragment Frag
#include "Unitycg.cginc"


#pragma glsl_no_auto_normalization

Sampler2d _maintex;
float _curtime;
float _frame2time;
float _frame3time;
FLOAT4 _color;

struct AppData {
FLOAT4 vertex:position;
FLOAT3 Vertex1:normal;
FLOAT4 vertex2:tangent;
FLOAT2 texcoord:texcoord0;
FLOAT2 Vertex3:texcoord1;
FLOAT3 Vertex4:color;
};

struct V2F {
FLOAT4 pos:position;
FLOAT2 uv:texcoord0;
};


v2f Vert (AppData v) {
v2f result;

float a = _curtime-_frame2time;
Float B = _curtime-_frame3time;

FLOAT3 VEC;

FLOAT3 vertex3 = FLOAT3 (V.VERTEX2.W,V.VERTEX3.XY);

if (a<0)
VEC = v.vertex.xyz + (v.vertex1-v.vertex.xyz) * _curtime/_frame2time;
else if (a>=0 && b<0)
{
VEC = V.vertex1 + (V.VERTEX2.XYZ-V.VERTEX1) * A/(_frame3time-_frame2time);
}
Else
VEC = v.vertex2.xyz + (vertex3-v.vertex2.xyz) * b/(1-_frame3time);


Result.pos = Mul (UNITY_MATRIX_MVP, FLOAT4 (vec,1));
Result.pos = Mul (UNITY_MATRIX_MVP, FLOAT4 (v.vertex1.xyz,1));
Result.pos = Mul (UNITY_MATRIX_MVP, FLOAT4 (v.vertex2.xyz,1));
Result.pos = Mul (UNITY_MATRIX_MVP, FLOAT4 (vertex3.xyz,1));
Result.pos = Mul (UNITY_MATRIX_MVP, FLOAT4 (vertex4.xyz,1));
RESULT.UV = V.texcoord;

return result;
}

FLOAT4 Frag (v2f i): COLOR
{
FLOAT4 color = tex2d (_maintex, I.UV);
return color *_color;
}

Endcg
}
}
FallBack "Diffuse"
}

If you have a better plan, welcome to the exchange. Not good at writing things, so directly affixed to the code, there is a need to be able to communicate with me continuously ...

Unity3d Mobile platform to achieve a large-scale (in fact, compared to the PC or small-scale) animated character rendering scheme---absolute original solution ...

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.