顶点动画纹理在blender中的实现
顶点动画是一个很高效地实现动画手段,现在说下如何在blender中使用插件,在unity实现顶点动画。
blender中的插件代码
bl_info = {
"name": "VAT_Tool",
"author": "Wang",
"version": (0, 1),
"blender": (3, 1, 0),
"location": "",
"description": "VAT tool",
"warning": "",
"doc_url": "",
"category": "Generic",
}
import copy
import json
import os
from itertools import chain
import bmesh
import bpy
from bpy.types import AddonPreferences, Operator, Panel, PropertyGroup
class Anim_property(bpy.types.PropertyGroup):
name:bpy.props.StringProperty(default="name",description='name')
is_recode:bpy.props.BoolProperty(default=False,description='is need recoded')
class VAT_property(bpy.types.PropertyGroup):
go_name:bpy.props.StringProperty(default="Armature",description='最上层父物体的名字')
mesh_name:bpy.props.StringProperty(default="elf",description='mesh物体的名字')
x_min:bpy.props.FloatProperty(default=-4,description ="min x")
x_max:bpy.props.FloatProperty(default=4,description ="max x")
y_min:bpy.props.FloatProperty(default=-4,description ="min y")
y_max:bpy.props.FloatProperty(default=4,description ="max y")
z_min:bpy.props.FloatProperty(default=-4,description ="min z")
z_max:bpy.props.FloatProperty(default=4,description ="max z")
size:bpy.props.IntProperty(default=512,description ="vat size")
out_dir:bpy.props.StringProperty(default="C:\\Users\\vert_anim_out",description='输出文件夹')
class OT_VAT__Bake(Operator):
bl_idname = "vat.vat_bake"
bl_label = "bake vertex animation"
bl_options = {
'REGISTER', 'UNDO'}
def save_image_as(self ,image: bpy.types.Image, path: str=bpy.app.tempdir, file_format: str='PNG', color: str='RGBA', color_depth: str='16', compression: int=0):
scene = bpy.data.scenes.new("Temp")
# use docs.blender.org/api/current/bpy.types.ImageFormatSettings.html for more properties
settings = scene.render.image_settings
settings.file_format = file_format # Options: 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW', 'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF', 'WEBP'
settings.color_mode = color # Options: 'BW', 'RGB', 'RGBA' (depends on file_format)
settings.color_depth = color_depth # Options: '8', '10', '12', '16', '32' (depends on file_format)
settings.compression = compression # Range: 0 - 100
image.save_render(path, scene)
bpy.data.scenes.remove(scene)
def execute(self, context):
scene = context.scene
bpy.context.scene.render.image_settings.file_format = "PNG" # Options: 'BMP', 'IRIS', 'PNG', 'JPEG', 'JPEG2000', 'TARGA', 'TARGA_RAW', 'CINEON', 'DPX', 'OPEN_EXR_MULTILAYER', 'OPEN_EXR', 'HDR', 'TIFF', 'WEBP'
bpy.context.scene.render.image_settings.color_mode = 'RGBA' # Options: 'BW', 'RGB', 'RGBA' (depends on file_format)
bpy.context.scene.render.image_settings.color_depth = '16' # Options: '8', '10', '12', '16', '32' (depends on file_format)
bpy.context.scene.render.image_settings.compression = 0 # Range: 0 - 100
vat_property = scene.vat_property
anims_settings = scene.anims_settings
anims_settings_map = {
}
for item in anims_settings:
anims_settings_map[item.name] = item.is_recode
x_range = vat_property.x_max - vat_property.x_min
y_range = vat_property.y_max - vat_property.y_min
z_range = vat_property.z_max - vat_property.z_min
max_verts_count = vat_property.size
self.left_count = max_verts_count
armature = bpy.context.scene.objects[vat_property.go_name]
#ob = bpy.context.active_object
ob = bpy.context.scene.objects[vat_property.mesh_name]
me = ob.data
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.uv_texture_add()
bm = bmesh.from_edit_mesh(me)
uv_layer = bm.loops.layers.uv.verify()
#t = ob.modifiers["Armature"]
#t.use_vertex_groups = False
# t.ratio =(1.0*max_verts_count)/verts_len
# bbbb = []
# for i in range(len(me.vertices)):
# bbbb.append(copy.deepcopy(me.vertices[i].co))
# t.use_vertex_groups = True
# me = ob.data
# bpy.ops.object.mode_set(mode='OBJECT')
# t = ob.modifiers.new(name="Remesh", type='DECIMATE')
# t.ratio =(1.0*max_verts_count)/verts_len
# bpy.ops.object.mode_set(mode='EDIT')
# bm = bmesh.from_edit_mesh(me)
#bpy.ops.object.mode_set(mode='OBJECT')
file_dir = vat_property.out_dir
if not os.path.exists(file_dir) :
os.makedirs(file_dir)
# adjust uv coordinates
for face in bm.faces:
for loop in face.loops:
loop_uv = loop[uv_layer]
# use xy position of the vertex as a uv coordinate
loop_uv.uv.x = loop.vert.index/max_verts_count + (1.0/(2*max_verts_count))
loop_uv.uv.y = 0.5
bmesh.update_edit_mesh(me)
verts_count = len(bm.verts)
print(verts_count)
acts_frame_count = {
}
acts_name = {
}
acts_recode_info = {
}
json_info = {
}
json_info['size'] = max_verts_count
json_info['info'] = acts_recode_info
for act in bpy.data.actions:
frame_count = act.frame_range[1] - act.frame_range[0] + 1.0
frame_count = frame_count
if frame_count > max_verts_count :
continue
#name = act.name.split('|')[-1]
name = act.name
print(name)
acts_name[name] = act.name
acts_frame_count[name] = frame_count
acts_sort = []
for name in acts_frame_count:
index = len(acts_sort)
for i in range(0, len(acts_sort)):
if acts_frame_count[acts_sort[i]] < acts_frame_count[name] :
index = i
break
acts_sort.insert(index ,name)
acts_is_recode = []
for i in range(0 ,len(acts_sort)):
is_recode = anims_settings_map[acts_sort[i]]
if is_recode:
acts_is_recode.append(False)
else:
acts_is_recode.append(True)
img_index = 0
def isCompleted():
for is_recode in acts_is_recode:
if not is_recode :
return False
return True
bpy.ops.image.new
while not isCompleted():
oldImage = bpy.data.images.get("POS"+str(img_index), None)
oldNormalImage = bpy.data.images.get("NORMAL"+str(img_index), None)
if oldImage:
bpy.data.images.remove(oldImage)
if oldNormalImage:
bpy.data.images.remove(oldNormalImage)
newImage = bpy.data.images.new("POS"+str(img_index), max_verts_count, max_verts_count, alpha=True)
newNormalImage = bpy.data.images.new("NORMAL"+str(img_index), max_verts_count, max_verts_count, alpha=True)
left_start_index = 0
self.left_count = max_verts_count
img_pixels = []
img_normals = []
def bake_vat_img(self ,i):
print(acts_name[ acts_sort[i] ])
action = bpy.data.actions[acts_name[ acts_sort[i] ]]
#bpy.context.object.animation_data.action = action
armature.animation_data.action = action
acts_is_recode[i] = True
start = int(action.frame_range[0])
end = int(action.frame_range[1])
bpy.context.scene.frame_start = start
bpy.context.scene.frame_end = end
cur_frame_count = end - start + 1
self.left_count = self.left_count - cur_frame_count
acts_recode_info[acts_sort[i]] = [max_verts_count - (self.left_count + cur_frame_count) ,cur_frame_count]
print("left_count : " + str(self.left_count))
print([x_range, y_range, z_range])
for x in range(start ,end + 1) :
bpy.context.scene.frame_set(x)
ob.update_from_editmode()
depsgraph = bpy.context.evaluated_depsgraph_get()
ob_eval = ob.evaluated_get(depsgraph)
me = ob_eval.to_mesh()
for a in range(max_verts_count):
if a >= verts_count :
img_pixels.extend([0.0, 0.0, 0.0, 0.0])
img_normals.extend([0.0, 0.0, 0.0, 0.0])
else :
red = ((me.vertices[a].co.x)-vat_property.x_min) / x_range
green = ((me.vertices[a].co.y)-vat_property.y_min) / y_range
blue = ((me.vertices[a].co.z) - vat_property.z_min) / z_range
alpha = 1.0
img_pixels.extend([red, green, blue, alpha])
red = (me.vertices[a].normal.x)*0.5 +0.5
green = (me.vertices[a].normal.y)*0.5 +0.5
blue = (me.vertices[a].normal.z)*0.5 +0.5
alpha = 1.0
img_normals.extend([red, green, blue, alpha])
ob_eval.to_mesh_clear()
def recode(self):
for i in range(0 ,len(acts_sort)):
if not acts_is_recode[i] and acts_frame_count[acts_sort[i]] < self.left_count :
bake_vat_img(self ,i)
return True
return False
while recode(self):
print("------")
newImage.pixels = img_pixels
newImage.update()
# newImage.file_format = 'PNG'
# newImage.filepath_raw = file_dir + '\\POS_'+str(img_index) + ".PNG"
# newImage.save()
newImage.save_render( file_dir + '\\POS'+str(img_index) + ".PNG",scene=bpy.context.scene)
#self.save_image_as(newImage ,file_dir + '\\POS_'+str(img_index) + ".PNG")
newNormalImage.pixels = img_normals
newNormalImage.update()
newNormalImage.save_render( file_dir + '\\NORMAL'+str(img_index) + ".PNG",scene=bpy.context.scene)
# newNormalImage.file_format = 'PNG'
# newNormalImage.filepath_raw = file_dir + '\\NORMAL_'+str(img_index) + ".PNG"
# newNormalImage.save()
#self.save_image_as(newNormalImage ,file_dir + '\\NORMAL_'+str(img_index) + ".PNG")
img_index = img_index + 1
f = open(file_dir + '\\anim_info.txt', 'w', encoding='utf-8')
f.write(json.dumps(json_info))
f.close()
print("----------------FINISHED")
bpy.ops.object.mode_set(mode='OBJECT')
return {
'FINISHED'}
class OT_VAT_Anim(Operator):
bl_idname = "vat.vat_anim"
bl_label = "select vertex animation"
bl_options = {
'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
anims_settings = scene.anims_settings
for act in bpy.data.actions:
item = anims_settings.add()
item.name = act.name
item.is_recode = True
return {
'FINISHED'}
class VAT_PT_Panel(bpy.types.Panel):
"""Creates a Panel in the scene context of the properties editor"""
bl_label="VAT Panel"
bl_idname="VAT_PT_Panel"
bl_space_type="VIEW_3D"
bl_region_type="UI"
bl_category="VAT_Tool"
def draw(self, context):
layout = self.layout
scene = context.scene
vat_property = scene.vat_property
anims_settings = scene.anims_settings
layout.label(text="VAT 工具")
row = layout.row()
row.scale_y = 3.0
layout.label(text="物体的name")
layout.prop(vat_property,'go_name',text="name")
layout.label(text="mesh物体的name")
layout.prop(vat_property,'mesh_name',text="name")
layout.label(text="VAT的size")
layout.prop(vat_property,'size',text="size")
layout.label(text="选择动画")
row = layout.row()
row.operator("vat.vat_anim")
for item in anims_settings :
layout.prop(item,'is_recode',text=item.name)
layout.label(text="X坐标范围")
row = layout.row()
row.prop(vat_property, "x_min" ,text = "min X")
row.prop(vat_property, "x_max" ,text = "max X")
layout.label(text="y坐标范围")
row = layout.row()
row.prop(vat_property, "y_min" ,text = "min Y")
row.prop(vat_property, "y_max" ,text = "max Y")
layout.label(text="z坐标范围")
row = layout.row()
row.prop(vat_property, "z_min" ,text = "min Z")
row.prop(vat_property, "z_max" ,text = "max Z")
layout.label(text="输出文件夹")
layout.prop(vat_property,'out_dir',text="dir")
layout.label(text="开始生成VAT")
row = layout.row()
row.scale_y = 3.0
row.operator("vat.vat_bake")
def register():
bpy.utils.register_class(VAT_property)
bpy.utils.register_class(Anim_property)
bpy.types.Scene.vat_property = bpy.props.PointerProperty(type=VAT_property)
bpy.types.Scene.anims_settings = bpy.props.CollectionProperty(type=Anim_property)
bpy.utils.register_class(OT_VAT__Bake)
bpy.utils.register_class(OT_VAT_Anim)
bpy.utils.register_class(VAT_PT_Panel)
def unregister():
bpy.utils.unregister_class(OT_VAT__Bake)
bpy.utils.unregister_class(OT_VAT_Anim)
bpy.utils.unregister_class(VAT_PT_Panel)
if __name__ == "__main__":
register()
点击启用后,会出现面板。
其中name表示选中物体的名字。mesh name表示mesh的名字,然后勾选要烘培的动画名称。
点击bake按钮。会在输出文件夹生成如下文件:
这里是unity中的C#代码:
using System.Collections.Generic;
using System.IO;
using LitJson;
using UnityEditor;
using UnityEngine;
namespace Game.VAT
{
[CreateAssetMenu(fileName ="VAT_Info",menuName ="ScriptableObject/NewTab",order = 1 )]
public class VAT_Info : ScriptableObject
{
public List<string> AnimNames;
public List<Vector2> AnimInfos;
public int VAT_Size;
public int RunAnimIndex;
public int AtkAnimIndex;
public int DeathAnimIndex;
}
//++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++编辑器代码
#if UNITY_EDITOR
[CustomEditor(typeof(VAT_Info))]
public class VertsAnimationMonoBehaviourEditor : Editor
{
VAT_Info vertsAnimation;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("载入动画json信息", GUILayout.Width(200f)))
{
vertsAnimation = (VAT_Info) target;
if (vertsAnimation.AnimInfos != null)
{
vertsAnimation.AnimInfos.Clear();
}
else
{
vertsAnimation.AnimInfos = new List<Vector2>();
}
if (vertsAnimation.AnimNames != null)
{
vertsAnimation.AnimNames.Clear();
}
else
{
vertsAnimation.AnimNames = new List<string>();
}
string jsonPath = EditorUtility.OpenFilePanel("载入动画json信息", Application.dataPath, "txt");
string jsonStr= File.ReadAllText(jsonPath);
JsonData jsonObject = JsonMapper.ToObject(jsonStr);
vertsAnimation.VAT_Size = (int)jsonObject["size"];
JsonData info = jsonObject["info"];
foreach(string key in info.Keys)
{
Vector2 startIndex_count = Vector2.zero;
startIndex_count.x = (int) info[key][0] + 0.5f;
startIndex_count.y = (int) info[key][1] - 1.0f;
vertsAnimation.AnimNames.Add(key);
vertsAnimation.AnimInfos.Add(startIndex_count);
}
EditorUtility.SetDirty(vertsAnimation);
}
}
}
#endif
//------------------------------------
//------------------------------------
}
创建一个vat的scriptableobject资产,
点击载入json信息,选择之前生成的json文件。
附上shader代码:
Shader "Unlit/VertAnimCharacter"
{
Properties
{
_BaseMap ("Base Texture",2D) = "white"{
}
_BaseColor("Base Color",Color)=(1,1,1,1)
_AnimMap ("VAT 位置图",2D) = "black"{
}
_NormalMap("VAT 法线图",2D) = "black"{
}
_PlayPos("播放位置" ,Range(0.01,1)) = 0.1
_RimLightWidth("_OutLineWidth" ,Range(0,1)) = 0.2
[Toggle]_IsSpecular("是否开启高光", Float) = 1
[Toggle(_IS_OPEN_SHADOW_ON)]_IS_OPEN_SHADOW ("_IS_OPEN_SHADOW", Float) = 0
[Toggle(_IS_ADDLIGHTS_ON)]_IS_ADDLIGHTS ("_IS_ADDLIGHTS", Float) = 0
[Toggle(_IS_RimLight_ON)]_IS_RimLight ("_IS_RimLight_ON", Float) = 0
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"Queue"="Geometry"
"RenderType"="Opaque"
}
HLSLINCLUDE
#include "../Common/Common.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _AnimMap_ST;
half4 _BaseColor;
half _IsSpecular;
half _RimLightWidth;
CBUFFER_END
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(float, _PlayPos)
UNITY_INSTANCING_BUFFER_END(Props)
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
TEXTURE2D(_AnimMap);
SAMPLER(sampler_AnimMap);
ENDHLSL
Pass
{
Tags{
"LightMode"="UniversalForward"}
Cull Front
HLSLPROGRAM //CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _IS_OPEN_SHADOW_ON
#pragma multi_compile _ _IS_ADDLIGHTS_ON
#pragma multi_compile _ _IS_RimLight_ON
TEXTURE2D(_NormalMap);
struct Attributes
{
float4 positionOS : POSITION;
float4 normalOS : NORMAL;
float2 uv : TEXCOORD;
float2 uv2 : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varings//这就是v2f
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD;
float3 normalWS : NORMAL;
float3 positionWS : TEXCOORD1;
float3 viewDirWS : TEXCOORD2;
#ifdef _IS_RimLight_ON
float3 normalVS : TEXCOORD3;
float4 scrPos : TEXCOORD4;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float CorrectDepth(float rawDepth)
{
float persp = LinearEyeDepth(rawDepth ,_ZBufferParams);
float ortho = (_ProjectionParams.z-_ProjectionParams.y)*(1-rawDepth)+_ProjectionParams.y;
return lerp(persp,ortho,unity_OrthoParams.w);
}
Varings vert(Attributes IN)
{
Varings OUT;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
float playPos =UNITY_ACCESS_INSTANCED_PROP(Props, _PlayPos);
//half3 h = tex2Dlod(_AnimMap, float4(IN.uv2,1,1)).xyz;
half3 h = SAMPLE_TEXTURE2D_LOD(_AnimMap, sampler_AnimMap ,float2(IN.uv2.x ,playPos) ,0.0).rgb;
//half3 h = half3(1.0,1.0,1.0);
h = h*half3(4,4,4) - half3(2,2,0);
half3 n = SAMPLE_TEXTURE2D_LOD(_NormalMap, sampler_AnimMap ,float2(IN.uv2.x ,playPos) ,0.0).rgb;
n = 2 * (n - half3(0.5,0.5,0.5));
//h = h*half3(1,-1,1);
//h = IN.positionOS.xyz + h;
//VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz + h);
VertexPositionInputs positionInputs = GetVertexPositionInputs(h);
VertexNormalInputs normalInputs = GetVertexNormalInputs(n);
OUT.positionCS = positionInputs.positionCS;
OUT.positionWS = positionInputs.positionWS;
//OUT.positionCS = TransformWorldToHClip(OUT.positionWS);
OUT.uv=TRANSFORM_TEX(IN.uv,_BaseMap);
OUT.viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
OUT.normalWS = normalInputs.normalWS;
#ifdef _IS_RimLight_ON
OUT.normalVS = normalize(mul(normalInputs.normalWS, UNITY_MATRIX_IT_MV));
OUT.scrPos = ComputeScreenPos(positionInputs.positionCS);
#endif
return OUT;
}
half4 frag(Varings IN):SV_Target
{
#ifdef _IS_RimLight_ON
float3 nVS = normalize(IN.normalVS);
float d = SampleSceneDepth(IN.scrPos.xy/IN.scrPos.w + nVS.xy * 5 * _RimLightWidth / _ScreenParams.xy);
d = CorrectDepth(d);
#endif
UNITY_SETUP_INSTANCE_ID(IN);
half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
//half3 ddx_positionWS = ddx(IN.positionWS);
//half3 ddy_positionWS = ddy(IN.positionWS);
#ifdef _IS_OPEN_SHADOW_ON
float4 SHADOW_COORDS = TransformWorldToShadowCoord(IN.positionWS);
Light light = GetMainLight(SHADOW_COORDS);
#else
Light light = GetMainLight();
#endif
half3 n = normalize(IN.normalWS);
//half3 n = normalize(-cross(ddx_positionWS ,ddy_positionWS));
half3 v = normalize(IN.viewDirWS);
half3 h = normalize(light.direction + v);
half nl = max(0.0,dot(light.direction ,n));
half nh = max(0.0,dot(h ,n));
#ifdef _IS_OPEN_SHADOW_ON
half atten = step(0.5, light.shadowAttenuation);
#else
half atten = 1.0;
#endif
half3 diffuse = lerp(0.2 ,1,atten) * lerp(0.2*baseMap.xyz ,baseMap.xyz ,nl);
half3 specular = _IsSpecular * atten * light.color * step(0.9,pow(nh ,8));
#ifdef _IS_ADDLIGHTS_ON
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex)
{
Light add_light = GetAdditionalLight(lightIndex, IN.positionWS);
half3 add_h = normalize(add_light.direction + v);
half add_nl = max(0.0,dot(add_light.direction ,n));
half add_nh = max(0.0,dot(add_h ,n));
diffuse += baseMap.xyz * add_nl* add_light.color * add_light.distanceAttenuation;
specular += _IsSpecular * add_light.color * add_light.distanceAttenuation * step(0.9,pow(add_nh ,8));
}
#endif
half3 color = diffuse*_BaseColor.xyz;
#ifdef _IS_RimLight_ON
#if UNITY_REVERSED_Z
float aa = saturate(d - CorrectDepth(IN.scrPos.z/IN.scrPos.w));
#else
float aa = saturate(d - CorrectDepth(IN.scrPos.z/IN.scrPos.w));
#endif
half4 final = lerp( half4(color ,1.0) ,half4(1,0,0,1) ,aa );
#else
half4 final = half4(color ,1.0);
#endif
return final;
}
ENDHLSL //ENDCG
}
pass {
Tags{
"LightMode" = "ShadowCaster" }
Cull OFF
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _IS_OPEN_SHADOW_ON
struct Attributes
{
float4 vertex : POSITION;
float2 uv : TEXCOORD;
float2 uv2 : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varings
{
float4 pos : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varings vert(Attributes v)
{
Varings o = (Varings)0;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
float playPos =UNITY_ACCESS_INSTANCED_PROP(Props, _PlayPos);
half3 h = SAMPLE_TEXTURE2D_LOD(_AnimMap, sampler_AnimMap ,float2(v.uv2.x ,playPos) ,0.0).rgb;
h = h*half3(4,4,4) - half3(2,2,0);
o.pos = mul(UNITY_MATRIX_MVP,float4(h ,1.0));
//o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
return o;
}
float4 frag(Varings i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
return half4(0.0,0.0,0.0,1.0);
}
ENDHLSL
}
}
}